# an octothorpe begins a line comment.
# all whitespace is equivalent; indentation is stylistic.

###################################################
#
#  1. Simple Datatypes and Variables
#
###################################################

"one"                      # strings are enclosed in double-quotes,
"two\nthree\"four"         # and may include the escapes \n \" \\.

-23.84                     # numbers are floating point values.

2*3+5      # -> 16         # expressions evaluate right-to-left,
(2*3)+5    # -> 11         # ...unless overridden by parentheses.

()         # -> ()         # the empty list
11,22      # -> (11,22)    # "," joins values to form lists.
list 42    # -> (42)       # "list" makes a list of count 1.

a:42       # -> 42         # the symbol ":" ("becomes") performs assignment.
z          # -> 0          # referencing unbound variables returns 0.

d:("a","b") dict 11,22     # "dict" makes a dictionary from lists of keys and values.
keys  d    # -> ("a","b")  # "keys" extracts the keys of a dict.
range d    # -> (11,22)    # "range" extracts the elements of a dict.
range 4    # -> (0,1,2,3)  # ...or generates a sequence of integers [0,n).

(3,5,7)[1] # -> 5          # lists can be indexed with [], and count from 0.
d.a        # -> 11         # dicts can be indexed with a dot and a name,
d["b"]     # -> 22         # ...or equivalently, with []; required for non-string keys.

# collection operators:
count  11,22,33 # -> 3
first  11,22,33 # -> 11
last   11,22,33 # -> 33
2 take 11,22,33 # -> (11,22)
2 drop 11,22,33 # -> (33)
typeof 11,22,33 # -> "list"

###################################################
#
#  2. Control Flow
#
###################################################

# the "each" loop iterates over the elements of a list, string, or dict:
each v k i in ("a","b","c") dict 11,22,33
 # up to three variable names: value, key, and index.
 show[2*v k i] # show[] prettyprints values to stdout.
end
# prints:
# 22 "a" 0
# 44 "b" 1
# 66 "c" 2
# ...and returns the dictionary {"a":22,"b":44,"c":66}.

# the "while" loop:
v:2
while v<256
 show[v:v*4]
end
# prints:
# 8
# 32
# 128
# 512
# ...and returns the number 512.

# conditionals:
if age<21
 "can't rent cars"
elseif age>65
 "senior discounts"
else
 "just a regular slob"
end

# "if" and "while" treat 0, or the empty string, list, or dict as "falsey".
# any other value is considered "truthy".
if 3  "yes" end  # -> "yes"
if () "no"  end  # -> 0

###################################################
#
#  3. Functions and Scope
#
###################################################

on myfunc x y do     # functions are declared with "on".
 show[x y]           # function body can contain any number of expressions.
 x + 10 * y          # function body returns the last expression.
end

# call functions with []
# parameters are NOT separated by commas!
myfunc[2 5] # -> 52

# functions are values, and can be passed around:
on twice f x do
 f[f[x]]
end
twice[on double x do x*2 end  10] # -> 40

# functions can be given a variadic argument with "..."
on vary ...x do
 show[x]
end
f[11 22 33]          # -> (11,22,33)

# variables have lexical scope:
on outer x do        # function argument defines local x.
 on inner x do       # function argument shadows outer x.
  x:x*2              # redefine the local x, don't mutate the outer x.
  show[x]            # -> 10
  local z:99         # "local" explicitly declares a local variable.
  show[z]            # -> 99
 end
 inner[x]
 show[x]             # -> 5
 show[z]             # -> 0   # z is not defined in this scope.
end
outer[5]

###################################################
#
#  4. Tables and Queries
#
###################################################

# make tables with "insert":
people:insert name age job with
 "Alice"  25 "Development" 
 "Sam"    28 "Sales"
 "Thomas" 40 "Development"
 "Sara"   34 "Development"
 "Walter" 43 "Accounting"
end

# tables are like string-keyed dictionaries of uniform-count columns.
# tables are like a list of dictionaries with the same string keys.
people.name[2]  # -> "Thomas"
people[2].name  # -> "Thomas"

# query tables using a SQL-like syntax:
select from people
# +----------+-----+---------------+
# | name     | age | job           |
# +----------+-----+---------------+
# | "Alice"  | 25  | "Development" |
# | "Sam"    | 28  | "Sales"       |
# | "Thomas" | 40  | "Development" |
# | "Sara"   | 34  | "Development" |
# | "Walter" | 43  | "Accounting"  |
# +----------+-----+---------------+

# naming and computing columns, filtering with "where":
select firstName:name dogYears:7*age where job="Development" from people
# +-----------+----------+
# | firstName | dogYears |
# +-----------+----------+
# | "Alice"   | 175      |
# | "Thomas"  | 280      |
# | "Sara"    | 238      |
# +-----------+----------+

