{
  "format": "subtype-spec/1",
  "version": "0.0.1",
  "language": "Subtype",
  "keywords": [
    {
      "word": "needs",
      "kind": "reserved",
      "summary": "the permission header: everything the script may touch, first thing in the file",
      "example": "needs [ read [./src], clock ]"
    },
    {
      "word": "let",
      "kind": "reserved",
      "summary": "names a value (`let mut` makes the name changeable); also starts a pattern in `if (let ...)`",
      "example": "let x = 1"
    },
    {
      "word": "mut",
      "kind": "reserved",
      "summary": "a name that can be assigned again",
      "example": "let mut n = 0\nn += 1"
    },
    {
      "word": "fn",
      "kind": "reserved",
      "summary": "a function (a declaration at the top level, or a value inside an expression)",
      "example": "fn double(n: Int) -> Int { n * 2 }"
    },
    {
      "word": "if",
      "kind": "reserved",
      "summary": "a condition in parentheses; also a guard after a `match` pattern",
      "example": "if (1 < 2) { print(\"yes\") } else { print(\"no\") }"
    },
    {
      "word": "else",
      "kind": "reserved",
      "summary": "the other branch of an `if`",
      "example": "if (false) { } else { }"
    },
    {
      "word": "for",
      "kind": "reserved",
      "summary": "goes through a list, map, set or text; the item may be a pattern",
      "example": "for (x in [1, 2]) { print(x) }"
    },
    {
      "word": "in",
      "kind": "reserved",
      "summary": "between the item and the collection of a `for`",
      "example": "for (c in \"ab\") { print(c) }"
    },
    {
      "word": "while",
      "kind": "reserved",
      "summary": "repeats while a condition holds",
      "example": "let mut i = 0\nwhile (i < 3) { i += 1 }"
    },
    {
      "word": "break",
      "kind": "reserved",
      "summary": "leaves the innermost loop",
      "example": "while (true) { break }"
    },
    {
      "word": "continue",
      "kind": "reserved",
      "summary": "goes on with the next round of the innermost loop",
      "example": "for (i in 0..3) { if (i == 1) { continue }\n  print(i) }"
    },
    {
      "word": "match",
      "kind": "reserved",
      "summary": "takes a value apart with patterns; the cases must cover every value",
      "example": "match (1) { 1 => print(\"one\"), _ => print(\"other\") }"
    },
    {
      "word": "or",
      "kind": "reserved",
      "summary": "what to do when the left side failed: a value or a block (`error` is the failure inside)",
      "example": "let n = int(\"x\") or 0"
    },
    {
      "word": "return",
      "kind": "reserved",
      "summary": "leaves the function (or the script, with an exit code) with a value",
      "example": "fn f() { return 1 }"
    },
    {
      "word": "true",
      "kind": "reserved",
      "summary": "the yes value",
      "example": "let ok = true"
    },
    {
      "word": "false",
      "kind": "reserved",
      "summary": "the no value",
      "example": "let ok = false"
    },
    {
      "word": "type",
      "kind": "contextual",
      "summary": "a new name for a type, optionally refined",
      "example": "type Port = Int where 1..=65535"
    },
    {
      "word": "enum",
      "kind": "contextual",
      "summary": "a type with a fixed set of variants",
      "example": "enum Light { Red, Green }"
    },
    {
      "word": "error",
      "kind": "contextual",
      "summary": "a kind of failure, optionally inside a family; also the failure inside an `or { }` block",
      "example": "error Missing <: IoError"
    },
    {
      "word": "use",
      "kind": "contextual",
      "summary": "brings in another file of declarations",
      "example": "use \"./lib.sub\""
    },
    {
      "word": "defer",
      "kind": "contextual",
      "summary": "runs an expression when the block ends, however it ends",
      "example": "fn f() { defer print(\"done\")\n  1 }"
    },
    {
      "word": "test",
      "kind": "contextual",
      "summary": "a test block, run by `sub test`",
      "example": "test \"adds\" { assert_eq(1 + 1, 2) }"
    },
    {
      "word": "where",
      "kind": "contextual",
      "summary": "the condition of a refined type",
      "example": "type Small = Int where 0..=9"
    },
    {
      "word": "matching",
      "kind": "contextual",
      "summary": "a refined text type that must match a pattern",
      "example": "type Id = String matching \"^[a-z]+$\""
    }
  ],
  "operators": [
    {
      "op": "or",
      "name": "handle a failure",
      "precedence": 1,
      "assoc": "left",
      "summary": "the left side, or the right side (a value or a block) when the left failed",
      "example": "let n = int(\"x\") or 0"
    },
    {
      "op": "??",
      "name": "coalesce",
      "precedence": 2,
      "assoc": "right",
      "summary": "the left side when it is there, else the right (for `Option` and failures)",
      "example": "let n = first([]) ?? 0"
    },
    {
      "op": "||",
      "name": "or",
      "precedence": 3,
      "assoc": "left",
      "summary": "yes when either side is yes (the right is not evaluated if the left is)",
      "example": "let b = true || false"
    },
    {
      "op": "&&",
      "name": "and",
      "precedence": 4,
      "assoc": "left",
      "summary": "yes when both sides are yes (the right is not evaluated if the left is no)",
      "example": "let b = true && false"
    },
    {
      "op": "== !=",
      "name": "equal, not equal",
      "precedence": 5,
      "assoc": "left",
      "summary": "structural equality (records, lists, tuples, sets; 1 equals 1.0); a secret equals nothing",
      "example": "let b = [1, 2] == [1, 2]"
    },
    {
      "op": "< > <= >=",
      "name": "compare",
      "precedence": 6,
      "assoc": "left",
      "summary": "orders numbers, and text by characters",
      "example": "let b = 1 < 2.5"
    },
    {
      "op": ".. ..=",
      "name": "range",
      "precedence": 7,
      "assoc": "none",
      "summary": "the list of whole numbers from a up to (or through) b",
      "example": "let r = 0..3"
    },
    {
      "op": "+ -",
      "name": "add, subtract",
      "precedence": 8,
      "assoc": "left",
      "summary": "arithmetic; `+` also joins text and lists",
      "example": "let s = \"a\" + 1"
    },
    {
      "op": "* / %",
      "name": "multiply, divide, remainder",
      "precedence": 9,
      "assoc": "left",
      "summary": "arithmetic; whole numbers overflow into an error, `/` by zero is an error",
      "example": "let q = 7 / 2"
    },
    {
      "op": "! -",
      "name": "not, negate",
      "precedence": 10,
      "assoc": "prefix",
      "summary": "turns yes into no; flips the sign of a number",
      "example": "let n = -(1 + 2)"
    },
    {
      "op": "? . ?. [] ()",
      "name": "postfix",
      "precedence": 11,
      "assoc": "left",
      "summary": "`?` passes a failure up, `.f` a field, `?.f` an optional field, `[i]` an item, `(a)` a call; no space before `[` or `(`",
      "example": "let xs = [[1]]\nlet n = xs[0][0]"
    },
    {
      "op": "= += -= *= /= %=",
      "name": "assign",
      "precedence": 0,
      "assoc": "statement",
      "summary": "changes a `mut` name, a field (`r.n = 1`) or an item (`xs[0] = 1`)",
      "example": "let mut n = 1\nn += 2"
    }
  ],
  "literals": [
    {
      "kind": "Int",
      "syntax": "123  1_000  0xFF  0b1010  0o17",
      "summary": "a 64-bit whole number; a result that does not fit is an error, never a wrap-around",
      "example": "let n = 0xFF + 1_000"
    },
    {
      "kind": "Float",
      "syntax": "1.5  2e9  1.5E-3",
      "summary": "a 64-bit fraction; a whole number is accepted where a Float is wanted",
      "example": "let f = 2.5e-1"
    },
    {
      "kind": "Bool",
      "syntax": "true  false",
      "summary": "yes or no",
      "example": "let b = true"
    },
    {
      "kind": "String",
      "syntax": "\"text {expr} \\n\"",
      "summary": "text with escapes (\\n \\t \\r \\0 \\\" \\\\ \\{ \\} \\xNN \\u{N}) and `{expression}` parts; `{x:>8}` formats",
      "example": "let s = \"{1 + 1} items\""
    },
    {
      "kind": "String (raw)",
      "syntax": "r\"no \\escapes {here}\"  r#\"quotes \" ok\"#",
      "summary": "text exactly as written",
      "example": "let s = r\"C:\\dir\""
    },
    {
      "kind": "String (triple)",
      "syntax": "\"\"\"several lines\"\"\"",
      "summary": "text that spans lines; a line break right after the opening quotes is dropped",
      "example": "let s = \"\"\"\nline one\nline two\"\"\""
    },
    {
      "kind": "Path",
      "syntax": "./src/main.ts  ../up  ~/notes  /etc/hosts  ./out/{name}.txt",
      "summary": "a file path written without quotes; `{expr}` parts allowed",
      "example": "let p = ./src/main.ts"
    },
    {
      "kind": "List",
      "syntax": "[1, 2, 3]",
      "summary": "an ordered collection of one kind of value",
      "example": "let xs = [1, 2, 3]"
    },
    {
      "kind": "Record",
      "syntax": "{ name: \"ann\", age: 3 }  { ..r, age: 4 }",
      "summary": "named fields of different types; `{ ..r, f: v }` is a copy with changes",
      "example": "let r = { name: \"ann\" }"
    },
    {
      "kind": "Tuple",
      "syntax": "(1, \"a\")",
      "summary": "a fixed number of values of different types; `t.0` is the first",
      "example": "let t = (1, \"a\")"
    },
    {
      "kind": "Map",
      "syntax": "#{ \"a\": 1, \"b\": 2 }",
      "summary": "text keys to one kind of value; `m[\"a\"]` looks one up",
      "example": "let m = #{ \"a\": 1 }"
    },
    {
      "kind": "Set",
      "syntax": "#[1, 2, 3]",
      "summary": "members once each, in the order added",
      "example": "let s = #[1, 2, 2]"
    },
    {
      "kind": "Function",
      "syntax": "fn(a, b) { a + b }",
      "summary": "a function value that captures the names it uses",
      "example": "let add = fn(a, b) { a + b }"
    },
    {
      "kind": "Capability",
      "syntax": "read[./src]  run[\"git status\"]",
      "summary": "a permission as a value, never bigger than `needs`; `demote` makes one smaller",
      "example": "needs [ read [./src] ]\nlet c = read[./src]"
    }
  ],
  "types": [
    {
      "name": "Int",
      "summary": "whole number (64-bit)",
      "example": "let n: Int = 1"
    },
    {
      "name": "Float",
      "summary": "fraction; an Int is accepted where a Float is wanted",
      "example": "let f: Float = 1"
    },
    {
      "name": "Bool",
      "summary": "true or false",
      "example": "let b: Bool = true"
    },
    {
      "name": "String",
      "summary": "text; a Path is accepted where a String is wanted",
      "example": "let s: String = \"a\""
    },
    {
      "name": "Path",
      "summary": "a file path",
      "example": "let p: Path = ./a"
    },
    {
      "name": "Unit",
      "summary": "no value",
      "example": "fn f() -> Unit { print(1) }"
    },
    {
      "name": "Any",
      "summary": "any value (the checker does not look inside)",
      "example": "let a: Any = 1"
    },
    {
      "name": "Never",
      "summary": "a function that never returns",
      "example": "fn stop() -> Never { panic(\"x\") }"
    },
    {
      "name": "List<T>",
      "summary": "list of T",
      "example": "let xs: List<Int> = [1]"
    },
    {
      "name": "Map<T>",
      "summary": "text keys to T",
      "example": "let m: Map<Int> = #{ \"a\": 1 }"
    },
    {
      "name": "Set<T>",
      "summary": "set of T",
      "example": "let s: Set<Int> = #[1]"
    },
    {
      "name": "Option<T>",
      "summary": "Some(x) or None",
      "example": "let o: Option<Int> = Some(1)"
    },
    {
      "name": "Result<T, E>",
      "summary": "a T, or a failure of family E (`Result<T>` means any failure)",
      "example": "fn f() -> Result<Int, IoError> { 1 }"
    },
    {
      "name": "Secret<T>",
      "summary": "a T that cannot be printed, put in text or compared with ==; not a subtype of T nor T of it",
      "example": "fn f(k: Secret<String>) { 1 }"
    },
    {
      "name": "Task<T>",
      "summary": "work started with `spawn`",
      "example": "let t: Task<Int> = spawn(fn() { 1 })"
    },
    {
      "name": "(A, B)",
      "summary": "tuple",
      "example": "let t: (Int, String) = (1, \"a\")"
    },
    {
      "name": "Fn<A, B>",
      "summary": "a function from A to B (the last type is the result)",
      "example": "let f: Fn<Int, Int> = fn(x) { x }"
    }
  ],
  "failure_families": [
    {
      "name": "Failure",
      "parent": null
    },
    {
      "name": "IoError",
      "parent": "Failure"
    },
    {
      "name": "NotFound",
      "parent": "IoError"
    },
    {
      "name": "OsDenied",
      "parent": "IoError"
    },
    {
      "name": "Io",
      "parent": "IoError"
    },
    {
      "name": "ParseError",
      "parent": "Failure"
    },
    {
      "name": "Parse",
      "parent": "ParseError"
    },
    {
      "name": "OutOfRange",
      "parent": "Failure"
    },
    {
      "name": "NetworkError",
      "parent": "Failure"
    },
    {
      "name": "NetUnavailable",
      "parent": "NetworkError"
    },
    {
      "name": "TaskFailed",
      "parent": "Failure"
    },
    {
      "name": "Cancelled",
      "parent": "TaskFailed"
    },
    {
      "name": "Refinement",
      "parent": "Failure"
    },
    {
      "name": "Assertion",
      "parent": "Failure"
    }
  ],
  "builtins": [
    {
      "name": "print",
      "module": "core",
      "signature": "print(Any...) -> Unit",
      "effects": null,
      "summary": "writes the values, separated by spaces, and a new line, to standard output",
      "example": "print(\"total:\", 3, [1, 2])",
      "fragment": false
    },
    {
      "name": "log",
      "module": "core",
      "signature": "log(Any...) -> Unit",
      "effects": null,
      "summary": "the same, to standard error",
      "example": "log(\"progress:\", 50)",
      "fragment": false
    },
    {
      "name": "read",
      "module": "core",
      "signature": "read(Str) -> Result<Str, IoError>",
      "effects": "read",
      "summary": "the text of a file",
      "example": "needs [ read [./notes] ]\nlet text = read(./notes/today.md) or \"\"\nprint(len(text))",
      "fragment": false
    },
    {
      "name": "exists",
      "module": "core",
      "signature": "exists(Str) -> Bool",
      "effects": "read",
      "summary": "whether a file or folder exists",
      "example": "needs [ read [./notes] ]\nprint(exists(./notes/today.md))",
      "fragment": false
    },
    {
      "name": "list_dir",
      "module": "core",
      "signature": "list_dir(Str) -> Result<List<Str>, IoError>",
      "effects": "read",
      "summary": "the names in a folder, sorted",
      "example": "needs [ read [./notes] ]\nlet names = list_dir(./notes) or []\nprint(names)",
      "fragment": false
    },
    {
      "name": "write",
      "module": "core",
      "signature": "write(Str, Str) -> Result<Unit, IoError>",
      "effects": "write",
      "summary": "writes text to a file",
      "example": "needs [ write [./out] ]\nwrite(./out/hello.txt, \"hi\\n\")?",
      "fragment": false
    },
    {
      "name": "run",
      "module": "core",
      "signature": "run(Any) -> RunResult",
      "effects": "run",
      "summary": "runs a program (a command string or a list of words, no shell); a program that cannot start is a result with `failed` set and code 127",
      "example": "needs [ run [\"git status\"] ]\nlet r = run(\"git status\")\nprint(r.failed, r.output)",
      "fragment": false
    },
    {
      "name": "fetch",
      "module": "core",
      "signature": "fetch(Str) -> Result<Str, NetworkError>",
      "effects": "net",
      "summary": "an HTTP GET; the body as text",
      "example": "needs [ net [\"api.github.com:443\"] ]\nlet body = fetch(\"https://api.github.com/zen\") or \"offline\"\nprint(body)",
      "fragment": false
    },
    {
      "name": "env",
      "module": "core",
      "signature": "env(Str) -> Result<Secret<Str>, NotFound>",
      "effects": "env",
      "summary": "an environment variable, as a secret",
      "example": "needs [ env [\"HOME\"] ]\nlet home = env(\"HOME\") or \"\"\nprint(secret_eq(home, home))",
      "fragment": false
    },
    {
      "name": "now",
      "module": "core",
      "signature": "now() -> Int",
      "effects": "clock",
      "summary": "milliseconds since 1970",
      "example": "needs [ clock ]\nprint(now() > 0)",
      "fragment": false
    },
    {
      "name": "sleep",
      "module": "core",
      "signature": "sleep(Int) -> Unit",
      "effects": "clock",
      "summary": "waits this many milliseconds",
      "example": "needs [ clock ]\nsleep(10)",
      "fragment": false
    },
    {
      "name": "random",
      "module": "core",
      "signature": "random() -> Int",
      "effects": "random",
      "summary": "a random non-negative integer",
      "example": "needs [ random ]\nprint(random() >= 0)",
      "fragment": false
    },
    {
      "name": "read_in",
      "module": "core",
      "signature": "read_in(CapRead, Str) -> Result<Str, IoError>",
      "effects": "read",
      "summary": "reads a file inside a read capability",
      "example": "needs [ read [./notes] ]\nlet dir = read[./notes]\nlet text = read_in(dir, \"./notes/today.md\") or \"\"\nprint(len(text))",
      "fragment": false
    },
    {
      "name": "write_in",
      "module": "core",
      "signature": "write_in(CapWrite, Str, Str) -> Result<Unit, IoError>",
      "effects": "write",
      "summary": "writes a file inside a write capability",
      "example": "needs [ write [./out] ]\nlet dir = write[./out]\nwrite_in(dir, \"./out/a.txt\", \"x\")?",
      "fragment": false
    },
    {
      "name": "demote",
      "module": "core",
      "signature": "demote(T, T) -> T",
      "effects": null,
      "summary": "hands over a smaller capability",
      "example": "needs [ read [./docs] ]\nlet all = read[./docs]\nlet guides = demote(all, read[./docs/guides])\nprint(read_in(guides, \"./docs/guides/a.md\") or \"\")",
      "fragment": false
    },
    {
      "name": "args",
      "module": "core",
      "signature": "args() -> List<Str>",
      "effects": null,
      "summary": "the arguments the script was run with",
      "example": "print(len(args))",
      "fragment": false
    },
    {
      "name": "len",
      "module": "core",
      "signature": "len(Any) -> Int",
      "effects": null,
      "summary": "the length of a string, list, map or record",
      "example": "print(len(\"héllo\"), len([1, 2, 3]), len(#{ \"a\": 1 }))",
      "fragment": false
    },
    {
      "name": "lines",
      "module": "core",
      "signature": "lines(Str) -> List<Str>",
      "effects": null,
      "summary": "splits text into lines",
      "example": "print(lines(\"a\\nb\\nc\"))",
      "fragment": false
    },
    {
      "name": "split",
      "module": "core",
      "signature": "split(Str, Str) -> List<Str>",
      "effects": null,
      "summary": "splits text at a separator",
      "example": "print(split(\"a,b,c\", \",\"))",
      "fragment": false
    },
    {
      "name": "trim",
      "module": "core",
      "signature": "trim(Str) -> Str",
      "effects": null,
      "summary": "removes spaces at both ends",
      "example": "print(trim(\"  padded  \") + \"|\")",
      "fragment": false
    },
    {
      "name": "join",
      "module": "core",
      "signature": "join(List<Any>, Str) -> Str",
      "effects": null,
      "summary": "joins items with a separator",
      "example": "print(join([\"a\", \"b\", \"c\"], \"-\"))",
      "fragment": false
    },
    {
      "name": "contains",
      "module": "core",
      "signature": "contains(Any, Any) -> Bool",
      "effects": null,
      "summary": "whether a string contains text, or a list contains an item",
      "example": "print(contains(\"hello\", \"ell\"), contains([1, 2], 2), contains(#[1, 2], 3))",
      "fragment": false
    },
    {
      "name": "starts_with",
      "module": "core",
      "signature": "starts_with(Str, Str) -> Bool",
      "effects": null,
      "summary": "whether text begins with the given text",
      "example": "print(starts_with(\"hello\", \"he\"))",
      "fragment": false
    },
    {
      "name": "ends_with",
      "module": "core",
      "signature": "ends_with(Str, Str) -> Bool",
      "effects": null,
      "summary": "whether text ends with the given text",
      "example": "print(ends_with(\"hello\", \"lo\"))",
      "fragment": false
    },
    {
      "name": "replace",
      "module": "core",
      "signature": "replace(Str, Str, Str) -> Str",
      "effects": null,
      "summary": "replaces every occurrence",
      "example": "print(replace(\"a-b-c\", \"-\", \"+\"))",
      "fragment": false
    },
    {
      "name": "upper",
      "module": "core",
      "signature": "upper(Str) -> Str",
      "effects": null,
      "summary": "the text in capital letters",
      "example": "print(upper(\"abc\"))",
      "fragment": false
    },
    {
      "name": "lower",
      "module": "core",
      "signature": "lower(Str) -> Str",
      "effects": null,
      "summary": "the text in small letters",
      "example": "print(lower(\"ABC\"))",
      "fragment": false
    },
    {
      "name": "slice",
      "module": "core",
      "signature": "slice(T, Int, Int) -> T",
      "effects": null,
      "summary": "the characters of text, or the items of a list, from the first position up to, not including, the second (negative positions count from the end); `xs[1..3]` is the same",
      "example": "print(slice(\"hello\", 1, 3), slice([1, 2, 3, 4], 1, 3), \"hello\"[1..3])",
      "fragment": false
    },
    {
      "name": "slice_from",
      "module": "core",
      "signature": "slice_from(T, Int) -> T",
      "effects": null,
      "summary": "everything from a position on; `xs[2..]` is the same",
      "example": "print(slice_from(\"hello\", 2), [1, 2, 3][1..])",
      "fragment": false
    },
    {
      "name": "slice_to",
      "module": "core",
      "signature": "slice_to(T, Int) -> T",
      "effects": null,
      "summary": "everything before a position; `xs[..3]` is the same",
      "example": "print(slice_to(\"hello\", 2), [1, 2, 3][..2])",
      "fragment": false
    },
    {
      "name": "index_of",
      "module": "core",
      "signature": "index_of(Str, Str) -> Option<Int>",
      "effects": null,
      "summary": "where text first occurs (in characters)",
      "example": "print(index_of(\"hello\", \"l\"), index_of(\"hello\", \"z\"))",
      "fragment": false
    },
    {
      "name": "repeat",
      "module": "core",
      "signature": "repeat(Str, Int) -> Str",
      "effects": null,
      "summary": "the text repeated n times",
      "example": "print(repeat(\"ab\", 3))",
      "fragment": false
    },
    {
      "name": "chars",
      "module": "core",
      "signature": "chars(Str) -> List<Str>",
      "effects": null,
      "summary": "the characters, one string each",
      "example": "print(chars(\"abc\"))",
      "fragment": false
    },
    {
      "name": "str",
      "module": "core",
      "signature": "str(Any) -> Str",
      "effects": null,
      "summary": "any value as text (a secret is refused)",
      "example": "print(str(42) + \"!\", str([1, 2]))",
      "fragment": false
    },
    {
      "name": "secret_eq",
      "module": "core",
      "signature": "secret_eq(Any, Any) -> Bool",
      "effects": null,
      "summary": "compares a secret with a secret or text",
      "example": "needs [ env [\"TOKEN\"] ]\nlet t = env(\"TOKEN\") or \"\"\nprint(secret_eq(t, \"expected\"))",
      "fragment": false
    },
    {
      "name": "join_path",
      "module": "core",
      "signature": "join_path(Str, Str) -> Path",
      "effects": null,
      "summary": "appends a part to a path",
      "example": "print(join_path(\"./src\", \"main.ts\"))",
      "fragment": false
    },
    {
      "name": "path_parent",
      "module": "core",
      "signature": "path_parent(Str) -> Str",
      "effects": null,
      "summary": "the folder a path is in",
      "example": "print(path_parent(\"./src/main.ts\"))",
      "fragment": false
    },
    {
      "name": "path_name",
      "module": "core",
      "signature": "path_name(Str) -> Str",
      "effects": null,
      "summary": "the last part of a path",
      "example": "print(path_name(\"./src/main.ts\"))",
      "fragment": false
    },
    {
      "name": "path_stem",
      "module": "core",
      "signature": "path_stem(Str) -> Str",
      "effects": null,
      "summary": "the last part without its extension",
      "example": "print(path_stem(\"./src/main.ts\"))",
      "fragment": false
    },
    {
      "name": "path_ext",
      "module": "core",
      "signature": "path_ext(Str) -> Str",
      "effects": null,
      "summary": "the extension without the dot",
      "example": "print(path_ext(\"./src/main.ts\"))",
      "fragment": false
    },
    {
      "name": "int",
      "module": "core",
      "signature": "int(Any) -> Result<Int, ParseError>",
      "effects": null,
      "summary": "a number from text, or a float cut to a whole number",
      "example": "print(int(\"42\") or 0, int(\"x\") or -1)",
      "fragment": false
    },
    {
      "name": "float",
      "module": "core",
      "signature": "float(Any) -> Result<Float, ParseError>",
      "effects": null,
      "summary": "a number from text or an integer",
      "example": "print(float(\"2.5\") or 0.0, float(3) or 0.0)",
      "fragment": false
    },
    {
      "name": "abs",
      "module": "core",
      "signature": "abs(T) -> T",
      "effects": null,
      "summary": "the size of a number without its sign (an error for the smallest whole number)",
      "example": "print(abs(-3), abs(-2.5))",
      "fragment": false
    },
    {
      "name": "min",
      "module": "core",
      "signature": "min(T, T) -> T",
      "effects": null,
      "summary": "the smaller of two numbers (or two texts)",
      "example": "print(min(3, 7), min(2.5, 1), min(\"b\", \"a\"))",
      "fragment": false
    },
    {
      "name": "max",
      "module": "core",
      "signature": "max(T, T) -> T",
      "effects": null,
      "summary": "the larger of two numbers (or two texts)",
      "example": "print(max(3, 7), max(\"b\", \"a\"))",
      "fragment": false
    },
    {
      "name": "floor",
      "module": "core",
      "signature": "floor(Float) -> Int",
      "effects": null,
      "summary": "the whole number at or below a fraction",
      "example": "print(floor(2.7))",
      "fragment": false
    },
    {
      "name": "ceil",
      "module": "core",
      "signature": "ceil(Float) -> Int",
      "effects": null,
      "summary": "the whole number at or above a fraction",
      "example": "print(ceil(2.1))",
      "fragment": false
    },
    {
      "name": "round",
      "module": "core",
      "signature": "round(Float) -> Int",
      "effects": null,
      "summary": "the nearest whole number (halves go away from zero)",
      "example": "print(round(2.5))",
      "fragment": false
    },
    {
      "name": "sqrt",
      "module": "core",
      "signature": "sqrt(Float) -> Float",
      "effects": null,
      "summary": "the square root",
      "example": "print(sqrt(16))",
      "fragment": false
    },
    {
      "name": "pow",
      "module": "core",
      "signature": "pow(Float, Float) -> Float",
      "effects": null,
      "summary": "a number raised to a power",
      "example": "print(pow(2, 10))",
      "fragment": false
    },
    {
      "name": "push",
      "module": "core",
      "signature": "push(List<T>, T) -> List<T>",
      "effects": null,
      "summary": "a new list with the item added at the end",
      "example": "let xs = push([1, 2], 3)\nprint(xs)",
      "fragment": false
    },
    {
      "name": "first",
      "module": "core",
      "signature": "first(List<T>) -> Option<T>",
      "effects": null,
      "summary": "the first item, if the list has one",
      "example": "print(first([7, 8]), first([]))",
      "fragment": false
    },
    {
      "name": "last",
      "module": "core",
      "signature": "last(List<T>) -> Option<T>",
      "effects": null,
      "summary": "the last item, if the list has one",
      "example": "print(last([7, 8]))",
      "fragment": false
    },
    {
      "name": "get",
      "module": "core",
      "signature": "get(List<T>, Int) -> Option<T>",
      "effects": null,
      "summary": "the item at a position, if there is one",
      "example": "print(get([7, 8], 1), get([7, 8], 5))",
      "fragment": false
    },
    {
      "name": "unwrap_or",
      "module": "core",
      "signature": "unwrap_or(Option<T>, T) -> T",
      "effects": null,
      "summary": "the value inside, or the fallback when there is none",
      "example": "print(unwrap_or(first([]), 0))",
      "fragment": false
    },
    {
      "name": "is_some",
      "module": "core",
      "signature": "is_some(Option<T>) -> Bool",
      "effects": null,
      "summary": "whether an Option holds a value",
      "example": "print(is_some(first([1])))",
      "fragment": false
    },
    {
      "name": "is_none",
      "module": "core",
      "signature": "is_none(Option<T>) -> Bool",
      "effects": null,
      "summary": "whether an Option is empty",
      "example": "print(is_none(first([])))",
      "fragment": false
    },
    {
      "name": "take",
      "module": "core",
      "signature": "take(List<T>, Int) -> List<T>",
      "effects": null,
      "summary": "the first n items",
      "example": "print(take([1, 2, 3], 2))",
      "fragment": false
    },
    {
      "name": "drop",
      "module": "core",
      "signature": "drop(List<T>, Int) -> List<T>",
      "effects": null,
      "summary": "all but the first n items",
      "example": "print(drop([1, 2, 3], 2))",
      "fragment": false
    },
    {
      "name": "reverse",
      "module": "core",
      "signature": "reverse(List<T>) -> List<T>",
      "effects": null,
      "summary": "the list backwards",
      "example": "print(reverse([1, 2, 3]))",
      "fragment": false
    },
    {
      "name": "sort",
      "module": "core",
      "signature": "sort(List<T>) -> List<T>",
      "effects": null,
      "summary": "numbers or text, smallest first",
      "example": "print(sort([3, 1, 2]), sort([\"b\", \"a\"]))",
      "fragment": false
    },
    {
      "name": "sort_by",
      "module": "core",
      "signature": "sort_by(List<T>, Fn(T) -> Any) -> List<T>",
      "effects": null,
      "summary": "sorted by a key",
      "example": "print(sort_by([\"ccc\", \"a\", \"bb\"], len))",
      "fragment": false
    },
    {
      "name": "range",
      "module": "core",
      "signature": "range(Int, Int) -> List<Int>",
      "effects": null,
      "summary": "the numbers from the first up to, not including, the second",
      "example": "print(range(2, 5), 2..5)",
      "fragment": false
    },
    {
      "name": "map",
      "module": "core",
      "signature": "map(List<T>, Fn(T) -> U) -> List<U>",
      "effects": null,
      "summary": "a list of what a function gives for each item",
      "example": "print(map([1, 2, 3], fn(x) { x * 2 }))",
      "fragment": false
    },
    {
      "name": "filter",
      "module": "core",
      "signature": "filter(List<T>, Fn(T) -> Bool) -> List<T>",
      "effects": null,
      "summary": "the items a function says yes to",
      "example": "print(filter([1, 2, 3, 4], fn(x) { x % 2 == 0 }))",
      "fragment": false
    },
    {
      "name": "fold",
      "module": "core",
      "signature": "fold(List<T>, U, Fn(U, T) -> U) -> U",
      "effects": null,
      "summary": "combines the items one by one, starting from a value",
      "example": "print(fold([1, 2, 3], 0, fn(sum, x) { sum + x }))",
      "fragment": false
    },
    {
      "name": "any",
      "module": "core",
      "signature": "any(List<T>, Fn(T) -> Bool) -> Bool",
      "effects": null,
      "summary": "whether a function says yes to at least one item",
      "example": "print(any([1, 2, 3], fn(x) { x > 2 }))",
      "fragment": false
    },
    {
      "name": "all",
      "module": "core",
      "signature": "all(List<T>, Fn(T) -> Bool) -> Bool",
      "effects": null,
      "summary": "whether a function says yes to every item",
      "example": "print(all([1, 2, 3], fn(x) { x > 2 }))",
      "fragment": false
    },
    {
      "name": "find",
      "module": "core",
      "signature": "find(List<T>, Fn(T) -> Bool) -> Option<T>",
      "effects": null,
      "summary": "the first item a function says yes to, if there is one",
      "example": "print(find([1, 2, 3], fn(x) { x > 1 }))",
      "fragment": false
    },
    {
      "name": "map_new",
      "module": "core",
      "signature": "map_new() -> Map<T>",
      "effects": null,
      "summary": "an empty map",
      "example": "let m = map_new()\nprint(m, len(m))",
      "fragment": false
    },
    {
      "name": "map_set",
      "module": "core",
      "signature": "map_set(Map<T>, Str, T) -> Map<T>",
      "effects": null,
      "summary": "a new map with the key set",
      "example": "let m = map_set(map_new(), \"a\", 1)\nprint(m)",
      "fragment": false
    },
    {
      "name": "map_get",
      "module": "core",
      "signature": "map_get(Map<T>, Str) -> Option<T>",
      "effects": null,
      "summary": "the value for a key, if there is one",
      "example": "let m = #{ \"a\": 1 }\nprint(map_get(m, \"a\"), map_get(m, \"z\"))",
      "fragment": false
    },
    {
      "name": "map_has",
      "module": "core",
      "signature": "map_has(Map<T>, Str) -> Bool",
      "effects": null,
      "summary": "whether a map has a key",
      "example": "print(map_has(#{ \"a\": 1 }, \"a\"))",
      "fragment": false
    },
    {
      "name": "map_remove",
      "module": "core",
      "signature": "map_remove(Map<T>, Str) -> Map<T>",
      "effects": null,
      "summary": "a map without a key",
      "example": "print(map_remove(#{ \"a\": 1, \"b\": 2 }, \"a\"))",
      "fragment": false
    },
    {
      "name": "map_keys",
      "module": "core",
      "signature": "map_keys(Map<T>) -> List<Str>",
      "effects": null,
      "summary": "the keys, sorted",
      "example": "print(map_keys(#{ \"a\": 1, \"b\": 2 }))",
      "fragment": false
    },
    {
      "name": "map_values",
      "module": "core",
      "signature": "map_values(Map<T>) -> List<T>",
      "effects": null,
      "summary": "the values, in key order",
      "example": "print(map_values(#{ \"a\": 1, \"b\": 2 }))",
      "fragment": false
    },
    {
      "name": "json_parse",
      "module": "core",
      "signature": "json_parse(Str) -> Result<Any, ParseError>",
      "effects": null,
      "summary": "text to lists, maps, numbers, text, yes/no values",
      "example": "let v = json_parse(\"\\{\\\"n\\\": [1, 2]\\}\") or { return 1 }\nprint(v.n)",
      "fragment": false
    },
    {
      "name": "json_stringify",
      "module": "core",
      "signature": "json_stringify(Any) -> Str",
      "effects": null,
      "summary": "a value as JSON text",
      "example": "print(json_stringify({ name: \"ann\", tags: [\"a\", \"b\"] }))",
      "fragment": false
    },
    {
      "name": "matches",
      "module": "core",
      "signature": "matches(Str, Str) -> Bool",
      "effects": null,
      "summary": "whether text matches a pattern (letters, . * + ? [] [^] ^ $ | () {n,m} \\d \\w \\s)",
      "example": "print(matches(\"abc123\", \"^[a-z]+\\\\d+$\"))",
      "fragment": false
    },
    {
      "name": "regex_find",
      "module": "core",
      "signature": "regex_find(Str, Str) -> Option<Str>",
      "effects": null,
      "summary": "the first piece of text that matches a pattern",
      "example": "print(regex_find(\"id: 42!\", \"\\\\d+\"))",
      "fragment": false
    },
    {
      "name": "regex_replace",
      "module": "core",
      "signature": "regex_replace(Str, Str, Str) -> Str",
      "effects": null,
      "summary": "replaces every match of a pattern",
      "example": "print(regex_replace(\"a1b22\", \"\\\\d+\", \"#\"))",
      "fragment": false
    },
    {
      "name": "format_time",
      "module": "core",
      "signature": "format_time(Int) -> Str",
      "effects": null,
      "summary": "milliseconds since 1970 as 2026-10-12T09:30:00Z",
      "example": "print(format_time(0))",
      "fragment": false
    },
    {
      "name": "assert",
      "module": "core",
      "signature": "assert(Bool) -> Unit",
      "effects": null,
      "summary": "stops with a failure when the value is false",
      "example": "assert(1 + 1 == 2)",
      "fragment": false
    },
    {
      "name": "assert_eq",
      "module": "core",
      "signature": "assert_eq(T, T) -> Unit",
      "effects": null,
      "summary": "stops with a failure when the values differ",
      "example": "assert_eq(1 + 1, 2)",
      "fragment": false
    },
    {
      "name": "tuple",
      "module": "core",
      "signature": "tuple(Any...) -> Any",
      "effects": null,
      "summary": "a tuple of the values: `(1, \"a\")` is `tuple(1, \"a\")`; `t.0` is its first part",
      "example": "let t = tuple(1, \"a\")\nprint(t, t.0, t.1)",
      "fragment": false
    },
    {
      "name": "map_from",
      "module": "core",
      "signature": "map_from(Any...) -> Map<Any>",
      "effects": null,
      "summary": "a map from alternating keys and values: `#{ \"a\": 1 }` is `map_from(\"a\", 1)`",
      "example": "print(map_from(\"a\", 1, \"b\", 2))",
      "fragment": false
    },
    {
      "name": "map_entries",
      "module": "core",
      "signature": "map_entries(Map<T>) -> List<Tuple<Str, T>>",
      "effects": null,
      "summary": "the (key, value) pairs of a map",
      "example": "for ((k, v) in map_entries(#{ \"a\": 1 })) { print(k, v) }",
      "fragment": false
    },
    {
      "name": "set_of",
      "module": "core",
      "signature": "set_of(T...) -> Set<T>",
      "effects": null,
      "summary": "a set of the values, each once: `#[1, 2, 3]` is `set_of(1, 2, 3)`",
      "example": "print(set_of(1, 2, 2, 3))",
      "fragment": false
    },
    {
      "name": "set_from_list",
      "module": "core",
      "signature": "set_from_list(List<T>) -> Set<T>",
      "effects": null,
      "summary": "the members of a list, each once",
      "example": "print(set_from_list([3, 3, 1]))",
      "fragment": false
    },
    {
      "name": "set_add",
      "module": "core",
      "signature": "set_add(Set<T>, T) -> Set<T>",
      "effects": null,
      "summary": "a set with one more member",
      "example": "print(set_add(#[1], 2))",
      "fragment": false
    },
    {
      "name": "set_remove",
      "module": "core",
      "signature": "set_remove(Set<T>, T) -> Set<T>",
      "effects": null,
      "summary": "a set without a member",
      "example": "print(set_remove(#[1, 2], 1))",
      "fragment": false
    },
    {
      "name": "set_has",
      "module": "core",
      "signature": "set_has(Set<T>, T) -> Bool",
      "effects": null,
      "summary": "whether a set has a member",
      "example": "print(set_has(#[1, 2], 2))",
      "fragment": false
    },
    {
      "name": "set_union",
      "module": "core",
      "signature": "set_union(Set<T>, Set<T>) -> Set<T>",
      "effects": null,
      "summary": "the members of either set",
      "example": "print(set_union(#[1, 2], #[2, 3]))",
      "fragment": false
    },
    {
      "name": "set_intersect",
      "module": "core",
      "signature": "set_intersect(Set<T>, Set<T>) -> Set<T>",
      "effects": null,
      "summary": "the members of both sets",
      "example": "print(set_intersect(#[1, 2], #[2, 3]))",
      "fragment": false
    },
    {
      "name": "set_diff",
      "module": "core",
      "signature": "set_diff(Set<T>, Set<T>) -> Set<T>",
      "effects": null,
      "summary": "the members of the first set that the second does not have",
      "example": "print(set_diff(#[1, 2], #[2, 3]))",
      "fragment": false
    },
    {
      "name": "set_to_list",
      "module": "core",
      "signature": "set_to_list(Set<T>) -> List<T>",
      "effects": null,
      "summary": "the members as a list, in the order they were added",
      "example": "print(set_to_list(#[3, 1]))",
      "fragment": false
    },
    {
      "name": "set_at",
      "module": "core",
      "signature": "set_at(T, Any, Any) -> T",
      "effects": null,
      "summary": "a copy of a list, map, record or tuple with one item replaced: `xs[i] = v` is `xs = set_at(xs, i, v)`",
      "example": "let xs = set_at([1, 2, 3], 1, 20)\nprint(xs)",
      "fragment": false
    },
    {
      "name": "enumerate",
      "module": "core",
      "signature": "enumerate(List<T>) -> List<Tuple<Int, T>>",
      "effects": null,
      "summary": "the items with their positions: `for (i, x in enumerate(xs))`",
      "example": "for (i, x in enumerate([\"a\", \"b\"])) { print(i, x) }",
      "fragment": false
    },
    {
      "name": "zip",
      "module": "core",
      "signature": "zip(List<T>, List<U>) -> List<Tuple<T, U>>",
      "effects": null,
      "summary": "pairs of items from two lists, as many as the shorter list has",
      "example": "print(zip([1, 2], [\"a\", \"b\"]))",
      "fragment": false
    },
    {
      "name": "insert",
      "module": "core",
      "signature": "insert(List<T>, Int, T) -> List<T>",
      "effects": null,
      "summary": "a list with an item put in at a position",
      "example": "print(insert([1, 3], 1, 2))",
      "fragment": false
    },
    {
      "name": "remove_at",
      "module": "core",
      "signature": "remove_at(List<T>, Int) -> List<T>",
      "effects": null,
      "summary": "a list without the item at a position",
      "example": "print(remove_at([1, 2, 3], 0))",
      "fragment": false
    },
    {
      "name": "pop_last",
      "module": "core",
      "signature": "pop_last(List<T>) -> List<T>",
      "effects": null,
      "summary": "a list without its last item",
      "example": "print(pop_last([1, 2, 3]))",
      "fragment": false
    },
    {
      "name": "clear",
      "module": "core",
      "signature": "clear(T) -> T",
      "effects": null,
      "summary": "an empty list, map, set or text",
      "example": "print(clear([1, 2]), clear(\"text\") + \"|\")",
      "fragment": false
    },
    {
      "name": "extend",
      "module": "core",
      "signature": "extend(T, T) -> T",
      "effects": null,
      "summary": "two lists joined, or two maps merged (the second wins), or two sets united",
      "example": "print(extend([1], [2, 3]))",
      "fragment": false
    },
    {
      "name": "truncate",
      "module": "core",
      "signature": "truncate(List<T>, Int) -> List<T>",
      "effects": null,
      "summary": "the first n items",
      "example": "print(truncate([1, 2, 3], 2))",
      "fragment": false
    },
    {
      "name": "fmt_spec",
      "module": "core",
      "signature": "fmt_spec(Any, Str) -> Str",
      "effects": null,
      "summary": "a value formatted with a spec such as `>8` or `.2f`: `\"{x:.2f}\"` is `fmt_spec(x, \".2f\")`",
      "example": "print(fmt_spec(3.14159, \".2f\"), fmt_spec(42, \">6\"))",
      "fragment": false
    },
    {
      "name": "panic",
      "module": "core",
      "signature": "panic(Str) -> Never",
      "effects": null,
      "summary": "stops the script with a message",
      "example": "fn fail() -> Never { panic(\"unreachable state\") }\nprint(1)",
      "fragment": false
    },
    {
      "name": "spawn",
      "module": "core",
      "signature": "spawn(Fn() -> T) -> Task<T>",
      "effects": null,
      "summary": "starts a task; it is cancelled when the block that started it ends, unless awaited",
      "example": "let t = spawn(fn() { 6 * 7 })\nprint(await(t) or 0)",
      "fragment": false
    },
    {
      "name": "await",
      "module": "core",
      "signature": "await(Task<T>) -> Result<T, TaskFailed>",
      "effects": null,
      "summary": "waits for a task",
      "example": "let t = spawn(fn() { 1 })\nprint(await(t) or 0)",
      "fragment": false
    },
    {
      "name": "cancel",
      "module": "core",
      "signature": "cancel(Task<T>) -> Unit",
      "effects": null,
      "summary": "asks a task to stop",
      "example": "let t = spawn(fn() { 1 })\ncancel(t)",
      "fragment": false
    }
  ],
  "modules": [
    {
      "name": "core",
      "summary": "the built-in functions, available everywhere without an import",
      "functions": 110
    }
  ],
  "needs": [
    {
      "key": "read",
      "items": "paths: ./dir (everything below), ./dir/*.ts (files directly in it)",
      "summary": "read files and list folders",
      "example": "needs [ read [./src] ]"
    },
    {
      "key": "write",
      "items": "paths, as for read; write does not imply read",
      "summary": "create and change files",
      "example": "needs [ write [./out] ]"
    },
    {
      "key": "run",
      "items": "quoted commands: \"git status\" (and any arguments), \"git commit -m *\" (exactly these arguments, * is one word)",
      "summary": "run programs; there is no shell, so | ; & > < and backticks are errors",
      "example": "needs [ run [\"git status\"] ]"
    },
    {
      "key": "net",
      "items": "quoted hosts: \"api.github.com:443\", \"*.example.org\", \"*\"",
      "summary": "reach hosts over http and https",
      "example": "needs [ net [\"api.github.com:443\"] ]"
    },
    {
      "key": "env",
      "items": "quoted names: \"HOME\", \"AWS_*\"",
      "summary": "read environment variables (the values are secrets)",
      "example": "needs [ env [\"HOME\"] ]"
    },
    {
      "key": "clock",
      "items": "bare word (no list)",
      "summary": "read the time and sleep",
      "example": "needs [ clock ]"
    },
    {
      "key": "random",
      "items": "bare word (no list)",
      "summary": "random numbers",
      "example": "needs [ random ]"
    }
  ],
  "subset_rules": [
    "A script can do only what its `needs` allows; anything else is an error when it is checked, and again when it runs.",
    "A smaller permission is a subtype of a bigger one: `read [./src/a]` fits inside `read [./src]`, `run [\"git status\"]` inside `run [\"git\"]`, one host inside `*.example.org`.",
    "A function with its own `needs` runs inside the smaller set, and may not ask for more than the script has.",
    "A capability value is never bigger than `needs`; `demote(a, b)` only hands over less.",
    "A library (`use`) has no `needs` of its own: its functions say what they need, and the script that calls them must allow it.",
    "A `Secret` cannot reach text, `print`, `log`, or a function that does not take a `Secret`.",
    "A path is compared after symbolic links are resolved, so a link cannot lead out of a permitted folder."
  ],
  "errors": [
    {
      "code": "E0101",
      "title": "a string is never closed",
      "explanation": "A `\"` starts a string and needs another `\"` to end it (a raw string ends with its own quote and hashes).",
      "bad": "let s = \"open",
      "fix": "let s = \"closed\""
    },
    {
      "code": "E0102",
      "title": "a character that is not part of the language",
      "explanation": "Single `&` and `|` do not exist (use `&&` and `||`); there are no pipes, run each program with `run`. `|` only separates alternatives in a pattern.",
      "bad": "let a = 1 & 2",
      "fix": "let a = true && false"
    },
    {
      "code": "E0103",
      "title": "a number cannot be read",
      "explanation": "Whole numbers go up to 9223372036854775807; a hex, binary or octal number needs digits; a float must be finite.",
      "bad": "let n = 99999999999999999999",
      "fix": "let n = 9_000_000_000"
    },
    {
      "code": "E0104",
      "title": "an escape in a string is not valid",
      "explanation": "Escapes are \\n \\t \\r \\0 \\\" \\\\ \\{ \\} \\xNN (up to 7F) and \\u{N} (1 to 6 hex digits).",
      "bad": "let s = \"\\q\"",
      "fix": "let s = \"\\\\q\""
    },
    {
      "code": "E0105",
      "title": "a `{` in a string is never closed, or is empty",
      "explanation": "`{expression}` puts a value in a string; write `\\{` for a literal brace.",
      "bad": "let s = \"{x\"",
      "fix": "let x = 1\nlet s = \"{x}\""
    },
    {
      "code": "E0106",
      "title": "strings inside `{...}` are nested too deeply",
      "explanation": "The limit is 16 levels; compute the inner value first with `let`.",
      "bad": "",
      "fix": ""
    },
    {
      "code": "E0110",
      "title": "the parser expected something else",
      "explanation": "The message says what was expected and what was found.",
      "bad": "let = 1",
      "fix": "let x = 1"
    },
    {
      "code": "E0111",
      "title": "a bracket is never closed",
      "explanation": "Close the `(`, `[` or `{` opened earlier.",
      "bad": "print(1",
      "fix": "print(1)"
    },
    {
      "code": "E0112",
      "title": "a statement does not end where it should",
      "explanation": "Put each statement on its own line.",
      "bad": "let x = 1 2",
      "fix": "let x = 1"
    },
    {
      "code": "E0113",
      "title": "semicolons do not exist",
      "explanation": "A line break ends a statement.",
      "bad": "let x = 1;",
      "fix": "let x = 1"
    },
    {
      "code": "E0114",
      "title": "`needs` must be the first thing in the file",
      "explanation": "Move the `needs [ ... ]` block above all other code.",
      "bad": "print(1)\nneeds [ clock ]",
      "fix": "needs [ clock ]\nprint(1)"
    },
    {
      "code": "E0115",
      "title": "more than one expression inside `{...}` of a string",
      "explanation": "Only one name or expression fits in `{...}`.",
      "bad": "let a = 1\nlet s = \"{a a}\"",
      "fix": "let a = 1\nlet s = \"{a}\""
    },
    {
      "code": "E0116",
      "title": "a space between a function name and `(`",
      "explanation": "Write the call with the bracket right after the name.",
      "bad": "print (1)",
      "fix": "print(1)"
    },
    {
      "code": "E0117",
      "title": "a field name cannot contain `{...}`",
      "explanation": "Field names are fixed text.",
      "bad": "let r = { \"a{1}\": 1 }",
      "fix": "let r = { a: 1 }"
    },
    {
      "code": "E0118",
      "title": "a pattern, file path or test name cannot contain `{...}`",
      "explanation": "These are fixed text.",
      "bad": "match (1) { \"a{1}\" => 1, _ => 2 }",
      "fix": "match (\"a\") { \"a\" => 1, _ => 2 }"
    },
    {
      "code": "E0121",
      "title": "a permission must be written out in full",
      "explanation": "Permissions are fixed text, so a reader can approve them without running the script.",
      "bad": "needs [ read [./a/{x}] ]",
      "fix": "needs [ read [./a] ]"
    },
    {
      "code": "E0122",
      "title": "a name where a permission item is needed",
      "explanation": "Names are only allowed in a function's own `needs`, for its parameters.",
      "bad": "needs [ read [a] ]",
      "fix": "needs [ read [./a] ]"
    },
    {
      "code": "E0123",
      "title": "not a permission",
      "explanation": "The permissions are read, write, run, net, env, clock and random.",
      "bad": "needs [ teleport [./a] ]",
      "fix": "needs [ read [./a] ]"
    },
    {
      "code": "E0124",
      "title": "a permission that takes no list",
      "explanation": "`clock` and `random` are bare words.",
      "bad": "needs [ clock [x] ]",
      "fix": "needs [ clock ]"
    },
    {
      "code": "E0130",
      "title": "a declaration in a place where it is not allowed",
      "explanation": "Functions, types, enums, error kinds, `use` and tests go at the top level.",
      "bad": "if (true) {\n  fn f() { 1 }\n}",
      "fix": "fn f() { 1 }\nif (true) { f() }"
    },
    {
      "code": "E0131",
      "title": "the condition of `if` or `while` is not in parentheses",
      "explanation": "Write `if (condition) { ... }`.",
      "bad": "if true { }",
      "fix": "if (true) { }"
    },
    {
      "code": "E0140",
      "title": "a range with no ends",
      "explanation": "Write at least one end: `1..`, `..9`, `1..=9`.",
      "bad": "type P = Int where ..",
      "fix": "type P = Int where 1.."
    },
    {
      "code": "E0141",
      "title": "a range that is empty",
      "explanation": "The first end is after the second.",
      "bad": "type P = Int where 5..1",
      "fix": "type P = Int where 1..5"
    },
    {
      "code": "E0142",
      "title": "a variant name must start with a capital letter",
      "explanation": "Capital letters tell variants from names.",
      "bad": "enum E { lower }",
      "fix": "enum E { Lower }"
    },
    {
      "code": "E0150",
      "title": "`break` or `continue` outside a loop",
      "explanation": "They only make sense inside `for` or `while`; use `return` to leave a function.",
      "bad": "break",
      "fix": "while (true) { break }"
    },
    {
      "code": "E0151",
      "title": "this cannot be assigned to",
      "explanation": "Assign to a name, a field (`r.n = 1`) or an item (`xs[0] = 1`).",
      "bad": "f(x) = 1",
      "fix": "let mut x = 1\nx = 2"
    },
    {
      "code": "E0152",
      "title": "`..` must be last in a list pattern",
      "explanation": "Write `[first, second, ..rest]`.",
      "bad": "let [a, ..r, b] = [1, 2, 3]",
      "fix": "let [a, b, ..r] = [1, 2, 3]"
    },
    {
      "code": "E0170",
      "title": "not a library path",
      "explanation": "A path in `use` starts with ./ or ../ and ends in .sub.",
      "bad": "",
      "fix": ""
    },
    {
      "code": "E0171",
      "title": "a library cannot be read",
      "explanation": "Check the path, relative to the file with the `use`.",
      "bad": "",
      "fix": ""
    },
    {
      "code": "E0172",
      "title": "too many files",
      "explanation": "At most 64 files are used by one script.",
      "bad": "",
      "fix": ""
    },
    {
      "code": "E0173",
      "title": "a library has a `needs` block",
      "explanation": "Put `needs` on the functions that need something; the script that calls them must allow it.",
      "bad": "",
      "fix": ""
    },
    {
      "code": "E0174",
      "title": "a library holds a statement that runs",
      "explanation": "A library holds declarations only.",
      "bad": "",
      "fix": ""
    },
    {
      "code": "E0199",
      "title": "nested too deeply",
      "explanation": "The source nests deeper than the compiler reads.",
      "bad": "",
      "fix": ""
    },
    {
      "code": "E0201",
      "title": "a call is not allowed by `needs`",
      "explanation": "Add the permission to `needs`, or remove the call.",
      "bad": "run(\"ls\")",
      "fix": "needs [ run [\"ls\"] ]\nrun(\"ls\") or { return 1 }"
    },
    {
      "code": "E0202",
      "title": "a shell operator in a command",
      "explanation": "`run` has no shell: `|`, `;`, `&`, `>`, `<`, backticks and `$(` have no meaning. Give the program and its arguments, or run each program on its own.",
      "bad": "needs [ run [\"ls\"] ]\nrun(\"ls | wc\")",
      "fix": "needs [ run [\"ls\"] ]\nrun(\"ls\")"
    },
    {
      "code": "E0203",
      "title": "a command that is empty or cannot be read",
      "explanation": "A command is a program and its arguments.",
      "bad": "needs [ run [\"ls\"] ]\nrun(\"\")",
      "fix": "needs [ run [\"ls\"] ]\nrun(\"ls\")"
    },
    {
      "code": "E0204",
      "title": "a URL that cannot be used",
      "explanation": "Only http and https URLs with a host are fetched.",
      "bad": "",
      "fix": ""
    },
    {
      "code": "E0210",
      "title": "a permission item that is not valid",
      "explanation": "The message says what is wrong with the path, command or host.",
      "bad": "needs [ net [\"a:b:c\"] ]",
      "fix": "needs [ net [\"a:443\"] ]"
    },
    {
      "code": "E0211",
      "title": "the wrong kind of item for a permission",
      "explanation": "`run`, `net` and `env` take quoted text; `read` and `write` take paths.",
      "bad": "needs [ run [./x] ]",
      "fix": "needs [ run [\"x\"] ]"
    },
    {
      "code": "E0220",
      "title": "a capability value is bigger than `needs` allows",
      "explanation": "A capability can only be made from what the script was given.",
      "bad": "needs [ read [./a] ]\nlet c = read[./b]",
      "fix": "needs [ read [./a] ]\nlet c = read[./a/b]"
    },
    {
      "code": "E0221",
      "title": "a call needs more than is allowed here",
      "explanation": "The function called asks for a permission that the caller's set does not include.",
      "bad": "needs [ clock ]\nfn load() needs [ read [./a] ] { 1 }\nload()",
      "fix": "needs [ clock, read [./a] ]\nfn load() needs [ read [./a] ] { 1 }\nload()"
    },
    {
      "code": "E0222",
      "title": "`demote` must hand over less",
      "explanation": "The second capability must be inside the first.",
      "bad": "needs [ read [./a] ]\nlet d = demote(read[./a/x], read[./a])",
      "fix": "needs [ read [./a] ]\nlet d = demote(read[./a], read[./a/x])"
    },
    {
      "code": "E0223",
      "title": "a function asks for more than the script has",
      "explanation": "A function's `needs` must fit inside the script's.",
      "bad": "needs [ clock ]\nfn f() needs [ read [./a] ] { 1 }",
      "fix": "needs [ clock, read [./a] ]\nfn f() needs [ read [./a] ] { 1 }"
    },
    {
      "code": "E0224",
      "title": "a capability value holds exactly one item",
      "explanation": "Make one capability per item.",
      "bad": "needs [ read [./a] ]\nlet c = read[./a, ./a/b]",
      "fix": "needs [ read [./a] ]\nlet c = read[./a/b]"
    },
    {
      "code": "E0225",
      "title": "a name in `needs` that is not a parameter",
      "explanation": "The names in a function's `needs` must be its parameters.",
      "bad": "fn f(p) needs [ read [q] ] { 1 }",
      "fix": "fn f(p) needs [ read [p] ] { 1 }"
    },
    {
      "code": "E0301",
      "title": "a secret in a string",
      "explanation": "Secrets cannot be put into text.",
      "bad": "needs [ env [\"T\"] ]\nlet t = env(\"T\") or \"\"\nlet s = \"x{t}\"",
      "fix": "needs [ env [\"T\"] ]\nlet t = env(\"T\") or \"\"\nprint(secret_eq(t, \"x\"))"
    },
    {
      "code": "E0302",
      "title": "a secret passed to a function that prints",
      "explanation": "`print`, `log` and the like never take a secret.",
      "bad": "needs [ env [\"T\"] ]\nlet t = env(\"T\") or \"\"\nprint(t)",
      "fix": "needs [ env [\"T\"] ]\nlet t = env(\"T\") or \"\"\nprint(len(\"ok\"))"
    },
    {
      "code": "E0303",
      "title": "a secret passed to a parameter that is not a Secret",
      "explanation": "Declare the parameter `Secret<String>`.",
      "bad": "needs [ env [\"T\"] ]\nfn show(x: String) { len(x) }\nshow(env(\"T\") or \"\")",
      "fix": ""
    },
    {
      "code": "E0310",
      "title": "a name is not defined",
      "explanation": "Declare it with `let`; the message suggests close names.",
      "bad": "print(cuont)",
      "fix": "let count = 1\nprint(count)"
    },
    {
      "code": "E0311",
      "title": "there is no such function",
      "explanation": "The message suggests close names.",
      "bad": "rad()",
      "fix": "print(1)"
    },
    {
      "code": "E0312",
      "title": "the wrong number of arguments",
      "explanation": "The message says how many are wanted.",
      "bad": "len()",
      "fix": "len([1])"
    },
    {
      "code": "E0313",
      "title": "a built-in cannot be redefined, or a function is defined twice",
      "explanation": "Choose another name.",
      "bad": "fn len(x) { 1 }",
      "fix": "fn length(x) { 1 }"
    },
    {
      "code": "E0314",
      "title": "a name declared with `let` cannot be changed",
      "explanation": "Declare it with `let mut`.",
      "bad": "let x = 1\nx = 2",
      "fix": "let mut x = 1\nx = 2"
    },
    {
      "code": "E0501",
      "title": "not supported by the WebAssembly backend",
      "explanation": "The interpreter accepts it; the compiled module does not yet.",
      "bad": "",
      "fix": ""
    },
    {
      "code": "E0601",
      "title": "not a type",
      "explanation": "The message suggests close names.",
      "bad": "let x: Strng = \"a\"",
      "fix": "let x: String = \"a\""
    },
    {
      "code": "E0602",
      "title": "a type name is used twice",
      "explanation": "Choose another name.",
      "bad": "type A = Int\ntype A = String",
      "fix": "type A = Int\ntype B = String"
    },
    {
      "code": "E0603",
      "title": "not a failure family",
      "explanation": "A failure kind can be inside a family declared with `error`.",
      "bad": "error E <: Nothing",
      "fix": "error E"
    },
    {
      "code": "E0604",
      "title": "a variant name is used twice",
      "explanation": "Variant names are unique in a program.",
      "bad": "enum A { X }\nenum B { X }",
      "fix": "enum A { X }\nenum B { Y }"
    },
    {
      "code": "E0610",
      "title": "a value of the wrong type",
      "explanation": "The message says what was expected and what was found, and how to convert.",
      "bad": "let n: Int = \"three\"",
      "fix": "let n: Int = 3"
    },
    {
      "code": "E0611",
      "title": "two branches give different types",
      "explanation": "Make `if`, `match`, `or` and `??` give one kind of value.",
      "bad": "let x = if (true) { 1 } else { \"a\" }",
      "fix": "let x = if (true) { 1 } else { 2 }"
    },
    {
      "code": "E0612",
      "title": "a function returns on some paths only",
      "explanation": "Return a value on every path, or on none.",
      "bad": "fn f(n) {\n  if (n > 0) { return 1 }\n}",
      "fix": "fn f(n) {\n  if (n > 0) { return 1 }\n  0\n}"
    },
    {
      "code": "E0613",
      "title": "no such field",
      "explanation": "The message lists the fields.",
      "bad": "let r = { a: 1 }\nprint(r.b)",
      "fix": "let r = { a: 1 }\nprint(r.a)"
    },
    {
      "code": "E0620",
      "title": "`?` on a value that cannot fail",
      "explanation": "Remove the `?`.",
      "bad": "fn f() { let y = 5?\n  y }",
      "fix": "fn f() { let y = 5\n  y }"
    },
    {
      "code": "E0621",
      "title": "`?` in a function declared not to fail",
      "explanation": "Declare the failure in the return type, or handle it with `or`.",
      "bad": "needs [ read [./a] ]\nfn f() -> Int {\n  let t = read(./a/x)?\n  1\n}",
      "fix": "needs [ read [./a] ]\nfn f() -> Result<Int> {\n  let t = read(./a/x)?\n  1\n}"
    },
    {
      "code": "E0630",
      "title": "an operator on values it does not work with",
      "explanation": "The message says what the operator needs.",
      "bad": "let x = true - 1",
      "fix": "let x = 2 - 1"
    },
    {
      "code": "E0640",
      "title": "a pattern that does not fit the value",
      "explanation": "A case must be about the kind of value being matched.",
      "bad": "match (1) { \"a\" => 1, _ => 2 }",
      "fix": "match (1) { 1 => 1, _ => 2 }"
    },
    {
      "code": "E0641",
      "title": "a variant pattern with the wrong number of parts",
      "explanation": "Name every value of the variant, or use `_`.",
      "bad": "enum E { A(Int) }\nmatch (A(1)) { A(x, y) => 1 }",
      "fix": "enum E { A(Int) }\nmatch (A(1)) { A(x) => 1 }"
    },
    {
      "code": "E0642",
      "title": "a `match` that does not cover every value",
      "explanation": "The message names a missing case. Add it, or end with `_ => ...`. A case with an `if` condition does not count.",
      "bad": "enum C { R, G }\nmatch (R) { R => 1 }",
      "fix": "enum C { R, G }\nmatch (R) { R => 1, G => 2 }"
    },
    {
      "code": "E0643",
      "title": "alternatives of a pattern bind different names",
      "explanation": "Bind the same names in each `|` alternative.",
      "bad": "match (1) { 1 | x => 1, _ => 2 }",
      "fix": "match (1) { 1 | 2 => 1, _ => 2 }"
    },
    {
      "code": "E0644",
      "title": "a `let` pattern that may not fit",
      "explanation": "Use `if (let ...)` or `match` for a pattern that may not fit.",
      "bad": "let Some(x) = first([1])",
      "fix": "if (let Some(x) = first([1])) { print(x) }"
    },
    {
      "code": "E0645",
      "title": "a format that is not valid",
      "explanation": "Write `{value:[[fill]align][sign][#][0][width][,][.precision][type]}`.",
      "bad": "print(\"{1:zz}\")",
      "fix": "print(\"{1:>4}\")"
    },
    {
      "code": "E0650",
      "title": "a literal that does not fit a refined type",
      "explanation": "The type's condition is in the message; convert a computed value with `Port(x)`.",
      "bad": "type Port = Int where 1..=65535\nlet p: Port = 70000",
      "fix": "type Port = Int where 1..=65535\nlet p: Port = 8080"
    },
    {
      "code": "E0651",
      "title": "a refinement pattern that is not valid",
      "explanation": "The pattern after `matching` must be a valid regular expression.",
      "bad": "type Id = String matching \"[a\"",
      "fix": "type Id = String matching \"^[a-z]+$\""
    },
    {
      "code": "R0100",
      "title": "the script stopped while running",
      "explanation": "A step failed and nobody handled it, a pattern did not fit, a number was too large, or a limit was reached.",
      "bad": "",
      "fix": ""
    },
    {
      "code": "R0101",
      "title": "a permission was denied while running",
      "explanation": "The script tried something its `needs` does not allow; exit code 3.",
      "bad": "",
      "fix": ""
    }
  ],
  "warnings": [
    {
      "code": "W0203",
      "title": "a permission listed twice",
      "explanation": "Remove the repeat.",
      "bad": "needs [ clock, clock ]\nprint(now())",
      "fix": "needs [ clock ]\nprint(now())"
    },
    {
      "code": "W0204",
      "title": "an item listed twice",
      "explanation": "Remove the repeat.",
      "bad": "needs [ read [./a, ./a] ]\nprint(exists(./a))",
      "fix": "needs [ read [./a] ]\nprint(exists(./a))"
    },
    {
      "code": "W0205",
      "title": "an item already covered by a wider one",
      "explanation": "Remove the narrower item.",
      "bad": "needs [ read [./a, ./a/b] ]\nprint(exists(./a))",
      "fix": "needs [ read [./a] ]\nprint(exists(./a))"
    },
    {
      "code": "W0401",
      "title": "a permission the script never uses",
      "explanation": "Ask for less.",
      "bad": "needs [ clock ]\nprint(1)",
      "fix": "print(1)"
    },
    {
      "code": "W0402",
      "title": "an item of a permission that is never used",
      "explanation": "Remove it.",
      "bad": "needs [ run [\"ls\", \"wc\"] ]\nrun(\"ls\")",
      "fix": "needs [ run [\"ls\"] ]\nrun(\"ls\")"
    },
    {
      "code": "W0403",
      "title": "a permission that is broader than it looks",
      "explanation": "`run [\"sh\"]`, `net [\"*\"]` and paths outside the project allow far more than they say.",
      "bad": "needs [ run [\"sh\"] ]\nrun(\"sh\")",
      "fix": "needs [ run [\"git status\"] ]\nrun(\"git status\")"
    },
    {
      "code": "W0601",
      "title": "a failure used as a value without being handled",
      "explanation": "It stops the script when it happens. Add `?` or `or`.",
      "bad": "needs [ read [./a] ]\nlet t = read(./a/x)\nprint(len(t))",
      "fix": "needs [ read [./a] ]\nlet t = read(./a/x) or \"\"\nprint(len(t))"
    },
    {
      "code": "W0602",
      "title": "`or` or `??` on a value that cannot fail or be empty",
      "explanation": "Remove the `or` or `??` part.",
      "bad": "let v = 5 ?? 1",
      "fix": "let v = 5"
    },
    {
      "code": "W0603",
      "title": "the failure of a call is ignored",
      "explanation": "Add `?`, or `or { ... }` to say what to do.",
      "bad": "needs [ write [./a] ]\nwrite(./a/x, \"y\")",
      "fix": "needs [ write [./a] ]\nwrite(./a/x, \"y\")?"
    },
    {
      "code": "W0642",
      "title": "a case that can never be reached",
      "explanation": "An earlier case already matches everything.",
      "bad": "match (1) { _ => 1, 2 => 2 }",
      "fix": "match (1) { 2 => 2, _ => 1 }"
    }
  ],
  "cli": [
    {
      "command": "check",
      "usage": "sub check <file.sub> [--strict]",
      "summary": "find problems; --strict also fails on warnings",
      "flags": [
        {
          "flag": "--strict",
          "summary": "exit 1 on warnings too"
        }
      ]
    },
    {
      "command": "explain",
      "usage": "sub explain <file.sub|file.wasm> [--json]",
      "summary": "what the script can do, in plain words (or as data)",
      "flags": [
        {
          "flag": "--json",
          "summary": "data instead of words"
        }
      ]
    },
    {
      "command": "manifest",
      "usage": "sub manifest <file.sub|file.wasm> [--centcom]",
      "summary": "the permission set and calls, as JSON (--centcom: an approval card and agent allow rules for Centcom)",
      "flags": [
        {
          "flag": "--centcom",
          "summary": "the approval card, risk level and agent rules"
        }
      ]
    },
    {
      "command": "build",
      "usage": "sub build <file.sub> [-o out.wasm] [--wasi]",
      "summary": "compile to a WebAssembly module (--wasi: a WASI command module)",
      "flags": [
        {
          "flag": "-o",
          "summary": "the output file"
        },
        {
          "flag": "--wasi",
          "summary": "a WASI command module"
        }
      ]
    },
    {
      "command": "run",
      "usage": "sub run <file.sub|file.wasm> [--yes] [--root DIR] [--timeout SECS] [--max-output BYTES] [--fetch-max BYTES] [--interp] [-- args...]",
      "summary": "compile (if needed), ask to approve the permissions, run",
      "flags": [
        {
          "flag": "--yes",
          "summary": "approve without asking"
        },
        {
          "flag": "--root",
          "summary": "the folder the script runs in"
        },
        {
          "flag": "--timeout",
          "summary": "seconds for a request or a program"
        },
        {
          "flag": "--max-output",
          "summary": "bytes of output a program may produce"
        },
        {
          "flag": "--fetch-max",
          "summary": "bytes a fetch may read"
        },
        {
          "flag": "--interp",
          "summary": "use the interpreter instead of WebAssembly"
        }
      ]
    },
    {
      "command": "test",
      "usage": "sub test <file.sub> [--yes] [--root DIR]",
      "summary": "run the test blocks of a script",
      "flags": [
        {
          "flag": "--yes",
          "summary": "approve without asking"
        },
        {
          "flag": "--root",
          "summary": "the folder the tests run in"
        }
      ]
    },
    {
      "command": "fmt",
      "usage": "sub fmt <file.sub>... [--check] [--stdout]",
      "summary": "one layout for every script (idempotent)",
      "flags": [
        {
          "flag": "--check",
          "summary": "exit 1 if a file would change"
        },
        {
          "flag": "--stdout",
          "summary": "print instead of writing"
        }
      ]
    },
    {
      "command": "tokens",
      "usage": "sub tokens <file.sub>",
      "summary": "the tokens, one per line (the lexer in Rust; examples/selfhost has the one in Subtype)",
      "flags": []
    },
    {
      "command": "init",
      "usage": "sub init [dir]",
      "summary": "start a project: main.sub, a library and a test",
      "flags": []
    },
    {
      "command": "spec",
      "usage": "sub spec [--json|--markdown|--grammar]",
      "summary": "the language specification from the compiler's own tables",
      "flags": [
        {
          "flag": "--json",
          "summary": "one machine-readable document (default)"
        },
        {
          "flag": "--markdown",
          "summary": "a compact reference"
        },
        {
          "flag": "--grammar",
          "summary": "the grammar as EBNF"
        }
      ]
    }
  ],
  "exit_codes": [
    {
      "code": 0,
      "meaning": "done"
    },
    {
      "code": 1,
      "meaning": "failed, or did not check"
    },
    {
      "code": 2,
      "meaning": "usage error"
    },
    {
      "code": 3,
      "meaning": "a permission was denied while running"
    },
    {
      "code": 4,
      "meaning": "the permissions were not approved"
    }
  ],
  "files": [
    {
      "name": ".sub-allow",
      "summary": "permission sets approved with `always` (one hash per line)",
      "keys": []
    },
    {
      "name": "main.sub",
      "summary": "a script: an optional `needs` header, then statements and declarations",
      "keys": []
    }
  ],
  "grammar": "program   = [ needs NL ] { toplevel NL } ;\nneeds     = \"needs\" \"[\" [ entry { ( \",\" | NL ) entry } [ \",\" ] ] \"]\" ;\nentry     = ( \"read\" | \"write\" | \"run\" | \"net\" | \"env\" ) list\n          | ( \"clock\" | \"random\" ) [ list ] ;                 (* bare = granted; [] = none *)\nlist      = \"[\" [ item { \",\" item } [ \",\" ] ] \"]\" ;\nitem      = PATH | STRING | IDENT ;                             (* IDENT only in a function's needs *)\ntoplevel  = stmt | decl | \"use\" STRING | \"test\" STRING block ;   (* `use`, decl and `test` at the top only *)\ndecl      = \"type\" IDENT \"=\" type [ \"where\" range | \"where\" \"len\" range | \"matching\" STRING ]\n          | \"enum\" IDENT \"{\" variant { \",\" variant } \"}\"\n          | \"error\" IDENT [ \"(\" param { \",\" param } \")\" ] [ \"<:\" IDENT ] ;\nvariant   = UPPER-IDENT [ \"(\" type { \",\" type } \")\" ] ;\nrange     = INT \"..\" [ \"=\" ] INT | INT \"..\" | \"..\" [ \"=\" ] INT ;\nstmt      = ( \"let\" [ \"mut\" ] | \"mut\" ) ( IDENT [ \":\" type ] | pattern [ \":\" type ] ) \"=\" expr\n          | target ( \"=\" | \"+=\" | \"-=\" | \"*=\" | \"/=\" | \"%=\" ) expr   (* target = IDENT { \".\" IDENT | \"[\" expr \"]\" } *)\n          | \"for\" \"(\" ( IDENT | pattern { \",\" pattern } ) \"in\" expr \")\" block\n          | \"while\" \"(\" ( expr | \"let\" pattern \"=\" expr ) \")\" block\n          | \"break\" | \"continue\"\n          | \"return\" [ expr ]\n          | \"defer\" expr\n          | \"fn\" IDENT \"(\" [ param { \",\" param } [ \",\" ] ] \")\" { \"needs\" needs-list | \"->\" type } block\n          | expr ;\nparam     = IDENT [ \":\" type ] ;\ntype      = IDENT [ ( \"<\" | \"[\" ) type { \",\" type } ( \">\" | \"]\" ) ] | \"(\" type { \",\" type } \")\" ;\nblock     = \"{\" { stmt NL } \"}\" ;\nexpr      = or-else ;\nor-else   = coalesce { \"or\" ( block | coalesce ) } ;            (* `error` is the failure inside a block:\n                                                                  error.kind, error.message *)\ncoalesce  = logic-or [ \"??\" coalesce ] ;\nlogic-or  = logic-and { \"||\" logic-and } ;\nlogic-and = equality { \"&&\" equality } ;\nequality  = compare { ( \"==\" | \"!=\" ) compare } ;\ncompare   = range-ex { ( \"<\" | \">\" | \"<=\" | \">=\" ) range-ex } ;\nrange-ex  = additive [ ( \"..\" | \"..=\" ) additive ] ;            (* a..b is range(a, b) *)\nadditive  = term { ( \"+\" | \"-\" ) term } ;\nterm      = unary { ( \"*\" | \"/\" | \"%\" ) unary } ;\nunary     = ( \"!\" | \"-\" ) unary | postfix ;\npostfix   = primary { \"?\" | \".\" ( IDENT | INT ) [ \"(\" args \")\" ] | \"?.\" IDENT | \"[\" index \"]\" | \"(\" args \")\" } ;\n                                    (* \"[\" and \"(\" with no space before; `x.f(a)` is `f(x, a)` *)\nindex     = expr | [ additive ] ( \"..\" | \"..=\" ) [ additive ] ;  (* a slice *)\nprimary   = INT | FLOAT | \"true\" | \"false\" | STRING | PATH | IDENT [ \"(\" args \")\" ]\n          | ( \"read\" | \"write\" | \"run\" | \"net\" | \"env\" ) list   (* a capability value, \"[\" glued *)\n          | \"(\" expr \")\" | \"(\" expr \",\" { expr \",\" } \")\"          (* a tuple *)\n          | \"[\" [ args ] \"]\" | record\n          | \"#\" \"{\" [ expr \":\" expr { \",\" expr \":\" expr } ] \"}\"   (* a map *)\n          | \"#\" \"[\" [ args ] \"]\"                                  (* a set *)\n          | \"fn\" \"(\" [ param { \",\" param } ] \")\" [ \"->\" type ] block      (* a closure *)\n          | \"if\" \"(\" ( expr | \"let\" pattern \"=\" expr ) \")\" block [ \"else\" ( if | block ) ]\n          | \"match\" \"(\" expr \")\" \"{\" { pattern [ \"if\" expr ] \"=>\" ( block | expr ) ( \",\" | NL ) } \"}\" ;\nrecord    = \"{\" [ IDENT \":\" expr { \",\" IDENT \":\" expr } ] \"}\" | \"{\" \"..\" expr { \",\" IDENT \":\" expr } \"}\" ;\npattern   = alt { \"|\" alt } ;\nalt       = \"_\" | IDENT | INT | \"-\" INT | INT ( \"..\" | \"..=\" ) INT | STRING | \"true\" | \"false\"\n          | UPPER-IDENT [ \"(\" pattern { \",\" pattern } \")\" ]\n          | \"(\" pattern { \",\" pattern } \")\"                       (* a tuple *)\n          | \"[\" [ pattern { \",\" pattern } ] [ \"..\" [ IDENT ] ] \"]\"  (* a list, with a rest *)\n          | \"{\" IDENT [ \":\" pattern ] { \",\" IDENT [ \":\" pattern ] } [ \"..\" ] \"}\" ;\nSTRING    = '\"' { char | \"\\n\" | \"\\t\" | \"\\\"\" | \"\\\\\" | \"\\{\" | \"\\}\" | \"\\xNN\" | \"\\u{N}\"\n                | \"{\" expr [ \":\" spec ] \"}\" } '\"'\n          | 'r' { '#' } '\"' { any } '\"' { '#' } | '\"\"\"' { any } '\"\"\"' ;\nPATH      = ( \"./\" | \"../\" | \"~/\" | \"/\" ) { path-char | \"{\" expr \"}\" } | \".\" | \"..\" ;\nINT       = digits | \"0x\" hex | \"0b\" bin | \"0o\" oct ;  (* with _ between digits *)\nFLOAT     = digits \".\" digits [ exp ] | digits exp ;\n"
}
