about summary refs log tree commit diff stats
path: root/lil
diff options
context:
space:
mode:
authorelioat <hi@eli.li>2024-06-08 15:18:49 -0400
committerelioat <hi@eli.li>2024-06-08 15:18:49 -0400
commit1d8144ccf117e8db562e8adb520f582c9be7fd4e (patch)
tree8bf5ce0c7ed60ddb1974f499ca43cd9b868c1b2a /lil
parent8c13d496a5434dd05fcd821e2e171ae9733fc05a (diff)
downloadtour-1d8144ccf117e8db562e8adb520f582c9be7fd4e.tar.gz
*
Diffstat (limited to 'lil')
-rw-r--r--lil/combinators.lil0
-rw-r--r--lil/learn.lil426
2 files changed, 426 insertions, 0 deletions
diff --git a/lil/combinators.lil b/lil/combinators.lil
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lil/combinators.lil
diff --git a/lil/learn.lil b/lil/learn.lil
new file mode 100644
index 0000000..2e44789
--- /dev/null
+++ b/lil/learn.lil
@@ -0,0 +1,426 @@
+# 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"
\ No newline at end of file