system macrosstd/System.xtlm

The system macros (lang-choices MC18, MC19, MC24), loaded before every file with no import and no alias: each is called unprefixed, as "c" i_f< "a; b". A macro is a function of the source text on each side of its call; the text it gives replaces the call. @ on a side means the macro takes nothing there (MC22). What only the compiler knows comes from hooks (MC20), usable only in a macro body.

source

Importing

ˢu̲se< : Char -> Char -> Unit

built-in macro · line 23

Import a library under an alias of your choice: the alias on the left (lowercase letters and digits, then a colon), the library's name or path on the right. A name Name finds Name.xtl and Name.xtlm together, beside the importing file, then in userlibs/, then in each directory of XETAL_PATH, then among the standard libraries built into xetal; the first place holding either gives both. Their exports are then written with the alias (s:m_ean). Built into xetal: it makes names exist under the alias, which no macro's text can do (a macro gives source, and source can only use names that exist), so it has a signature here and no definition.

      ˢ⁼u̲se< "Stats"
      ˢm̲ean 1 2 3
2.0
ˢu̲se< :: Char → Char → Unit

Choosing and repeating

ˢi̲f< : Char -> Char -> Char

macro · line 33

The value of the first expression on the right when the condition on the left holds, else of the second. The condition is tested when the program runs, and only the expression chosen is evaluated (MC14).

      "2 > 0" i̲f< "1; -1"
1
ˢi̲f< ← { cond both →
  p ← w̲here ʰs̲eps both
  1 ≠ t̲ally p ? "bad-macro-argument right" ⎕R̲EJECT ifArgs
  a ← ʰt̲rim ((f̲irst p) − 1) t̲ake both
  b ← ʰt̲rim (f̲irst p) d̲rop both
  (0 = t̲ally a) ∨ 0 = t̲ally b ? "bad-macro-argument right" ⎕R̲EJECT ifArgs
  "{ @ -> (" c̲at (ʰt̲rim cond) c̲at ") ? " c̲at a c̲at "; " c̲at b c̲at " } @"
}

ˢu̲nless< : Char -> Char -> Char

macro · line 47

Run the statements on the right unless the condition on the left holds; the value is @ either way (MC15).

      "1 = 0" u̲nless< "p_rint! 7"
7
@
ˢu̲nless< ← { cond body →
  "{ @ -> (" c̲at (ʰt̲rim cond) c̲at ") ? @; " c̲at (ʰt̲rim body) c̲at "; @ } @"
}

ˢe̲ach< : Char -> Char -> Char

macro · line 57

One statement per word on the left, each a copy of the template on the right with $w replaced by the word; it stands as a statement of its own (MC16).

      "m_ax m_in" e̲ach< "u:$w/ := { '$w r_/ _r }"
      ᵘm̲ax/ 3 1 4
4
ˢe̲ach< ← { words template →
  n̲ot ⎕S̲TATEMENT @ ? "misplaced-macro call" ⎕R̲EJECT eachAlone
  ws ← ʰw̲ords words
  0 = t̲ally ws ? "bad-macro-argument left" ⎕R̲EJECT "e_ach< needs words on its left"
  0 = '+ r̲/ 0 + ʰh̲oles template ? "bad-macro-argument right" ⎕R̲EJECT eachHole
  d̲isclose '{ a b → e̲nclose (d̲isclose a) c̲at "\n" c̲at d̲isclose b } r̲/ '{ w → template ʰc̲opy d̲isclose w } m̲ap ws
}

What the compiler knows

ˢl̲ine< : Unit -> Unit -> Char

macro · line 71

The line the call is written on, as a number (inside another macro's expansion, the line of that macro's call).

      @ l̲ine< @
1
ˢl̲ine< ← { @ @ → f̲ormat ⎕L̲INE @ }

ˢf̲ile< : Unit -> Unit -> Char

macro · line 77

The file the call is written in, as a string (-e for text given on the command line).

      @ f̲ile< @
-e
ˢf̲ile< ← { @ @ → ʰq̲uote ⎕F̲ILE @ }

ˢi̲nclude< : Unit -> Char -> Char

macro · line 83

