compile/apply-fence
Symbol: compile/apply-fence
Syntax: (apply-fence NAME RAW META) – normally produced by the
triple-backtick fence reader syntax rather than written by hand.
Use-case: Compile-time expansion of a fenced foreign literal. A fence captures raw source in a declared foreign format – a bash script, and in principle any notation awkward to write as s-expressions – and lowers it into ordinary Koskari source before compilation continues.
Reader shorthand:
```NAME OPTION: VALUE ...
raw fence body
```
The fence name is the first word after the opening backticks (whitespace
optional, so ```bash and ``` bash are equivalent). The
rest of the opener line is read as keyword/value option pairs; each option
requires a value on the same line. The body is captured as raw text,
verbatim, until a line containing only three backticks closes the fence.
Body lines are dedented by the closing line’s indentation, so an indented
fence does not leak its indentation into the payload.
Fence reading is governed by the reader/fences pragma (default on);
(pragma [reader/fences False]) restores backtick’s plain quasiquote
readings for the module.
A fence reads as a single value, so it may appear in expression position:
var script ```bash
cd foo/bar/baz
curl -s https://example.org/api/data/
```
(script)
Behaviour: NAME and RAW are required; META is an optional keyword
plist. The special:
resolves the handler
fence/NAMEin the module namespacecalls it as
(handler RAW META-DICT)whereMETA-DICTis a dict withname,filename,line,col, andoptionsentriesrequires the handler to return valid Koskari source
compiles the returned source in place of the fence
META carries filename:, line: and col: (the payload’s origin line
and dedent base, used to remap diagnostics into the fence body), and
options: (the opener-line pairs as a nested keyword plist).
Handler resolution: a bare-name binding resolves only when it is a
FenceHandler (the wrapper used by built-in fences); a fence/-prefixed
binding resolves when it is a FenceHandler or any callable. A module can
therefore define its own fence by binding a two-argument function to
fence/NAME. Handlers run at compile time and must not suspend; a
suspending Koskari handler raises KoskariSyncViolation.
Errors: SpecialError for malformed apply-fence forms, unknown
fences, and handlers returning non-source; FenceError for invalid fence
options and foreign-source parse errors. A handler may attach a source
position (see the line/col entries in META-DICT) so its FenceError
points into the fence body.
Built-in fences:
fence/bash– bash script capture, built on therun-processbuiltin. By default the fence evaluates to an async callable: invoking it runs the script viabash -c, and any positional arguments passed to the callable are forwarded as the script’s positional parameters –(script "alpha" "beta")makes$1bealphaand$2bebeta, with$@behaving accordingly. With theimmediate: Trueoption there is no callable at all: the fence expands to therun-processcall in place, so the fence expression itself evaluates to the finished result.Either way a run produces a
ProcessResult, the subprocess analogue of an HTTP client’s Response: it carriesargs,returncode, and the rawstdout/stderrbytes, withtext/stderr_textfor decoded output and(.json result),(.jsonl result), and(.yaml result)(requires PyYAML) for parsed stdout. A nonzero exit status is reported, never raised – call(.raise_for_status result)to raisesubprocess.CalledProcessErrorinstead; it returns the result on success, so strict-and-parsed chains as(.json (.raise_for_status (script))).
Built-in fences are defined in Koskari (koskari/bootstrap/_fences.ki,
via the define-fence syntax). Fence expansions are self-contained:
they reference only universal builtins (fence/bash lowers to
run-process), so a fence works without any pre-existing helper
bindings in the surrounding module’s namespace.
Trust model: fence handlers are compile-time plugins; compiling a module that uses a custom fence executes that fence’s handler, exactly as with other compile-time extensibility mechanisms.
Examples:
Positional arguments passed to the callable become the script’s $1,
$2, … (and so $@), each coerced to a string; the call returns a
ProcessResult whose text is the decoded stdout:
>>> var rev ```bash
... echo -n "$@" | rev
... ```
>>> var ohai (rev "Hello" 123 "World")
>>> ohai
<ProcessResult 'bash' returncode=0>
>>> ohai.text
"dlroW 321 olleH"
A script that returns JSON, parsed and status-checked in one chain; and
immediate: True, which runs the script where the fence sits so the fence
expression evaluates directly to the result:
;; each (report) launches the subprocess and yields a ProcessResult
var report ```bash
curl -s https://example.org/api/summary/
```
var summary (.json (.raise_for_status (report)))
;; immediate: True -- no callable; the fence is the ProcessResult
var lookup ```bash immediate: True
hostname -f
```
var hostname lookup.text