The ShEx WASI extension is a generic semantic-action extension: the action's code is a program — the WebAssembly Text (WAT) source of a WASI command module — run once per invocation with the evaluation context passed as WASI argv. Whatever the module prints to WASI fd 1 is collected as the extension's results; the module's exit status is the verdict. Because the module imports only wasi_snapshot_preview1, the same semantic actions run under any conforming WASI host, in any ShEx implementation, in any language.

Example

A schema may carry Test and WASI actions side by side (implementations fire the actions of extensions they register and skip the rest), so the two suites can share test schemas:

<http://a.example/S1> {
   <http://a.example/p1> .
     %<http://shex.io/extensions/Test/>{ print(s) %}
     %<http://shex.io/extensions/WASI/>{ (func $main (call $println_s)) %}
     %<http://shex.io/extensions/Test/>{ print(p) %}
     %<http://shex.io/extensions/WASI/>{ (func $main (call $println_p)) %}
     %<http://shex.io/extensions/Test/>{ print(o) %}
     %<http://shex.io/extensions/WASI/>{ (func $main (call $println_o)) %}
}
<http://a.example/s1> <http://a.example/p1> <http://a.example/o1> .

Validating <http://a.example/s1> against <http://a.example/S1> with the WASI extension registered collects:

{
  "prints": [
    "http://a.example/s1",
    "http://a.example/p1",
    "http://a.example/o1"
  ]
}

Invocation contract

Input: WASI argv

The evaluation context arrives as command arguments:

argv[0] = "http://shex.io/extensions/WASI/"
argv[1…] = one "<letter>=<value>" per in-scope binding:
    s=, p=, o=   the matched triple's terms (TripleConstraint actions)
    n=           the focus node (Shape and NodeConstraint actions)

Start actions run with no bindings. Term values are serialized as in the Test extension: the term's lexical value, except literals with a non-xsd:string datatype, which appear in Turtle form ("1"^^http://www.w3.org/2001/XMLSchema#integer).

Output: WASI stdout

Whatever the module writes to fd 1 is collected; each newline-terminated line (plus any unterminated tail at exit) is appended to the extension's results (in shex.js, validator.semActHandler.results["http://shex.io/extensions/WASI/"]).

Verdict: the exit status

0success
1failure — validation treats the action as failed; lines printed before exiting are still recorded (like Test's fail())
2 and upinvocation error — the implementation throws (e.g. the library's binding accessors exit 2 when the binding is absent)

A trap or a WAT compilation error is also an invocation error.

Escaping

ShExC code blocks reserve % and \, so WAT embedded in a schema writes them \% and \\ (a WAT string's \0a newline escape becomes \\0a). The parser unescapes before the extension sees the text.

Code forms and the library prelude

The code takes one of two forms:

The prelude text is part of this extension's definition: any host performing the same composition runs the same actions. Its helpers:

$put (ptr len)write bytes to fd 1
$nl ()write "\n"
$println (ptr len)$put then $nl
$put_s $put_p $put_o $put_nwrite a binding's value (exit 2 if absent)
$println_s $println_p $println_o $println_nditto, newline-terminated
$fail ()exit 1 — a failed action
$strlen (ptr) → lenNUL-terminated string length

Author data segments start at offset 8192; below that is the prelude's argv and scratch space.

Open issue: gating actions need a richer library

The prelude as defined makes WASI a competent reporting extension — everything the Test extension's print/fail grammar expresses is a few library calls. To be useful as a gating semantic action — one that passes or fails validation based on the content of the bound terms — authors need more than print helpers: comparing binding values against literals, datatype-aware numeric comparison, substring/regex tests, perhaps date arithmetic. Writing these from scratch in WAT for every action is not realistic; the extension should adopt some standard set of libraries (further prelude functions, or a convention for importing shared modules) to make gating actions practical. Community input on the shape and scope of that library set is very much appreciated — as are opinions on accepting binary (.wasm) actions alongside WAT, and on tracking WASI preview 2 / the component model.

Implementations