From 1d8144ccf117e8db562e8adb520f582c9be7fd4e Mon Sep 17 00:00:00 2001 From: elioat Date: Sat, 8 Jun 2024 15:18:49 -0400 Subject: * --- lil/combinators.lil | 0 lil/learn.lil | 426 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 426 insertions(+) create mode 100644 lil/combinators.lil create mode 100644 lil/learn.lil (limited to 'lil') diff --git a/lil/combinators.lil b/lil/combinators.lil new file mode 100644 index 0000000..e69de29 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 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 -- cgit 1.4.1-2-gfad0