# summarizing with "by" (groupby):
select job:first job employees:count name by job from people
# +---------------+-----------+
# | job           | employees |
# +---------------+-----------+
# | "Development" | 3         |
# | "Sales"       | 1         |
# | "Accounting"  | 1         |
# +---------------+-----------+

# "update" works like "select" but patches selected cells of the table.
# note that tables, like lists and dicts, are immutable!
# "update" returns a new, amended table value and does not reassign the variable "people":
update job:"Software Engineering" where job="Development" from people
# +----------+-----+------------------------+
# | name     | age | job                    |
# +----------+-----+------------------------+
# | "Alice"  | 25  | "Software Engineering" |
# | "Sam"    | 28  | "Sales"                |
# | "Thomas" | 40  | "Software Engineering" |
# | "Sara"   | 34  | "Software Engineering" |
# | "Walter" | 43  | "Accounting"           |
# +----------+-----+------------------------+

# queries apply to strings, lists, dicts, with columns "key", "value", "index".
# sorting with "orderby" (asc for ascending, desc for descending):
select index value orderby value asc from "BACB"
# +-------+-------+
# | index | value |
# +-------+-------+
# | 1     | "A"   |
# | 0     | "B"   |
# | 3     | "B"   |
# | 2     | "C"   |
# +-------+-------+

# "extract" works like "select", but returns a list instead of a table:
extract name orderby name asc from people
# -> ("Alice","Sam","Sara","Thomas","Walter")

# if you don't specify a column expression, extracts the first column:
extract orderby name asc from people
# (same as above)

# more table and collection operators:
a join b    # natural join (by column name).
a cross b   # cross join / cartesian product.
a , b       # concatenate rows of two tables.
3 limit t   # at most 3 rows of a table.
rows t      # list of rows as dictionaries.
cols t      # dictionary of columns as lists.
table x     # make a table from dict-of-list / list-of-dict.
raze t      # form a dictionary from the first two columns of a table.
flip x      # transpose a table's rows and columns.

###################################################
#
#  5. String Manipulation
#
###################################################

",|" split "one,|two,|three"    # break a string on a delimiter.
# -> ("one","two","three")

"::" fuse ("one","two","three") # concatenate strings with a delimiter.
# -> "one::two::three"

# the "format" operator uses a printf()-like format string
# to control converting one or more values into a string:
"%i : %s : %f" format (42,"Apple",-23.7)
# -> "42 : Apple : -23.7"

"%08.4f" format pi           # 0-padding, field width, precision.
# -> "003.1416"

"%l : %u" format "One","Two" # lowercase or uppercase.
# -> "one : TWO"

d:("a","b") dict 11,22
"%j" format list d           # JSON.
# -> "{\"a\":11,\"b\":22}"

# the "parse" operator tokenizes a string into values.
# "parse" and "format" use the same pattern syntax:
"%v[%i]" parse "foo[23]"
# -> ("foo",23)

"%j" parse "{'foo':[1,2,],'bar':34.5}"  # tolerant JSON parsing.
# -> {"foo":(1,2),"bar":34.5}

"< %[b]s %[a]s >" format d              # named elements.
# -> "< 22 11 >"

# the "like" operator performs glob-matching:
"foo"       like "f*"  # -> 1      (prefix match; * matches 0 or more chars)
"foo"       like "*oo" # -> 1      (suffix match)
"fxo"       like "f.o" # -> 1      (. matches any single char)
"a4"        like "a#"  # -> 1      (# matches any single digit)
"c#"        like ".`#" # -> 1      (` escapes special chars)
("a2","b2") like "a#"  # -> (1,0)  (left argument can be a column)

###################################################
#
#  6. Implicit Iteration
#
###################################################

# arithmetic operators "conform" over lists;
# this is how column expressions in queries work, too:
1+2                 # -> 3              (number plus number)
1+10,20,30          # -> (11,21,31)     (number plus each of list)
(10,20,30)+1        # -> (11,22,31)     (each of list plus number)
(10,100,1000)+1,2,3 # -> (11,102,1003)  (each of list plus each of list)

# the "=" equality operator conforms; use in query expressions:
3=3                 # 1
3=2,3,5             # (0,1,0)

# the "~" equality operator compares entire values; use with "if":
3~3                 # -> 1
3~2,3,5             # -> 0

# the "@" operator applies the left argument to each item on the right:
(11,22,33) @ 2,1,2       # -> (33,22,33)    (indexing a list)
double @ 10,20           # -> (20,40)       (applying a function)
first @ ("Alpha","Beta") # -> ("A","B")     (applying a unary operator)

# ".." is shorthand for "at every index":
t:"%j" parse "[{'a':22,'b':33},{'a':55}]"
t..a                     # -> 22,55
each v in t v.a end      # -> 22,55

