text:
Guide to Embedding
==================

(in progress... TODO)

Grabbing the Correct Files
==========================

Embedding starts with dropping in the correct files:

### C99

Copy the files into your project:

* `src/sink.h`
* `src/sink.c`

Their location doesn't matter, as long as they're in the same directory.  Add `sink.c` to your
build so it's compiled.

### TypeScript

Copy the files into your project:

* `dist/sink.ts`
* `dist/sink.d.ts` (declaration file, optional)

Import using:

```typescript
import sink = require('./sink.js');
```

### JavaScript

Copy the file into your project:

* `dist/sink.js`

Use in node via:

```js
var sink = require('./sink');
```

Or the browser via [RequireJS](http://requirejs.org/):

```html
<script src="require.js"></script>
<script>
requirejs(['sink'], function(sink){
  ... your code here ...
});
</script>
```

API
===

The API is very similar across the implementations.  The TypeScript API is accessed via
`sink.some_function`, whereas the C99 API is `sink_some_function`.  See the appropriate header files
([`sink.h`](https://github.com/voidqk/sink/blob/master/src/sink.h) and
[`sink.d.ts`](https://github.com/voidqk/sink/blob/master/dist/sink.d.ts)) for the exact
declarations.

The API is in four basic sections:

| Section                     | Description                                           |
|-----------------------------|-------------------------------------------------------|
| [Script API](#script-api)   | Loading a program into memory, compiling if necessary |
| [Context API](#context-api) | Executing a program, pausing/resuming execution       |
| [Standard Library API](#standard-library-api) | Executing commands from the [standard library](https://github.com/voidqk/sink/blob/master/docs/lib.md) inside a context |
| [Misc/Helper Functions](#mischelper-functions) | Assorted functions to make life easier |

Script API
==========

The Script API is used for loading a program into memory.  It can load a script and compile it into
bytecode, or load bytecode directly.  It does not *execute* any code.

| Functions                       | Description                                               |
|---------------------------------|-----------------------------------------------------------|
| [`scr_new`](#scr_new)           | Create a new Script object                                |
| [`scr_addpath`](#scr_addpath)   | Add a search path for including/embedding files           |
| [`scr_incbody`](#scr_incbody)   | Provide a string to be included for a special filename    |
| [`scr_incfile`](#scr_incfile)   | Provide a file to be included for a special filename      |
| [`scr_getfile`](#scr_getfile)   | Get the fully resolved filename of the script             |
| [`scr_getcwd`](#scr_getcwd)     | Get the current working directory of the script           |
| [`scr_geterr`](#scr_geterr)     | Get any error message associated with the script          |
| [`scr_cleanup`](#scr_cleanup)   | Add user-defined objects to be freed when Script is freed |
| [`scr_setuser`](#scr_setuser)   | Set a user-defined value associated with the Script       |
| [`scr_getuser`](#scr_getuser)   | Get a previously set user-defined value                   |
| [`scr_loadfile`](#scr_loadfile) | Load a file through the include system                    |
| [`scr_write`](#scr_write)       | Write file contents into the Script object                |
| [`scr_level`](#scr_level)       | Get the level of nesting for REPLs                        |
| [`scr_dump`](#scr_dump)         | Dump the compiled bytecode                                |
| [`scr_free`](#scr_free)         | Free a Script object                                      |

scr_new
-------

Create a new Script object.

```c
typedef enum {
  SINK_FSTYPE_NONE,
  SINK_FSTYPE_FILE,
  SINK_FSTYPE_DIR
} sink_fstype;

typedef sink_fstype (*sink_fstype_f)(sink_scr scr, const char *file, void *incuser);
typedef bool (*sink_fsread_f)(sink_scr scr, const char *file, void *incuser);

typedef struct {
  sink_fstype_f f_fstype;
  sink_fsread_f f_fsread;
  void *user;
} sink_inc_st;

sink_scr sink_scr_new(sink_inc_st inc, const char *curdir, bool posix, bool repl);
```

```typescript
enum sink.fstype {
  NONE,
  FILE,
  DIR
}

type sink.fstype_f = (scr: sink.scr, file: string, incuser: any) => Promise<sink.fstype>;
type sink.fsread_f = (scr: sink.scr, file: string, incuser: any) => Promise<boolean>;

interface sink.inc_st {
  f_fstype: sink.fstype_f;
  f_fsread: sink.fsread_f;
  user?: any;
}

function sink.scr_new(inc: sink.inc_st, curdir: string | null, posix: boolean, repl: boolean):
  sink.scr;
```

### `inc`

An object that provides functions for the compiler to read files from the filesystem.

When a script uses `include` or `embed`, the compiler will query these functions (using the
[search path](#scr_addpath)) to figure out how to resolve to the correct file, and read the file.

The `f_fstype` function should query the filesystem, and return one of the results: `NONE`
(i.e., `SINK_FSTYPE_NONE` in C, and `sink.fstype.NONE` in TypeScript) for a file that doesn't exist,
`FILE` for a file, and `DIR` for a directory.

The `f_fsread` function should attempt to open the provided file, and write it to the Script object
using [`scr_write`](#scr_write).  It should return `true` if the file was read successfully, and
`false` if the file failed to be read.

See [cmd.c](https://github.com/voidqk/sink/blob/master/src/cmd.c) or
[cmd.ts](https://github.com/voidqk/sink/blob/master/src/cmd.ts) for example implementations of these
functions.  Note that the TypeScript/JavaScript version must return a Promise.

The `user` field is passed through to the `incuser` argument in the functions, at your discretion.

### `curdir`

The current working directory (or `null`/`NULL`).  This must be an absolute path.

This is used when a script includes or embeds a relative path, in order to construct an absolute
path.

### `posix`

If true, then path routines will use POSIX logic.  It will treat `/` as a path seperator, and any
path starting with `/` will be considered an absolute path.

If false, then path routines will use Windows logic.  It will treat `/` and `\` as path seperators,
join paths using `\`, and paths starting with a drive letter or network name will be considered
absolute (i.e., `c:\foo` or `\\host\computer\foo`).

### `repl`

Flag indicating whether the script is a [REPL](https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop).

REPL scripts have slightly different rules during compilation, centered around allowing the user to
re-define symbols.

For example, the following script will result in a compile-time error, but works fine in a REPL
entered one line at a time:

```
var test = 1
var test = 2
say test # => outputs 2 in a REPL
```

Normally, defining something twice will result in a compile-time error (`Cannot redefine "test"`).
In a REPL, re-definitions are allowed.

scr_addpath
-----------

Add a path to the list of search paths during an `include` or `embed` statement.

```c
void sink_scr_addpath(sink_scr scr, const char *path);
```

```typescript
function sink.scr_addpath(scr: sink.scr, path: string): void;
```

If a file with a relative path is included or embedded, sink will iterate over the search paths.
If a search path is relative, it will be combined with the [current working directory](#curdir),
along with the target file, to create an absolute path.

It's recommended to at least add `"."` as a search path so that the current directory will be
searched when including/embedding a file.

### `scr`

The Script object.

### `path`

The path to add to the list of search paths.

scr_incbody
-----------

Define a module in-memory, so that if the end-user performs an `include 'name'`, the provided
source code (`body`) is used as the content.

```c
void sink_scr_incbody(sink_scr scr, const char *name, const char *body);
```

```typescript
function sink.scr_incbody(scr: sink.scr, name: string, body: string): void;
```

This function is used for native libraries to provide a short name that end-users can use to include
the necessary definitions.

For example, suppose you want to build a `shapes` library, that included commands for drawing
circles and rectangles.  You would do:

```typescript
sink.scr_incbody(scr, 'shapes',
  'declare circle     "company.shapes.circle"     \n' +
  'declare rectangle  "company.shapes.rectangle"  \n');
```

Then when the end-user runs `include 'shapes'`, instead of searching for a `'shapes'` file in the
filesystem, it will pull the contents directly from memory.

### `src`

The Script object.

### `name`

The literal name the user must type in the `include` statement to pull in the content.

### `body`

The content of the included library.

scr_incfile
-----------

Define a module that references a file, so that if the end-user performs an `include 'name'`, the
file searched for in the include system is `file`.

```c
void sink_scr_incfile(sink_scr scr, const char *name, const char *file);
```

```typescript
function sink.scr_incfile(scr: sink.scr, name: string, file: string): void;
```

This function has the same basic usage as [`scr_incbody`](#scr_incbody), except instead of providing
the contents directly, the file contents are stored inside an actual file.

This is useful if a library wants to make the definitions available as an external file, and simply
wants to reference the file instead of having the contents directly in memory.

### `scr`

The Script object.

### `name`

The literal name the user must type in the `include` statement to search for the file.

### `file`

The file to actually search for during the include.

scr_getfile
-----------

Get the fully resolved filename of the current script.

```c
const char *sink_scr_getfile(sink_scr scr);
```

```typescript
function sink.scr_getfile(scr: sink.scr): string | null;
```

If you kick-off loading the script via an [`scr_loadfile`](#scr_loadfile) call with a relative file,
then this function will return the fully resolved (absolute path) of the filename that was found and
loaded.

### `scr`

The Script object.

scr_getcwd
----------

Returns the current working directory, as specified earlier from the [`scr_new`](#scr_new) call.

```c
const char *sink_scr_getcwd(sink_scr scr);
```

```typescript
function sink.scr_getcwd(scr: sink.scr): string | null;
```

scr_geterr
----------

Returns the current error message of the script (compile-time error, or `null`/`NULL` for no error).
Only needs to be checked if [`scr_write`](#scr_write) returns `false`.

```c
const char *sink_scr_geterr(sink_scr scr);
```

```typescript
function sink.scr_geterr(scr: sink.scr): string | null;
```

### `scr`

The Script object.

scr_cleanup
-----------

Provide a pointer and free function to be executed when the Script object is freed (C only).

```c
typedef void (*sink_free_f)(void *ptr);

void sink_scr_cleanup(sink_scr scr, void *cuser, sink_free_f f_free);
```

This is useful for libraries that allocate their own objects when loading into a sink Script object.
They can ensure the objects are cleaned up when the Script object is freed.

### `scr`

The Script object.

### `cuser`

The pointer to be freed when the Script object is freed.

### `f_free`

The function used to free `cuser`.

scr_setuser
-----------

Set a user-defined value associated with the Script object.  The value can be retrieved by
[`scr_getuser`](#scr_getuser).

```c
typedef void (*sink_free_f)(void *ptr);

void sink_scr_setuser(sink_scr scr, void *user, sink_free_f f_free);
```

```typescript
function sink.scr_setuser(scr: sink.scr, user: any): void;
```

### `scr`

The Script object.

### `user`

The user-defined value.

### `f_free`

The function used to free the object (C only).  If the user-defined value is overwritten by another
call to `scr_setuser`, or if the Script object is freed, then the current `user` value is freed via
`f_free`.

scr_getuser
-----------

Get the user-defined value associated with the Script object, previously set with
[`scr_setuser`](#scr_setuser).

```c
void *sink_scr_getuser(sink_scr scr);
```

```typescript
function sink.scr_getuser(scr: sink.scr): any;
```

### `scr`

The Script object.

scr_loadfile
------------

Load the script's file from the include filesystem.  Returns `true` if the file was loaded without
error, or `false` if there was an error.  The error is accessible via [`scr_geterr`](#scr_geterr).

```c
bool sink_scr_loadfile(sink_scr scr, const char *file);
```

```typescript
function sink.scr_loadfile(scr: sink.scr, file: string): Promise<boolean>;
```

A script can be loaded directly via [`scr_write`](#scr_write), but the compiler won't have any
filename information.  Instead, using `scr_loadfile` will kick off loading the data using the
include system, so that any errors are correctly identified as coming from the source file.

This will query for the file using [`f_fstype`](#inc), and load the file using [`f_fsread`](#inc),
which should call `scr_write`.

Note that the TypeScript/JavaScript version returns a Promise.

### `scr`

The Script object.

### `file`

The file to load using the include system.

scr_write
---------

Write the contents of a file or buffer into the Script object.  This is how all data is loaded into
the Script.  Returns `true` if the data was processed without error, or `false` if a compile-time
error occurred.  The error can be retrieved via [`scr_geterr`](#scr_geterr).

```c
bool sink_scr_write(sink_scr scr, int size, const uint8_t *bytes);
```

```typescript
function sink.scr_write(scr: sink.scr, bytes: string): Promise<boolean>;
```

Loading source code into the Script object is done with `scr_write`.  The function can be called
directly after creating a Script object, or it can be called indirectly through
[`scr_loadfile`](#scr_loadfile) -- which will eventually call [`f_fsread`](#inc) which should call
`scr_write` to write the data.

Note that the TypeScript/JavaScript version returns a Promise.

### `scr`

The Script object.

### `size`

The size of the `bytes` array (C only).

### `bytes`

The raw bytes to load.  Note: in TypeScript/JavaScript, the string is interpretted as
[`'binary'` encoding](https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings).

scr_level
---------

Returns the current nesting level for a REPL.  A value of `0` indicates no nesting.

```c
int sink_scr_level(sink_scr scr);
```

```typescript
function sink.scr_level(scr: sink.scr): number;
```

When typing in a series of lines in a REPL, the nesting level is how many indentations should be
shown in the prompt.  For example, a session might look like this:

```
 1: if rand.num < 0.5     # nesting level 0
 2... for var v: range 5  # nesting level 1
 3..... say v + 100       # nesting level 2
 4..... end               # nesting level 2
 5... end                 # nesting level 1
100
101
102
103
104
 6:                       # nesting level 0
 ```

The nesting level determines how many periods to draw to indent the line, and when the nesting
level hits `0`, it's known that a full statement has been entered, so the context is ran to catch
up to what's been entered.

### `scr`

The Script object.

scr_dump
--------

Output the compiled bytecode using the supplied `f_dump` function.

```c
typedef size_t (*sink_dump_f)(const void *restrict ptr, size_t size, size_t nitems,
  void *restrict dumpuser);

void sink_scr_dump(sink_scr scr, bool debug, void *user, sink_dump_f f_dump);
```

```typescript
type dump_f = (data: string, dumpuser: any) => void;

function sink.scr_dump(scr: sink.scr, debug: boolean, user: any, f_dump: sink.dump_f): void;
```

### `scr`

The Script object.

### `debug`

Flag whether to output debug information.

Debug information allows for stacktraces to be populated for run-time errors, but increases the size
of the bytecode, and exposes filenames and line numbers to the end-user.

### `user`

User-supplied value that is passed directly to `f_dump` as parameter `dumpuser`.

### `f_dump`

The function called to dump the bytecode.

Note that this function has the same signature as `fwrite` in the C implementation, so that the
file pointer can be passed in as `user`, and `fwrite` can be passed in as `f_dump`, for convenience.
The `size_t` return value is ignored.

The TypeScript implementation is passed string `data` in
[`'binary'` encoding](https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings).

scr_free
--------

Free the Script object (C only).

```c
void sink_scr_free(sink_scr scr);
```

Note this will also free any [`scr_setuser`](#scr_setuser) value, and any
[`scr_cleanup`](#scr_cleanup) pointers that have been associated with the Script object, before
freeing the Script object itself.

### `scr`

The Script object.

Context API
===========

The Context API is used for executing a Script.  It controls how and when to run the bytecode on the
virtual machine.

| Functions                               | Description                                           |
|-----------------------------------------|-------------------------------------------------------|
| [`ctx_new`](#ctx_new)                   | Create a new Context object                           |
| [`ctx_getstatus`](#ctx_getstatus)       | Get the status of the Context's virtual machine       |
| [`ctx_geterr`](#ctx_geterr)             | Get any error message associated with the run-time    |
| [`ctx_native`](#ctx_native)             | Add a native command using a string                   |
| [`ctx_nativehash`](#ctx_nativehash)     | Add a native command using a 64-bit hash              |
| [`ctx_cleanup`](#ctx_cleanup)           | Add user-defined objects to be freed when Context is freed (C only) |
| [`ctx_setuser`](#ctx_setuser)           | Set a user-defined value associated with the Context  |
| [`ctx_getuser`](#ctx_getuser)           | Get a previously set user-defined value               |
| [`ctx_addusertype`](#ctx_addusertype)   | Add a new usertype, for use with list's user data     |
| [`ctx_getuserfree`](#ctx_getuserfree)   | Get a usertype's free function (C only)               |
| [`ctx_getuserhint`](#ctx_getuserhint)   | Get a usertype's hint string                          |
| [`ctx_settimeout`](#ctx_settimeout)     | Set a timeout so the machine pauses itself            |
| [`ctx_gettimeout`](#ctx_gettimeout)     | Get the current timeout value                         |
| [`ctx_consumeticks`](#ctx_consumeticks) | Decrease the current tick counter by an amount        |
| [`ctx_forcetimeout`](#ctx_forcetimeout) | Force a timeout to occur immediately                  |
| [`ctx_run`](#ctx_run)                   | Run the virtual machine                               |
| [`ctx_free`](#ctx_free)                 | Free a Context object (C only)                        |
| [`waiter`](#waiter)                     | Create a Wait object (C only)                         |
| [`done`](#done)                         | Create a Wait object that already has a result (C only) |
| [`then`](#then)                         | Attach a handler to a Wait object (C only)            |
| [`result`](#result)                     | Provide a result for a Wait object (C only)           |

ctx_new
-------

Create a new Context object.

```c
typedef sink_wait (*sink_io_f)(sink_ctx ctx, sink_str str, void *iouser);

typedef struct {
  sink_io_f f_say;
  sink_io_f f_warn;
  sink_io_f f_ask;
  void *user;
} sink_io_st;

sink_ctx sink_ctx_new(sink_scr scr, sink_io_st io);
```

```typescript
type sink.io_f = (ctx: sink.ctx, str: sink.str, iouser: any) => Promise<sink.val>;

interface sink.io_st {
    f_say?: sink.io_f;
    f_warn?: sink.io_f;
    f_ask?: sink.io_f;
    user?: any;
}

function sink.ctx_new(scr: sink.scr, io: sink.io_st): sink.ctx;
```

### `scr`

The Script object to execute in the Context.

### `io`

The input/output functions for the machine.

The `f_say`, `f_warn`, and `f_ask` functions are called when the associated `say`, `warn`, and `ask`
commands are executed in the script.

The C versions use the [`sink_wait`](#waiter) system to deal with asynchronous operations.

The TypeScript versions must return a Promise.

It's recommended that `f_say` and `f_warn` always return `NIL`, but not required.

The `user` field is mapped to `iouser`, and can be used for anything.

ctx_getstatus
-------------

Get the status of the Context's virtual machine.

```c
typedef enum {
  SINK_READY,
  SINK_WAITING,
  SINK_PASSED,
  SINK_FAILED
} sink_status;

sink_status sink_ctx_getstatus(sink_ctx ctx);
```

```typescript
enum sink.status {
  READY,
  WAITING,
  PASSED,
  FAILED
}

function sink.ctx_getstatus(ctx: sink.ctx): sink.status;
```

Returns one of the following values:

* `READY` - The virtual machine is ready for execution via [`ctx_run`](#ctx_run).
* `WAITING` - The virtual machine is waiting for an asynchronous operation to finish.
* `PASSED` - The virtual machine finished executing the script and exited successfully.
* `FAILED` - The virtual machine finished executing the script and exited in failure.

### `ctx`

The Context object.

ctx_geterr
----------

Returns the current run-time error message of the virtual machine, or `null`/`NULL` for no message.

```c
const char *sink_ctx_geterr(sink_ctx ctx);
```

```typescript
function sink.ctx_geterr(ctx: sink.ctx): string | null;
```

Note: This function will return `null` if the script calls `abort` without any parameters, so this
function cannot be used to check the error status of the VM.  Use [`ctx_getstatus`](#ctx_getstatus)
instead.

### `ctx`

The Context object.

ctx_native
----------

Add a native command implementation to the virtual machine using a string identifier.

```c
typedef sink_wait (*sink_native_f)(sink_ctx ctx, int size, const sink_val *args, void *natuser);

void sink_ctx_native(sink_ctx ctx, const char *name, void *natuser, sink_native_f f_native);
```

```typescript
type sink.native_f = (ctx: sink.ctx, args: sink.val[], natuser: any) => Promise<sink.val>;

function sink.ctx_native(ctx: sink.ctx, name: string, natuser: any, f_native: sink.native_f): void;
```

Native commands are stored using 64-bit hashes of string keys.  This function will perform the hash
on `name`, then call [`ctx_nativehash`](#ctx_nativehash) to add the native function to the run-time.

Native commands are accessed in sink via the `declare` statement:

```
# somewhere in sink
declare foo 'company.product.foo'
```

This is wired to a host function via:

```c
sink_wait my_foo(sink_ctx ctx, int size, const sink_val *args, void *natuser){
  // implementation here
}

...

sink_ctx_native(ctx, "company.product.foo", NULL, my_foo);
```

```typescript
function my_foo(ctx: sink.ctx, args: sink.val[], natuser: any): Promise<sink.val> {
  // implementation here
}

...

sink.ctx_native(ctx, 'company.product.foo', null, my_foo);
```

Notice that the `name` parameter (`'company.product.foo'`) matches the declaration in sink.

### `ctx`

The Context object.

### `name`

The string to be hashed for eventual lookup.

### `natuser`

User-defined value passed to `f_native`.

### `f_native`

The native function implementation.  This function returns a [sink_wait](#waiter) object in C, and
a Promise in TypeScript.

ctx_nativehash
--------------

Add a native command implementation to the virtual machine using a specific hash value.

```c
typedef sink_wait (*sink_native_f)(sink_ctx ctx, int size, const sink_val *args, void *natuser);

void sink_ctx_nativehash(sink_ctx ctx, uint64_t hash, void *natuser, sink_native_f f_native);
```

```typescript
type sink.u64 = [number, number];
type sink.native_f = (ctx: sink.ctx, args: sink.val[], natuser: any) => Promise<sink.val>;

function sink.ctx_nativehash(ctx: sink.ctx, hash: sink.u64, natuser: any,
  f_native: sink.native_f): void;
```

See [`ctx_native`](#ctx_native) for documentation on native functions.

The hash value can be calculated using the algorithm below (written in sink):

```
# calculate the uint64_t C hash value
def native_c_hash name
  var h = str.hash name, 0 | num.hex 8
  return h[1] ~ h[0][2:]
end

# calculate the sink.u64 TypeScript hash value
def native_ts_hash name
  var h = str.hash name, 0 | num.hex 8
  return "[ ${h[0]}, ${h[1]} ]"
end
```

### `ctx`

The Context object.

### `hash`

The 64-bit hash.  In TypeScript this is stored as an array with two elements, each an unsigned
32-bit number.

### `natuser`

User-defined value passed to `f_native`.

### `f_native`

The native function implementation.  This function returns a [sink_wait](#waiter) object in C, and
a Promise in TypeScript.

ctx_cleanup
-----------

Provide a pointer and free function to be executed when the Context object is freed (C only).

```c
typedef void (*sink_free_f)(void *ptr);

void sink_ctx_cleanup(sink_ctx ctx, void *cuser, sink_free_f f_free);
```

This is useful for libraries that allocate their own objects when loading into a sink Context
object.  They can ensure the objects are cleaned up when the Context object is freed.

### `ctx`

The Context object.

### `cuser`

The pointer to be freed when the Context object is freed.

### `f_free`

The function used to free `cuser`.

ctx_setuser
-----------

Set a user-defined value associated with the Context object.  The value can be retrieved by
[`ctx_getuser`](#ctx_getuser).

```c
typedef void (*sink_free_f)(void *ptr);

void sink_ctx_setuser(sink_ctx ctx, void *user, sink_free_f f_free);
```

```typescript
function sink.ctx_setuser(ctx: sink.ctx, user: any): void;
```

### `ctx`

The Context object.

### `user`

The user-defined value.

### `f_free`

The function used to free the object (C only).  If the user-defined value is overwritten by another
call to `ctx_setuser`, or if the Context object is freed, then the current `user` value is freed via
`f_free`.

ctx_getuser
-----------

Get the user-defined value associated with the Context object, previously set with
[`ctx_setuser`](#ctx_setuser).

```c
void *sink_ctx_getuser(sink_ctx ctx);
```

```typescript
function sink.ctx_getuser(ctx: sink.ctx): any;
```

### `ctx`

The Context object.

ctx_addusertype
---------------

Add a new user type to be associated with hidden data attached to lists.

```c
typedef int sink_user;
typedef void (*sink_free_f)(void *ptr);

sink_user sink_ctx_addusertype(sink_ctx ctx, const char *hint, sink_free_f f_free);
```

```typescript
type sink.user = number;

function sink.ctx_addusertype(ctx: sink.ctx, hint: string): sink.user;
```

User types are ways for a host to attach custom data to lists.

For example, suppose you want a sprite object:

```c
// register the type (once)
sink_user sprite_type = sink_ctx_addusertype(ctx, "sprite", sprite_free);

// creating a list with the_sprite attached to it
sprite the_sprite = sprite_new();
sink_val s = sink_user_new(ctx, sprite_type, the_sprite);
// `s` can be returned to sink scripts
// it will look like {'sprite'} to them, but have `the_sprite` attached to it

// extract a sprite from a list
if (sink_list_hasuser(ctx, s, sprite_type)){
  sprite the_sprite = sink_list_getuser(ctx, s);
  // use the_sprite
}
```

```typescript
// register the type (once)
let sprite_type: sink.user = sink.ctx_addusertype(ctx, 'sprite');

// creating a list with the_sprite attached to it
let the_sprite: sprite = new sprite();
let s: sink.val = sink.user_new(ctx, sprite_type, the_sprite);
// `s` can be returned to sink scripts
// it will look like {'sprite'} to them, but have `the_sprite` attached to it

// extract a sprite from a list
if (sink.list_hasuser(ctx, s, sprite_type)){
  let the_sprite: sprite = sink.list_getuser(ctx, s) as sprite;
  // use the_sprite
}
```

### `ctx`

The Context object.

### `hint`

The string used inside the list when building a user object.

Note that this doesn't have any real meaning -- it simply exists so that when the end-user prints
out the object, it says something useful.

This should never be used for type checking.  Instead, a host should provide a native function
like `issprite` that will use [`list_hasuser`](#list_hasuser) to query the actual underlying type.

### `f_free`

The function used to free the object, or `NULL` (C only).

This function will be called when the garbage collector determines that the list container is
unreachable and should be collected.  It will also be called if the user value is ever overwritten
via [`list_setuser`](#list_setuser).

ctx_getuserfree
---------------

Get the `f_free` function associated with a user type (C only).  See:
[`ctx_addusertype`](#ctx_addusertype).

```c
typedef int sink_user;

sink_free_f sink_ctx_getuserfree(sink_ctx ctx, sink_user usertype);
```

### `ctx`

The Context object.

### `usertype`

The user type returned from `ctx_addusertype`.

ctx_getuserhint
---------------

Get the `hint` associated with a user type.  See: [`ctx_addusertype`](#ctx_addusertype).

```c
typedef int sink_user;

const char *sink_ctx_getuserhint(sink_ctx ctx, sink_user usertype);
```

```typescript
type sink.user = number;

function sink.ctx_getuserhint(ctx: sink.ctx, usertype: sink.user): string;
```

### `ctx`

The Context object.

### `usertype`

The user type returned from `ctx_addusertype`.

ctx_settimeout
--------------

Set a timeout so that the virtual machine will only execute a certain number of operations before
returning from [`ctx_run`](#ctx_run).

```c
void sink_ctx_settimeout(sink_ctx ctx, int timeout);
```

```typescript
function sink.ctx_settimeout(ctx: sink.ctx, timeout: number): void;
```

For example, if the timeout is 1000, then the VM will run 1000 ticks before timing out.  Each
machine instruction counts as 1 tick, and a garbage collection cycle counts as 100 ticks (defined by
`SINK_GC_TICKS`).  Native functions can consume ticks via [`ctx_consumeticks`](#ctx_consumeticks),
or empty the available ticks to 0 via [`ctx_forcetimeout`](#ctx_forcetimeout).

When there are no ticks left, `ctx_run` returns `TIMEOUT` and resets the available ticks to
`timeout`.  The machine is resumed with another call to `ctx_run`, and the process repeats.

### `ctx`

The Context object.

### `timeout`

Roughly the number of operations to execute before returning from `ctx_run` with a `TIMEOUT` result.
Use `0` to disable a timeout entirely, which is the default state.

ctx_gettimeout
--------------

Get the current timeout setting from the virtual machine.  See: [`ctx_settimeout`](#ctx_settimeout).

```c
int sink_ctx_gettimeout(sink_ctx ctx);
```

```typescript
function sink.ctx_gettimeout(ctx: sink.ctx): number;
```

The return value will be `0` if timing out is disabled.

### `ctx`

The Context object.

ctx_consumeticks
---------------

Decrease the current tick counter by `amount`.

```c
void sink_ctx_consumeticks(sink_ctx ctx, int amount);
```

```typescript
function sink.ctx_consumeticks(ctx: sink.ctx, amount: number): void
```

Use this function to inform the virtual machine that an operation has taken a long time, so the
value used to track when a timeout happens reflects this delay.

For example, if a native command takes a long time, it could inform the machine to decrease the
internal timer by 50 ticks via `sink.ctx_consumeticks(ctx, 50)`.

### `ctx`

The Context object.

### `amount`

The amount of ticks to decrease the tick counter.  This value should be positive.

ctx_forcetimeout
----------------

Force a timeout to occur immediately.  Timeouts must be enabled via
[`ctx_settimeout`](#ctx_settimeout) for this to have an effect.

```c
void sink_ctx_forcetimeout(sink_ctx ctx);
```

```typescript
function sink.ctx_forcetimeout(ctx: sink.ctx): void;
```

This will immediately expire the internal timer, so that when control returns to
[`ctx_run`](#ctx_run), it will immediately return `TIMEOUT`.

### `ctx`

The Context object.

ctx_run
-------

Run the virtual machine.  This is the main function used to execute a script.

```c
typedef enum {
  SINK_RUN_PASS,
  SINK_RUN_FAIL,
  SINK_RUN_ASYNC,
  SINK_RUN_TIMEOUT,
  SINK_RUN_REPLMORE
} sink_run;

sink_wait sink_ctx_run(sink_ctx ctx);
```

```typescript
enum sink.run {
  PASS,
  FAIL,
  ASYNC,
  TIMEOUT,
  REPLMORE
}

function sink.ctx_run(ctx: sink.ctx): Promise<sink.run>;
```

This function will execute the bytecode and dispatch [I/O](#ctx_new) and [native](#ctx_native)
functions as needed.

It will return one of the following values:

* `PASS` - Execution has finished and the script exited successfully.
* `FAIL` - Execution has finished and the script exited in failure.  Use [`ctx_geterr`](#ctx_geterr)
  to get the run-time error message, if it exists.
* `ASYNC` - Machine is waiting for an asynchronous result to continue.
* `TIMEOUT` - The machine's [timeout](#ctx_settimeout) has triggered.  Run `ctx_run` to resume.
* `REPLMORE` - The machine has detected it has executed as much as it could before needing more
  source code entered from the REPL.  This only happens if the Script is in [REPL mode](#scr_new).

The TypeScript version will return a Promise.

The C version will return a [`sink_wait`](#waiter) object, that will resolve to a number that
corresponds to the `sink_run` result, i.e.:

```c
void run_finished(sink_ctx ctx, sink_val statusv, void *thenuser){
  sink_run status = (sink_run)sink_castnum(statusv);
  switch (status){
    case SINK_RUN_PASS:     // ... etc ...
    case SINK_RUN_FAIL:     // ... etc ...
    case SINK_RUN_ASYNC:    // ... etc ...
    case SINK_RUN_TIMEOUT:  // ... etc ...
    case SINK_RUN_REPLMORE: // ... etc ...
  }
}

// somewhere else:
sink_then(
  sink_ctx_run(ctx),
  (sink_then_st){
    .f_then = run_finished,
    .f_cancel = NULL,
    .user = NULL
  }
);
```

### `ctx`

The Context object.

ctx_free
--------

Free the Context object (C only).

```c
void sink_ctx_free(sink_ctx ctx);
```

Note this will also free any [`ctx_setuser`](#ctx_setuser) value, and any
[`ctx_cleanup`](#ctx_cleanup) pointers that have been associated with the Context object, before
freeing the Context object itself.

### `ctx`

The Context object.

waiter
------

Create a Wait object that will eventually be resolved with a result and handler (C only).

```c
sink_wait sink_waiter(sink_ctx ctx);
```

Wait objects are used to implement asynchronous operations within the Context.  A Wait object
becomes resolved when two things are provided: a handler (via [`sink_then`](#then)), and a result
(via [`sink_result`](#result)).  When both items are provided, the handler's `f_then` function is
immediately called with the result.

Once a Wait object is resolved, it is automatically freed.  If a Wait object never resolves (for
example, if [`ctx_free`](#ctx_free) is called on the Context with an outstanding Wait object), it is
cancelled in order to free any dangling user data.

Wait objects are used in [I/O](#ctx_new) and [native](#ctx_native) functions.  These functions can
return a Wait object, or `NULL` to indicate the result is `NIL` (in order to avoid allocating a new
Wait object).

### `ctx`

The Context object.

done
----

Create a Wait object that already has a result (C only).

```c
sink_wait sink_done(sink_ctx ctx, sink_val result);
```

Many functions will not be asynchronous, and can just return a result directly.  Use `sink_done`
for convenience.  It is equivalent to:

```c
sink_wait w = sink_waiter(ctx);
sink_result(w, result);
return w;
```

### `ctx`

The Context object.

### `result`

The result to provide to the Wait object.

then
----

Attach a handler to a Wait object (C only).

```c
typedef void (*sink_then_f)(sink_ctx ctx, sink_val result, void *thenuser);
typedef void (*sink_cancel_f)(void *thenuser);

typedef struct {
  sink_then_f f_then;
  sink_cancel_f f_cancel;
  void *user;
} sink_then_st;

void sink_then(sink_wait w, sink_then_st then);
```

If the Wait object already has a result, the `f_then` function will be called immediately with the
result.

### `w`

The Wait object.

### `then`

The handler for the Wait object.

The `f_then` function is what will be called when both the handler and result are provided to a Wait
object.

The `f_cancel` function will be called if an outstanding Wait object is cancelled.  This can happen
if the Context is freed in the middle of an asynchronous operation.  The `f_cancel` function should
free any data inside the `user` field.

The `user` field is passed through to the `thenuser` argument in the functions, at your discretion.

result
------

Provide a result for a Wait object (C only).

```c
void sink_result(sink_wait w, sink_val result);
```

If the Wait object already has a handler, its `f_then` function will be called immediately with the
result.

### `w`

The Wait object.

### `result`

The final result for the operation.

Standard Library API
====================

The entire [standard library](https://github.com/voidqk/sink/blob/master/docs/lib.md) is available
from the host environment.

In C, the host function is prefixed with `sink_`, and in TypeScript/JavaScript, the host function
is prefixed with `sink.`.

See the [C header file](https://github.com/voidqk/sink/blob/master/src/sink.h) and the
[TypeScript declaration file](https://github.com/voidqk/sink/blob/master/dist/sink.d.ts) for
function parameters.  It should be straight-forward.

Note: the following commands are not available at run-time because they only work at compile-time:

* `pick` - this is compiled into an equivalent `if` statement, in order to implement
  [short-circuit evaluation](https://en.wikipedia.org/wiki/Short-circuit_evaluation)
* `embed` - this loads files strictly at compile-time as strings
* `include` - this loads files strictly at compile-time as code

| Sink Command      | Host Function     |
|-------------------|-------------------|
| `+x`              | `tonum`           |
| `'' ~ x`          | `tostr`           |
| `&x`              | `size`            |
| `say`             | `say`             |
| `warn`            | `warn`            |
| `ask`             | `ask`             |
| `exit`            | `exit`            |
| `abort`           | `abort`           |
| `isnum`           | `isnum`           |
| `isstr`           | `isstr`           |
| `islist`          | `islist`          |
| `isnative`        | `isnative` or `isnativehash` |
| `range`           | `range`           |
| `order`           | `order`           |
| `stacktrace`      | `stacktrace`      |
| `-x`              | `num_neg`         |
| `x + y`           | `num_add`         |
| `x - y`           | `num_sub`         |
| `x * y`           | `num_mul`         |
| `x / y`           | `num_div`         |
| `x % y`           | `num_mod`         |
| `x ^ y`           | `num_pow`         |
| `num.abs`         | `num_abs`         |
| `num.sign`        | `num_sign`        |
| `num.max`         | `num_max`         |
| `num.min`         | `num_min`         |
| `num.clamp`       | `num_clamp`       |
| `num.floor`       | `num_floor`       |
| `num.ceil`        | `num_ceil`        |
| `num.trunc`       | `num_trunc`       |
| `num.nan`         | `num_nan`         |
| `num.inf`         | `num_inf`         |
| `num.isnan`       | `num_isnan`       |
| `num.isfinite`    | `num_isfinite`    |
| `num.e`           | `num_e`           |
| `num.pi`          | `num_pi`          |
| `num.tau`         | `num_tau`         |
| `num.sin`         | `num_sin`         |
| `num.cos`         | `num_cos`         |
| `num.tan`         | `num_tan`         |
| `num.asin`        | `num_asin`        |
| `num.acos`        | `num_acos`        |
| `num.atan`        | `num_atan`        |
| `num.atan2`       | `num_atan2`       |
| `num.log`         | `num_log`         |
| `num.log2`        | `num_log2`        |
| `num.log10`       | `num_log10`       |
| `num.exp`         | `num_exp`         |
| `num.lerp`        | `num_lerp`        |
| `num.hex`         | `num_hex`         |
| `num.oct`         | `num_oct`         |
| `num.bin`         | `num_bin`         |
| `int.new`         | `int_new`         |
| `int.not`         | `int_not`         |
| `int.and`         | `int_and`         |
| `int.or`          | `int_or`          |
| `int.xor`         | `int_xor`         |
| `int.shl`         | `int_shl`         |
| `int.shr`         | `int_shr`         |
| `int.sar`         | `int_sar`         |
| `int.add`         | `int_add`         |
| `int.sub`         | `int_sub`         |
| `int.mul`         | `int_mul`         |
| `int.div`         | `int_div`         |
| `int.mod`         | `int_mod`         |
| `int.clz`         | `int_clz`         |
| `int.pop`         | `int_pop`         |
| `int.bswap`       | `int_bswap`       |
| `rand.seed`       | `rand_seed`       |
| `rand.seedauto`   | `rand_seedauto`   |
| `rand.int`        | `rand_int`        |
| `rand.num`        | `rand_num`        |
| `rand.range`      | `rand_range`      |
| `rand.getstate`   | `rand_getstate`   |
| `rand.setstate`   | `rand_setstate`   |
| `rand.pick`       | `rand_pick`       |
| `rand.shuffle`    | `rand_shuffle`    |
| `str.new`         | `str_new`         |
| `x ~ y`           | `str_cat`         |
| `x[y:z]`          | `str_slice`       |
| `x[y:z] = w`      | `str_splice`      |
| `str.split`       | `str_split`       |
| `str.replace`     | `str_replace`     |
| `str.begins`      | `str_begins`      |
| `str.ends`        | `str_ends`        |
| `str.pad`         | `str_pad`         |
| `str.find`        | `str_find`        |
| `str.rfind`       | `str_rfind`       |
| `str.lower`       | `str_lower`       |
| `str.upper`       | `str_upper`       |
| `str.trim`        | `str_trim`        |
| `str.rev`         | `str_rev`         |
| `str.list`        | `str_list`        |
| `str.byte`        | `str_byte`        |
| `str.hash`        | `str_hash`        |
| `utf8.valid`      | `utf8_valid`      |
| `utf8.list`       | `utf8_list`       |
| `utf8.str`        | `utf8_str`        |
| `struct.size`     | `struct_size`     |
| `struct.str`      | `struct_str`      |
| `struct.list`     | `struct_list`     |
| `struct.isLE`     | `struct_isLE`     |
| `list.new`        | `list_new`        |
| `x ~ y`           | `list_cat`        |
| `x[y:z]`          | `list_slice`      |
| `x[y:z] = w`      | `list_splice`     |
| `list.shift`      | `list_shift`      |
| `list.pop`        | `list_pop`        |
| `list.push`       | `list_push`       |
| `list.unshift`    | `list_unshift`    |
| `list.append`     | `list_append`     |
| `list.prepend`    | `list_prepend`    |
| `list.find`       | `list_find`       |
| `list.rfind`      | `list_rfind`      |
| `list.join`       | `list_join`       |
| `list.rev`        | `list_rev`        |
| `list.str`        | `list_str`        |
| `list.sort`       | `list_sort`       |
| `list.rsort`      | `list_rsort`      |
| `pickle.json`     | `pickle_json`     |
| `pickle.bin`      | `pickle_bin`      |
| `pickle.val`      | `pickle_val`      |
| `pickle.valid`    | `pickle_valid`    |
| `pickle.sibling`  | `pickle_sibling`  |
| `pickle.circular` | `pickle_circular` |
| `pickle.copy`     | `pickle_copy`     |
| `gc.getlevel`     | `gc_getlevel`     |
| `gc.setlevel`     | `gc_setlevel`     |
| `gc.run`          | `gc_run`          |

Misc/Helper Functions
=====================

| Function/Value                          | Description                                           |
|-----------------------------------------|-------------------------------------------------------|
| [`NIL`](#NIL)                           | The literal `nil` value                               |
| [`bool`](#bool)                         | Convert a boolean to a sink value                     |
| [`isnil`](#isnil)                       | Test if a sink value is `nil`                         |
| [`isfalse`](#isfalse)                   | Test if a sink value is false (`nil`)                 |
| [`istrue`](#istrue)                     | Test if a sink value is true (non-`nil`)              |
| [`typeof`](#typeof)                     | Get the type of a sink value                          |
| [`castnum`](#castnum)                   | Reinterpret a sink value as a number (C only)         |
| [`num`](#num)                           | Convert a number to a sink value (C only)             |
| [`caststr`](#caststr)                   | Reinterpret a sink value as a string (C only)         |
| [`str_newcstr`](#str_newcstr)           | Create sink string by copying a C string (C only)     |
| [`str_newcstrgive`](#str_newcstrgive)   | Create sink string from a C string, giving memory ownership to sink (C only) |
| [`str_newblob`](#str_newblob)           | Create sink string by copying a blob of data (C only) |
| [`str_newblobgive`](#str_newblobgive)   | Create sink string from a blob of data, giving memory ownership to sink (C only) |
| [`str_newempty`](#str_newempty)         | Create an empty string (C only)                       |
| [`str_newformat`](#str_newformat)       | Create a string from a `printf`-formatted expression (C only) |
| [`str_hashplain`](#str_hashplain)       | Hash a string directly                                |
| [`castlist`](#castlist)                 | Reinterpret a sink value as a list (C only)           |
| [`list_newblob`](#list_newblob)         | Create sink list by copying a list of values (C only) |
| [`list_newblobgive`](#list_newblobgive) | Create sink list from a list of values, giving memory ownership to sink (C only) |
| [`list_newempty`](#list_newempty)       | Create an empty list (C only)                         |
| [`list_setuser`](#list_setuser)         | Set the user-defined data associated with a list      |
| [`list_hasuser`](#list_hasuser)         | Check if a list has a certain type of user-defined data |
| [`list_getuser`](#list_getuser)         | Get the user-defined data associated with a list      |
| [`list_joinplain`](#list_joinplain)     | Join a list directly                                  |
| [`user_new`](#user_new)                 | Create a list with user-defined data attached to it   |
| [`pickle_binstr`](#pickle_binstr)       | Pickle a sink value into binary data for marshalling  |
| [`pickle_binstrfree`](#pickle_binstrfree)| Free the results of `pickle_binstr` (C only)         |
| [`pickle_valstr`](#pickle_valstr)       | Convert a marshalled string into a sink value         |
| [`gc_pin`](#gc_pin)                     | Pin a value to prevent it from being garbage-collected (C only) |
| [`gc_unpin`](#gc_unpin)                 | Unpin a previously pinned value, allowing it to be garbage-collected (C only) |
| [`abortstr`](#abortstr)                 | Abort with a formatted string (C only)                |
| [`arg_bool`](#arg_bool)                 | Convert a native argument to a boolean                |
| [`arg_num`](#arg_num)                   | Convert a native argument to a number, if possible    |
| [`arg_str`](#arg_str)                   | Convert a native argument to a string, if possible    |
| [`arg_list`](#arg_list)                 | Convert a native argument to a list, if possible      |
| [`arg_user`](#arg_user)                 | Convert a native argument to a user-defined value, if possible |
| [`seedauto_src`](#seedauto_src)         | The function that provides the source random data for `rand.seedauto` |
| [`malloc`](#malloc)                     | The function that provides all memory allocation (C only) |
| [`realloc`](#realloc)                   | The function that provides all memory reallocation (C only) |
| [`free`](#free)                         | The function that provides all memory freeing (C only)|

NIL
---

The literal `nil` value.

```c
const sink_val SINK_NIL;
```

```typescript
const sink.NIL: null;
```

bool
----

Convert a boolean to a sink value.

```c
sink_val sink_bool(bool f);
```

```typescript
function sink.bool(f: boolean): sink.val;
```

This is a convenience function that will return `nil` for `false`, and `1` for `true`.

### `f`

The boolean flag to convert.

isnil
-----

Test if a sink value is `nil`.

```c
bool sink_isnil(sink_val v);
```

```typescript
function sink.isnil(v: sink.val): boolean;
```

### `v`

The value to test.

isfalse
-------

Test if a sink value is false (`nil`).

```c
bool sink_isfalse(sink_val v);
```

```typescript
function sink.isfalse(v: sink.val): boolean;
```

### `v`

The value to test.

istrue
------

Test if a sink value is true (non-`nil`).

```c
bool sink_istrue(sink_val v);
```

```typescript
function sink.istrue(v: sink.val): boolean;
```

### `v`

The value to test.

typeof
------

Get the type of a sink value.

```c
typedef enum {
  SINK_TYPE_NIL,
  SINK_TYPE_NUM,
  SINK_TYPE_STR,
  SINK_TYPE_LIST
} sink_type;

sink_type sink_typeof(sink_val v);
```

```typescript
enum sink.type {
  NIL,
  NUM,
  STR,
  LIST
}

function sink.sink_typeof(v: sink.val): sink.type;
```

Note that the TypeScript version is `sink.sink_typeof` instead of `sink.typeof` because `typeof` is
a reserved keyword in TypeScript/JavaScript.

### `v`

The value that should be type checked.

castnum
-------

Reinterpret a sink value as a number (C only).

```c
double sink_castnum(sink_val v);
```

Note that this function does not perform type checking.  If a non-number is cast to a number, the
results are undefined.

### `v`

The value to be reinterpretted.

num
---

Convert a number to a sink value (C only).

```c
sink_val sink_num(double v);
```

### `v`

The value to be converted.

caststr
-------

Reinterpret a sink value as a string (C only).

```c
typedef struct {
  const uint8_t *bytes;
  const int size;
} sink_str;

sink_str sink_caststr(sink_ctx ctx, sink_val str);
```

Note that this function does not perform type checking.  If a non-string is cast to a string, the
results are undefined.

In order to *convert* a value to a string, use `sink_tostr`.

The return value is a `sink_str` structure, where the application can access the raw bytes and size
of the array.  The `bytes` value can be `NULL` for an empty string, otherwise it is guaranteed to be
`NULL`-terminated (i.e., `result.bytes[result.size] == 0`).

### `ctx`

The Context object.

### `str`

The value to be reinterpretted.

str_newcstr
-----------

Create a sink string by copying a C string (C only).

```c
sink_val sink_str_newcstr(sink_ctx ctx, const char *str);
```

### `ctx`

The Context object.

### `str`

The `NULL`-terminated C string.

str_newblob
-----------

Create sink string by copying a blob of data (C only).

```c
sink_val sink_str_newblob(sink_ctx ctx, int size, const uint8_t *bytes);
```

### `ctx`

The Context object.

### `size`

The size of the `bytes` array, or `0` for an empty string.

### `bytes`

The raw bytes to copy, or `NULL` for an empty string.

str_newblobgive
---------------

Create sink string from a blob of data, giving memory ownership to sink (C only).

```c
sink_val sink_str_newblobgive(sink_ctx ctx, int size, uint8_t *bytes);
```

**Note:** non-empty strings *must* have `bytes` allocated to store `size + 1` bytes, where
`bytes[size]` is set to `0`, guaranteeing that the string is `NULL`-terminated.

**Note:** non-empty strings *must* use [`sink_malloc`](#malloc) to allocate the `bytes` array.  It
will be freed by the garbage collector using [`sink_free`](#free) automatically.

### `ctx`

The Context object.

### `size`

The size of the `bytes` array, or `0` for an empty string.

### `bytes`

The raw bytes to use as the string, or `NULL` for an empty string.

str_newempty
------------

Create an empty string (C only).

```c
sink_val sink_str_newempty(sink_ctx ctx);
```

### `ctx`

The Context object.

str_newformat
-------------

Create a string from a `printf`-formatted expression (C only).

```c
sink_val sink_str_newformat(sink_ctx ctx, const char *fmt, ...);
```

### `ctx`

The Context object.

### `fmt`

The `printf`-style format string.

### `...`

The arguments to feed to the formatter.

str_hashplain
-------------

Hash a string directly.

```c
void sink_str_hashplain(int size, const uint8_t *bytes, uint32_t seed, uint32_t *out);
```

```typescript
function sink.str_hashplain(bytes: string, seed: number): [number, number, number, number];
```

This function is useful because it can calculate hashes without a Context object.  It doesn't
require any parameters other than the string and seed.

### `size`

The size of the `bytes` array (C only).

### `bytes`

The raw bytes.  Note: in TypeScript/JavaScript, the string is interpretted as
[`'binary'` encoding](https://nodejs.org/api/buffer.html#buffer_buffers_and_character_encodings).

### `seed`

The seed (see: [`str.hash`](https://github.com/voidqk/sink/blob/master/docs/hash.md)).

### `out`

An array of `uint32_t` values that must have space for the 4 `uint32_t` results.  I.e., `out[0]`,
`out[1]`, `out[2]`, and `out[3]` will have the hash result (C only).

castlist
--------

Reinterpret a sink value as a list (C only).

```c
typedef struct {
  const sink_val *vals;
  int size;
} sink_list;

sink_list sink_castlist(sink_ctx ctx, sink_val ls);
```

Note that this function does not perform type checking.  If a non-list is cast to a list, the
results are undefined.

The return value is a `sink_list` structure, where the application can access the values and size of
the array.

The `vals` element is the array of sink values in the list.

The `size` element is the size of the list.

### `ctx`

The Context object.

### `ls`

The value to be reinterpretted.

TODO
----

```
sink_val sink_list_newblob(sink_ctx ctx, int size, const sink_val *vals);
sink_val sink_list_newblobgive(sink_ctx ctx, int size, int count, sink_val *vals);
sink_val sink_list_newempty(sink_ctx ctx);

void sink_list_setuser(sink_ctx ctx, sink_val ls, sink_user usertype, void *user);
function sink.list_setuser(ctx: sink.ctx, ls: sink.val, usertype: sink.user, user: any): void;

bool sink_list_hasuser(sink_ctx ctx, sink_val ls, sink_user usertype);
function sink.list_hasuser(ctx: sink.ctx, ls: sink.val, usertype: sink.user): boolean;

void *sink_list_getuser(sink_ctx ctx, sink_val ls);
function sink.list_getuser(ctx: sink.ctx, ls: sink.val): any;

sink_val sink_list_joinplain(sink_ctx ctx, int size, const sink_val *vals, int sepz, const uint8_t *sep);
function sink.list_joinplain(vals: list | val[], sep: string): val;

sink_val sink_user_new(sink_ctx ctx, sink_user usertype, void *user);
function sink.user_new(ctx: sink.ctx, usertype: sink.user, user: any): sink.val;

bool sink_pickle_binstr(sink_ctx ctx, sink_val a, sink_str_st *out);
function sink.pickle_binstr(a: val): string;

void sink_pickle_binstrfree(sink_str_st str);

bool sink_pickle_valstr(sink_ctx ctx, sink_str_st str, sink_val *out);
function sink.pickle_valstr(s: sink.str): sink.val | false;

void sink_gc_pin(sink_ctx ctx, sink_val v);
void sink_gc_unpin(sink_ctx ctx, sink_val v);

// always returns NIL
sink_wait sink_abortstr(sink_ctx ctx, const char *fmt, ...);
function sink.abortstr(ctx: sink.ctx, str: string): Promise<sink.val>;

bool sink_arg_bool(int size, sink_val *args, int index);
function sink.arg_bool(args: sink.val[], index: number): boolean;

bool sink_arg_num(sink_ctx ctx, int size, sink_val *args, int index, double *num);
function sink.arg_num(ctx: ctx, args: sink.val[], index: number): number;

bool sink_arg_str(sink_ctx ctx, int size, sink_val *args, int index, sink_str *str);
function sink.arg_str(ctx: ctx, args: sink.val[], index: number): string;

bool sink_arg_list(sink_ctx ctx, int size, sink_val *args, int index, sink_list *ls);
function sink.arg_list(ctx: ctx, args: sink.val[], index: number): sink.list;

bool sink_arg_user(sink_ctx ctx, int size, sink_val *args, int index, sink_user usertype,
  void **user);
function sink.arg_user(ctx: ctx, args: sink.val[], index: number, usertype: sink.user): any;

sink_seedauto_src_f sink_seedauto_src;
let seedauto_src: () => number;

sink_malloc_f  sink_malloc;
sink_realloc_f sink_realloc;
sink_free_f    sink_free;
```


Joint dan training sessions will be held at the Kamppailuareena Mikkeli dojo every month on Sundays at 5 – 6.30pm. The training sessions focus on Wadokai Finland dan syllabus techniques and on supplementary exercises. The sessions will be instructed by black belt instructors from the participating clubs.


text:
A*Star launches Advanced Remanufacturing and Technology Centre
Wed, Jan 28, 2015 - 2:11 PM
Chan Yi Wenyiwenc@sph.com.sg@ChanYiWenBT
SINGAPORE is taking a further step to improve the sustainability of its high-tech manufacturing sector. On Wednesday, 21/2 years after the groundbreaking ceremony, the Agency for Science, Technology and Research (A*Star) launched the Advanced Remanufacturing and Technology Centre (ARTC), Asia's first centre for test-bedding and developing manufacturing technologies.
The event was attended by the Second Minister for Trade & Industry, S Iswaran.
ARTC is a public-private collaboration between A*Star, Nanyang Technological University (NTU) and industry partners that seeks to bridge technological gaps in the adoption of advanced remanufacturing processes.
The programme first began operations in 2012 at JTC CleanTech One, a building located at CleanTech Park, Singapore's first eco-business park. Now ARTC is expanding to a bigger location at JTC CleanTech Two, where it is the anchor tenant.
Remanufacturing is the buzzword for transforming old products through disassembly, cleaning, testing and other operations, into like-new products that can be re-introduced to the market. It is often seen as the future for manufacturing, using less energy and generating less waste compared to traditional manufacturing processes. Through research & development, companies can also develop advanced sustainable manufacturing technologies to strengthen their capabilities and knowhow.
A report by Global Industry Analysts (GIA) predicts the global remanufacturing market to reach US$104.8 billion by this year. In Singapore, sectors such as aerospace, oil and gas, automotive and machinery have remanufacturing activities located here, with aerospace maintenance, repair and overhaul being the biggest contributor.
ARTC's CEO David Low said: "Through this unique collaborative model, like-minded companies come together at ARTC to provide the real industry requirements, spark new ideas, catalyst new opportunities, leverage on shared resources and accelerate the development of remanufacturing capabilities in a faster, better and cheaper way than doing it alone."
Since 2012, ARTC has completed over 50 industry projects, ranging from an automated process to protect surfaces during repair processes, to a robotised inspection process to check for defects in components.
To date, the centre has 29 industry members on board, including Rolls-Royce plc, Singapore Aero Engine Services and Siemens Industry Software.
Twelve small and medium enterprises have also joined ARTC's board, including AmpTec Industrial Heating, which is involved in one of ARTC's projects with Rolls-Royce. AmpTec was commissioned to develop a dry ice blasting machine to clean aircraft engine components, as it does not use polluting industrial chemicals and heavy scrubbing, thus retaining the quality and performance of the component.
ARTC also works with NTU to develop a talent pipeline to support the shift towards eco-friendly production processes and techniques. Developing these projects at the ARTC will be 18 NTU PhD students on scholarships awarded specifically for this collaboration. They will work with 10 NTU faculty members and a dozen undergraduate and master's students.
WHO convenes emergency committee early over coronavirus variants
LTA awards S$34.3m civil contract for Hume station on Downtown Line
China's Dec exports rise 18.1% y-o-y, imports up 6.5%, top forecasts
China reports first Covid-19 death in eight months
Biden to unveil economic plan as US recovery buckles
Italy plunged into political crisis, government risks collapse
A*Star
Advanced Remanufacturing and Technology Centre
ARTC
Cargill in talks to exit global sugar trading business
[CHICAGO] Cargill is in talks to exit its global sugar trading business as the top agricultural commodities firm...
Chinese drone firm DJI builds team to work on self-driving tech
[BEIJING] China's SZ DJI Technology, the world's largest drone maker, is building an engineering team to work on...
Poshmark prices IPO above marketed range at US$42 a share
[NEW YORK] Poshmark, the online marketplace for second-hand goods, raised US$277 million in a US initial public...
E-payments, fund transfers pip card transactions in South-east Asia
"E-MONEY" and interbank transfers are duking it out in the region, as their growth outstrips that for debit and...
Gold retreats as US dollar, Treasury yields rebound
[BENGALURU] Gold prices fell on Thursday as US Treasury yields and the dollar rebounded, while investors awaited...
Stocks to watch: SPH Reit, HC Surgical, Aspen, GL, GKE Corp, Keppel DC Reit
Wave of regional buyers expected to land on Sentosa Cove
Margin squeeze puts F&B players in a spot over food delivery demand
Keppel DC Reit's MTN programme limit raised to S$2b from S$500m
UK's Johnson warns against 'unthinking sinophobia' in China ties

redpajama_set_name: RedPajamaCommonCrawl


Case Samsung Galaxy Grand Prime G530 Slim Armor Biru Dongker Free ... - Case Tough Armor Carbon For Samsung Galaxy J510 J5 2016 Biru Source Case .
Case Tough Armor Carbon For Samsung Galaxy Grand Prime Abu Free Usb ... - SHTL Carbon TPU Armor Case for Samsung Galaxy Grand Prime G530 Black .
Case Tough Armor Carbon For Samsung Galaxy J3 Abu Free Tempered ... - Hp Samsung J2 .
Case Tough Armor Carbon For Samsung Galaxy J2 Prime Hitam Free ... - CASE SPIGEN TOUGH ARMOR SAMSUNG GALAXY J2 PRIME HARD BACK CASE CASING Shopee .
Case Tough Armor Carbon For Samsung Galaxy Grand Prime Abu Free Usb ... - Nillkin Nature TPU Soft Case Samsung Galaxy S9+ / S9 Plus .
Case Tough Armor Carbon For Samsung Galaxy Grand Prime Gold - Wiring ... - Grand Prime Biru Source Prime Source Harga Case Tough Armor Carbon for Samsung .
Case Tough Armor Carbon For Samsung Galaxy J510 J5 2016 Abu Free ... - 2016 Slim Armor Abu Tempered Glass Daftar Source · Galaxy J7 Prime Series .
Case Tough Armor Carbon For Samsung Galaxy Grand Prime Hitam Free ... - Garskin Glitter Samsung J5 Prime .
Case Tough Armor Carbon For Iphone 7 Plus Biru Dongker Free Tempered ... - Samsung Galaxy J3 Hitam Free Tempered ... - Jual Case Tough .
Softcase Tought Armor Carbon Series Samsung Galaxy Grand Prime Hitam ... - Hybrid Armor Kickstand Protective Case For Samsung Galaxy Grand Prime Plus / Galaxy .
Case Tough Armor Carbon For Samsung Galaxy J510 J5 2016 Abu Free Usb ... - J1 2016 Hitam Daftar Source Case Tough Armor Carbon for Samsung Galaxy J5 .
Case Samsung Galaxy Grand Prime G530 Slim Armor Biru Dongker Power ... - Biru Source · Case Tough Armor Carbon For Samsung Galaxy J2 Abu Free .
Case Tough Armor Carbon For Samsung Galaxy J7 Prime Series Hitam ... - Hitam Free Waterproof Source Samsung Galaxy J7 Prime Tough Armor Hybrid Case jpg .
Case Tough Armor Carbon For Samsung Galaxy J510 J5 2016 Abu Free ... - Tempered Source Gold Free Source Carbon Rugged Armor Cover Case for Samsung Galaxy .
Case Tough Armor Carbon For Vivo V5 Series Abu - Daftar Harga ... - Home · Case Tough Armor Carbon For Vivo V5 Series Abu; Page - 5.
Case Tough Armor Carbon For Samsung Galaxy Grand Prime Abu Free Usb ... - CASE TOUGH ARMOR CARBON FOR SAMSUNG GALAXY J2 PRIME ROSEGOLD FREE USB OTG MICRO ✓.
Alat Bantu Untuk Pria Vagin Getar Goyang Suara • Bearhug Legging Pants Bayi Laki-laki 6-12M Polos Navy • New Fashion Women Synthetic Leather Zip Purse Card Holder Clutch Long Wallet (Black) - Intl • Kebunbibit Tanaman Anggur Hijau 25cm • Spon Beauty Blender - 1 Pcs - Random Colour • Trim N Slim Skirt Starry • Navy Club Tas Ransel Kasual 6262 Backpack Daypack Bonus Bag Cover - Merah • Han Han Kucing Siklus Otomatis Penyaring Minum Air Mancur Otomatis Minum • Evod Single Blister Pack - 1100mAh - Hitam • Asus X540YA-BX102D - AMD E1-7010 - 2GB - 500GB - 15\ - DOS - Silver"


text:
O'Shea and Whelan était une entreprise familiale irlandaise de taille de pierre et de sculpture originaire de Ballyhooly dans le comté de Cork. La firme se distingue par ses travaux dans l'architecture gothique au milieu du .

Parmi les artisans de cette entreprise, il y a les frères James et John O'Shea, ainsi que leur neveu Edward Whelan. James et John sont les fils d'un artisan qui est venu s'installer à Cork afin de trouver du travail, notamment dans le chantier du futur Convamore House. Il fonde ensuite un atelier de taille de pierre qui, après sa mort, est hérité par John et James, qui eux-mêmes sont souvent appelés par leur proches . Edward Whelan était également une figure importante de l'entreprise. Néanmoins, James O'Shea était le plus notable de la famille. En plus de son travail dans la maçonnerie architecturale, il a exposé des sculptures d'art de style classique. O'Shea and Whelan a notamment taillé la croix celtique qui orne la tombe de Talbot Baines Reed.

Sculpteur irlandais du XIXe siècle
Entreprise ayant son siège en Irlande

redpajama_set_name: RedPajamaWikipedia


Academic researchers in Nigeria under the aegis of Academic Staff Union of Research Institutions (ASURI) on Monday, stormed the National Assembly in Abuja, protesting against the exclusion of Colleges of Agriculture and other research institutions in the country as beneficiaries of research funds by the Tertiary Education Trust Fund (TETFUND).
The researchers who carried placards with various inscriptions lamented that all the Colleges of Agriculture and Research Institutions in the country put together have not received the budgetary capital allocation that a university or polytechnic receives from same TETFUNF in a year.
Some of the placards read, "Universities are saturated with TETFUND while Colleges of Agriculture are dying; TETFUND act now", "To change Nigeria, fund research now", "No research fund, no development, no future" and "Amend TETFUN Act now; 70% of Nigerians are farmers yet Colleges of Agric are denied funding".
Speaking on behalf of the lecturers, Secretary-General of ASURI, Dr. Theophilus Ndubuaku, said the union was in support of the move by the House of Representatives to amend the TETFUND Act to expressly provide that Colleges of Agriculture and research institutions benefit from the fund.
He lamented that whereas TETFUND was established specifically for rehabilitation, restoration and consolidation of tertiary education in the country, Colleges of Agriculture were unfortunately not included among tertiary institutions that the fund was supposed to fund.
Ndubuaku stated that whereas the lecturers were not against the funding of tertiary institutions, they strongly feel that justice can only be seen to be done if resources are equitably distributed.
He expressed disappointment that former President Goodluck Jonathan's administration could not make good its promise to run the economy based on science and technology, just like the other governments before it.
He said: "It is apt to mention that for now, most of the technologies generated in research institutes are largely funded by the researchers with their personal resources because if they do not conduct research, publish and sponsor their training for higher degrees, they will stagnate and perish, in other words they will not get promotion.
"Given this scenario, the Nigerian research-institute based researchers are compelled to use their personal resources to conduct research and publish it.
"Nigeria is endowed with an array of research and development infrastructure that cover almost all sectors of human endeavour. It is an indisputable statement of fact that the reason why Nigeria has remained under-developed is because past leaders have not had the political will to adequately fund the many research and development institutions.


Korean White Kimchi Recipe – Kimchi, also spelled gimchi or kimchee, is a traditional Korean fermented dish made of some select vegetables with varied seasonings, most commonly referring to the spicy baechu variety. Kimchi is the most common Korean banchan, or side dish, eaten with rice along with other banchan dishes.
Kimchi is also a common ingredient and cooked with other ingredients to make dishes such as kimchi stew (kimchi jjigae) and kimchi fried rice (kimchi bokkeumbap). Kimchi often tastes better with age.
Korean White Kimchi, this type of kimchi is not as common as the spicy variety and is a delicacy of Korea's fiercely independent northern provinces. Fresh pear gives this variety of kimchi a tart, mild flavor that has little in common with the tear-inducing red chili kimchi found throughout the rest of the country.
Remove the outer leaves of the cabbage and halve it lengthwise.
Rub a handful of the salt between all the leaves. Set aside.
Combine the water and remaining salt in a large pot and stir until the salt is dissolved.
Soak the cabbage in the salt water for about 3-6 hours.
Drain and briefly rinse the cabbage with water, allowing it to retain some of the salt water.
To prepare the Stuffing, combine all the ingredients in a large bowl and mix thoroughly.
Using your fingers, separate the leaves of the cabbage halves and spread some Stuffing between each, ensuring all the leaves are well coated on both sides.
Place the stuffed cabbage halves in an airtight container and allow them to sit at room temperature for 3-4 hours, then store in the refrigerator overnight before serving.


60 Tix Left for NOV 2 ALBUM RELEASE!
A great show. He played some older songs at the request of the audience and new ones as well. I really enjoyed it he plays several harmonicas & a kazoo. Can't wait to see him again.
Genuine, moving night. Great performances by Todd & all.


text:
Haino, Keiji / Charles Hayward - A Loss Permitted To Open Its Eyes... vinyl lp (due to size and weight, this price for the USA only. Outside of the USA, the price will be adjusted as needed)
Lloyd, Richard - The Radiant Monkey (Mega Blowout Sale)
Ichiyanag, Toshii / Claude Ledoux - Cloud Atlas / Vertical Study
Zappa, Frank - Live At The Palladium New York Halloween 1977 (Mega Blowout Sale)
Brandt Brauer Frick - Echo 2 x vinyl lps + CD (due to size and weight, this price for the USA only. Outside of the USA, the price will be adjusted as needed)
Mahavishnu Orchestra - Whiskey A-Go-Go, 27 March 1972 (Mega Blowout Sale)
Red Kite - Red Kite
Moondoc, Jemeel / Hilliard Greene - Cosmic Nickelodeon
Parker, Evan / Sylvie Courvoisier - Either Or And
Acid Mothers Temple & The Melting Paraiso U.F.O. - Wandering The Outer Space vinyl lp (due to size and weight, this price for the USA only. Outside of the USA, the price will be adjusted as needed)
Renaldo & The Loaf - Songs For Swinging Larvae / Songs From The Surgery 2 x vinyl lps (due to size and weight, this price for the USA only. Outside of the USA, the price will be adjusted as needed)
Alesia Cosmos - Aeroproducts (expanded / remastered) 2 x CDs
Hifiklub / Mike Cooper - Aran Stories
SKU 05-RPTD 025CD
"British guitarist Mike Cooper collaborates with Hifiklub, a French instrumental ensemble from Toulon, for an impressive musical reading of Robert J. Flaherty's 1934 silent film Man of Aran.
Since 2007, French quartet Hifiklub has been building a protean work that evolves through an impressive series of collaborations. The band's catalog is wide and far-reaching, and includes more than 130 collaborations with such renowned musicians as Lee Ranaldo, Alain Johannes, Jad Fair, Fatso Jetson, R. Stevie Moore, amongst others.
For the past 50 years Mike Cooper has been an international musical explorer, performing and recording, solo and in a number of inspired groupings and a variety of genres. Initially a folk-blues guitarist and singer songwriter, his work has diversified to include improvised and electronic music, live music for silent films, radio art and sound installations.
In 2017, Hifiklub got in touch with Mike Cooper to record (and eventually perform) a live soundtrack to controversial filmmaker Robert J. Flaherty's silent movie Man of Aran. Flaherty was an American filmmaker who directed and produced the first commercially successful feature-length documentary film, Nanook of the North, in 1922. Following the success of this film, he continued the development of this new genre of narrative documentary with Moana (1926), set in the South Seas, and Man of Aran (1934), filmed in Ireland's Aran Islands.
When devising the lyrics for his performance, Cooper dug into Irish writer John Millington Synge's book The Aran Islands, published in 1907. This book provided the initial inspiration for Flaherty's film. Cooper cut up texts from the book and sang them for the record, while relying on a different set of texts from the same source, for the live performance. It was Cooper who contacted Lebanese indie label Ruptured for the release of the album; no stranger to the Lebanese experimental scene and its various key players, Cooper has already recorded two albums in Beirut. Edition of 400."
LabelRuptured


You've heard the saying, "finding a new job IS a full-time job", but your success is based less on how much time you spend than by how you spend your time.
While applying online and working with Recruiters is recommended, the ROI on these strategies tends to be lower (25%) than networking ( 75%), so use your time wisely. If you only have 10 hours/week to spend on your search, networking activities should account for 7+ hours of your efforts.
– Get your materials (resume, Linked in, branding statement, etc.) aligned with your target role.
– Research and identify a list of companies (15+) in your target market.
– Build your contacts and brand consistently (e.g., add connections/get recommended on Linked In, etc.).
– Research who you know at your target companies. Review your Linked In network including 2nd level connections. Set up meetings to get your foot in the door at your target companies.
– Follow company pages on social media/news sites to stay abreast of what's happening in their world. Attend networking events where company contacts may be speaking or attending.
– Where appropriate, pass your resume along through your contacts. Be proactive about staying in touch.
– Watch for job postings and any news online that indicates your target companies are hiring in your area (e.g., mergers, expansion, etc.). Keep expanding your network.
– Prepare for interviews by writing out answers to common questions and doing mock interviews.
– Be proactive on social media and at networking events to stay top of mind with your contacts.
– Evaluate. Are your target companies panning out or do you need to expand the list? Have you discovered new data that causes you to revise your pitch/target slightly?
– Be open to new info along the way. The best way to gain clarity is to take action, so it's not usual to refine your search (but be careful not to change course with every piece of feedback).
– Conduct phone screens and interviews for the roles you're interested in.
– At this stage you should be a candidate for existing opportunities. If you're not finding viable openings, re-evaluate your target, materials, and approach to identify potential obstacles.
– Make final decisions about which job you'll accept. Do your homework on company culture, future growth opportunities, and alignment with your career goals. Ask questions to make sure it's a two-way fit.
– Negotiate the offer. Always.
– Once you have an official offer you intend to accept, gracefully decline opportunities that are "in process".
– Thank your contacts for their assistance and support (even if they weren't instrumental in helping you obtain the job you ultimately accepted). Offer to help them in the future.
– Maintain good will among your networks and continue to cultivate them. Building your connections is the hard part. Maintaining them is easier.
Lastly, be aware of situations that can lengthen the time you may need for a job transition including: relocation/change in geography, Visa/needing sponsorship, major career change/industry shift, change in level (big promotion or step back), lack of job target clarity, etc.


text:
HomeEntertainmentSquid Game Season 2 Storyline Came into Light
Squid Game Season 2 Storyline Came into Light
By Ayman Bhorania
Let's discuss the well-known Korean Netflix Show that captured the hearts of its viewers. And the wait for the next season is challenging. You're all aware of what we're referring to. The one and only 'Squid Game'.
The series centers around a competition in which 456 players. All of whom are deeply in debt, risk their lives to play a series of lethal children's games for the chance to win a 45.6 billion reward.
It is Netflix's most-watched series as of November 2021, having become the top-viewed show in 94 countries. Garnering more than 142 million member residences. And accumulating 1.65 billion viewing hours during its first four weeks of release, overtaking Bridgerton for the title of the most-watched show.
Well, the show's main character, Gi-hun, made a statement about the future season of Squid Game. The statement gave us light into what's coming up next, actually sort of.
Squid Game Star Lee Jung-Jae Teases Storyline For Season 2
This knowledge can be obtained as a spoiler to individuals who haven't seen the first season. You're all aware of what happened at the end of Season 1. The contest was won by player 456, but at what cost?
After ensuring that the family of those who were unable to complete the games were cared for, the actor appears to plan to return to the venue where the tournament was conducted to exact his vengeance.
The additional material is intended for people who are eagerly anticipating the new season. Lee has spoken with the show's creator, Hwang Dong-hyuk, about the fate of his character.
Lee says he's hoping for a twist in the season 2 storyline.
He stated, "I hope there's some sort of twist that'll be exciting and surprising for me and the viewers. If it was predictable, it would be no fun. I have no idea what will happen or the scale of it, but the only thing [Hwang] did tell me was that Seong Gi-hun is going to be in the show again, and he's going to be playing in the arena again."
Creator Of 'Squid Game' Is In Talks Regarding Season 3
Hwang Dong-Hyuk, the show's creator, stated in a new conversation that he has not only finalized the plotline for the second season. But he is also in discussions with Netflix about the show's third season.
According to a source, Hwang stated in a recent interview with Korean network KBS, "I'm currently in the midst of discussions with Netflix about season 2 and season 3."
The director did state that the conversations are still in the early stages and that nothing has been finalized. "I think we'll be reaching some sort of conclusion soon. We know that many people are waiting, so everyone is working hard to prepare for the next season with a positive outlook," he continued.
Even if you haven't seen the show, it's difficult to avoid hearing about it. This well-known show will now run for an extended length of time. As not only season 2 is renewed but also season 3 is in the talks. We hope we get a Season 3 too, but for now, let's stay focused on Season 2.
If you're looking forward to Season 2, and the wait isn't getting any easier, you can read this article in the meantime. Hopefully, the shows recommended will keep you entertained while you wait.
Also, as soon as any official announcement about the upcoming season becomes available, we will keep you informed. Till then, stay tuned with us.
Previous articleHow and Where To Watch Jujutsu Kaisen 0 Movie?
Next articleWitchbrook by Chucklefish: Expected Release Date, Gameplay and Updates
Ayman Bhorania
A Gamer And A Writer By Heart. I Write News Articles On TheTealMango And Also Specialize In Writing Technical Guides Around Different Operating Systems. I Love Watching Movies And Shows. Definitely A Complete Extrovert.


I was searching for a Property and found this listing (MLS® #5980549). I have the following questions about this property - - - Please send me more information regarding 687 Meadowbrook Cir, Steamboat Springs, CO, 80487. Thank you!
I'd like to request a showing of 687 Meadowbrook Cir, Steamboat Springs, CO, 80487 (MLS® #5980549). Thank you!


- CLASSROOM CRAFTS - ART IDEAS MARYANN F. KOHL - Teachers.Net Gazette - STRESS RELIEF, STUDENT MOTIVATION, ORGANIZATION IDEAS, CLASSROOM MANAGEMENT, LESSON PLANS, DISCIPLINE, AND MORE - featuring columns and articles by top names in education and your teacher colleagues around the world! The Teachers.Net Gazette is a clearinghouse for teacher writing, from well-reasoned education essays to teacher prose, poems and humor! Bookmark the Teachers.Net Gazette and tell a friend!
Toddlers wrap silvery aluminum foil around cardboard shapes, pressing and pinching it to fit, discovering foil's unique gripping characteristics.
Tear a stack of aluminum foil squares and fan them out on the worktable.
Arrange cardboard shapes or small boxes on the worktable.
Encourage the toddlers to wrap foil around cardboard shapes, pressing and pinching it to fit.
They can cover and wrap the shape until satisfied.
Toddlers like to stack their finished foil pieces into tall towers, enjoying how they tumble down again.
Provide brightly colored tape for toddlers to join shapes together.
Encourage the toddlers to stick their shapes to a background to make a 3-D collage with loops of masking tape.
Pour a puddle of liquid starch on the paper, and then squirt a tablespoon (15-30ml) or so of liquid tempera paint in the puddle. The toddler mixes the paint by hand on the paper while spreading the paint and fingerpainting.
Premix 1/2 cup (120ml) liquid starch and 1/2 cup (120ml) tempera paint in a cup. Spoon onto paper.
Dissolve 1 cup (240ml) cornstarch with 1/2 cup (120ml) cold water and set aside. Boil 3 cups (.72liter/720ml) water in a pan and remove from heat. Add cornstarch mixture to hot water, stirring constantly. Place pan back on heat and boil until clear and thick for about one minute. Add 1/2 teaspoon (2.5ml) food coloring. (Make sure cornstarch is dissolved before adding or it will be lumpy.) Paint with warm paint.
Mix 1/4 cup (60ml) tempera paint with 1/8 cup (30ml) liquid soap.
Printed with permission from The Big Messy Art Book, by Kohl, published by Gryphon House.
Children love toilet paper! Wrapping each other with long streamers of toilet paper is great fun and makes for a play day sculpture that will not be forgotten.
Begin by unwrapping or unrolling a roll of toilet paper a little ways. See how it works. Hint: Don't be surprised to see the roll of paper bouncing across the floor as it unravels.
For the first play sculpture; one friend volunteers to be the sculpture model, and the other volunteers to be the artist. The artist wraps the sculpture model with toilet paper in anyway enjoyable to both. Make an entire "mummy", or try for other costumes or designs.
When one sculpture is finished, trade places!
Use leftover sculpture paper for making homemade paper, or for papier-m�ch� projects, or paper dough.
Make outfits for toy animals or dolls.
Make a huge pile of toilet paper to hide in.
Wrap furniture like the famous artist, Cristo.
Wrap objects in toilet paper to disguise them.
Stack rolls of toilet paper like blocks.
Cover a doorway with strips of toilet paper.
Create a spider web or maze with toilet paper in an entire room.
Use toilet paper to create a hiding house, cloud world, or other pretend area.
Paint with a roll of toilet paper.
Drip Liquid Watercolors, food coloring, or paper dyes on a roll of toilet paper.
easy, spicy modeling dough...can be mixed by hand, baked, and is delightfully edible.
Pour 1 package gingerbread mix into a large bowl.
Add 1/4 cup milk and 1/4 cup salad oil.
Remove from bowl and wrap with plastic, chill for 1 hour.
Mold little balls of dough into free form sculptures, or roll dough 1/4" thick and cut out shapes with cookie cutters or a knife. Bake on greased cookie sheet with room between for 12 minutes at 350F.
Then cool and remove from the sheet.
Decorate with icing and candy, raisins, etc.
Before baking, poke a hole in the gingerbread with a toothpick or straw. Then bake. Tie a string, ribbon, or yarn through when cool.


text:
% ARABICA
CO-WORKING HUB
TENANTS OFFICE
LIFESTYLE CUBE
ROSEWOOD PHNOM PENH
Brasserie Louis
Zhan Liang
Vattanac Capital celebrates its official inauguration.
Clarins grand open.
Dare to Bare 2015
Copyright © 2022 - Vattanac Capital
Poltrona Frau is a furniture-maker founded in 1912 by Sardinian-born Renzo Frau in Turin, Italy, headquartered since the early 1960s in Tolentino, Italy and specializing in leather seating for interior and automotive applications.
Vattanac Capital Mall
Level 1, Unit M1 and Level 6, Suite 06-02 & 06-03, Preah Monivong Boulevard, Sangkat Wat Phnom, Khan Daun Penh, Phnom Penh, Kingdom of Cambodia.
Connect with us on Telegram
Vattanac Capital
enquiries@vattanaccapital.com
66 Preah Monivong BOULEVARD, Sangkat Wat Phnom Khan Daun Penh, Phnom Penh
Clarins grand open – 22nd January, 2016 Copy
Vattanac Capital celebrates its official inauguration in the heart of the capital, Phnom Penh
Clarins grand open – 22nd January, 2016
Dare to bare – May 10th, 2015


text:
The Salvation Army Hawaii gives back to Hawaii's communities on Giving Tuesday.
Written By: David Sayre
(Nov. 30, 2021) – On any given Tuesday, The Salvation Army provides social service outreach programs to hundreds of individuals and families across Hawaii. And, Giving Tuesday 2021 on Tuesday, Nov. 30, is no exception. The Salvation Army estimates they will be providing hot meals and non-perishable food packs to more than 700 people in the islands on Tuesday as part of their regular outreach.
"We're fortunate to serve those in need in our island communities every day of the year," said Major Phil Lum, Divisional Commander of The Salvation Army Hawaiian & Pacific Islands. "On this Giving Tuesday, for example, a snapshot into services we are holding includes regular food pantries on each island, distributing meals on Kauai, Maui and Hawaii Islands, and even providing hot showers for homeless individuals on Kauai. Of course it wouldn't be a Giving Tuesday without an ask; monetary donations to support our programs across Hawaii can be made at Hawaii.SalvationArmy.org."
As part of The Salvation Army's national observances of Giving Tuesday to highlight the 2021 holiday campaign of Hope Marches On, The Salvation Army Hawaiian & Pacific Islands debuted a virtual percussion choir video featuring Polynesian rhythmic influence in a marching cadence using traditional Hawaiian and Polynesian musical implements and hula.
[Media Note – here is a link to the video}
Examples of ongoing social service outreach activities happening statewide on most Tuesdays throughout the year, including Giving Tuesday, Nov. 30:
The Salvation Army Lihue Corps
Hot meals from 11 a.m. to noon – serving 60 to 80 individuals
Social Services – 10 a.m. to 2 p.m. – serving 10 to 15 individuals
Mobile hot shower – 10 a.m. to 1 p.m. – serving 5 to 15 individuals
The Salvation Army Kaneohe Corps
Social Services - Food Pantry from 9 a.m. to noon – serving 25 to 35 individuals.
The Salvation Army Kahului Corps
Homeless outreach breakfast and food bag distribution in Paia – 9 a.m. – serving 25 to 30 individuals
Homeless outreach mobile food bag distribution in Kahului/Wailuku – 11:30 a.m. – serving 20 to 25 individuals
Social Service Office open 9 a.m. to 3 p.m.
The Salvation Army Kona Corps
Hot meal distribution from 5:30 p.m. to 6:30 p.m. – serving 225 individuals
Food Pantry from 11:30 a.m. to 1 p.m. – serving 320 individuals.
Food bag delivery to seniors – noon to 1 p.m. – serving 50 seniors.
For more information about The Salvation Army's social service outreach efforts in Hawaii and to provide monetary donations, visit hawaii.salvationarmy.org.
About The Salvation Army – Hawaiian & Pacific Islands Division
The Salvation Army – Hawaiian & Pacific Islands Division covers the state of Hawaii and the Pacific Islands including Guam, Republic of the Marshall Islands and The Federated States of Micronesia. In 2021, The Salvation Army was ranked #2 on the list of "America's Favorite Charities" by The Chronicle of Philanthropy. The Division offers a wide variety of programs throughout the islands including: affordable senior housing; at-risk youth services and housing; camp & conference center; church/religious services and programs; emergency disaster services; family stores; food distribution and feeding programs; homeless services; The Kroc Center – Hawaii's largest community center; preschools & day care services; older adults; social services – emergency assistance; substance abuse treatment; women's auxiliary; and women's ministries. For more information, call 808-988-2136, visit hawaii.salvationarmy.org, or follow us on Facebook and Instagram.
Related Content: Press Releases
Subscribe to receive more stories, directly in your inbox!
B2S, Bell Ringer, Christmas, Disaster Service, Eruption, Giving Tuesday, Hawaii Island, Kauai, Love Beyond, Maui, Mauna Loa, Oahu, Press Releases, Red Kettles, Thanksgiving, Thrift Store, Volcano, Volunteers


We hope you can find everything you need. At Hannah Hair Braiding Styles we are focused on providing high-quality service and customer satisfaction - we will do everything we can to meet your expectations.
At Hannah Hair Braiding Styles (Lawrence Street Bryan) we install extensions, weaves, Micro Braids, Senegalese Twist, Goddess Braids, Box Braids, Crochet Twist, Simple Ghana Weaving, Dreadlocks, Gorgeous Twist Out Natural Hair, Havana Twist Marley Hair Men Twists Corn Roll, Ghana Braids, Nubian and the list goes on.


Add butter to large stock pot.
Place diced bacon( optional) in large stock pot over medium-high heat.
Cook until almost crisp; add onions, and cook 5 minutes.
Stir in potatoes, and season with salt and pepper.
Bring to a simmer and add flour to form paste.
Cook uncovered for 5 minutes, add milk and simmer for 15 minutes, or until potatoes are fork tender.
Drain clams, reserving clam liquid; stir clams and 1/2 of the clam liquid into the soup.
Cook for about 5 minutes, or until heated through. Do not allow to boil.


Photography technology research paper "Amelia Earhart said adventure is worthwhile in itself," her namesake says. "I think there's a new focus on adventure that we've only seen in the last five to 10 years. But whatever your version of flying is – it could be starting a business, it could be something entrepreneurial – we want to encourage people to pursue their own adventure."
linda pastan ethics A network of human rights activists are drafting a letter to Secretary of State John Kerry and working to petition the Inter-American Commission on Human Rights and the U.N. Human Rights Committee based on alleged violations of international law.
steroid research paper As its name suggests, the Galaxy Round is rounded on both sides of its Full HD Super Flexible AMOLED display, allowing it to rock back and forth while on a flat surface. In fact, similar to the Moto X's active display, Galaxy Round owners can gently rock their Round smartphone to one side or another - known as Roll Effect - for a quick view at the time and alerts (see it in action in the video below).
custom written advantages Currency traders contacted by Reuters said it was unsurprising that dealers tried to profit from advance knowledge of client orders but given the size of the market, manipulating the benchmarks would be difficult unless the trader had a very large order or was dealing in two rarely traded currencies.


text:
Glaucoma in the African American and Hispanic Communities
African Americans and Hispanics are at increased risk of developing glaucoma. Find out why and learn about important steps that can prevent vision loss from this eye disease.
Cataracts are the leading cause of blindness worldwide, but they can be "cured" with cataract surgery. However, a leading cause of irreversible blindness is glaucoma, and it is a significant public health problem because it is estimated that half of all people who have glaucoma do not know that they have it. This is because the most common form, open-angle glaucoma, has no symptoms, and is thus often called the "silent thief of sight."
Primary open-angle glaucoma is estimated to affect 2 to 3 percent of the population in the United States in adults ages 40 and older, and the prevalence is estimated to increase as the population overall skews more towards people in older age groups. More than 3 million Americans age 40 and older have glaucoma, with an estimated 2.72 million have open-angle glaucoma, the most common form of this disease1. It has been estimated that in the United States there will be more than 4 million people over the age of 40 with glaucoma by the year 2030, and will increase to more than 6 million people in this same age group by the year 20502. View our glaucoma infographic.
The Leading Cause of Blindness in African Americans
Based on data analyzed in 2012, more than 520,000 of Americans with open-angle glaucoma are African-American. Indeed, open-angle glaucoma is the leading cause of irreversible blindness in African Americans. One of the landmark studies to examine the prevalence of open-angle glaucoma in the African-American community was the Baltimore Eye Survey3, for which the results were published in 1991. This study showed that the prevalence of glaucoma was 4 percent in African Americans ages 50-59, and increased to approximately 13 percent in African Americans ages 80-89. Adjusting for age, African-Americans were 3 to 4 times more likely to have a diagnosis of glaucoma compared to Caucasians. And not only was the overall prevalence higher in African Americans, but the disease began at an earlier age. This study also showed that half of all patients, regardless of ethnicity, were unaware that they had glaucoma.
Why are African Americans at Increased Risk?
Why are African Americans more susceptible to open-angle glaucoma compared to Non-Hispanic Whites? The reasons for this are unclear, although some experts believe it may be related to genetic differences, such as those that lead to differences in the anatomic structure of the optic nerve. More recently, the ADAGES study (African Descent And Glaucoma Evaluation Study)4 was designed to examine the vision and optic nerve structure of Americans of African ancestry versus of those of European ancestry. At the time of the study, more advanced optic nerve imaging techniques were used to examine the optic nerve structure. The study found that there were differences in optic nerve structure between these two groups, including a larger optic nerve area in healthy eyes in people of African descent. The larger size of the nerve can sometimes confuse the diagnosis of glaucoma, since larger nerves can give a false impression of glaucoma.
Overall, in the African American population, open-angle glaucoma is more common, more difficult to treat, and more severe at the time of diagnosis, which results in more blindness.
Glaucoma Risk in Hispanics
Hispanics are another ethnic group that show increased risk of open-angle glaucoma. The Los Angeles Latino Eye Study (LALES)5 was designed to study the prevalence of eye diseases in Latinos, with most of the patients in this study being of Mexican ancestry. The prevalence of open-angle glaucoma in Latinos (approximately 5 percent) was found to be similar to those of African Americans. In this study, 75 percent of Latinos with glaucoma were previously undiagnosed, which may be attributed the large percentage of patients who did not have health insurance. The LALES study also supported findings from other studies that suggest that blood pressure is very important in patients with glaucoma. Patients with very high blood pressure have higher eye pressure, which is known to be a major risk factor in open-angle glaucoma. On the other hand, patients with very low blood pressure also have increased risk of open-angle glaucoma, which is thought to be related to poor blood flow to the optic nerve.
In another recent study6, it was estimated that of the more than 2.7 million people with open-angle glaucoma in the United States, the highest number are people aged 70 to 79 years, women, and non-Hispanic whites. However, by 2050, they estimated that more than 7.3 million people will have open-angle glaucoma, with the highest number still among people ages 70 to 79 years and women, but there will be a shift to Hispanics. Notably, this study predicted that the largest demographic group with glaucoma will be Hispanic men.
Decreasing Your Risk
So what should you do? The best defense against blindness related to glaucoma is having a comprehensive, dilated eye exam. Since glaucoma is age-related, and the diagnosis is often made over a period of time as the eye doctor monitors you, it is worth having a baseline examination by age 40. It is also important to consider whether you have other risk factors for open-angle glaucoma, including increasing age over 40, high eye pressures, a family history of glaucoma, very high or very low blood pressures, extreme nearsightedness, or diabetes. If you have one or more of these risk factors, a comprehensive eye exam including pupil dilation is advisable.
View the entire glaucoma infographic.
Glaucoma: Essential Facts (Publication)