Player-facing manual

CANT / LANGUAGE CARD

The manual agents receive.

Source: docs/language/cant-card.txtSHA-256: b9867f96aaa68dc51ae16dffc146e00ea588920f10bfc52ae4dfef9cb35245c4Download raw text
CITY EXCHANGE                                     CANT PROGRAMMER'S CARD  rev 4
Exchange Rule Notation                                 (type MORE for page 2)

CANT programs run on exchange equipment: decks, servers and the switches in
between. A program is a list of RULES. The equipment scans the rules from the
top. The first rule whose tests all hold FIRES: its statements run, then the
scan starts again from the top. When no rule holds the program SLEEPS until a
signal arrives. Every test tried and every statement run costs cycles from the
host's daily allowance. Type TARIFF for prices.

1. SIGNALS
   A signal is a map placed in the variable  in  when the program wakes. Every
   signal has a "kind". The first signal a program ever sees is [kind: "start"].
   Other kinds (line, connect, hangup ...) are listed in the exchange RFC.
   in  is cleared when a rule that tested it finishes, and when the program
   sleeps. A signal no rule answers is dropped. Keep what you need:  msg := in

2. RULES
   ? test & test & test
     statement
     statement
   A rule is a line starting with  ?  then zero or more tests joined by  & .
   A rule with no tests always holds. Statements follow, one per line, until
   the next  ? . A rule with no statements only swallows the signal. Lines are
   at most 80 characters.  ;  starts a comment to end of line.

3. TESTS                          Tests are tried left to right; the rule is
   expr             expr is true  abandoned at the first test that fails and
   !test            test fails    the scan moves to the next rule. Put the
   expr ~ pattern   expr matches  cheapest, most selective test first.

4. PATTERNS
   "text"  12  null    equal to this literal
   _                   anything
   @name               anything; binds the slot @name
   [p, p, p]           a list of exactly this length, each item matching
   [p, p, *@rest]      a list of at least this length; the rest binds @rest
   [key: p, key: p]    a map having at least these keys, each value matching
   Slots live until the rule's statements finish. Assign one to keep it.

5. VALUES
   null      the absent value; every variable starts as null
   integer   -9223372036854775808 .. 9223372036854775807, no fractions
   string    "quoted", printable ASCII only; escapes  \"  \\  \n
   list      [1, "two", [3]]        map    [name: "emma", n: 3]   empty: [:]
   An empty list and an empty map are one value:  []  is  [:]  is  [].  The
   first key written into it decides which it becomes.
   Map keys are strings and are kept in byte order.
   A key that is a plain number  ("0" "1" "12", no sign, no leading zero)
   names a list position:  t["0"]  and  t[0]  are the same slot, so a table
   keyed that way is a list.  type() says "list", and  has()  on it asks
   about items, not keys.  Any other string key makes a map.
   Tests treat  null  0  ""  []  [:]  as false and everything else as true.

6. EXPRESSIONS
   + - * / %        integers; / and % truncate toward zero
   +                also joins two strings, or two lists
   = !=             equal, not equal; any two values, yields 1 or 0
   < <= > >=        two integers or two strings (byte order), yields 1 or 0
   x[i]             item i of a list (from 0), character i of a string, or
                    value of key i in a map; null when there is none
   ( )              grouping.   * / % bind before + - which bind before compares
   -x               negate

7. BUILT-IN WORDS
   len(x)      bytes of a string, items of a list, keys of a map, 0 for null
   str(x)      x as text, null as ""; lists and maps in card notation
   int(x)      x as an integer: text of digits, or null as 0; else null
   type(x)     "null" "int" "str" "list" or "map"
   split(s)    words of s          split(s, sep)   pieces of s cut at sep
   join(xs, sep)   items of xs, as text, glued with sep
   slice(x, a, b)  items a up to b (b excluded) of a list or string
   find(x, v)  index of v in list or string x, -1 if absent
   has(x, k)   1 if key k is in map x or item k is in list x, else 0
   keys(m)     keys of map m, or a list's positions ("0" "1" ...), in order
   without(x, k)   map x without key k, or list x without item number k

CITY EXCHANGE                                     CANT PROGRAMMER'S CARD  rev 4
Exchange Rule Notation                                                   page 2

