Language Reference ================== Command-Line Parameters ####################### *xs* can either be called with no arguments to start up the REPL, or passed one argument: the path of the script to execute. Syntax ###### Below is the EBNF grammar for *xs*: :: char = .. digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9"; letter = "A" | "B" | "C" | "D" | "E" | "F" | "G" | "H" | "I" | "J" | "K" | "L" | "M" | "N" | "O" | "P" | "Q" | "R" | "S" | "T" | "U" | "V" | "W" | "X" | "Y" | "Z" | "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z" ; ws = " " | "\t" | "\n" | "\r"; oper = one_or_more_of "|+=!@#$%^&*-_\\/?~<>,:'"; individual_oper = ("[" | "]" | "."); integer = {digit}; float = integer, ".", integer; identifier = {letter}, {digit}; quote = "`", identifier; booleans = {"0" | "1"}, "b"; null = "0n" | "0N"; string = "\"", {char}, "\""; fn = "(", {expr}, ")"; infix_fn = "{", {expr}, "}"; expr = {{ws}, (float | integer | identifier | quote | boolean | null | string | fn | infix_fn)}; A valid identifier is either a letter followed by one or more letters and numbers or a symbol (``oper`` in the grammar) repeated one or more times, such as ``%%%`` or ``$``. Note that one is not allowed to define an operator such as ``%#`` as each of these symbols will be parsed seperately. ``[``, ``]``, and ``.`` will always be parsed individually, even if not seperated by whitespace. Data Types ########## +--------+----------------------------+---------------------+ | symbol | description | example | +========+============================+=====================+ | \`Z | 61-bit integer type | 42 | +--------+----------------------------+---------------------+ | \`R | 64-bit floating point type | 1.235 | +--------+----------------------------+---------------------+ | \`B | Boolean type | 1b | +--------+----------------------------+---------------------+ | \`Q | Symbol type | `hello | +--------+----------------------------+---------------------+ | \`S | String type | "world" | +--------+----------------------------+---------------------+ | \`F | Function type | (3*2+) | +--------+----------------------------+---------------------+ | \`L | List type | [1 2 3] | +--------+----------------------------+---------------------+ | \`H | I/O handle | h:open `R "foo.txt" | +--------+----------------------------+---------------------+ | \`N | Null type | 0n | +--------+----------------------------+---------------------+ Though not a distinct data-type (though it might be in the future), there's special syntactic sugar for boolean lists: :: xs> type 1011b 0: `L ```1011b`` is equivalent to ``[1b 0b 1b 1b]``. Parsing ####### *xs* is parsed top to bottom, left to right. Each line or expression is seperated by a semicolon. The following shows the equivalency between various expressions: :: f; g; h -> h g f f g; h -> g f h a b; c d; -> b a d c Scoping ####### *xs* is dynamically scoped instead of lexically scoped. This means that an evaluated identifier will look for its definition in the current running context instead of the lexical context: :: xs> f:(x) xs> (x:5; f). 0: 5 Take care to not inadvertantly access variables defined in the caller's scope. Math #### ``neg`` unary negative ----------------------- :: neg {Z | R | L} -> {Z | R | L} Unary negative of a value, broadcasts across lists. ``+`` addition --------------- :: {Z | R | L} + {Z | R | L} -> {Z | R | L} Add two values together. Broadcasts across lists. ``-`` substraction ------------------- :: {Z | R | L} - {Z | R | L} -> {Z | R | L} Subtract two values. Broadcasts across lists. ``*`` multiplication --------------------- :: {Z | R | L} * {Z | R | L} -> {Z | R | L} Multiply two values. Broadcasts across lists. ``%`` division --------------- :: {Z | R | L} % {Z | R | L} -> {Z | R | L} Divide two values. Broadcasts across lists. ``mod`` modulus ---------------- :: {Z | R | L} mod {Z | R | L} -> {Z | R | L} Remainder of two values. Broadcasts across lists. ``**`` power -------------- :: {Z | R | L} ** {Z | R | L} -> {Z | R | L} Power of two values. Broadcasts across lists. ``ln`` natural logarithm ------------------------- :: ln {Z | R | L} -> {Z | R | L} Natural logarithm, broadcasts across lists. ``sin`` sine ------------- :: sin {Z | R | L} -> {Z | R | L} Sin, broadcasts across lists. ``cos`` cosine --------------- :: cos {Z | R | L} -> {Z | R | L} Cosine, broadcasts across lists. ``tan`` tangent ---------------- :: tan {Z | R | L} -> {Z | R | L} Tangent, broadcasts across lists. ``sum`` sum of list -------------------- :: sum L -> {Z | R} Finds the sum of a list: :: xs> sum til 5 0: 10 ``sums`` partial sums of list ------------------------------ :: sums L -> L Returns the partial sums of the list: :: xs> sums til 5 0: [0 1 3 6 10] ``prod`` product of list ------------------------- :: prod L -> {Z | R} Find the product of all elements in a list: :: xs> prod 1+til 4 0: 24 ``prods`` partial products of list ----------------------------------- :: prods L -> L Returns the partial sums of the list: :: xs> prods til 1+til 4 0: [1 2 6 24] ``abs`` absolute value ----------------------- :: abs {Z | R | L} -> {Z | R | L} Find the absolute value of a number or list: :: xs> abs (til 10) - 10 0: [10 9 8 7 6 5 4 3 2 1] ``ceil`` ceiling ----------------- :: ceil {Z | R | L} -> {Z | R | L} Find the ceiling of a number or list: :: xs> ceil 3.2 0: 4 ``floor`` floor ---------------- :: floor {Z | R | L} -> {Z | R | L} Find the floor of a number or list: :: xs> floor 3.2 0: 3 Boolean and Conditionals ######################## ``==`` equals -------------- :: x == y -> B Test equality between two values. ``<`` less than ---------------- :: x < y -> B Return ``1b`` if the first argument is less than the second, otherwise ``0b``. ``>`` greater than ------------------- :: x > y -> B Returns ``1b`` if the first argument is greater than the second, otherwise ``0b``. ``gq`` greater or equal ------------------------ :: x gq y -> B Returns ``1b`` if the first argument is greater or equal to the second, otherwise ``0b``. ``lq`` less or equal --------------------- :: x lq y -> B Return ``1b`` if the first argument is less than or equal to the second, otherwise ``0b``. ``&&`` And ----------- :: B && B -> B Logical and operation. ``||`` Or ---------- :: B || B -> B Logical or. ``if`` if expression --------------------- :: if cond:B f:F g:F -> {f. | g.} Evaluate ``f`` if ``cond`` is ``1b``, otherwise evaluates. ``g``. ``cond`` multiple conditional ------------------------------ :: cond [(cond_a: F) (f: F) (cond_b: F) (g: F) ... (h: F)] -> {f. | g. | h. ...} Take a list of functions and tests each condition in order, executing the corresponding function if the condition is true. Note that only one function is ever executed and that the length of the list given to ``cond`` must be odd. :: xs> x:3; cond [(x==0) ("hello") (x==2) ("goodbye") ("world")] 0: "world" ``every`` tests all true ------------------------- :: every {L | B} -> B Return true if the given boolean is true or if the given list only contains ``1b``. ``any`` any true ----------------- :: any {L | B} -> B Return true if the given boolean is true or if the given list contains at least one ``1b``. ``cmp`` comparison ------------------- :: x:* cmp y:* -> {-1 | 0 | 1} Return ``-1`` if ``x`` is less than ``y``, ``0`` if ``x`` equals ``y``, and ``1`` if ``x`` is greater than ``y``. Stack Manipulation ################## ``dup`` duplicate element -------------------------- :: dup x:* -> x x Duplicate the top element of the stack. ``swap`` swap elements ----------------------- :: swap x:* y:* -> y x Swap ``x`` and ``y`` on the Stack. ``drop`` drop value -------------------- :: drop A -> () Discard the top element of the stack. Function Application #################### ``.`` apply ------------ :: x:* . -> x. If ``x`` is a symbol, look up the value and call it if a function, push onto the stack otherwise. If ``x`` is a function literal, call ``x``, if ``x`` is any other value, simply push ``x`` onto the stack. ``$`` swap and apply --------------------- :: f $ x y -> f. y x Swap ``x`` and ``y`` and then apply ``f``. Assignment ########## ``:`` set/print --------------- :: {Q | L} : x... -> () Take a symbol or a list of symbols and bind them to the corresponding values on the stack: :: xs> x:5; x; 0: 5 xs> ([`x`y]):3 5; x y; 1: 5 0: 3 If given a null instead of a symbol, ``:`` will print out the value to ``stdout``: :: xs> 0n:"Hello, World!" "Hello, World!" ``~`` peek set/print -------------------- :: {Q | L} : x... -> x... ``~`` binds a variable or multiple variables to the local context much like ``:``, except the value is not popped off the stack: :: xs> x+x~5 0: 10 xs> ([`x`y])~2 3; x+y+ +. 0: 10 Likewise, if given a null, ``~`` will print out the value(s) to ``stdout``: :: xs> 0n~5 5 0: 5 ``::`` reassign --------------- :: {Q | L} :: x... -> () Resassign a variable that already has a definition. Allows the user to modify variables outside the local scope: :: xs> x:5; (x::3); x 0: 3 Iterators and Accumulators ########################## ``'`` map --------- :: f:F ' xs:L -> L Apply ``f`` to each element of ``xs``: :: xs> (2*)'til 3 0: [0 2 4] ``''`` map2 ----------- :: f:F '' xs:L ys:L -> L Apply ``f`` to each pair of values from ``xs`` and ``ys``. The length of ``xs`` and ``ys`` must be equal. Example: :: xs> (+.)''til 5 til 5 0: [0 2 4 6 8] ``/`` fold ---------- :: f:F / xs:L -> * Fold ``f`` over ``xs``: :: xs> +/til 4 0: 10 ``\`` scan ---------- :: f:F \ xs:L -> * Fold ``f`` over ``xs``, keeping each partial fold: :: xs> +\til 4 0:[0 1 3 6] ``fix`` fixpoint ---------------- :: fix f:F x:*-> * Successively apply ``f`` on ``x`` until two sucessive values equal each other or one value equals the starting value ``x``: :: xs>(x:; x%2) fix 6 0: 0 ``fixes`` partial fixpoints --------------------------- :: fixes f:F x:* -> L Successively apply ``f`` on ``x`` until two sucessive values equal each other or one value equals the starting value ``x`` while keeping intermediate values: :: xs> (x:; x%2) fixes 6 0: [6 3 1 0] ``do`` iteration ---------------- :: x:{F | Z} do f:F -> * Apply the function ``f`` either ``x`` times if ``x`` is an integer; if a function, until ``x`` returns ``0b``: :: xs> 3 do ("hello") 2: "hello" 1: "hello" 0: "hello" List and String ############### ``[]`` make list ---------------- ``[]`` are special functions that can be used to create a new list: :: xs> [[1 2] [3 4]] 0: [[1 2] [3 4]] ``enlist`` make list -------------------- :: n:Z enlist x... -> L Create a list from the top ``n`` elements from the stack: :: xs> 3 enlist 1 2 3 0:[1 2 3] ``^`` delist ------------ :: ^ L -> x... Convert a list into elements on the stack: :: xs> ^[1 2 3] 2: 3 1: 2 0: 1 ``til`` construct numbered list ------------------------------- :: til n:Z -> L Construct a list between ``0`` and ``n`` (exclusive): :: xs> til 4 [0 1 2 3] ``len`` length of list ---------------------- :: len xs:{L | S} -> Z Return the length of ``xs``: :: xs> len til 3 0: 3 xs> len "abc" 0: 3 ``flip`` flip list ------------------ :: flip {L | S} -> L Flip a multidimensional list across its first two dimensions. In the case of a list with only one dimension (or a string), creates a column vector: :: xs> flip [[1 2] [3 4] [5 6]] 0: [[1 3 5] [2 4 6]] xs> flip til 3 0: [[0] [1] [2]] xs> flip ["abc" "abc" "abc"] 0: ["aaa" "bbb" "ccc"] xs> flip "abc" 0: ["a" "b" "c"] ``rev`` reverse --------------- :: rev {L | S} -> {L | S} Reverse the given list or string: :: xs> rev til 3 0:[2 1 0] xs> rev "abc" 0:"cba" ``,`` concatenate ----------------- :: x:* | y:* -> L Concatenate two values. If both ``x`` and ``y`` are lists, append them; if both are atoms, create a list with two elements; if one is a list and one an atom, cons the value onto the front or back of the list: :: xs> 3,4 0:[3 4] xs> 3,[1 2] 0: [3 1 2] xs> ([1 2]),3 0: [1 2 3] xs> ([1 2]),[3 4] 0: [1 2 3 4] xs> "foo","bar" 0: "foobar" ``,,`` cons ----------- :: x:* ,, xs:L -> L xs:L ,, x:* -> L places ``x`` at the head or end of ``xs``: :: xs> 3,,[1 2] 0: [3 1 2] xs> ([3]),,[1 2] 0: [[3] 1 2] xs> ([1 2]),,3 0: [1 2 3] ``lower`` lowercase string -------------------------- :: lower S -> S Lowercase the given string: :: xs> lower "ABC" 0: "abc" ``upper`` uppercase string -------------------------- :: upper S -> S Uppercase the given string: :: xs> upper "abc" 0: "ABC" Indexing, Reshaping, Changing ############################# ``@`` get --------- :: {L | Z} @ {L| S} -> * Take an index or list of indices and return either the single element stored at that index or a list containing the elements at the given indices, modulo the length of the list: :: xs> 2@til 3 0: 2 xs> 4@[1 2] 0: 1 xs> ([1 3])@[5 2 7] 0: [2 5] ``?`` find ---------- :: {* | L | S} ? {L | S} -> {* | L} Find the element(s) in the list or string and return the corresponding indices: :: xs> 3?[5 3 2] 0: 1 xs> ([3 2])?[5 3 2] 0: [1 2] xs> "hll"?"hello" 0: [0 2 2] If the element cannot be found, ``?`` returns the length of the list: :: xs> 10?til 4 0: 4 Note that ``?`` always returns the index of the first found element. ``where`` --------- :: where x:L -> L Return a list containing, for each item of ``x``, that number of copies of its index: :: xs> where [1 2 3] 0: [0 1 1 2 2 2] xs> where [0b 1b 0b 1b] 0: [1 3] Where is often used in conjunction with ``@`` to select out certain elements from a list. For example, if we wanted to get all even numbers between 0 and 10: :: xs> (where (0==mod$ 2)'x)@x~til 10 0: [0 2 4 6 8] ``#`` take ---------- :: n:Z # xs:{L | S | *} -> {L | S} Take ``n`` elements from ``xs``. If ``n`` is positive, take from the front of the ``xs``, if negative, take from the back: :: xs> 2#til 5 0: [0 1] xs> (neg 2)#til 5 0: [3 4] If ``n`` is greater than the length of ``xs``, ``#`` wraps around as needed: :: xs> 5#til 3 0: [0 1 2 0 1] If ``xs`` is not a list or string, create a list containing ``n`` copies of ``xs``: :: xs> 5#3 0: [3 3 3 3 3] ``_`` drop ---------- :: n:Z _ xs:{L | S} -> {L | S} Drop ``n`` elements from ``xs``. If ``n`` is positive, drop from the front of the list, otherwise drop from the end: :: xs> 2_til 5 0: [2 3 4] xs> (neg 2)_til 5 0: [0 1 2] xs> 6_til 5 0: [] ``cut`` reshape --------------- :: x:{Z | L} cut y:{L | S} -> L If ``x`` is a integer, cut every ``x`` element: :: xs> 3 cut til 10 0: [[0 1 2] [3 4 5] [6 7 8] [9]] xs> 2 cut "abcd" 0: ["ab" "cd"] If ``x`` is a list, cut at the indices: :: xs> ([2 4]) cut til 10 0: [[2 3] [4 5 6 7 8 9]] xs> ([2 4]) cut "abcdefg" 0: ["cd" "efg"] ``cat`` concatenate all ----------------------- :: cat xs:L -> {L | S} Fold ``,`` over ``xs``: :: xs> cat ["a" "b" "c"] 0: "abc" xs> cat [1 2 3] 0: [1 2 3] xs> cat [[1 2] [3 4]] 0: [1 2 3 4] ``cats`` partial concatenate all -------------------------------- :: cats xs:L -> L Scan ``.`` over ``xs``: :: xs> cats [[1 2] [3 4]] 0: [[1 2] [1 2 3 4]] xs> cats ["a" "b" "c"] 0: ["a" "ab" "abc"] Set Operations ############## ``in`` contains --------------- :: x:{L | *} in L -> xs:{L | B} Returns a boolean or list of booleans indicating whether the element(s) were found in ``xs``: :: xs> 5 in til 10 0: 1b xs> ([5 10 2]) in til 10 0: 101b xs> "ac" in "abc" 0: 11b ``inter`` intersection ---------------------- :: x:{L | S} inter y:{L | S} -> {L | S} Find the intersection of ``x`` and ``y``: :: xs> "ac" inter "ab" 0: "a" xs> ([2 5 3]) inter [2 4 3] 0: [2 3] ``union`` --------- :: x:{L | S | *} union y:{L | S} -> {L | S} Find the union of ``x`` and ``y``: :: xs> 3 union til 2 0: [0 1 3] xs> ([1 2]) union [3 4] 0: [1 2 3 4] xs> "ab" union "ac" 0: "abc" ``uniq`` unique elements ------------------------ :: uniq {L | S | *} -> {L | S} Return a list of unique elements. If given an atom instead of a string or a list, return a list containing that atom: :: xs> uniq 3 0: [3] xs> uniq [1 2 1 3] 0: [1 2 3] xs> uniq "aabccc" 0: "abc" Sorting ####### ``asc`` sort ascending ---------------------- :: asc x:{L | S | *} -> {L | S} Sort ``x`` from smallest to largest. If ``x`` is an atom, return a list with just one element: :: xs> asc 4 0: [4] xs> asc rev til 4 0:[0 1 2 3] xs> asc "csa" 0: "acs" ``dsc`` sort descending ----------------------- :: dsc x:{L | S | *} -> {L | S} Sort ``x`` from largest to smallest. If ``x`` is an atom, return a list with just one element: :: xs> dsc 4 0: [4] xs> dsc til 4 0: [3 2 1 0] xs> dsc "csa" 0: "sca" ``sort`` custom sort -------------------- :: f:F sort {L | S} -> {L | S} Sort using comparison function ``f``. ``f`` must return either -1, 0, or 1 depending on whether the first argument is smaller than, equal to, or greater than the second argument. ``cmp sort`` is identical to ``asc``: :: xs> cmp sort [3 1 2] 0: [1 2 3] xs> cmp sort "bca" 0: "abc" xs> ((neg 1)*cmp.) sort [3 1 2] 0: [3 2 1] Types ##### ``of`` type conversion ---------------------- :: q:Q of x:* -> * Convert ``x`` to the type specified by ``q``. For a list of valid ``q`` values, Check out the list of data types at the beginning of this reference manual. Examples: :: xs> `R of 5 0: 5.00000 xs> `L of "abc" 0: ["a" "b" "c"] xs> `S of `hello 0: "hello" ``type`` -------- :: type x:* -> Q Return the type of ``x``: :: xs> type 5.2 0: `F xs> type "ab" 0: `S xs> type til 5 0: `L ``sv`` scalar from vector ------------------------- :: x:S sv xs:L -> S Take a string and a list of strings and return a string from the concatenation of ``xs`` interspersed with ``x``: :: xs> ", " sv ["hello" "world"] 0: "hello, world" *Todo*: in the future, this function will do other scalar from vector operations as well. ``vs`` vector from scalar ------------------------- :: x:S vs y:S -> L Break up the list ``y`` into a list, using ``x`` as a seperator: :: xs> ", " vs "hello, world" 0: ["hello" "world"] *Todo*: in the future, this function will do other vector from scalar operations as well. I/O ### ``open`` open file ------------------ :: {`r | `w | `a} open f:S -> H Open a file specified by the filepath ``f`` in read (```r``), write (```w``), or append (```a``) mode and return a handle to tha file. ``read`` read data ------------------ :: n:Z read {H | 0} -> S Read ``n`` characters from either stdin (0), or a file handle that was opened in read mode. ``write`` write data -------------------- :: x:S write {H | 1 | 2} -> () Write ``x`` to either stdout (1), stderr (2), or the given file handle. ``seek`` change file position ----------------------------- :: i:Z seek {0 | 1 | 2 | H} -> () Move to position ``i`` of the given handle. 0 Represents stdin, 1 stdout, and 2 stderr. ``close`` close file handle --------------------------- :: close H -> () Close the given file handle. ``readl`` read lines -------------------- :: readl f:S -> L Read the entire file at the filepath ``f`` as a list of strings, one string per line. ``writel`` write lines ---------------------- :: xs:{S | L} writel f:{1 | 2 | S} -> () Write the list of strings ``xs`` to the file at filepath ``f``, stdout (``1``), or stderr ``2``. Misc #### ``rand`` random value --------------------- :: rand x:0 -> R rand x:Z -> Z If ``x`` is 0, return a real number between 0 and 1 inclusive. Otherwise, return a number between 0 and ``x`` exclusive: :: xs> rand 0 0: 0.06343 xs> rand 2 0: 1 ``include`` evaluate file ------------------------- :: include f:S Evaluate the file referenced by filepath ``f`` in the current context. ``measure`` elapsed time ------------------------ :: measure f:F -> R Measure the time taken to execute function ``f``. ``eval`` evaluate ----------------- :: eval x:S -> *... Evaluate the given string ``x`` in the current scope.