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 "; @ } @"
}

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 188

In a handler: recover with the value on the right, of the body's type; the try's value is this one ([]R_ECOVER).

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

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

macro · line 194

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 199

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 205

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 209
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 213
m̲atchCode ← { w → "(([]E_CODE e) m_atch " c̲at (q̲uote w) c̲at ") | " }
Used in: ˢc̲atch<

ifArgs : Char

value (private) · line 216
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 217
eachAlone ← "e_ach< writes statements: it stands as a statement of its own"
Used in: ˢe̲ach<

eachHole : Char

value (private) · line 218
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 221
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 229
w̲ords ← { t → (n̲ot t m̲ember? " \n\t") p̲artition t }

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

function (private) · line 233
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 240
h̲oles ← { t → (t = f̲irst "$") ∧ (1 d̲rop t = f̲irst "w") c̲at 0 }
Used in: ˢe̲ach<, c̲opy

j̲oin : Box Char -> Char

function (private) · line 243
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 247
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 254
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 263
w̲hy ← { m → "@" m̲atch m ? ""◆ " (" c̲at m c̲at ")" }
Used in: ˢa̲ssert<

o̲rEmpty : Char -> Char

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

l̲it : Char -> Char

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

c̲j : Char -> Char -> Char

function (private) · line 272
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 276
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
}