The text of a file, as a string, by a path relative to the file the call is written in; from the disk at the command line, from the store in the browser (Rust's include_str!). For example, t := @ i_nclude< "data.txt" binds the text of data.txt.

ˢi̲nclude< ← { @ path → ʰq̲uote ⎕I̲NCLUDE ʰt̲rim path }

ˢc̲fg< : Unit -> Char -> Char

macro · line 89

1 when a configuration fact holds, else 0: the platform (cli, or web in the browser) or a flag set with xetal --cfg NAME.

      @ c̲fg< "cli"
1
ˢc̲fg< ← { @ name → f̲ormat 0 + ⎕C̲FG ʰt̲rim name }

ˢe̲rror< : Char -> Char -> Char

macro · line 96

Stop with a compile error at the call, with the code on the left and the message on the right (Rust's compile_error!); a macro writes it into its text to refuse a call.

      "too-big" e̲rror< "the grid must be at most 9 wide"
error[too-big]
ˢe̲rror< ← { code message → ((ʰt̲rim code) c̲at " call") ⎕R̲EJECT message }

Debugging and checking

ˢd̲bg< : Unit -> Char -> Char

macro · line 104

The value of the expression on the right, after writing [file:line] expr = value to standard error (Rust's dbg!).

      1 + @ d̲bg< "2 * 3"
7
ˢd̲bg< ← { @ expr →
  e ← ʰt̲rim expr
  shown ← "[" c̲at (⎕F̲ILE @) c̲at ":" c̲at (f̲ormat ⎕L̲INE @) c̲at "] " c̲at e c̲at " = "
  "{ v -> []E_RR " c̲at (ʰq̲uote shown) c̲at " c_at f_ormat v; v } (" c̲at e c̲at ")"
}

ˢa̲ssert< : Char -> a -> Char

macro · line 117

When the condition on the left does not hold, write assertion failed: cond (message) [file:line] to standard error, the condition as written, and go on; the message on the right may be @ for none. The value is @ either way. Unlike Rust's assert!, it never stops the program (p_anic< does).

      "1 > 2" a̲ssert< "one is not more than two"
@
ˢa̲ssert< ← { cond note →
  c ← ʰt̲rim cond
  line ← "assertion failed: " c̲at c c̲at (ʰw̲hy f̲ormat note) c̲at " [" c̲at (⎕F̲ILE @) c̲at ":" c̲at (f̲ormat ⎕L̲INE @) c̲at "]"
  "{ @ -> (" c̲at c c̲at ") ? @; []E_RR " c̲at (ʰq̲uote line) c̲at "; @ } @"
}
Used in: ok, ok, ok, ok, ok, ok, ok, ok, ok, ok, ok, ok

Text

ˢf̲ormat< : Unit -> Char -> Char

macro · line 131

The text on the right with each {expr} replaced by the value of expr, as the function f_ormat writes it; {{ and }} are braces. An unclosed {, an empty {} or a lone } fails before the program runs (Rust's format!).

      @ f̲ormat< "two and two: {2 + 2} {{four}}"
two and two: 4 {four}
ˢf̲ormat< ← { @ t → ʰo̲rEmpty ʰp̲ieces t }

ˢp̲anic< : Unit -> Char -> Char

macro · line 138

Stop the program with error[panic] and the message on the right, formatted as f_ormat< does, at the call; it stands where any value may (Rust's panic!).

      1 + @ p̲anic< "no {1 + 1}"
error[panic]
ˢp̲anic< ← { @ t → "[]P_ANIC (" c̲at (ʰo̲rEmpty ʰp̲ieces t) c̲at ")" }

ˢt̲odo< : Unit -> Char -> Char

macro · line 146

Stop the program with error[panic] and the message "not yet implemented: " followed by the text on the right (formatted as f_ormat< does); a placeholder for code still to write, standing where any value may (Rust's todo!).

      1 + @ t̲odo< "the size check"
error[panic]
ˢt̲odo< ← { @ t → "@ p_anic< " c̲at ʰq̲uote "not yet implemented: " c̲at t }

Errors

ˢt̲ry< : Char -> Char -> Char

macro · line 158 · binds e

Run the statements on the left under a trap: when they stop with an error, the handler on the right runs with the error as e and answers an outcome (r_ecover<, r_etry<, h_alt<, c_ontinue<, or a c_atch< choosing among them); the value is the body's or the recovery. Both sides are written as lambda bodies ([]T_RAP).

      "1 d_iv 0" t̲ry< "@ r_ecover< \"-1\""
-1
ˢt̲ry< ← { body handler →
  "'{ @ -> " c̲at (ʰt̲rim body) c̲at " } []T_RAP '{ e -> " c̲at (ʰt̲rim handler) c̲at " }"
}

ˢc̲atch< : Char -> Char -> Char

macro · line 166

In a handler: the outcome on the right for an error whose code is one of the words on the left, and h_alt< for any other error.

      "\"io\" []S_IGNAL \"gone\"" t̲ry< "\"io empty\" c_atch< \"@ r_ecover< \\\"0\\\"\""
0
ˢc̲atch< ← { codes outcome →
  ws ← ʰw̲ords codes
  0 = t̲ally ws ? "bad-macro-argument left" ⎕R̲EJECT catchCodes
  tests ← ʰj̲oin '{ w → ʰm̲atchCode d̲isclose w } m̲ap ws
  "(" c̲at (((t̲ally tests) − 3) t̲ake tests) c̲at ") ? " c̲at (ʰt̲rim outcome) c̲at "; []H_ALT e"
}

ˢf̲inally< : Char -> Char -> Char

macro · line 180

Run the statements on the left, then the cleanup on the right, whether the body gave a value or an error; the value is the body's, or its error going on ([]E_NSURE).

      "p_rint! 1; 2" f̲inally< "p_rint! \"cleaned\""
1
cleaned
2
ˢf̲inally< ← { body cleanup →
  "'{ @ -> " c̲at (ʰt̲rim body) c̲at " } []E_NSURE '{ @ -> " c̲at (ʰt̲rim cleanup) c̲at " }"
}

ˢr̲ecover< : Unit -> Char -> Char

macro · line 193

In a handler: recover with the value on the right, of the body's type; the try's value is this one ([]R_ECOVER). The right side is code, like every macro argument: to recover with a string, write a string literal there, its quotes escaped once more inside t_ry<.

      "1 d_iv 0" t̲ry< "@ r_ecover< \"2 + 2\""
4
      "\"io\" []S_IGNAL \"gone\"" t̲ry< "@ r_ecover< \"\\\"none\\\"\""
none
ˢr̲ecover< ← { @ value → "[]R_ECOVER (" c̲at (ʰt̲rim value) c̲at ")" }

ˢr̲etry< : Unit -> Unit -> Char

macro · line 199

In a handler: run the body again (at most 1000 times; []R_ETRY).

      n! ← 0
      "n! := n! + 1; n! < 3 ? \"again\" []S_IGNAL \"not yet\"; n!" t̲ry< "@ r_etry< @"
3
ˢr̲etry< ← { @ @ → "[]R_ETRY e" }

ˢh̲alt< : Unit -> Unit -> Char

macro · line 204

In a handler: let the error go on as it was ([]H_ALT).

      "1 d_iv 0" t̲ry< "@ h_alt< @"
error[division-by-zero]
ˢh̲alt< ← { @ @ → "[]H_ALT e" }

ˢc̲ontinue< : Unit -> Unit -> Char

macro · line 210

In a handler: go on from a warning ([]W_ARN) with its value ([]C_ONTINUE).

      "10 + (0 []W_ARN \"empty\" \"nothing\")" t̲ry< "@ c_ontinue< @"
10
ˢc̲ontinue< ← { @ @ → "[]C_ONTINUE e" }

Private helpers

catchCodes : Char

value (private) · line 214
catchCodes ← "c_atch< needs error codes on its left: \"io\" c_atch< \"@ r_ecover< \\\"0\\\"\""
Used in: ˢc̲atch<

ʰm̲atchCode : Char -> Char

function (private) · line 218
ʰm̲atchCode ← { w → "(([]E_CODE e) m_atch " c̲at (ʰq̲uote w) c̲at ") | " }
Used in: ˢc̲atch<

ifArgs : Char

value (private) · line 221
ifArgs ← "i_f< takes two expressions on its right, separated by ;: \"c\" i_f< \"a; b\""
Used in: ˢi̲f<

eachAlone : Char

value (private) · line 222
eachAlone ← "e_ach< writes statements: it stands as a statement of its own"
Used in: ˢe̲ach<

eachHole : Char

value (private) · line 223
eachHole ← "an e_ach< template names the word as $w: \"a b\" e_ach< \"u:$w := 1\""
Used in: ˢe̲ach<

ʰt̲rim : Char -> Char

function (private) · line 226
ʰt̲rim ← { t →
  m ← n̲ot t m̲ember? " \n\t"
  0 = '+ r̲/ 0 + m ? ""
  i ← w̲here m
  (1 + (f̲irst r̲ev i) − f̲irst i) t̲ake ((f̲irst i) − 1) d̲rop t
}

ʰw̲ords : Char -> Box Char

function (private) · line 234
ʰw̲ords ← { t → (n̲ot t m̲ember? " \n\t") p̲artition t }

ʰs̲eps : Truthy a => Char -> a

function (private) · line 238
ʰs̲eps ← { t →
  q ← ('+ s̲\ 0 + t = f̲irst "\"") m̲od 2
  d ← '+ s̲\ (0 + t m̲ember? "([{") − 0 + t m̲ember? ")]}"
  (t m̲ember? ";\n") ∧ (0 = d) ∧ 0 = q
}
Used in: ˢi̲f<

ʰh̲oles : (Num a, Truthy a) => Char -> a

function (private) · line 245
ʰh̲oles ← { t → (t = f̲irst "$") ∧ (1 d̲rop t = f̲irst "w") c̲at 0 }

ʰj̲oin : Box Char -> Char

function (private) · line 248
ʰj̲oin ← { b → d̲isclose '{ x y → e̲nclose (d̲isclose x) c̲at d̲isclose y } r̲/ b }

ʰq̲uote : Char -> Char

function (private) · line 252
ʰq̲uote ← { t →
  0 = t̲ally t ? "\"\""
  e̲ ← { c → c = f̲irst "\"" ? "\\\""◆ c = f̲irst "\\" ? "\\\\"◆ c = f̲irst "\n" ? "\\n"◆ c = f̲irst "\t" ? "\\t"◆ c }
  "\"" c̲at (ʰj̲oin 'e̲ m̲ap t) c̲at "\""
}

ʰc̲opy : Char -> Char -> Char

function (private) · line 259
ʰc̲opy ← { t w →
  h ← ʰh̲oles t
  g ← 0 c̲at -1 d̲rop h
  p̲ ← { i → (i s̲elect h) ? w◆ (i s̲elect g) ? ""◆ i s̲elect t }
  ʰj̲oin 'p̲ m̲ap r̲ange t̲ally t
}
Used in: ˢe̲ach<

ʰw̲hy : Char -> Char

function (private) · line 268
ʰw̲hy ← { m → "@" m̲atch m ? ""◆ " (" c̲at m c̲at ")" }
Used in: ˢa̲ssert<

ʰo̲rEmpty : Char -> Char

function (private) · line 271
ʰo̲rEmpty ← { p → 0 = t̲ally p ? "\"\""◆ p }

ʰl̲it : Char -> Char

function (private) · line 274
ʰl̲it ← { t → 0 = t̲ally t ? ""◆ ʰq̲uote t }
Used in: ʰp̲ieces

ʰc̲j : Char -> Char -> Char

function (private) · line 277
ʰc̲j ← { a b → 0 = t̲ally a ? b◆ 0 = t̲ally b ? a◆ a c̲at " c_at " c̲at b }
Used in: ʰp̲ieces

ʰp̲ieces : Char -> Char

function (private) · line 281
ʰp̲ieces ← { t →
  b ← t m̲ember? "{}"
  0 = '+ r̲/ 0 + b ? ʰl̲it t
  k ← f̲irst w̲here b
  pre ← (k − 1) t̲ake t
  c ← k s̲elect t
  rest ← k d̲rop t
  n ← (k + 1) s̲elect t c̲at " "
  c = n ? (ʰl̲it pre c̲at c) ʰc̲j ʰp̲ieces 1 d̲rop rest
  c = f̲irst "}" ? "bad-format right" ⎕R̲EJECT "a } in a format text is written }}"
  e ← w̲here rest = f̲irst "}"
  0 = t̲ally e ? "bad-format right" ⎕R̲EJECT "an unclosed { in a format text"
  x ← ʰt̲rim ((f̲irst e) − 1) t̲ake rest
  0 = t̲ally x ? "bad-format right" ⎕R̲EJECT "an empty {} in a format text: write {expr}, or {{ for a brace"
  (ʰl̲it pre) ʰc̲j ("(f_ormat (" c̲at x c̲at "))") ʰc̲j ʰp̲ieces (f̲irst e) d̲rop rest
}