# unary operators for reducing lists to a residue:
sum  1,2,3                    # -> 6
prod 2,3,4                    # -> 24       (product)
min  9,5,7                    # -> 5        (minimum; all)
max  9,5,7                    # -> 9        (maximum; some)
raze ((list 1,2),(list 3,4))  # -> 1,2,3,4  (flatten nesting)

###################################################
#
#  7. Arithmetic and Logic
#
###################################################

2 ^ 3           # -> 8                (exponentiation)
5 % 3,4,5,6,7   # -> (3,4,0,1,2)      (y modulo x)
3 | 5           # -> 5                (maximum; logical OR  for 0/1)
3 & 5           # -> 3                (minimum; logical AND for 0/1)
3 < 2,3,5       # -> (0,0,1)          (less)
3 > 2,3,5       # -> (1,0,0)          (more)
! 0,1,3,5       # -> (1,0,0,0)        (not)

# no x<=y or x>=y operators; use !x>y or !x<y instead

# named unary operators, which all conform:
# floor cos sin tan exp ln sqrt

# the constants "pi" and "e" are predefined as globals

mag 3,4           # -> 5              (vector magnitude)
u:unit pi/3       # -> (0.5,0.866025) (unit vector at angle)
(pi/3)~heading u  # -> 1              (angle of vector)

###################################################
#
#  8. Interfaces and Built-in Functions
#
###################################################

# interfaces are dictionary-like values representing system resources, mutable data.
# singleton utility interfaces:
sys.now                                       # sys: workspace info, rng, time
bits.xor[3 42]                                # bits: bit-wise arithmetic
rtext.replace["the orange!" "orange" "apple"] # rtext: rich text tables

# built-in functions for constructing instances of certain interfaces:
arr:array[16 "i16b"] # array: a 1D mutable array for manipulating binary data
img:image[(10,20)]   # image: a 2D mutable byte array for graphics operations

sys~sys              # -> 1          # interfaces compare with reference-equality.
sys=(sys,sys,123)    # -> (1,1,0)
typeof sys           # -> "system"   # two ways to identify interface type.
sys.type             # -> "system"
keys sys             # -> ()         # interfaces are non-enumerable!

# more built-in functions:
eval[str vars] # evaluate a string of Lil with a dict of local variable bindings
random[x]      # choose a random element from a string, list, dict, or table
readcsv[x]     # parse a string with CSV data into a table
writecsv[x]    # format a table into a CSV string
readxml[x]     # parse an XML/HTML document into a tree of dictionaries
writexml[x]    # format a tree of dictionaries into well-formed XML

###################################################
#
#  9. Decker
#
###################################################

# Decker is a GUI-builder like HyperCard or VisualBASIC:
go["http://beyondloom.com/decker/"]

# the parts of a "Deck" have corresponding interfaces, and can be given scripts.
# if a Widget, Card, or Deck contains function definitions with specific names,
# they will be called in response to events:
on click do                  # (imagine this is in a Button's script)
 me                          # the current Widget's interface.
 card                        # the Card interface containing the Widget.
 deck                        # the Deck interface containing the Card.
 myField.text:1+myField.text # we can refer to other widgets on the same card by name.

 # we can also refer to Cards in the Deck by name.
 # for a Widget on another Card, index through "card.widgets":
 f:otherCard.widgets.otherField
 f.text:f.text+1

 # we can send "synthetic" events to other parts of the Deck:
 otherButton.event["click"]
end

# each event handler is its own fresh universe; variables do not persist.
# the only state preserved between events is state stored in Widgets!
field.text          # string.
button.value        # 1/0 boolean.
slider.value        # number.
grid.value          # table.
canvas.copy[]       # Image interface.
# ...and so on. Contraptions are custom widgets, with a user-defined API.

# we can also treat Cards like records, with consistently-named Widgets as their fields.
# consider a checkbox on each Card in a Deck tracking whether it had been visited:
on view do          # (in the script for each Card) 
 visited.value:1    # "visited" is a checkbox.
end

# we could then find the Cards that have been visited so far with a query:
extract value where value..widgets.visited.value from deck.cards

# ...or reset all our visited flags:
deck.cards..widgets.visited.value:0

# we can override builtins and insert side-effects:
on go x trans delay do
 if trans show["start transition: " trans] end
 # "send" here calls the definition of "go" in the next-highest lexical scope:
 send go[x trans delay]
end

# if unhandled, events "bubble up": Widget -> Card -> Deck.
# to prevent bubbling, define an empty handler:
on navigate x do end

# some important attributes that apply to all Widgets:
x.pos        # (x,y) position on the Card, in pixels.
x.size       # (width,height) dimensions, in pixels.
x.name       # string uniquely identifying this Widget on a Card.
x.index      # drawing order on the Card, back-to-front.
x.show       # one of "solid", "invert", "transparent", or "none"