fusion-lang 0.0.1.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,505 @@
1
+ # Reference
2
+
3
+ ## 1. Values
4
+
5
+ A Fusion value is one of:
6
+
7
+ | Kind | Examples | Notes |
8
+ | -------- | --------------------------------- | --------------------------------------------- |
9
+ | null | `null` | Ordinary data: a legitimate "absent" value. |
10
+ | boolean | `true`, `false` | |
11
+ | integer | `0`, `-7`, `42` | Distinct from float. |
12
+ | float | `3.14`, `1e9`, `-0.5` | Distinct from integer. |
13
+ | string | `"hi"`, `"a\nb"` | JSON string syntax and escapes. |
14
+ | array | `[]`, `[1, 2, 3]`, `[1, [2]]` | Ordered, heterogeneous. |
15
+ | object | `{}`, `{"k": 1}` | String keys; insertion order preserved. |
16
+ | function | `(p => o, ...)` | One input, one output. A first-class value. |
17
+ | error | `!42`, `!"oops"`, `!null` | An error with a payload. Not a regular value. |
18
+
19
+ The only atomic values are `null`, booleans, integers, floats and strings.
20
+
21
+ The only composite data structures are arrays and objects.
22
+
23
+ Functions are first-class values. They behave like all other values with 3 small exceptions:
24
+ - They can't cross the CLI boundary. All values on STDIN, STDOUT and STDERR may only
25
+ contain regular JSON values.
26
+ - In contrast to arrays and objects, there's no syntax for pattern matching on functions.
27
+ - Functions can't be an error payload.
28
+
29
+ Errors are not regular values:
30
+ - They contain a regular value as "payload", but aren't regular values themselves.
31
+ - They can't be stored in arrays or objects. They always "bubble" and turn that whole
32
+ data structure into an error.
33
+
34
+ ---
35
+
36
+ ## 2. Syntax
37
+
38
+ ### 2.1 Expressions
39
+
40
+ Precedence, tightest to loosest:
41
+
42
+ 1. Primary: literals, `[...]`, `{...}`, `(...)` grouping, function literals,
43
+ identifiers, `@`-references.
44
+ 2. Postfix member/index access: `x.key`, `x[expr]`.
45
+ 3. Error prefix: `!expr` (construct an error). Bare `!` (with no operand) is the
46
+ same as `!null`.
47
+ 4. Pipe (application): `value | function`. Left-associative
48
+ (`a | f | g` ≡ `(a | f) | g`).
49
+ 5. Clause arrow: `=>` (loosest, so the entire right-hand side of a clause is one
50
+ expression).
51
+
52
+ ### 2.2 Function literals
53
+
54
+ ```
55
+ ( pattern => expr , pattern => expr , ... )
56
+ ```
57
+
58
+ One or more clauses, comma-separated, parenthesized; an optional trailing comma is
59
+ allowed. A single parenthesized expression with no `=>` at the top level is a grouped
60
+ expression, not a function.
61
+
62
+ ### 2.3 Array and object literals
63
+
64
+ Array elements and object members may be **spread** with `...`:
65
+
66
+ ```fusion
67
+ # Splice an array's elements in place
68
+ [1, ...other, 9]
69
+ ```
70
+
71
+ ```fusion
72
+ # Merge another object's keys in place
73
+ {"a": 1, ...other}
74
+ ```
75
+
76
+ In a result, `...expr` requires `expr` to be an array (in an array literal) or an
77
+ object (in an object literal); otherwise the literal evaluates to `!`.
78
+
79
+ ### 2.4 Comments
80
+
81
+ Fusion only has **whole-line comments**. A line is a comment iff its first non-whitespace
82
+ character is `#`. Comments can be stripped without parsing the language, because string
83
+ literals cannot span physical lines:
84
+
85
+ ```bash
86
+ # Canonical specification of comment stripping
87
+ grep -v '^[[:space:]]*#' program.fsn
88
+ ```
89
+
90
+ A shebang line needs no special treatment — `#!/usr/bin/env fusion` is just a comment
91
+ by the same rule.
92
+
93
+ ### 2.5 Grammar (EBNF)
94
+
95
+ ```ebnf
96
+ file = expr ;
97
+
98
+ expr = pipe ;
99
+ pipe = prefix { "|" prefix } ;
100
+ prefix = "!" [ prefix ] | postfix ; (* bare "!" -> !null *)
101
+ postfix = primary { "." identifier | "[" expr "]" } ;
102
+ primary = atom | array | object | function | identifier | fileref | "(" expr ")" ;
103
+
104
+ fileref = "@" [ refpath ] ; (* bare "@" = current file *)
105
+ refpath = { "../" } segment { "/" segment } ; (* ".fsn" implied *)
106
+ segment = identifier ;
107
+
108
+ atom = "null" | "true" | "false" | number | string ;
109
+ (* errors are `!`+payload (see prefix) *)
110
+ array = "[" [ elem { "," elem } [ "," ] ] "]" ;
111
+ elem = expr | spread ;
112
+ spread = "..." expr ;
113
+ object = "{" [ member { "," member } [ "," ] ] "}" ;
114
+ member = string ":" expr | spread ;
115
+
116
+ function = "(" clause { "," clause } [ "," ] ")" ;
117
+ clause = pattern "=>" expr ;
118
+
119
+ pattern = errpat | guardedpat ;
120
+ errpat = "!" | "!" guardedpat ; (* bare "!" matches any error, binds nothing *)
121
+ guardedpat = corepat [ "?" predicate ] ;
122
+ predicate = prefix ; (* any expression yielding a function *)
123
+ corepat = literalpat | bindpat | wildcard | arraypat | objectpat ;
124
+ literalpat = atom ;
125
+ wildcard = "_" ;
126
+ bindpat = identifier ;
127
+ arraypat = "[" [ pelem { "," pelem } [ "," ] ] "]" ;
128
+ pelem = guardedpat | "..." [ identifier ] ;
129
+ objectpat = "{" [ pmember { "," pmember } [ "," ] ] "}" ;
130
+ pmember = string ":" guardedpat | "..." [ identifier ] ;
131
+
132
+ identifier = letter { letter | digit | "_" } ;
133
+ number = int_lit | float_lit ;
134
+ int_lit = [ "-" ] digit { digit } ;
135
+ float_lit = int_lit "." digit { digit } [ exp ] | int_lit exp ;
136
+ exp = ("e" | "E") [ "+" | "-" ] digit { digit } ;
137
+ string = '"' { char | escape } '"' ; (* char excludes raw newline; use \n *)
138
+ ```
139
+
140
+ ---
141
+
142
+ ## 3. Functions and application
143
+
144
+ A function is an ordered list of clauses. Applying a function to a value `v`:
145
+
146
+ 1. If the function value itself is an error (e.g. piping into an unresolved name),
147
+ that error is the result.
148
+ 2. Otherwise clauses are tried in order. The first clause whose pattern matches
149
+ `v` produces the result, evaluated with that clause's bindings in scope.
150
+ 3. If a `?` predicate evaluates to an error during matching, that error becomes
151
+ the function's result; no further clauses are tried (see §6.4).
152
+ 4. **If no clause matches:** the result is `v` itself when `v` is an error
153
+ (propagation — an unmatched error is never silently swallowed), and `null`
154
+ otherwise (the lenient default).
155
+
156
+ A consequence of rule 4: a function with error clauses that only match *some*
157
+ shapes of error (e.g. `(!42 => "got 42", _ => "ok")`) still propagates any
158
+ other-shaped error it receives — `!"oops"` is not caught by `!42`, the `_`
159
+ clause rejects errors, no clause matches, so `!"oops"` propagates. To handle
160
+ "any unrecognized error" you must add a catch-all error clause: `!` (or
161
+ equivalently `!_`) for "catch any error, ignore the payload"; `!msg` to bind
162
+ the payload.
163
+
164
+ Functions take exactly one argument. Multiple arguments are passed as an array or
165
+ object. Currying is done by returning a function: `(x => (y => [x, y] | @add))`.
166
+
167
+ Functions are values: they may be elements of arrays, values of object keys, results
168
+ of clauses, and arguments to other functions.
169
+
170
+ ---
171
+
172
+ ## 4. Patterns and binding
173
+
174
+ A pattern both tests structure and extracts parts. Pattern forms:
175
+
176
+ | Form | Matches | Binds |
177
+ | ------------------------------------------ | ----------------------------------------- | ---------------------------------- |
178
+ | literal (`42`, `"x"`, `true`, `null`) | exactly that value | nothing |
179
+ | `_` (wildcard) | anything **except** an error | nothing |
180
+ | identifier (`a`) | anything **except** an error | the value, under that name |
181
+ | Fixed size arrays, e.g. `[x_1, x_2]` | array of matching length, elementwise | each `x_i` binds |
182
+ | Variably arrays, e.g. `[p, ...rest]` | array with ≥ fixed elements | `rest` = remaining elements |
183
+ | Fixed member objects, e.g. `{"a": p}` | object having key `a` (and others) | as `p` binds |
184
+ | Variable objects, e.g. `{"a": p, ...rest}` | object | `rest` = unmatched key/value pairs |
185
+ | `corepat ? pred` | `corepat` matches **and** pred is `true` | as `corepat` binds |
186
+ | `!`, `!_`, `!pat` | an error; `!pat` destructures the payload | as `pat` binds |
187
+
188
+ Rules:
189
+
190
+ **Bare identifiers are holes**:
191
+ - In a pattern they bind. In an expression they read the value bound to that name in
192
+ the current clause.
193
+ - Reading an unbound bare identifier yields an error.
194
+ - A bare identifier never denotes a builtin. Builtins are reached with `@` (see §7,
195
+ §9.2).
196
+
197
+ **No sibling scope**:
198
+ - All bindings in a clause are produced simultaneously. A `?` predicate sees **only**
199
+ the value matched by the pattern it is attached to and never another part of the
200
+ same clause.
201
+ - For `!pat ? pred`, the `?` binds *inside* the `!` (the grammar parses it as
202
+ `!(pat ? pred)`), so the predicate runs against the error's payload and sees exactly
203
+ what `pat` binds. `(!a ? @Integer => ...)` checks whether the payload `a` is an integer.
204
+ - If a `?` predicate evaluates to an error, that error bubbles up as the function's
205
+ result (see §6.4).
206
+
207
+ **`...rest` in patterns**:
208
+ - May appear at most once.
209
+ - In an array pattern it may appear in any position and captures the start / middle / end
210
+ of the array.
211
+ - In an object pattern it may appear at the end and captures all keys not explicitly matched.
212
+ - A bare `...` with no name matches without binding.
213
+ - An object pattern matches if all its named keys are present; extra keys are allowed
214
+ (and captured by `...rest` if present).
215
+
216
+ ---
217
+
218
+ ## 5. The `?` predicate (refinement / types)
219
+
220
+ `corepat ? predicate` matches when `corepat` matches structurally **and** piping the
221
+ matched value into `predicate` yields exactly `true`. The predicate is any
222
+ expression evaluating to a function (a name, a member access, or an inline function
223
+ literal).
224
+
225
+ "Types" are not a separate construct: the built-in predicates `@Integer`, `@String`,
226
+ etc. are ordinary functions returning booleans, reached with `@` and used with `?`
227
+ (e.g. `n ? @Integer`). User-defined predicates work identically (e.g. `n ? @isEven`).
228
+
229
+ ---
230
+
231
+ ## 6. Errors
232
+
233
+ An **error value** is `!` followed by a payload: `!42`, `!"divide by zero"`,
234
+ `!{"kind":"missing_key","key":"id"}`, `!null`. The payload may be any regular Fusion
235
+ value (including `null`). Not allowed are functions and nested errors.
236
+
237
+ Error values are distinct from ordinary data:
238
+ - `null` means legitimate absence.
239
+ - An error means something went wrong.
240
+
241
+ ### 6.1 Constructing errors
242
+
243
+ In expression position, `!` is a prefix operator that constructs an error from its
244
+ operand:
245
+
246
+ - `!42` is an error with payload `42`.
247
+ - `!"oops"` is an error with payload the string `"oops"`.
248
+ - `!` alone (with nothing following it) is shorthand for `!null`.
249
+ - `!expr` where `expr` itself evaluates to an error **propagates** that inner error
250
+ rather than wrapping it (so you cannot accidentally bury an error inside another
251
+ error's payload).
252
+
253
+ Precedence: `!` binds tighter than `|` so `!x | f` is `(!x) | f`; looser than
254
+ postfix `.`/`[]` so `!x.foo` is `!(x.foo)`.
255
+
256
+ ### 6.2 Matching errors
257
+
258
+ In pattern position, `!` introduces an **error pattern**:
259
+
260
+ | Pattern | Matches |
261
+ | ------------------- | ------------------------------------------------------------------------ |
262
+ | `!` | any error; payload is not bound. Cannot carry a `?` predicate. |
263
+ | `!_` | any error; payload is not bound. Can carry a predicate (`!_ ? @pred`). |
264
+ | `!pattern` | any error with a **payload** that matches `pattern` |
265
+
266
+ The payload pattern (the `pattern` in `!pattern`) is a full `guardedpat`:
267
+ - It uses the same destructuring semantics you know from regular values.
268
+ - It fully supports `?` predicates. Predicates only refer to the payload, not the `!`.
269
+ Caution: `! ? @Integer` is a syntax error. The bare `!` has no payload pattern
270
+ for the predicate to refer to. Use `!_ ? @Integer` instead.
271
+ - It cannot itself contain another `!`. At runtime there is no error nested inside another error
272
+ or value. Errors propagate before they can sit inside a collection, so this syntax is rejected
273
+ at parse time.
274
+
275
+ ### 6.3 Propagation
276
+
277
+ **Errors are not first-class values.** At any moment of execution there is either
278
+ a value in motion or an error in motion, never both. An error reaches user code
279
+ only through a clause whose pattern catches it; outside of that catch site, an
280
+ error encountered where a value is expected always propagates.
281
+
282
+ The propagation rule is uniform — there is no special handling for predicates or
283
+ particular built-ins:
284
+
285
+ - **Applying any function to an error** returns that same error (payload
286
+ preserved), **unless** a clause's pattern actually matches it. The matching
287
+ is per-call: it is not enough for the function to have *some* error clause;
288
+ that clause must match the specific error received. An error of a shape no
289
+ clause catches propagates unchanged.
290
+ - **Built-in operations (`@add`, `@divide`, `@equals`, `@Integer`, …) all
291
+ propagate** their input error without examining it. To inspect or compare an
292
+ error's payload, you must catch it first and operate on the extracted payload:
293
+ `!42 | (!a => a) | @Integer` returns `true` (the payload `42` *is* an integer);
294
+ `!42 | @Integer` returns `!42` (the predicate doesn't handle the error,
295
+ evaluates to `!42` and that becomes the return value of the whole function).
296
+ - **Building an array or object propagates** any error encountered while
297
+ evaluating an element/member. `[1, !"bad", 2]` evaluates to `!"bad"`, not to
298
+ an array of three things.
299
+ - **Constructing an error from an erroring expression** propagates the inner
300
+ error rather than wrapping it. `!([5,0] | @divide)` evaluates to
301
+ `!"divide: division by zero"`, never to an error wrapping an error. (This
302
+ preserves the rule that there is never more than one error simultaneously.)
303
+ - **When the function value itself is an error** (e.g. `value | @undefined_name`
304
+ where `@undefined_name` resolves to an error), that error is the result.
305
+
306
+ ### 6.4 `?`-predicate errors bubble up
307
+
308
+ If a `?` predicate evaluates to an error (the predicate function itself errored,
309
+ or it was a non-function error value), that error becomes the function's return
310
+ value immediately. Subsequent clauses are **not** tried — predicate-errors are
311
+ treated as program failures, not as "no match." This is the key reason to make
312
+ your predicates *total* (end with `_ => false`): a predicate that can crash will
313
+ short-circuit your whole function.
314
+
315
+ ### 6.5 Sources of errors
316
+
317
+ Built-in errors are produced by:
318
+
319
+ - A strict function (`_ => !`) on no match — payload `null`.
320
+ - Built-in operations on bad input — payload is a descriptive string,
321
+ e.g. `"divide: division by zero"`, `"add: expected a pair of numbers"`.
322
+ - Member access on a missing key or non-object, index out of range, or bad index
323
+ type — payload is a structured object like
324
+ `{"kind":"missing_key","key":"foo"}` or `{"kind":"index_out_of_range",...}`.
325
+ - Spread of a non-array or non-object — `{"kind":"spread_non_array",...}`.
326
+ - Unresolved `@`-reference — `{"kind":"unresolved_ref","name":"..."}`.
327
+ - Non-productive data cycle — `{"kind":"data_cycle","path":"..."}`.
328
+ - Applying a non-function — `{"kind":"apply_non_function","got":"..."}`.
329
+
330
+ User code constructs errors with `!payload` as described above.
331
+
332
+ ---
333
+
334
+ ## 7. Built-in functions
335
+
336
+ All built-ins are ordinary one-argument functions, **reached with an `@` prefix**
337
+ (`@add`, `@Integer`, …); see §9.2 for how `@name` resolves. The names in the tables
338
+ below are the built-in names; write `@` before them to use them. **Operations** return
339
+ `!` on type-invalid or domain-invalid input. **Predicates** return `false` on any
340
+ input that is not of the queried type (they never return `!`).
341
+
342
+ ### 7.1 Arithmetic (operations)
343
+
344
+ | Name | Input | Result |
345
+ | ---------- | ----------------- | -------------------------------------------------- |
346
+ | `add` | `[number, number]`| sum |
347
+ | `subtract` | `[number, number]`| difference |
348
+ | `multiply` | `[number, number]`| product |
349
+ | `divide` | `[number, number]`| quotient; integer if evenly divisible, else float; `!` if divisor is 0 |
350
+ | `mod` | `[number, number]`| remainder; `!` if divisor is 0 |
351
+ | `negate` | `number` | negation |
352
+ | `floor` | `number` | floor (integer) |
353
+
354
+ ### 7.2 Comparison (operations)
355
+
356
+ | Name | Input | Result |
357
+ | ----------- | --------------------------------------- | --------------- |
358
+ | `equals` | `[any, any]` | deep structural equality (boolean) |
359
+ | `lessThan` | `[number, number]` or `[string, string]`| boolean; `!` on mismatched/invalid types |
360
+
361
+ Other comparisons (`lessEq`, `greaterThan`, `greaterEq`, `notEquals`) are specified
362
+ for the standard library, derivable from `equals` and `lessThan`.
363
+
364
+ ### 7.3 Boolean (operations)
365
+
366
+ | Name | Input | Result |
367
+ | ----- | -------------------- | ------------- |
368
+ | `and` | `[boolean, boolean]` | logical and |
369
+ | `or` | `[boolean, boolean]` | logical or |
370
+ | `not` | `boolean` | logical not |
371
+
372
+ ### 7.4 Strings and structure bridges (operations)
373
+
374
+ | Name | Input | Result |
375
+ | ------------- | --------------------------- | --------------------------------------- |
376
+ | `length` | string / array / object | element/character/key count (integer) |
377
+ | `concat` | `[string, string]` | concatenation |
378
+ | `chars` | string | array of single-character strings |
379
+ | `join` | `[array-of-strings, string]`| joined string |
380
+ | `toString` | any | string form of the value |
381
+ | `parseNumber` | string | integer or float; `!` if not numeric |
382
+ | `keys` | object | array of key strings |
383
+ | `values` | object | array of values |
384
+
385
+ ### 7.5 Type predicates (predicates)
386
+
387
+ `Integer`, `Float`, `Number`, `String`, `Boolean`, `Array`, `Object`, `Null`. Each
388
+ takes any value and returns a boolean. `Number` is true for integers and floats.
389
+
390
+ ### 7.6 Special built-ins: `ENV` and `load`
391
+
392
+ These resolve in the `@name` chain like other built-ins (so a sibling file of the
393
+ same name shadows them), but they are not plain unary value functions:
394
+
395
+ - **`@ENV`** evaluates directly to an object mapping every environment variable name
396
+ to its value. All values are strings; nothing is parsed. Read one with member
397
+ access: `@ENV.PATH`. A missing variable yields `!`.
398
+ - **`@load`** evaluates to a function taking a filename **string verbatim** (no `.fsn`
399
+ appended), resolved relative to the referencing file's directory; it returns that
400
+ file's value, or `!` if the file does not exist. Use it to load files whose names
401
+ are computed at runtime or are not plain identifiers, e.g. `"a.b.fsn" | @load`.
402
+
403
+ ---
404
+
405
+ ## 8. Member and index access
406
+
407
+ - `x.key` — if `x` is an object containing `key`, its value; otherwise `!`.
408
+ - `x[expr]` — if `x` is an array and `expr` is an integer in range, the element
409
+ (negative indices count from the end); if `x` is an object and `expr` is a string
410
+ key that exists, its value; otherwise `!`.
411
+
412
+ Both `.` and `[]` bind tighter than `|`.
413
+
414
+ ---
415
+
416
+ ## 9. Files, references, and the runtime
417
+
418
+ ### 9.1 Files
419
+
420
+ A `.fsn` file contains **exactly one expression**, which is its value. A file is
421
+ **executable** if that value is a function.
422
+
423
+ ### 9.2 References
424
+
425
+ A `@` reference takes one of these forms:
426
+
427
+ - **`@`** (nothing after it) — the **current file**'s value. Used for self-recursion.
428
+ - **`@ENV`** — an object of all environment variables (string keys, string values;
429
+ no parsing). Resolved in the `@name` chain below, so it is shadowable.
430
+ - **`@name`** — a single bare identifier (no `/`, no `../`).
431
+ - **`@dir/name`, `@a/b/c`** — a downward path.
432
+ - **`@../name`, `@../../a/b`** — an upward path (contains one or more `../`).
433
+
434
+ **Resolution of `@name` and downward paths** (any reference *without* `../`) proceeds
435
+ in order, first match winning:
436
+
437
+ 1. a **sibling file** at `<referencing dir>/<name>.fsn`;
438
+ 2. a **built-in** of that exact name (including `ENV` and `load`);
439
+ 3. a **standard-library file** at `<stdlib root>/<name>.fsn`.
440
+
441
+ If none match, the result is `!`. Downward paths participate fully: `@math/sqrt`
442
+ checks a sibling `math/sqrt.fsn`, then a built-in named `math/sqrt`, then a stdlib
443
+ `math/sqrt.fsn`.
444
+
445
+ **Resolution of upward paths** (any reference containing `../`) is **file-only**: it
446
+ resolves solely to a file relative to the referencing directory and never falls back
447
+ to a built-in or the standard library.
448
+
449
+ The `.fsn` extension is implied and never written in a `@` reference. File resolution
450
+ is relative to the **referencing file's** directory; built-ins and the standard
451
+ library are global to the runtime but, per the order above, are shadowed by a sibling
452
+ file of the same name. That shadowing is per-directory, not global.
453
+
454
+ **Built-ins are reached through this same mechanism**: `@add`, `@Integer`, etc. are
455
+ `@name` references that resolve at step 2. A *bare* identifier (without `@`) is only
456
+ a pattern hole; it never denotes a built-in.
457
+
458
+ Two built-ins are special in how they resolve:
459
+
460
+ - **`@ENV`** resolves (at step 2) to a fresh object of environment variables.
461
+ - **`@load`** resolves (at step 2) to a function that loads a file by a **verbatim**
462
+ filename string — no `.fsn` is appended — relative to the referencing directory,
463
+ returning that file's value. If the argument is not a string, or the file does not
464
+ exist, the result is an error (`{"kind":"load_bad_arg",...}` or
465
+ `{"kind":"file_not_found","path":...}` respectively). This is the only way to load
466
+ a file whose name is computed at runtime or is not a plain identifier (e.g.
467
+ `"data.config.fsn"`).
468
+
469
+ References are:
470
+
471
+ - **Lazy** — resolved when evaluated/applied, not when a file loads. A file may
472
+ reference itself (recursion) or another file may reference it back (mutual
473
+ recursion) without an infinite load loop.
474
+ - **Memoized** — each file path is loaded and evaluated once per run; shared
475
+ dependencies load once.
476
+
477
+ A **non-productive data cycle** (files whose values reference each other as data, not
478
+ through a function boundary) yields `!` at the point of the cyclic self-reference.
479
+ This error then immediatly bubbles up according to the propagation semantics described
480
+ above. Recursion through functions is not a data cycle.
481
+
482
+ ### 9.3 Runtime contract
483
+
484
+ The interpreter reads standard input as JSON, converts it to a Fusion value `v`,
485
+ computes `v | programFunction`, and prints the result on standard output as JSON.
486
+
487
+ - Empty input is treated as `null`.
488
+ - Non-JSON input yields an error (payload `{"kind":"stdin_not_json"}`).
489
+ - **If the final result is an error**, the interpreter prints **nothing** to
490
+ standard output, prints the error's **payload** (as JSON) to standard error, and
491
+ exits with status `1`. Otherwise the result is printed to standard output and the
492
+ interpreter exits `0`. This makes Fusion programs first-class Unix filters: the
493
+ stdout stream carries the value-or-nothing, the stderr stream carries the failure
494
+ detail, and the exit code is `0`/`1` accordingly.
495
+
496
+ ### 9.4 Command-line interface
497
+
498
+ ```
499
+ fusion <file.fsn> [json-input]
500
+ fusion -e '<source>' [json-input]
501
+ ```
502
+
503
+ Input comes from the `[json-input]` argument if present, otherwise from standard
504
+ input. Setting the environment variable `FUSION_DEBUG` causes file-not-found and
505
+ parse errors during reference resolution to be reported on standard error.