What it is
Recoil can build a .rcl file as a loadable Rebol 3 native extension (.rebx), so Rebol commands are written in Recoil and imported from a Rebol host with no handwritten C glue.
This is different from the ext/ modules, which wrap a C library for use from Recoil (see FFI & Systems). A .rebx goes the other way: Recoil code is called by Rebol. It is also different from the rebol [...] form, which embeds the Rebol interpreter inside a Recoil program.
Use it when a Rebol script needs native-speed commands, native handles or a typed wrapper over a C library. The generated glue targets the Oldes Rebol3 extension ABI (lib/rebol-extension.h).
Building an extension
r3 -s recoil.r3 --rebol-extension examples/rebol-extension/scalars-and-series.rcl --name scalar-series-demo
This writes scalar-series-demo.rebx. Without --name the base name comes from the source file; -o FILE chooses the output path. Import it from Rebol:
import %./scalar-series-demo.rebx
print addi 40 2
- The artifact is always a dynamic shared library;
--staticis rejected with--rebol-extension. - The source declares
Type: rebol-extensionin its header. The generated C exports the entry pointsRX_Init,RX_CallandRX_Quit;RX_Initreturns generated Rebol module source with onecommand [...]declaration per command. - Use
import %file.rebxin scripts;load-extensionis the lower-level host primitive. - See CLI & Targets for the shared build options.
A worked example
A command is a function carrying a #rebol-command annotation. The annotation holds the Rebol spec; the function signature holds the Recoil types. The command name is the function's name, and it must be listed in Exports. This is a trimmed excerpt of examples/rebol-extension/scalars-and-series.rcl:
Recoil [
Title: "Add Extension"
Type: rebol-extension
Name: add-test
Imports: [
std/rebol/extension [rebol-block rebol-object]
]
Exports: [addi boom]
]
addi-impl: func [
a [i64!]
b [i64!]
return: [i64!]
] [
return a + b
]
boom-impl: func [return: [i64!]] [
return make error! "boom"
]
addi: func [
#rebol-command [
"Add two integers"
a [integer!]
b [integer!]
return: [integer!]
]
a [i64!]
b [i64!]
return: [i64!]
] [
return addi-impl a b
]
boom: func [
#rebol-command [return: [integer!]]
return: [i64!]
] [
return boom-impl
]
import %./add-test.rebx
print addi 40 2 ; 42
err: try [boom]
if error? err [print mold err]
The doc-comment string in the spec is emitted into the generated Rebol module source. A Recoil error returned from a command is converted to RXR_ERROR and is catchable with try. An argument of the wrong Rebol type gives Rebol's generic bad-arguments error.
How types map
| Command spec type | Recoil target type | Notes |
|---|---|---|
integer! | i64! | scalar |
logic! | logic! | scalar |
decimal!, percent! | f64! | percent is a decimal in the frame |
none! | none! | returns |
string!, file!, url! | rebol-string-view! / rebol-file-view! / rebol-url-view! (borrowed), or string! / file! / url! (copied) | returns are copied into Rebol-owned values |
binary! | rebol-binary-view! (borrowed), binary! (copied); slice! [u8!] for returns | respects the series index, so next #{010203} exposes two bytes |
block! | rebol-block! in, rebol-block-builder! out | helpers in std/rebol/extension: rebol-block/len, /type-at, /at, /word-named-at, /make, /append |
object! | rebol-object! in, rebol-object-builder! out | helpers rebol-object/field, /set-field, /make, /append |
word! | i64! (argument only) | used with an object! for callbacks |
handle! | c-pointer! in; none! target out | see below |
Refinements in the spec become extra logic! target parameters after the ordinary arguments, in spec order; a refinement may carry one typed value, passed as the next parameter (/limit count [integer!]).
Handles and callbacks
#rebol-handle name [size: N free: fn mold: fn get: [...] set: [...]] registers a native handle kind; a command declared return: [handle!] allocates a host-owned handle, and a handle! argument arrives as an opaque c-pointer! after the host validates the handle id. With several handle kinds, name the kind in the metadata: return: [handle! recoil-stream]. get:/set: hooks expose path access such as stream/level. See examples/rebol-extension/handles.rcl.
A callback is an object! context plus a word! naming a function in it, invoked with rebol-callback/call or rebol-callback/bound-call (synchronous; i64!, logic! or f64! scalars only), or queued with rebol-callback/async-call.
Ownership at the boundary
go capture, and do not return it. Copy it into Recoil-owned storage first (declare the parameter as string!, binary!, file! or url! to have the wrapper copy it), especially if the extension may call back into Rebol before it finishes reading.- Returns are copied. The wrapper copies string, file, URL and binary results into Rebol-owned series. A
string!return is released after copying; afile!orurl!return is a borrowedchar *that is never freed, so return a literal, or return astring!if you need to hand back owned text. - Builders are host-owned.
rebol-block-builder!andrebol-object-builder!append scalars, copied strings/binaries and nested builders into Rebol-owned results. - Handles are host-owned. The optional
free:hook runs when the host frees the handle. rebol-callback!is only a carrier for the borrowed context/word pair; the compiler rejects it as a command return type, a module-scope binding, and in aggregate or closure/task captures.
Limits
- Block and object returns are built once with the builder helpers; mutating them is not supported.
- Callbacks: fixed scalar shapes only (one
i64!/logic!/f64!argument, or twoi64!); async callbacks are fire-and-forget and return only a queue status. rebol-command/do-commandssubmits a borrowed block to the host; it does not return the evaluated result.- Command names are the function names; aliases are not supported. Commands and handle specs are top-level only, and a command may not exceed the ABI's argument maximum. Malformed specs are rejected with
P1040. - Handle names go into the host's global handle registry, so prefer prefixed names such as
recoil-stream. --staticis rejected;--nameis accepted with--rebol-extension.
Full reference: docs/user/rebol-native-extensions.md. Runnable sources live in examples/rebol-extension/; the focused tests run with -tg rebol-extension. To wrap a C library for use from Recoil instead, see docs/user/extension-authoring.md and FFI & Systems.