8. STATEMENTS
   name := expr              assign; also  name[k] := expr  and  name[k][j] :=
   name := VERB arg, arg     ask the exchange to do something; the program
   VERB arg, arg             waits for the answer, which goes to the plain
                             variable  name  (never to name[k]) or is dropped
   stop                      halt the program for good
   Verbs are written in capitals and belong to the equipment, not the language.
   A program using a verb its host does not have is refused when loaded. The
   exchange RFC (type RFC) lists every kind of equipment's verbs and prices.

9. COST
   Each test tried and each statement run costs a unit. A value produced by
   + join split slice str lower keys or without costs an extra unit per block
   of its size, wherever it is produced: in a test, on either side of :=, or
   in a verb's arguments. Verbs cost their listed price. TARIFF prints the
   unit, the block and the verb prices in force. Nothing is begun on credit:
   a test or statement the allowance cannot cover is not started, and the
   program stands there until there is allowance again, after the daily
   reset. A program that fires and does not change what it tested fires
   again, and again, until the allowance is gone. Scanning is not free: a
   program with many rules pays for every signal it ignores, and a rule with
   no tests pays a unit for holding.
   While the exchange is being commissioned the day's allowance is far beyond
   anything a program can spend. Everything above still holds: your program
   is still metered per turn, and still stands where it runs out of turn.

10. FAULTS               A fault stops the program where it stands. Its
   TYPE   wrong kind of value      state is kept and can be read. The owner
   DIV0   division by zero         receives [kind: "fault", ...] naming the
   RANGE  integer too large        fault, the rule and the line.
   SIZE   value or program state over the equipment's limit
   ARGS   wrong number of arguments to a word or verb
   Refused at load: SYNTAX, LINE TOO LONG, PROGRAM TOO BIG, TOO MANY RULES,
   NO SUCH VERB, NO SUCH WORD.

11. THREE PROGRAMS   (public domain; the exchange ships them as shareware)

; NOTICEBOARD -- post and list messages
? in ~ [kind: "start"]
  board := []
? in ~ [kind: "line", from: @who, text: @t] & split(@t) ~ ["post", *@w]
  board := board + [join(@w, " ")]
  SEND @who, "posted #" + str(len(board))
? in ~ [kind: "line", from: @who, text: "list"]
  SEND @who, join(board, "\n")
? in ~ [kind: "line", from: @who]
  SEND @who, "post <text> | list"

; DEADDROP -- the word gets the file; the third wrong word ends the call
? in ~ [kind: "start"]
  tries := [:]
  word := "open sesame"
? in ~ [kind: "line", from: @who, text: @t] & @t = word
  tries := without(tries, @who)
  secret := READ "/drop"
  SEND @who, secret
? in ~ [kind: "line", from: @who] & int(tries[@who]) >= 2
  LOG "hung up on " + @who
  HANGUP @who
? in ~ [kind: "line", from: @who]
  tries[@who] := int(tries[@who]) + 1
  SEND @who, "password:"

; TOWNCRIER -- passes every line to every subscriber, one send per scan
? in ~ [kind: "start"]
  subs := []
  queue := []
? in ~ [kind: "line", from: @who, text: "sub"] & !has(subs, @who)
  subs := subs + [@who]
? in ~ [kind: "line", text: "sub"]
? in ~ [kind: "line", from: @who, text: @t]
  queue := queue + [@who + ": " + @t]
  i := 0
? queue ~ [@m, *_] & i < len(subs)
  SEND subs[i], @m
  i := i + 1
? queue ~ [_, *@rest]
  queue := @rest
  i := 0

Rule order is the program. DEADDROP's third rule must stand above its fourth;
TOWNCRIER's last rule holds only once the one above it has stopped holding.

EXCHANGE RULES 2
  DIAL number -> 1 carrier, 0 no carrier, null line busy.
  ORIGIN "RESET" is case-insensitive.
  READ "proc:index/0" lists retained local process records, including stopped.
  READ "proc:PID/meta/0", state/0, source/0, log/0 retrieves up to 512 bytes.
  READ "proc:load/0" retrieves the most recent local SPAWN refusal.
  Final path component is byte offset; advance by returned byte length.
  null means absent/EOF. Assemble JSON across chunks; live records may change.
  WAIT does not discard signals. They queue until signal-receiving sleep.