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.
mod
modulus¶
{Z | R | L} mod {Z | R | L} -> {Z | R | L}
Remainder of two values. Broadcasts across lists.
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¶
>
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
.
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¶
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.
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¶
''
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]
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]
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]
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.
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.