├── .gitignore ├── Decker.desktop ├── LICENSE.txt ├── Makefile ├── Readme.md ├── VERSION ├── apelilt.sh ├── c ├── decker.c ├── dom.h ├── io_sdl1.h ├── io_sdl2.h ├── lib │ ├── bestline.c │ └── bestline.h ├── lil.h └── lilt.c ├── docs ├── decker.md ├── format.md ├── images │ ├── action.gif │ ├── audio.gif │ ├── brushes.png │ ├── buttonprops.gif │ ├── buttontypes.gif │ ├── card.gif │ ├── cards.gif │ ├── cardwids.gif │ ├── conprops.gif │ ├── contraptions.gif │ ├── deck.gif │ ├── fieldtypes.gif │ ├── fonts.png │ ├── gridtypes.gif │ ├── icons.gif │ ├── listener.gif │ ├── logo.png │ ├── merge.gif │ ├── mover.gif │ ├── palette.png │ ├── position.gif │ ├── protoattrs.gif │ ├── protoinstances.gif │ ├── protomargins.gif │ ├── protoprops.gif │ ├── protosize.gif │ ├── protowids.gif │ ├── query.gif │ ├── scripteditor.gif │ ├── slidertypes.gif │ ├── sounds.gif │ └── wings.gif ├── learn.md ├── lil.md ├── lilquickref.md └── lilt.md ├── examples ├── decks │ ├── bignums.deck │ ├── breakout.deck │ ├── brushes.deck │ ├── chickenwave.deck │ ├── chip8.deck │ ├── cohostify.deck │ ├── color.deck │ ├── cylon.deck │ ├── dialog.deck │ ├── draggable.deck │ ├── ease.deck │ ├── enchilada.deck │ ├── fonts.deck │ ├── forbidden.deck │ ├── graphpad.deck │ ├── guis.deck │ ├── kobolds.deck │ ├── life.deck │ ├── loveletter.deck │ ├── ovum.deck │ ├── palimport.deck │ ├── patedit.deck │ ├── path.deck │ ├── pdf.deck │ ├── plot.deck │ ├── publictransit.deck │ ├── puppeteer.deck │ ├── sokoban.deck │ ├── sound.deck │ ├── tour.deck │ ├── transit.deck │ ├── twee.deck │ ├── utena.deck │ ├── wigglykit.deck │ └── zazz.deck └── lilt │ ├── fastlife.lil │ ├── gif.lil │ ├── life.lil │ ├── mandel.lil │ ├── maze.lil │ ├── mines.lil │ └── podcasts.lil ├── icon_128x128.png ├── icon_192x192.png ├── icon_256x256.png ├── icon_32x32.png ├── icon_512x512.png ├── icon_64x64.png ├── images └── wings.gif ├── js ├── danger.js ├── decker.html ├── decker.js ├── lil.js └── repl.js ├── scripts ├── install.sh ├── lildoc.lil ├── resources.sh ├── test_interpreter.sh ├── uninstall.sh └── web_decker.sh ├── shell.nix ├── syntax ├── Lil.tmbundle │ ├── Deck.sublime-syntax │ ├── Preferences │ │ └── Comments.tmPreferences │ ├── Syntaxes │ │ └── Lil.tmLanguage │ └── info.plist ├── codemirror │ └── lilmode.js ├── emacs │ └── lil-mode.el └── vim │ ├── ftdetect │ └── lil.vim │ └── syntax │ └── lil.vim ├── tests ├── amend.lil ├── amend.out ├── at.lil ├── at.out ├── badlit.lil ├── badlit.out ├── big.lil ├── big.out ├── bits.lil ├── bits.out ├── closure.lil ├── closure.out ├── cond.lil ├── cond.out ├── conform.lil ├── conform.out ├── convert.lil ├── convert.out ├── dom │ ├── a-blessed.gif │ ├── a-opaque.gif │ ├── a.gif │ ├── a.png │ ├── a4.wav │ ├── ab-blessed.gif │ ├── ab-opt.gif │ ├── arrays.lil │ ├── b.gif │ ├── badchars.deck │ ├── bare.deck │ ├── bare.deck.html │ ├── barenums.deck │ ├── bom.txt │ ├── construction.gif │ ├── contraptions.deck │ ├── cursed.deck │ ├── decomposed.txt │ ├── domtests.lil │ ├── images.lil │ ├── import.lil │ ├── logger.deck │ ├── logger2.deck │ ├── module.deck │ ├── quotes.txt │ ├── sink1.deck │ └── test_roundtrip.lil ├── err_badchar.err ├── err_badchar.lil ├── err_endless.err ├── err_endless.lil ├── err_escape.err ├── err_escape.lil ├── err_nodir.err ├── err_nodir.lil ├── err_preelse.err ├── err_preelse.lil ├── err_reservedname.err ├── err_reservedname.lil ├── err_unterm.err ├── err_unterm.lil ├── err_variadic.err ├── err_variadic.lil ├── eval.lil ├── eval.out ├── interfaces.lil ├── interfaces.out ├── like.lil ├── like.out ├── mode.lil ├── mode.out ├── nil.lil ├── nil.out ├── nocode.lil ├── nocode.out ├── parseformat.lil ├── parseformat.out ├── puzzles │ ├── top10.lil │ └── weeklychallenge.lil ├── queries.lil ├── queries.out ├── recursion.lil ├── recursion.out ├── rtext.lil ├── rtext.out ├── smalltests.lil ├── smalltests.out ├── spread.lil ├── spread.out ├── testall.lil ├── testall.out ├── xml.lil └── xml.out ├── tools ├── awk │ ├── Readme.md │ └── lila.awk └── twine │ ├── Readme.md │ ├── build_ply.lil │ ├── logo.gif │ └── template.html ├── x-decker.xml └── xdg-install.sh /.gitignore: -------------------------------------------------------------------------------- 1 | *.DS_Store 2 | c/build/ 3 | c/resources.h 4 | js/build/ 5 | docs/*.html 6 | *.o 7 | tools/twine/format.js 8 | -------------------------------------------------------------------------------- /Decker.desktop: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=Decker 3 | Comment=A multimedia sketchpad 4 | Exec=decker %f 5 | Icon=decker 6 | Terminal=false 7 | Type=Application 8 | Categories=Graphics;2DGraphics;RasterGraphics; 9 | MimeType=application/x-decker; 10 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 John Earnest 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION=$(shell cat VERSION) 2 | UNAME=$(shell uname) 3 | EXTRA_FLAGS?= 4 | 5 | ifneq ("$(wildcard /usr/bin/olpc-hwinfo)","") 6 | # building on an OLPC; use SDL 1.2 7 | SDL=$(shell sdl-config --cflags --libs) 8 | SDL:=$(SDL) -lSDL_image 9 | else 10 | SDL=$(shell sdl2-config --cflags --libs) 11 | SDL:=$(SDL) -lSDL2_image 12 | endif 13 | ifeq ($(UNAME),Darwin) 14 | OPEN=open 15 | COMPILER=clang 16 | FLAGS=-Wall -Werror -Wextra -Wpedantic -Os -Wstrict-prototypes 17 | # -Wno-misleading-indentation silences warnings which are entirely spurious. 18 | FLAGS:=$(FLAGS) -Wno-misleading-indentation -Wno-unknown-warning-option 19 | # -Wno-overlength-strings works around a standards limitation which in practice is still portable. 20 | FLAGS:=$(FLAGS) -Wno-overlength-strings 21 | # FLAGS:=$(FLAGS) -fsanitize=undefined 22 | # FLAGS:=$(FLAGS) -fsanitize=address 23 | endif 24 | ifeq ($(UNAME),Linux) 25 | OPEN=xdg-open 26 | COMPILER=gcc 27 | # _BSD_SOURCE is required by older versions of GCC to find various posix extensions like realpath(). 28 | # _DEFAULT_SOURCE is the same deal, except newer versions of GCC need it 29 | # _POSIX_C_SOURCE is also needed by bestline on older versions of GCC 30 | # -lm is required for math.h 31 | FLAGS=-std=c99 -D _BSD_SOURCE -D _DEFAULT_SOURCE -D _POSIX_C_SOURCE -lm -Wall -Wextra -O2 32 | # -Wno-misleading-indentation silences warnings which are entirely spurious. 33 | # -Wno-format-truncation likewise silences spurious warnings regarding snprintf() truncation. 34 | FLAGS:=$(FLAGS) -Wno-misleading-indentation -Wno-format-truncation 35 | endif 36 | ifeq ($(UNAME),OpenBSD) 37 | OPEN=xdg-open 38 | COMPILER=clang 39 | FLAGS=-Wall -Werror -Wextra -Wpedantic -O2 40 | # -Wno-misleading-indentation silences warnings which are entirely spurious. 41 | FLAGS:=$(FLAGS) -Wno-misleading-indentation -Wno-unknown-warning-option 42 | FLAGS:=$(FLAGS) -lm 43 | endif 44 | ifeq ($(UNAME),NetBSD) 45 | # Required packages: bash, gmake, SDL2, SDL2_image, xdg-tools 46 | OPEN=xdg-open 47 | COMPILER=gcc 48 | FLAGS=-std=c99 -lm -Wall -Wextra -O2 49 | FLAGS:=$(FLAGS) -Wno-misleading-indentation -Wno-format-truncation 50 | endif 51 | ifneq ("$(EXTRA_FLAGS)","") 52 | FLAGS:=$(FLAGS) $(EXTRA_FLAGS) 53 | endif 54 | ifneq ($(V),1) 55 | Q:=@ 56 | endif 57 | 58 | # include potentially unsafe/nonportable scripting APIs 59 | # FLAGS:=$(FLAGS) -DDANGER_ZONE 60 | 61 | decker: c/build/decker 62 | lilt: c/build/lilt 63 | 64 | c/build/decker: c/resources.h c/decker.c c/dom.h c/lil.h 65 | @mkdir -p c/build 66 | $(Q)$(COMPILER) ./c/decker.c -o ./c/build/decker $(SDL) $(FLAGS) -DVERSION="\"$(VERSION)\"" 67 | 68 | c/resources.h: examples/decks/tour.deck js/lil.js js/danger.js js/decker.html js/decker.html 69 | @chmod +x ./scripts/resources.sh 70 | $(Q)./scripts/resources.sh examples/decks/tour.deck 71 | 72 | c/build/lilt: c/resources.h c/lilt.c c/dom.h c/lil.h 73 | @mkdir -p c/build 74 | $(Q)$(COMPILER) ./c/lilt.c -o ./c/build/lilt $(FLAGS) -DVERSION="\"$(VERSION)\"" 75 | 76 | clean: 77 | @rm -rf ./c/resources.h 78 | @rm -rf ./c/build/ 79 | @rm -rf ./js/build/ 80 | @rm -f docs/*.html 81 | 82 | install: 83 | @chmod +x ./scripts/install.sh 84 | @./scripts/install.sh 85 | 86 | uninstall: 87 | @chmod +x ./scripts/uninstall.sh 88 | @./scripts/uninstall.sh 89 | 90 | test: lilt 91 | @chmod +x ./scripts/test_interpreter.sh 92 | @./scripts/test_interpreter.sh "./c/build/lilt " 93 | @./c/build/lilt tests/dom/arrays.lil 94 | @./c/build/lilt tests/dom/images.lil 95 | @./c/build/lilt tests/dom/domtests.lil 96 | @./c/build/lilt tests/dom/test_roundtrip.lil 97 | @./c/build/lilt tests/puzzles/weeklychallenge.lil 98 | 99 | run: lilt 100 | @./c/build/lilt 101 | 102 | rundecker: decker 103 | ./c/build/decker 104 | 105 | .PHONY: jsres 106 | js: jsres 107 | @mkdir -p js/build/ 108 | @echo "VERSION=\"${VERSION}\"" > js/build/lilt.js 109 | @cat js/lil.js js/repl.js >> js/build/lilt.js 110 | 111 | testjs: js 112 | @chmod +x ./scripts/test_interpreter.sh 113 | @./scripts/test_interpreter.sh "node js/build/lilt.js" 114 | @node js/build/lilt.js tests/dom/arrays.lil 115 | @node js/build/lilt.js tests/dom/images.lil 116 | @node js/build/lilt.js tests/dom/domtests.lil 117 | @node js/build/lilt.js tests/dom/test_roundtrip.lil 118 | @node js/build/lilt.js tests/puzzles/weeklychallenge.lil 119 | 120 | testawk: 121 | @chmod +x ./scripts/test_interpreter.sh 122 | @./scripts/test_interpreter.sh "awk -f tools/awk/lila.awk" 123 | @awk -f tools/awk/lila.awk tests/dom/arrays.lil 124 | @awk -f tools/awk/lila.awk tests/dom/images.lil 125 | @awk -f tools/awk/lila.awk tests/puzzles/weeklychallenge.lil 126 | 127 | web-decker: js 128 | @chmod +x ./scripts/web_decker.sh 129 | @./scripts/web_decker.sh examples/decks/tour.deck js/build/decker.html $(VERSION) 130 | 131 | runweb: web-decker 132 | $(OPEN) js/build/decker.html 133 | 134 | .PHONY: docs 135 | docs: 136 | @./c/build/lilt scripts/lildoc.lil docs/lil.md docs/lil.html 137 | @./c/build/lilt scripts/lildoc.lil docs/lilt.md docs/lilt.html 138 | @./c/build/lilt scripts/lildoc.lil docs/decker.md docs/decker.html 139 | @./c/build/lilt scripts/lildoc.lil docs/format.md docs/format.html 140 | @./c/build/lilt scripts/lildoc.lil docs/lilquickref.md docs/lilquickref.html 141 | @./c/build/lilt scripts/lildoc.lil docs/learn.md docs/learn.html 142 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Decker 2 | ====== 3 | Decker is a multimedia platform for creating and sharing interactive documents, with sound, images, hypertext, and scripted behavior. 4 | 5 | ![Decker, complete with toolbars](images/wings.gif) 6 | 7 | You can learn more about Decker on [my website](http://beyondloom.com/decker/), on the [community forum](https://internet-janitor.itch.io/decker/community), or you can just dive in and [try it online](http://beyondloom.com/decker/tour.html). Periodic binary releases of Decker for MacOS and Windows are available on [Itch.io](https://internet-janitor.itch.io/decker). 8 | 9 | If you're interested in _Lil_, Decker's scripting language, you can access documentation and play with it in your browser at [trylil](http://beyondloom.com/tools/trylil.html). 10 | 11 | 12 | Web-Decker 13 | ---------- 14 | Decker is available as [a web application](http://beyondloom.com/decker/tour.html) (written in vanilla JavaScript) which is distributed as a single freestanding HTML file. Web-Decker can be built with a `make` script. The test suite uses [Node.js](https://nodejs.org/en/): 15 | 16 | ``` 17 | make testjs 18 | make web-decker 19 | make runweb # (optional) open in your default browser 20 | ``` 21 | 22 | 23 | Native-Decker 24 | ------------- 25 | Decker is also available as a native application, written in C. Building Native-Decker from source requires: 26 | 27 | - a c compiler and libc 28 | - the `xxd` utility (standard with MacOS and most \*nix distros) 29 | - [SDL2](https://www.libsdl.org/download-2.0.php) 30 | - [SDL2_image](https://github.com/libsdl-org/SDL_image) 31 | 32 | On MacOS, BSD, or Linux, fetch the appropriate SDL2 packages and then build with `make`. This has also been reported to build and run successfully under WSL: 33 | 34 | ``` 35 | brew install sdl2 sdl2_image # MacOS/Homebrew 36 | sudo apt install libsdl2-2.0-0 libsdl2-dev libsdl2-image-dev # Debian 37 | nix-shell # Nix 38 | 39 | make lilt # (optional) command-line tools 40 | make docs # (optional) build documentation (requires Lilt) 41 | make decker # build decker itself 42 | make test # (optional) regression test suite 43 | sudo make install # (optional) install lilt, decker, and lil syntax profiles 44 | ``` 45 | 46 | If SDL2 is not available, Native-Decker can also be built with [reduced functionality](c/io_sdl1.h) against SDL1.2 and a corresponding version of `SDL_image`. This compatibility shim is presently designed with the [OLPC XO-4](https://wiki.laptop.org/go/XO-4_Touch) and its default Fedora 18 OS image in mind; expect to do some tinkering with the makefile for other platforms: 47 | ``` 48 | sudo yum install SDL-devel SDL_image-devel 49 | 50 | make decker 51 | ``` 52 | 53 | 54 | Lilt 55 | ---- 56 | Decker's scripting language, [Lil](http://beyondloom.com/tools/trylil.html), is available as a standalone interpreter, with extended IO functionality to make it suitable for general-purpose programming and scripting: this package is called [Lilt](http://beyondloom.com/decker/lilt.html). Lilt only requires libc and `xxd` to build from source: 57 | ``` 58 | make lilt 59 | ``` 60 | 61 | Lilt can be used to programmatically create, inspect, and manipulate decks, as well as package them as Web-Decker self-executing documents: 62 | ``` 63 | $ lilt 64 | d:read["examples/decks/color.deck"] 65 | 66 | d.card:d.cards.colhex 67 | 68 | d.card.widgets.hex.text:"FFAA00" 69 | "FFAA00" 70 | d.card.widgets.hex.event["change"] 71 | 0 72 | d.card.widgets.rgb.text 73 | "16755200" 74 | write["color.html" d] 75 | 1 76 | ``` 77 | 78 | You can build Lilt against [Cosmopolitan Libc](https://github.com/jart/cosmopolitan), producing a single binary that will run on most popular operating systems: 79 | ``` 80 | $ ./apelilt.sh 81 | successfully compiled lilt.com 82 | running tests against ./lilt.com... 83 | all interpreter tests passed. 84 | all dom tests passed. 85 | all roundtrip tests passed. 86 | 87 | $ sh ./lilt.com 88 | range 10 89 | (0,1,2,3,4,5,6,7,8,9) 90 | ``` 91 | 92 | The Danger Zone 93 | --------------- 94 | Decker normally sandboxes the execution of scripts within decks to prevent low-level access to the host computer and ensure parity between the capabilities of Web-Decker and Native-Decker. Both implementations offer opt-in APIs for performing more "dangerous" or non-portable operations called [The Danger Zone](http://beyondloom.com/decker/decker.html#thedangerzone). 95 | 96 | When building Native-Decker from source, you can enable _The Danger Zone_ by defining the `DANGER_ZONE` preprocessor flag: 97 | ``` 98 | FLAGS:=$(FLAGS) -DDANGER_ZONE 99 | ``` 100 | 101 | A "dangerous" build of Native-Decker can export "dangerous" Web-Decker builds. You can also temporarily enable _The Danger Zone_ for Web-Decker by calling the `endanger()` function from your browser's JavaScript console or modifying the `DANGEROUS=0` constant in the .html file to `DANGEROUS=1`. [The Forbidden Library](http://beyondloom.com/decker/forbidden.html) offers a suite of bindings for useful JavaScript APIs based on this interface. 102 | 103 | 104 | Contributing 105 | ------------ 106 | The Decker project is released under the MIT license. Any contributions to this repository are understood to fall under the same license. 107 | 108 | - Bug fixes and typo corrections are always welcome. 109 | - Bug reports must include simple steps for reproduction and clearly indicate the OS and/or web browser where the bug arises. 110 | - PRs should match the style of existing code. 111 | - PRs should be as small as possible, and must not contain bundled unrelated changes. 112 | - PRs must include updates for _both_ the C and JavaScript versions of Decker (or its associated tools) whenever relevant. 113 | - PRs must include updates for documentation (see: the `docs` directory) wherever relevant. 114 | - PRs must pass the entire test suite (see: `make test`/`make testjs`). 115 | - PRs _must not_ incorporate _any_ material generated by or with the assistance of _any_ so-called "generative AI" tool or language model. Place your trash in an appropriate receptacle. 116 | - When modifying the JavaScript version of Decker, _please_ test your changes in multiple web browsers and avoid using bleeding-edge features. As a rule of thumb, if it didn't exist 5 years ago, don't use it now. If it _only_ works in Chrome, it's better not to do it at all. 117 | - When modifying the C version of Decker, avoid generating warnings and _do not use_ compiler-specific features such as GCC extensions. 118 | 119 | Please refrain from submitting Pull Requests to this repository containing new features without first discussing their inclusion in an Issue. Decker is intended to be small, simple, and cozy. There are an infinite number of features that could potentially be added, but creative constraints are also valuable. If you have a differing vision, feel empowered to explore it in your own fork of the project- that's what permissive licenses are for. 120 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.55 -------------------------------------------------------------------------------- /apelilt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # This script builds an "Actually-Portable Executable" version of Lilt, 4 | # the standalone Lil interpreter, producing a single binary which 5 | # is usable on a wide range of platforms (Windows, OSX, Linux, etc) 6 | 7 | set -e 8 | 9 | # fetch cosmopolitan dep(s) if needed 10 | if [[ ! -f "./scripts/ape/cosmopolitan.h" ]]; then 11 | echo "fetching cosmopolitan libc..." 12 | mkdir -p scripts/ape 13 | pushd scripts/ape 14 | wget https://justine.lol/cosmopolitan/cosmopolitan.zip 15 | unzip cosmopolitan.zip 16 | popd 17 | fi 18 | 19 | # select tooling 20 | COMPILER="gcc" 21 | OBJCOPY="objcopy" 22 | if [[ "$OSTYPE" == "darwin"* ]]; then 23 | if ! command -v x86_64-elf-gcc &> /dev/null ; then 24 | echo -e "ERROR: missing gcc compiler collection. Install via homebrew with:\n brew install x86_64-elf-gcc" 25 | exit 26 | fi 27 | COMPILER="x86_64-elf-gcc" 28 | OBJCOPY="x86_64-elf-objcopy" 29 | fi 30 | 31 | # build the sucker 32 | VERSION=$(cat VERSION) 33 | $COMPILER -g -Os -static -nostdlib -nostdinc -fno-pie -no-pie -mno-red-zone \ 34 | -fno-omit-frame-pointer -mno-tls-direct-seg-refs -gdwarf-4 -DVERSION="\"$VERSION\"" \ 35 | -o ./c/build/lilt.com.dbg ./c/lilt.c -fuse-ld=bfd -Wl,-T,scripts/ape/ape.lds -Wl,--gc-sections \ 36 | -include scripts/ape/cosmopolitan.h scripts/ape/crt.o scripts/ape/ape-no-modify-self.o scripts/ape/cosmopolitan.a 37 | $OBJCOPY -S -O binary ./c/build/lilt.com.dbg lilt.com 38 | echo "successfully compiled lilt.com" 39 | 40 | # sanity-check against the full test suite 41 | ./scripts/test_interpreter.sh "./lilt.com" 42 | ./lilt.com tests/dom/arrays.lil 43 | ./lilt.com tests/dom/images.lil 44 | ./lilt.com tests/dom/domtests.lil 45 | ./lilt.com tests/dom/test_roundtrip.lil 46 | ./lilt.com tests/puzzles/weeklychallenge.lil 47 | 48 | # note: on osx (zsh) it is necessary to invoke the output as: 49 | # bash -c './lilt.com' 50 | -------------------------------------------------------------------------------- /c/lib/bestline.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | typedef struct bestlineCompletions { 4 | unsigned long len; 5 | char **cvec; 6 | } bestlineCompletions; 7 | 8 | typedef void(bestlineCompletionCallback)(const char *, bestlineCompletions *); 9 | typedef char *(bestlineHintsCallback)(const char *, const char **, 10 | const char **); 11 | typedef void(bestlineFreeHintsCallback)(void *); 12 | typedef unsigned(bestlineXlatCallback)(unsigned); 13 | 14 | void bestlineSetCompletionCallback(bestlineCompletionCallback *); 15 | void bestlineSetHintsCallback(bestlineHintsCallback *); 16 | void bestlineSetFreeHintsCallback(bestlineFreeHintsCallback *); 17 | void bestlineAddCompletion(bestlineCompletions *, const char *); 18 | void bestlineSetXlatCallback(bestlineXlatCallback *); 19 | 20 | char *bestline(const char *); 21 | char *bestlineRaw(const char *, int, int); 22 | char *bestlineWithHistory(const char *, const char *); 23 | int bestlineHistoryAdd(const char *); 24 | int bestlineHistorySave(const char *); 25 | int bestlineHistoryLoad(const char *); 26 | void bestlineFreeCompletions(bestlineCompletions *); 27 | void bestlineHistoryFree(void); 28 | void bestlineClearScreen(int); 29 | void bestlineMaskModeEnable(void); 30 | void bestlineMaskModeDisable(void); 31 | void bestlineDisableRawMode(void); 32 | void bestlineFree(void *); 33 | 34 | char bestlineIsSeparator(unsigned); 35 | char bestlineNotSeparator(unsigned); 36 | char bestlineIsXeparator(unsigned); 37 | unsigned bestlineUppercase(unsigned); 38 | unsigned bestlineLowercase(unsigned); 39 | long bestlineReadCharacter(int, char *, unsigned long); 40 | -------------------------------------------------------------------------------- /c/lilt.c: -------------------------------------------------------------------------------- 1 | // Lil Terminal 2 | #include "lil.h" 3 | #include "dom.h" 4 | #ifndef __COSMOPOLITAN__ 5 | #include 6 | #include 7 | #endif 8 | 9 | #include "lib/bestline.h" 10 | #include "lib/bestline.c" 11 | 12 | lv*n_exit(lv*self,lv*a){(void)self;exit(ln(l_first(a)));} 13 | lv*n_input(lv*self,lv*a){ 14 | (void)self;char*line=bestline(drom_to_utf8(a->c<2?ls(l_first(a)): l_format(ls(l_first(a)),l_drop(ONE,a)))->sv); 15 | if(!line)return LNIL;lv*r=lmutf8(line);free(line);return r; 16 | } 17 | lv*n_readwav(lv*self,lv*a){ 18 | // this polyfill is limited compared to the version in decker, but avoids SDL dependencies: 19 | (void)self;lv*name=ls(l_first(a));int offset=a->c>1?MAX(0,ln(a->lv[1])):0, size=0; 20 | struct stat st;if(stat(name->sv,&st)||st.st_size<13)return sound_make(lms(size)); 21 | char*data=calloc(size=st.st_size,1);FILE*f=fopen(name->sv,"rb"); 22 | if(fread(data,1,st.st_size,f)!=(unsigned)st.st_size)return fclose(f),free(data),sound_make(lms(0));fclose(f); 23 | char HEAD[]={'R','I','F','F',0xFF,0xFF,0xFF,0xFF,'W','A','V','E','f','m','t',' ',16,0,0,0,1,0,1,0,64,31,0,0,64,31,0,0,1,0,8,0,'d','a','t','a'}; 24 | for(int z=0;z<40&&zsv[z]=0xFF&(data[44+z+offset]-128); 27 | return free(data),sound_make(r); 28 | } 29 | lv*n_readfile(lv*self,lv*a){ 30 | lv*name=ls(l_first(a)); 31 | if(a->c>1&&matchr(lmistr("array"),a->lv[1]))return readbin(name); 32 | if(has_suffix(name->sv,".gif" ))return n_readgif(self,a); 33 | if(has_suffix(name->sv,".wav" ))return n_readwav(self,a); 34 | if(has_suffix(name->sv,".deck"))return n_readdeck(self,a); 35 | return n_read(self,a); 36 | } 37 | lv*runstring(char*t,lv*env){ 38 | lv* prog=parse(t);if(perr())return fprintf(stderr,"(%d:%d) %s\n",par.r+1,par.c+1,drom_to_utf8(lmcstr(par.error))->sv),LNIL; 39 | return run(prog,env); 40 | } 41 | lv*runfile(char*path,lv*env){ 42 | struct stat st;if(stat(path,&st)){fprintf(stderr,"unable to open '%s'\n",path);return LNIL;} 43 | return runstring(n_read(NULL,l_list(lmcstr(path)))->sv,env); 44 | } 45 | lv* print_array(lv*arr,FILE*out){array a=unpack_array(arr);for(int z=0;zc==1&&array_is(a->lv[0])?print_array(l_first(a),stdout):n_printf(a,1,stdout);} 47 | lv*n_error(lv*self,lv*a){(void)self;return a->c==1&&array_is(a->lv[0])?print_array(l_first(a),stderr):n_printf(a,1,stderr);} 48 | 49 | extern char **environ; 50 | 51 | void go_notify(lv*deck,lv*args,int dest){(void)deck,(void)args,(void)dest;} 52 | void field_notify(lv*field){(void)field;} 53 | lv* n_panic(lv*self,lv*z){(void)self,(void)z;return LNIL;} 54 | lv* n_alert(lv*self,lv*z){(void)self,(void)z;return ONE;} 55 | lv* n_open (lv*self,lv*z){(void)self,(void)z;return lmistr("");} 56 | lv* n_save (lv*self,lv*z){(void)self,(void)z;return LNIL;} 57 | lv* n_play (lv*self,lv*z){(void)self;lv*x=l_first(z);return x;} 58 | lv* n_show(lv*self,lv*a){ 59 | (void)self;str s=str_new();EACH(z,a){if(z)str_addc(&s,' ');show(&s,a->lv[z],a->c==1);} 60 | printf("%s\n",drom_to_utf8(lmstr(s))->sv);return l_first(a); 61 | } 62 | lv*interface_app(lv*self,lv*i,lv*x){ 63 | if(!x&&lis(i)){ 64 | ikey("show" )return lmnat(n_show,NULL); 65 | ikey("print" )return lmnat(n_print,NULL); 66 | }return x?x:LNIL;(void)self; 67 | } 68 | 69 | // Environment 70 | 71 | lv*n_import(lv*self,lv*a); // forward ref 72 | lv* globals(void){ 73 | lv*env=lmenv(NULL); 74 | dset(env,lmistr("show" ),lmnat(n_show,NULL)); 75 | dset(env,lmistr("print" ),lmnat(n_print,NULL)); 76 | dset(env,lmistr("error" ),lmnat(n_error,NULL)); 77 | dset(env,lmistr("input" ),lmnat(n_input,NULL)); 78 | dset(env,lmistr("dir" ),lmnat(n_dir,NULL)); 79 | dset(env,lmistr("read" ),lmnat(n_readfile,NULL)); 80 | dset(env,lmistr("write" ),lmnat(n_writefile,NULL)); 81 | dset(env,lmistr("path" ),lmnat(n_path,NULL)); 82 | dset(env,lmistr("exit" ),lmnat(n_exit,NULL)); 83 | dset(env,lmistr("shell" ),lmnat(n_shell,NULL)); 84 | dset(env,lmistr("eval" ),lmnat(n_eval,NULL)); 85 | dset(env,lmistr("import" ),lmnat(n_import,NULL)); 86 | dset(env,lmistr("random" ),lmnat(n_random,NULL)); 87 | dset(env,lmistr("array" ),lmnat(n_array,NULL)); 88 | dset(env,lmistr("image" ),lmnat(n_image,NULL)); 89 | dset(env,lmistr("sound" ),lmnat(n_sound,NULL)); 90 | dset(env,lmistr("newdeck" ),lmnat(n_newdeck,NULL)); 91 | dset(env,lmistr("readcsv" ),lmnat(n_readcsv,NULL)); 92 | dset(env,lmistr("writecsv" ),lmnat(n_writecsv,NULL)); 93 | dset(env,lmistr("readxml" ),lmnat(n_readxml,NULL)); 94 | dset(env,lmistr("writexml" ),lmnat(n_writexml,NULL)); 95 | constants(env); 96 | return env; 97 | } 98 | lv*n_import(lv*self,lv*a){ 99 | lv*filename=ls(l_first(a));if(has_suffix(filename->sv,".deck")||has_suffix(filename->sv,".html")){ 100 | lv*d=n_readdeck(self,a),*m=ifield(d,"modules"),*r=lmd(); 101 | EACH(z,m){dset(r,m->kv[z],ifield(m->lv[z],"value"));}return r; 102 | } 103 | lv*file=n_read(self,a);if(!file->c)return LNIL; 104 | lv*prog=parse(ls(file)->sv);if(perr())return LNIL; 105 | lv*root=lmenv(globals());pushstate(root),issue(root,prog); 106 | int c=0;while(running()){runop(),c++;if(c%100==0)lv_collect();} 107 | DMAP(r,root,root->lv[z]);return popstate(),r; 108 | } 109 | 110 | // Entrypoint 111 | 112 | int main(int argc,char**argv){ 113 | init_interns(); 114 | lv* env=globals(); 115 | lv* a=lml(argc);for(int z=0;zlv[z]=lmutf8(argv[z]); 116 | dset(env,lmistr("args"),a); 117 | dset(env,lmistr("env"),env_enumerate()); 118 | char*home=getenv("LIL_HOME");if(home){ 119 | struct dirent*find;DIR*dir=opendir(home);if(dir){while((find=readdir(dir))){ 120 | char path[4096];snprintf(path,sizeof(path),"%s/%s",home,find->d_name); 121 | if(has_suffix(path,".lil"))runfile(path,env); 122 | }}closedir(dir); 123 | } 124 | for(int z=1;z=argc)fprintf(stderr,"no expression specified.\n"),exit(1); 131 | runstring(argv[z+1],env),z++; 132 | } 133 | else{runfile(argv[z],env);} 134 | if(z==argc-1)exit(0); 135 | } 136 | while(1){ 137 | char*line=bestlineWithHistory(" ","lilt"); 138 | if (!line)break; 139 | lv*prog=parse(lmutf8(line)->sv);free(line); 140 | if(perr()){for(int z=0;zsv);} 141 | else{lv*x=run(prog,env);dset(env,lmistr("_"),x);debug_show(x);} 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /docs/images/action.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/action.gif -------------------------------------------------------------------------------- /docs/images/audio.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/audio.gif -------------------------------------------------------------------------------- /docs/images/brushes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/brushes.png -------------------------------------------------------------------------------- /docs/images/buttonprops.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/buttonprops.gif -------------------------------------------------------------------------------- /docs/images/buttontypes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/buttontypes.gif -------------------------------------------------------------------------------- /docs/images/card.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/card.gif -------------------------------------------------------------------------------- /docs/images/cards.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/cards.gif -------------------------------------------------------------------------------- /docs/images/cardwids.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/cardwids.gif -------------------------------------------------------------------------------- /docs/images/conprops.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/conprops.gif -------------------------------------------------------------------------------- /docs/images/contraptions.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/contraptions.gif -------------------------------------------------------------------------------- /docs/images/deck.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/deck.gif -------------------------------------------------------------------------------- /docs/images/fieldtypes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/fieldtypes.gif -------------------------------------------------------------------------------- /docs/images/fonts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/fonts.png -------------------------------------------------------------------------------- /docs/images/gridtypes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/gridtypes.gif -------------------------------------------------------------------------------- /docs/images/icons.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/icons.gif -------------------------------------------------------------------------------- /docs/images/listener.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/listener.gif -------------------------------------------------------------------------------- /docs/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/logo.png -------------------------------------------------------------------------------- /docs/images/merge.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/merge.gif -------------------------------------------------------------------------------- /docs/images/mover.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/mover.gif -------------------------------------------------------------------------------- /docs/images/palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/palette.png -------------------------------------------------------------------------------- /docs/images/position.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/position.gif -------------------------------------------------------------------------------- /docs/images/protoattrs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/protoattrs.gif -------------------------------------------------------------------------------- /docs/images/protoinstances.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/protoinstances.gif -------------------------------------------------------------------------------- /docs/images/protomargins.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/protomargins.gif -------------------------------------------------------------------------------- /docs/images/protoprops.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/protoprops.gif -------------------------------------------------------------------------------- /docs/images/protosize.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/protosize.gif -------------------------------------------------------------------------------- /docs/images/protowids.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/protowids.gif -------------------------------------------------------------------------------- /docs/images/query.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/query.gif -------------------------------------------------------------------------------- /docs/images/scripteditor.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/scripteditor.gif -------------------------------------------------------------------------------- /docs/images/slidertypes.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/slidertypes.gif -------------------------------------------------------------------------------- /docs/images/sounds.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/sounds.gif -------------------------------------------------------------------------------- /docs/images/wings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/docs/images/wings.gif -------------------------------------------------------------------------------- /docs/lilquickref.md: -------------------------------------------------------------------------------- 1 | title:Lil Quick Reference 2 | 3 | Lil Quick Reference 4 | =================== 5 | Types 6 | ----- 7 | | `typeof` | False | Examples | 8 | | :----------- | :--------- | :------------------------------------ | 9 | | `"nil"` | `nil` | `nil` | 10 | | `"number"` | `0` | `42` `37.5` `-29999` | 11 | | `"string"` | `""` | `"foo\nbar"` | 12 | | `"list"` | `()` | `11,22,33` `list 3` | 13 | | `"dict"` | `()dict()` | `("a","b") dict 11,22` | 14 | | `"table"` | n/a | `table range 2` `insert a with 1 end` | 15 | | `"function"` | n/a | `on x y do x+y end` | 16 | 17 | Primitives 18 | ---------- 19 | | Valence | Purpose | | 20 | | :------ | :--------- | :------------------------------------------------------------------------------------ | 21 | | monad | arithmetic | `-` `!` `floor` `cos` `sin` `tan` `exp` `ln` `sqrt` `mag` `unit` `heading` | 22 | | monad | reducers | `count` `first` `last` `sum` `prod` `min` `max` `raze` | 23 | | monad | data | `range` `keys` `list` (enlist) `rows` `cols` `table` `typeof` `flip` | 24 | | dyad | arithmetic | `+` `-` `*` `/` `%` (y mod x) `^` (pow) `&` (min) `|` (max) | 25 | | dyad | logical | `<` `>` `=` (conforming equal) `~` (match) `unless` (x if y is `0`) | 26 | | dyad | string | `fuse` `split` `parse` `format` `like` | 27 | | dyad | data | `,` (concat) `@` (index each right) `dict` `take` `drop` `limit` `window` `in` `fill` | 28 | | dyad | joins | `join` (natural join/zip) `cross` (cross join/cartesian product) | 29 | 30 | Flow 31 | ---- 32 | - `if bool ... end` 33 | - `if bool ... else ... end` 34 | - `if bool ... elseif bool ... else ... end` (etc) 35 | - `each val key index in x ... end` 36 | - `while bool ... end` 37 | - `send name[args]` 38 | 39 | Queries 40 | ------- 41 | - `select exprs clauses from y` reorder, compute, or filter a table 42 | - `update exprs clauses from y` modify rows/columns of a table in place 43 | - `extract exprs clauses from y` like select, but yields non-tabular values 44 | - `insert c1 c2 with "A" 11 "B" 22 end` create a new table 45 | - `insert c1 c2 with "A" 11 "B" 22 into d` append to a table 46 | 47 | - `exprs` can be any number of expressions in the forms: 48 | - an implicitly named bare expression (`id`, `2*index`) 49 | - an explicitly named expression (`ident:id`, `dogyears:7*age`) 50 | - a quoted name, for invalid identifiers (`"not a lil id":foo`) 51 | - if no expressions are provided, all columns will be returned, like `select *` in SQL 52 | 53 | - `clauses` can be any sequence of the following, evaluated right to left: 54 | - `by a`: group rows by the unique values of column b 55 | - `where a`: filter rows by a boolean column a 56 | - `orderby a asc`/`orderby a desc`: sort rows, comparing values of column a as by `<`/`>` 57 | 58 | - special columns/values available when computing any column expression: 59 | - `index`: magic column of original row numbers 60 | - `gindex`: magic column of current row number within group (or all rows if ungrouped) 61 | - `group`: magic column of row's group, by appearance (or `0` if ungrouped) 62 | - `column`: dictionary of the entire group's columns (or all rows if ungrouped) 63 | 64 | Formatting 65 | ---------- 66 | A format is `%[name]*-0N.DX`: `*` skip, `0` pad, `N` width. 67 | `-` is invert char class (`ro`) or left justify. 68 | `.D` is decimal places (`fc`), size of char class (`ro`), or truncate to `D` characters. 69 | 70 | | `X` | Purpose | | 71 | | :-------- | :------- | :--------------------------------------------------------------------------------------------- | 72 | | `%nmz` | Parsing | literal `%`, number of chars read, matches? matches to end? | 73 | | `ro` | Matching | repeat (0 or more in char class), optional (0 or 1 in char class) | 74 | | `sula` | String | string, uppercase string, lowercase string, ASCII/DeckRoman chars | 75 | | `bficChH` | Number | bool, float, int, currency (`-$1.23`), plain currency (`-1.23`), hex lowercase, hex uppercase | 76 | | `jJep` | Misc. | JSON, Lil data, unix epoch, time-parts {`year`, `month`, `day`, `hour`, `minute`, `second`} | 77 | | `qv` | Lil | quoted Lil string literal, Lil variable name | 78 | 79 | Glob patterns for `like`: 80 | 81 | - `.`: any single character. 82 | - `#`: any single digit 0-9. 83 | - `*`: 0 or more of any character. 84 | - backtick escapes a subsequent special character. 85 | -------------------------------------------------------------------------------- /examples/decks/cohostify.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[512,342] 5 | name:"cohostify.deck" 6 | 7 | {card:home} 8 | image:"%%IMG2AgABVgD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wChAT8A/wDBAUEAnw0IAP8AGQFBAJsNEAD/ABUBAwAPARkAEwEDAJoNEwD/ABMBAwAPAQMAEwEDABMBAwCXDRgA/wARAQMADwEDABMBAwATAQMAlg0aAP8AEAEDAA8BAwATAQMAEwEDAJMNHwD/AA4BAwAPAQMAEwEDABMBAwCSDSEA/wANAQMADwEDABMBAwATAQMAkQ0iAP8ADQEDAA8BAwATAQMAEwEDAI8NJQD/AAwBAwAPAQMAEwEDABMBAwCODScA/wALAQMADwEDABMBAwATAQMAjA0pAP8ACwEDAA8BAwATAQMAEwEDAIsNKwD/AAoBAwAPAQMAEwEDABMBAwCKDSwA/wAKAQMADwEDABMBAwATAQMAiQ0tAP8ACgEDAA8BAwATAQMAEwEDAFkBAgAuDS0A/wAKAQMADwEDABMBAwATAQMAWAEFACsNLgAGAQIASAECALcBAwAPAQMAEwEDABMBAwBYAQcAKA0vAAUBBABGAQQAtgEDAA8BGQATAQMAWQEIACYNLwAFAQQARQEGALUBAwAPARkAEwEDAFkBCgAjDTAABQEEAEUBBgC1AQMADwEZABMBAwBZAQwAIA0xAAUBBABGAQUAtQEDAA8BAwATAQMAEwEDAFkBDgAeDTEABQEEAEcBBAC1AQMADwEDABMBAwATAQMAHwFKABsNMgAFAQQARwEEALUBAwAPAQMAEwEDABMBAwAZAVEAGg0xAAYBBABHAQQAtQEDAA8BAwATAQMAEwEDABgBVAAYDQwBCQ0OAQYNCAAGAQQAMAEIAAoBDgCwAQMADwEDABMBAwATAQMAGQFWABQNCwEMDQoBDA0FAAYBBAAaAQgADAELAAgBEACvAQMADwEDABMBAwATAQMAGQFXABMNCgENDQkBDg0DAAcBBAADAQcADgELAAoBDAAIARAArwEDAA8BAwATAQMAEwEDABkBWAASDQoBDA0JAQ8NAwAHAQ8ADAENAAkBCwAKAQ4AsAEDAA8BAwATAQMAEwEDABkBVwATDQkBBg0PAQcNBAEGDQIABwEPAAsBDwAIAQQAFgEEALUBAwAPAQMAEwEDABMBAwAZAVQAFg0JAQUNEAEFDQcBBQ0BAAgBEAAKAQYABAEFAAgBCAASAQQAtQEDAA8BAwATAQMAEwEDABkBUwAWDQoBBA0RAQQNCQEEDQEACAEIAAMBBQAJAQUABwEEAAkBCQAQAQQAtQEDAA8BAwATAQMAEwEDABkBUQAYDQoBBA0RAQQNCAEFAAkBBAAIAQQACQEFAAcBBAAKAQkADwEEALUBAwAPAQMAEwEDABMBAwAaARsAJQEOABoNCgEEDREBBQ0HAQUACQEEAAgBBAAJAQUABwEEAAsBCQAOAQQAtQEDAA8BAwATAQMAEwEDABoBDwAxAQwAHA0KAQUNEQEGDQQBBQAKAQQACAEEAAoBBgAEAQUADwEFAA4BBAABAQMAsQEDAA8BAwATAQMAEwEDABoBCQABAQEANQELAB0NCgEFDREBBw0BAQcACgEEAAgBBAAKAQ8ACQELAA4BCQCwAQMADwEDABMBAwATAQMAGwEEADsBCgAeDQsBDA0KAQ0ACwEEAAgBBAALAQ0ACQEMAA4BCQCwAQMADwEZABMBAwBaAQcAIg0LAQwNCgELAAwBBAAIAQQADQEKAAoBCwAQAQcAsQEDAA8BGQATAQMAWgEGACMNDAELDQwBCAAOAQIACQEEAA0BCQAMAQkAEgEEALMBAwAQARgAEwEDAFoBBAAlDQ0BCQ0OAQUNAQAbAQIADwEDAOMBAwAlAQMAEwEDAFoBBAAmDQ4BBA0VAP8AFQEDACUBAwATAQMAWwECACcNJgD/ABYBAwAlAQMAEwEDAIUNJAD/ABcBAwAlAQMAEwEDAIYNIQD/ABkBAwAlAQMAEwEDAIcNHwD/ABoBAwAlAQMAEwEDAIgNHAD/ABwBAwAlAQMAEwEDAIkNGQD/AB4BAwAlAQMAEwEDAIoNFgD/ACABAwAlAQMAEwEDAIwNEQD/ACMBAwAlAQMAEwEDAI4NDAD/ACYBAwAlAQMAEwEDAP8AwAEDACUBAwATAQMA/wDAAQMAJQEDABMBAwD/AMABQQD/AMABQQD/AMEBPwD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AGE=" 9 | {widgets} 10 | field1:{"type":"field","size":[174,15],"pos":[52,89],"locked":1,"border":0,"align":"center","value":"Paste Lil Code Here:"} 11 | field2:{"type":"field","size":[174,15],"pos":[291,89],"locked":1,"border":0,"align":"center","value":"Obtain Eggbug-Compatible HTML Here:"} 12 | code:{"type":"field","size":[234,224],"pos":[15,106],"script":"home.0","scrollbar":1,"style":"code"} 13 | encoded:{"type":"field","size":[234,224],"pos":[263,106],"scrollbar":1,"style":"code"} 14 | 15 | {script:home.0} 16 | on change do 17 | encoded.text:highlight.html[code.text 1] 18 | end 19 | {end} 20 | 21 | {module:highlight} 22 | description:"convert lil source code into syntax-highlighted html" 23 | version:1 24 | {script} 25 | inlines.pre:"background:#272822;color:#f8f8f2;padding:.5em;" 26 | inlines.comment :"color:#75715e;" 27 | inlines.string :"color:#e6db74;" 28 | inlines.escape :"color:#ae81ff;" 29 | inlines.keyword :"color:#f92672;" 30 | inlines.verb :"color:#66d9ef;" 31 | inlines.call :"color:#66d9ef;" 32 | inlines.funcname:"color:#a6e22e;" 33 | 34 | keywords:"|"split"while|each|send|on|if|elseif|else|end|do|with|local|select|extract|update|insert|into|from|where|by|orderby|asc|desc" 35 | monads:"|"split"floor|cos|sin|tan|exp|ln|sqrt|unit|mag|heading|sum|prod|raze|max|min|count|first|last|keys|range|list|typeof|flip|rows|cols|table" 36 | dyads:"|"split"split|fuse|dict|take|drop|limit|in|unless|join|cross|parse|format|like|window|fill" 37 | verbs:monads,dyads 38 | escapes:"\\n","\\\"","\\\\" 39 | 40 | # tokenize the input file into an rtext-compatible table 41 | # this gives us several useful output options, 42 | # and also makes it easy to coalesce adjacent runs together: 43 | 44 | on chunk type x do 45 | insert text font arg with x type "" end 46 | end 47 | 48 | on highlight text do 49 | r:rtext.cat[] i:fn:0 50 | while i" 100 | " " " " 101 | "\"" """ 102 | "'" "'" 103 | "<" "<" 104 | ">" ">" 105 | "&" "&" 106 | end 107 | html_specials:"" fuse keys html_escapes 108 | 109 | on highlight_to_html text raw do 110 | r.tag:"pre" 111 | if raw r.attr.style:inlines.pre else r.attr.class:"lil" end 112 | r.children:each row in rows h:highlight[text] 113 | if row.font 114 | s.tag:"span" 115 | if raw s.attr.style:inlines[row.font] else s.attr.class:row.font end 116 | s.children:list row.text 117 | else 118 | t:row.text 119 | esc:array[] 120 | while count t 121 | if (first t) in html_specials esc.cat[html_escapes[first t]] t:1 drop t 122 | else c:"%-.7r\n \"'<>&%n" parse t esc.cat[first c ] t:(last c) drop t 123 | end 124 | end 125 | list esc 126 | end 127 | end 128 | writexml[r] 129 | end 130 | 131 | module.html:highlight_to_html 132 | {end} 133 | 134 | -------------------------------------------------------------------------------- /examples/decks/life.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[512,342] 5 | name:"The Game of Life" 6 | author:"John Earnest" 7 | 8 | {card:home} 9 | image:"%%IMG2AgABVgD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wDDAbgA/wBIAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboA/wBHAboACgQBAP8APAG6AAkEAwB1BAEAxAG6AAgEBQD/ADoBugAHBAcA/wA5AboABgQJAP8AOAG6AAUECwD/ADcBugAEBA0A/wA2AboAAwQPAGkIAQDKAboAAgQRAP8ANAG6AAEEEwBiDQEAzwG6BBUA/wAyAboEFgD/ADEBugQXAP8AMAG6BBgA/wAvAboEGQD/AC4BugQaAP8ALQG6BBsA/wAsAboEHAD/ACsBugQdAP8AKgG6BB4A/wApAboEHwD/ACgBugQgAP8AJwG6BCEA/wAnAbgEIwD/ANEEMQD/AM8EMwD/AM0ENQD/AMsENwD/AMkEOQD/AMcEOwD/AMUEPQD/AMMEPwD/AMEEQQD/AL8EQwD/AL0ERQD/ALsERwD/ALkESQD/ALcESwD/ALUETQD/ALMETwD/ALEEUQD/AK8EUwD/AK0EVQD/AKsEVwD/AKkEWQD/AKcEWwD/AKUEXQD/AKMEXwD/AKEEYQD/AJ8EYwD/AJ0EZQD/AJsEZwD/AJkEaQD/AJcEawD/AJUEbQD/AJMEbwD/AJEEcQD/AI8EcwD/AI0EdQD/AIsEdwD/AIkEeQD/AIcEewAGDQEA/wB+BH0ABA0BAP8AfgR/AAINAQD/AH4EggD/AIAEgQD/AIEEfgDxBAEAkQR8APEEAwCRBHoAAw0BAO0EBQCRBHgAAw0BAO0EBwCRBHYAAw0BAO0ECQCRBHQAAw0BAO0ECwCRBHIAAw0BAO0EDQCRBHAAAw0BAO0EDwCRBG4AAw0BAO0EEQCRBGwAAw0BAO0EEwCRBGoAAw0BAO0EFQCRBGgAAw0BAO0EFwCRBGYAAw0BAO0EGQCRBGQAAw0BAO0EGwCRBGIAAw0BAO0EHQCRBGAAAw0BAO0EHwCRBF4AAw0BAO0EIQCRBFwAAw0BAO0EIwCRBFoAAw0BAO0EJQCRBFgAAw0BAO0EJwCRBFYAAw0BAO0EKQCRBFQAAw0BAO0EKwCRBFIAAw0BAO0ELQCRBFAAAw0BAO0ELwCRBE4AAw0BAO0EMQCRBEwAAw0BAO0EMwCRBEoAAw0BAO0ENQCRBEgAAw0BAO0ENwCRBEYAAw0BAO0EOQCRBEQAAw0BAO0EOwCRBEIAAw0BAO0EPQCRBEAAAw0BAO0EPwCRBD4AAw0BAO0EQQCRBDwAAw0BAO0EQwCRBDoAAw0BAO0ERQCRBDgAAw0BAO0ERwCRBDYAAw0BAO0ESQCRBDQAAw0BAO0ESwCRBDIAAw0BAO0ETQCRBDAAAw0BAO0ETwCRBC4AAw0BAO0EUQCRBCwAAw0BAO0EUwCRBCoAAw0BAO0EVQCRBCgAAw0BAO0EVwCRBCYAAw0BAO0EWQCRBCQAAw0BAO0EWwCRBCIAAw0BAO0EXQCRBCAAAw0BAO0EXwCRBB4AAw0BAO0EYQCRBBwAAw0BAO0EYwCRBBoAAw0BAO0EZQCRBBgAAw0BAO0EZwCRBBYAAw0BAO0EaQCRBBQAAw0BAO0EawCRBBIAAw0BAO0EbQCRBBAAAw0BAO0EbwCRBA4AAw0BAO0EcQCRBAwAAw0BAO0EcwCRBAoAAw0BAO0EdQCRBAgAAw0BAO0EdwCRBAYAAw0BAO0EeQCRBAQAAw0BAO0EewAGDQEAigQCAAMNAQDtBH0ABA0BAIsEAgACDQEA7QR/AAINAQD/AH4EggD/AIAEgQD/AIEEfgD/AIQEfAD/AIYEegADDQEA/wCEBHgAAw0BAAQEAQCJBAEA9gR2AAMNAQAEBAMAhwQDAPYEdAADDQEABAQFAIUEBQD2BHIAAw0BAAQEBwCDBAcAIgFzAGEEcAADDQEABAQJAIEECQAhAQEAcQEBAGIEbgADDQEABAQLAH8ECwAgAQEAcQEBAGMEbAADDQEABAQNAH0EDQAfAQEAcQEBAGQEagADDQEABAQPAHsEDwAeAQEAcQEBAGUEaAADDQEABAQRAHkEEQAdAQEAcQEBAGYEZgADDQEABAQTAHcEEwAcAQEAcQEBAGcEZAADDQEABAQVAHUEFQAbAQEAcQEBAGgEYgADDQEABAQXAHMEFwAaAQEAcQEBAGkEYAADDQEABAQZAHEEGQAZAQEAcQEBAGoEXgADDQEABAQbAG8EGwAYAQEAcQEBAGsEXAADDQEABAQdAG0EHQAXAQEAcQEBAGwEWgADDQEABAQfAGsEHwAWAQEAcQEBAG0EWAADDQEABAQhAGkEIQAVAQEAcQEBAG4EVgADDQEABAQjAGcEIwAUAQEAcQEBAG8EVAADDQEABAQlAGUEJQATAQEAcQEBAHAEUgADDQEABAQnAGMEJwASAQEAcQEBAHEEUAADDQEABAQpAGEEKQARAQEAcQEBAHIETgADDQEABAQrAF8EKwAQAQEAcQEBAHMETAADDQEABAQtAF0ELQAPAQEAcQEBAHQESgADDQEABAQvAFsELwAOAQEAcQEBAHUESAADDQEABAQxAFkEMQANAQEAcQEBAHYERgADDQEABAQzAFcEMwAMAQEAcQEBAHcERAADDQEABAQ1AFUENQALAQEAcQEBAHgEQgADDQEABAQ3AFMENwAKAQEAcQEBAHkEQAADDQEABAQ5AFEEOQAJAQEAcQEBAHoEPgADDQEABAQ7AE8EOwAIAQEAcQEBAHsEPAADDQEABAQ9AE0EPQAHAQEAcQEBAHwEOgADDQEABAQ/AEsEPwAGAQEAcQEBAH0EOAADDQEABARBAEkEQQAFAQEAcQEBAH4ENgADDQEABARDAEcEQwAEAQEAcQEBAH8ENAADDQEABARFAEUERQADAQEAcQEBAIAEMgADDQEABARHAEMERwACAQEAcQEBAIEEMAADDQEABARJAEEESQABAQEAcQEBAIIELgADDQEABARLAD8ESwEBAHEBAQCDBCwAAw0BAAQETQA9BEwBAQBxAQEAhAQqAAMNAQAEBE8AOwRNAQEAcQEBAIUEKAADDQEABARRADkETgEBAHEBAQCGBCYAAw0BAAQEUwA3BE8BAQBxAQEAhwQkAAMNAQAEBFUANQRQAQEAcQEBAIgEIgADDQEABARXADMEUQEBAHEBAQCJBCAAAw0BAAQEWQAxBFIBAQBxAQEAigQeAAMNAQAEBFsALwRTAQEAcQEBAIsEHAADDQEABARdAC0EVAEBAHEBAQCMBBoAAw0BAAQEXwArBFUBAQBxAQEAjQQYAAMNAQAEBGEAKQRWAQEAcQEBAI4EFgADDQEABARjACcEVwEBAHEBAQCPBBQAAw0BAAQEZQAlBFgBAQBxAQEAkAQSAAMNAQAEBGcAIwRZAQEAcQEBAJEEEAADDQEABARpACEEWgEBAHEBAQCSBA4AAw0BAAQEawAfBFsBAQBxAQEAkwQMAAMNAQAEBG0AHQRcAQEAcQEBAJQECgADDQEABARvABsEXQEBAHEBAQCVBAgAAw0BAAQEcQAZBF4BAQBxAQEAlgQGAAMNAQAEBHMAFwRfAQEAcQEBAJcEBAADDQEABAR1ABUEYAEBAHEBAQCYBAIAAw0BAAQEdwATBGEBAQBxAQEAmAQCAAINAQAEBHkAEQRiAQEAcQEBAKAEewAGDQEACARjAQEAcQEBAJ8EfQAEDQEACARkAQEAcQEBAJ4EfwACDQEACARlAQEAcQEBAJ0EggAIBGYBAQBxAQEAngSBAAkEZQEBAHEBAQCfBH4ADARkAQEAcQEBAKAEfAAOBGMBAQBxAQEAoQR6AAMNAQAMBGIBAQBxAQEAogR4AAMNAQAOBGEBAQBxAQEAowR2AAMNAQAGBAEACQRgAQEAcQEBAKQEdAADDQEABgQDAAkEXwEBAHEBAQClBHIAAw0BAAYEBQAJBF4BAQBxAQEApgRwAAMNAQAGBAcACQRdAQEAcQEBAKcEbgADDQEABgQJAAkEXAEBAHEBAQCoBGwAAw0BAAYECwAJBFsBAQBxAQEAqQRqAAMNAQAGBA0ACQRaAQEAcQEBAKoEaAADDQEABgQPAAkEWQEBAHEBAQCrBGYAAw0BAAYEEQAJBFgBAQBxAQEArARkAAMNAQAGBBMACQRXAQEAcQEBAK0EYgADDQEABgQVAAkEVgEBAHEBAQCuBGAAAw0BAAYEFwAJBFUBAQBxAQEArwReAAMNAQAGBBkACQRUAQEAcQEBALAEXAADDQEABgQbAAkEUwEBAHEBAQCxBFoAAw0BAAYEHQAJBFIBAQBxAQEAsgRYAAMNAQAGBB8ACQRRAQEAcQEBALMEVgADDQEABgQhAAkEUAEBAHEBAQC0BFQAAw0BAAYEIwAJBE8BAQBxAQEAtQRSAAMNAQAGBCUACQROAQEAcQEBALYEUAADDQEABgQnAAkETQEBAHEBAQC3BE4AAw0BAAYEKQAJBEwBAQBxAQEAuARMAAMNAQAGBCsACQRLAQEAcQEBALkESgADDQEABgQtAAkESgEBAHEBAQC6BEgAAw0BAAYELwAJBEgAAQEBAHEBAQC7BEYAAw0BAAYEMQAJBEYAAgEBAHEBAQC8BEQAAw0BAAYEMwAJBEQAAwEBAHEBAQC9BEIAAw0BAAYENQAJBEIAAw0BAQEAcQEBAL4EQAADDQEABgQ3AAkEQAADDQEAAQEBAHEBAQC/BD4AAw0BAAYEOQAJBD4AAw0BAAIBAQBxAQEAwAQ8AAMNAQAGBDsACQQ8AAMNAQADAQEAcQEBAMEEOgADDQEABgQ9AAkEOgADDQEABAEBAHEBAQDCBDgAAw0BAAYEPwAJBDgAAw0BAAUBAQBxAQEAwwQ2AAMNAQAGBEEACQQ2AAMNAQAGAQEAcQEBAMQENAADDQEABgRDAAkENAADDQEABwEBAHEBAQDFBDIAAw0BAAYERQAJBDIAAw0BAAgBAQBxAQEAxgQwAAMNAQAGBEcACQQwAAMNAQAJAQEAcQEBAMcELgADDQEABgRJAAkELgADDQEACgEBAHEBAQDIBCwAAw0BAAYESwAJBCwAAw0BAAsBAQBxAQEAyQQqAAMNAQAGBE0ACQQqAAMNAQAMAQEAcQEBAMoEKAADDQEABgRPAAkEKAADDQEADQEBAHEBAQDLBCYAAw0BAAYEUQAJBCYAAw0BAA4BAQBxAQEAzAQkAAMNAQAGBFMACQQkAAMNAQAPAQEAcQEBAM0EIgADDQEABgRVAAkEIgADDQEAEAEBAHEBAQDOBCAAAw0BAAYEVwAJBCAAAw0BABEBcwDPBB4AAw0BAAYEWQAJBB4AAw0BAP8AVgQcAAMNAQAGBFsACQQcAAMNAQD/AFgEGgADDQEABgRdAAkEGgADDQEA/wBaBBgAAw0BAAYEXwAJBBgAAw0BAP8AXAQWAAMNAQAGBGEACQQWAAMNAQD/AF4EFAADDQEABgRjAAkEFAADDQEA/wBgBBIAAw0BAAYEZQAJBBIAAw0BAP8AYgQQAAMNAQAGBGcACQQQAAMNAQD/AGQEDgADDQEABgRpAAkEDgADDQEA/wBmBAwAAw0BAAYEawAJBAwAAw0BAP8AaAQKAAMNAQAGBG0ACQQKAAMNAQD/AGoECAADDQEABgRvAAkECAADDQEA/wBsBAYAAw0BAAYEcQAJBAYAAw0BAP8AbgQEAAMNAQAGBHMACQQEAAMNAQD/AHAEAgADDQEABgR1AAkEAgADDQEA/wBxBAIAAg0BAAYEdwAIBAIAAg0BAP8AfAR5AP8AhwR7AAYNAQD/AH4EfQAEDQEA/wB+BH8AAg0BAP8AfgSCAP8AgASBAP8AgQR+AP8AhAR8AP8AhgR6AAMNAQD/AIQEeAADDQEA/wCGBHYAAw0BAP8AiAR0AAMNAQD/AIoEcgADDQEA/wCMBHAAAw0BAP8AjgRuAAMNAQD/AJAEbAADDQEA/wCSBGoAAw0BAP8AlARoAAMNAQD/AJYEZgADDQEA/wCYBGQAAw0BAP8AmgRiAAMNAQD/AJwEYAADDQEA/wCeBF4AAw0BAP8AoARcAAMNAQDH" 10 | script:"home.0" 11 | {widgets} 12 | field1:{"type":"field","size":[164,20],"pos":[147,44],"locked":1,"font":"menu","show":"invert","border":0,"align":"center","value":"Conway's Game of Life"} 13 | board:{"type":"canvas","size":[204,195],"pos":[126,106],"script":"home.1","show":"invert","image":"%%IMG0ABoAGWBSCAAATGDABgAFQIcAAAAYgAAAIjY4gIh/AADCfyeACD4wQCIcN0CiCChAEIEhAKyBCYCQYIGAMACcwKAAUIAsdYEATBQpgAoWLQCkQDAApQ+/AMEREYDgKBKAgIAbACZgAEA=","pattern":0,"scale":8} 14 | button1:{"type":"button","size":[87,24],"pos":[381,119],"script":"home.2","text":"Clear"} 15 | button2:{"type":"button","size":[87,24],"pos":[381,150],"script":"home.3","text":"Randomize"} 16 | button3:{"type":"button","size":[87,24],"pos":[381,203],"script":"home.4","text":"1"} 17 | field2:{"type":"field","size":[44,14],"pos":[401,184],"locked":1,"border":0,"align":"center","value":"step"} 18 | button4:{"type":"button","size":[87,24],"pos":[381,235],"script":"home.4","text":"10"} 19 | button5:{"type":"button","size":[87,24],"pos":[381,267],"script":"home.4","text":"100"} 20 | 21 | {script:home.0} 22 | on step do 23 | counts:image[board.size] 24 | each delta in (3 cross 3)-1 25 | counts.merge["+" board.copy[].translate[delta]] 26 | end 27 | stay:counts.copy[].map[((3,4) dict 1,1) 0].merge["&" board.copy[]] 28 | born:counts .map[(3 dict 1 ) 0] 29 | board.paste[born.merge["|" stay]] 30 | sleep[5] 31 | end 32 | 33 | on steps x do 34 | step @ range floor x 35 | end 36 | {end} 37 | 38 | {script:home.1} 39 | on click pos do 40 | me.pattern:!me[pos] 41 | end 42 | 43 | on release pos do 44 | drag[pos] 45 | end 46 | {end} 47 | 48 | {script:home.2} 49 | on click do 50 | board.clear[] 51 | end 52 | {end} 53 | 54 | {script:home.3} 55 | on click do 56 | b:image[board.lsize] 57 | b.pixels:random[(0,0,1) prod board.lsize] 58 | board.paste[b] 59 | end 60 | {end} 61 | 62 | {script:home.4} 63 | on click do 64 | steps[me.text] 65 | end 66 | {end} 67 | 68 | -------------------------------------------------------------------------------- /examples/decks/palimport.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[512,342] 5 | name:"palimport.deck" 6 | patterns:"%%IMG2AAgA5gBAAUEABwEBAAcBAQAHAQgABAEBAAcBAQAHAQEAAwEIAAIBAQAGAQEABgEBAAcBAgAFAQEAAgEBAAMBAQAEAQMABgEBAAYBAQAEAQEAEwEBAAsBAQATAQEACwEIAAEBAwABAQ0AAQEDAAEBCQABAQMAAQENAAEBAwABAQEAAQEDAAMBAQACAQEAAwEBAAQBAQABAQQAAwEEAAEBAQADAQMAAgEBAAMBAQACAQMAAQEBAAIBBQADAQEAAwEBAAQBAQABAQEABgEBAAsBAQAGAQEAAQECAAMBAQADAQEAAwEBAAMBAQADAQEAAwEBAAMBAQANAQEAAwEBAAkBAQADAQEADQEBAAMBAQAKAQMAAQEFAAEBAwABAQEAAQEDAAEBBQABAQMAAQEBAAEBAwABAQUAAQEDAAEBAQABAQMAAQEFAAEBAwABAQEAAQEBAAYBAQATAQEACAEBAAgBAQALAQEADAEBAAcBAQAGAQMABAEBAAMBAQACAQEABQECAAgBAgAIAQEAAQEBAAMBAQAFAQEAAwEBAAEBAQADAQEABQEBAAMBAQABAQEAAwEBAAUBAQADAQEAAQEBAAMBAQAFAQEAAwEBAAEBAQABAQEAAQEBAAEBAQACAQEAAQEBAAEBAQABAQIAAQEBAAEBAQABAQEAAgEBAAEBAQABAQEAAQECAAEBAQABAQEAAQEBAAIBAQABAQEAAQEBAAEBAgABAQEAAQEBAAEBAQACAQEAAQEBAAEBAQABAQIADAEBAAMBAQAKAQEACgEBAAIBAQALAQEABAEBAAQBAgAKAQIAAgEBAAMBAgABAQIAAQECAAMBAQACAQIACgECAAMBAgABAQQAAQECAAMBAQABAQEAAQEBAAEBAQAJAQEAAQEBAAEBAQABAQEACQEBAAEBAQABAQEAAQEBAAkBAQABAQEAAQEBAAEBAQAJAQgAAQEBAAEBAQABAQEAAQEJAAEBAQABAQEAAQEBAAEBCQABAQEAAQEBAAEBAQABAQkAAQEBAAEBAQABAQEAAQEJAAgBCAAIAQgACAEIAAgBAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAQABAQEAAQEBAAEBAQABAQEAAgEBAAMBAQACAQEAAwEBAAYBAQADAQEAAgEBAAMBAQACAQEAAwEBAAIBAQADAQEABgEBAAMBAQACAQEAAwEBAAEBAgABAQMAAQECAAEBAwABAQIAAQEDAAEBBgABAQMAAQECAAEBAwABAQIAAQEDAAEBAgABAQMAAQEGAAEBAwACAQEABgEBAA4BAQAGAQEABgEBAAYBAQAGAQEABgEBAAUBAQABAQYAAQEOAAEBBgABAQYAAQEGAAEBBgABAQYAAQEFAAQBAQALAQEAAQEBAAEBAQABAQEADQEBAAsBAQADAQEACwEBAAMBBQADAQQAAQEDAAEBBAACAQIAAwEFAAMBBQAEAQMAAQEEAAMBAQACAQIAAQEBAAEBAQABAQEACQEBAAMBAQAGAQEAAQEBAAQBAQADAQEAAgEBAAUBAgADAQEACwEBAAEBAgAEAQEAAQECAAQBAQABAQIABAEBAAEBBgAIAQEAAQEHAAEBBwABAQIABP8DLgEiAS8BRQEpAT8BegEwAUUBmQE9AUEBzQFoAT0B+wG5AVQB8gHsAYsBsAGpAYcBmQF/AXMBZgFZAWQBRAE4AUYBVwFgAWkBeAGKAYcBqQGyAaIBAAM=" 7 | 8 | {card:home} 9 | image:"%%IMG2AgABVgD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wCNAQ8A/wDkAR8A/wDhASIA/wDfASQA/wDdASUA/wDdARAACwELAP8A+AEKAP8A+QEJAP8A+gEIAP8A+gEHAP8A/AEFAP8A/AEGAP8A+wEGAP8A/AEFAP8A/AEGAP8A+wEGAP8A/AEFAP8A/AEFAP8A/AEFAP8A/AEFAP8A+wEGAP8A+wEGAP8A+wEFAP8A/AEFAP8A/AEFAP8A/AEFAP8A+wEGAP8A+wEGAP8A+gEGAP8A+wEGAP8A+wEFAP8A/AEFAP8A/AEFAP8A+wEGAP8A+wEGAP8A+gEGAP8A8gEDAAYBBgD/APEBBQAFAQUA/wDyAQUABQEFAP8A8gEFAAQBBgD/APIBBQAEAQYA/wDyAQUAAwEGAP8A8wEFAAIBBwD/APMBBQABAQcA/wD0AQ0A/wD0AQwA/wD1AQsABQEEAP8A7QEKAAEBCgD/AOwBFQD/AOwBFQD/AOwBFAD/AO0BEgD/AO8BDQD/APQBCgD/APcBBwD/APsBAwD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8A/wD/AP8ApQ==" 10 | {widgets} 11 | palImport1:{"type":"contraption","size":[137,100],"pos":[334,171],"def":"palImport","widgets":{"button1":{},"button2":{},"p":{"value":"2e222f\n45293f\n7a3045\n993d41\ncd683d\nfbb954\nf2ec8b\nb0a987\n997f73\n665964\n443846\n576069\n788a87\na9b2a2\n"}}} 12 | canvas1:{"type":"canvas","size":[150,150],"pos":[47,49],"image":"%%IMG2AJYAlgD/AO0lIABkLSAlEgBkLSAlEgBkLSAlEgBkLSAlEgBkLSAlEgBkLSAlEgBkLSAlEgBkLSAlEgBkLSAlEgBkLSAlEgBkLSAlEgBkLSAlEgBkLSAlEgBGIQYAGC0gJRIARiEGABgtICUSAEYhBgAYLSAlEgBGIQYAGC0gJRIARiEGABgtICUSAAgvIAAeIQYAGC0gJRIACC8gAB4hBgAYLSAlEgAILyAAHiEGABgtICUSAAgvIAAeIQYAGC0gJRIACC8gAB4hBgAYLSAlEgAILyAAHiEGABgtICUSAAgvIAAeIQYAGC0gJRIACC8gAB4hBgAOJAotICUSAAgvIAAeIQYADiQKLSAlEgAILyAAHiEGAA4kCi0gJRIACC8gAB4hBgAOJAotICUSAAgvIAAeIQYADiQKLSAlEgAILyAAHiEGAA4kCi0gJRIACC8gAB4hBgAOJAotICIJABEvIAAeIQYADiQgIhMAES8gAB4hBgAOJCAiEwARLyAAHiEGAA4kICITABEvIAAeIQYADiQgIhMAES8gAB4hBgAOJCAiEwARLyAAHiEGAA4kICITABEvIAAeIQYADiQgIhMAES8gAB4hBgAOJCAiEwARLyAjBAAaIQYADiQgIhMAES8gIwQAGiEGAA4kEyYgABEvICMEABohBgAOJBMmIAARLyAjBAAaIQYADiQTJiAAES8gIwQAGiEGAA4kEyYgABEvICMEAC4kEyYgABEvICMEAC4kEyYgABEvICMEAC4kEyYgABEvICMEAC4kEyYgABEvICMEAC4kEyYgABUjIAAuJBMmIAAVIyAALiQTJiAAFSMgAC4kEyYgABUjIAAuJBMmIAAVIyAALiQTJiAAFSMgAC4kEyYgABUjIAAuJBMmIAAVIyAAQSYgABUjIABBJiAAFSMgAEEmIAAVIxEpIAAwJiAAFSMRKSAAMCYgABUjESkgADAmIAAVIxEpIAAwJiAAFSMRKSAAMCYgABUjESkgADAmIAAVIxEpIAAwJiAAFSMRKSAAMCYgABUjESkgADAmIAAVIxEpIAAwJiAAFSMRKSAAMCYgABUjESkgADAmIAAmKSAAMCYgACYpIAB2KSAAdikgAHYpIAB2KSAAdikgAHYpIAB2KSAAdikgAHYpIAB2KSAAdikgAHYpIABeJxgpIABeJxgpIABeJxgpIABeJxgpIABeJxgpIABeJxgpIABeJyAAdicFKCAAcScFKCAAcScFKCAAcScFKCAAcScFKCAAcScFKCAAcScFKCAAcScFKCAAcScFKCAAcScFKCAAcScFKCAAcScFKCAAcScFKCAAcScFKCAAWiwgKBwAWiwgKBwAWiwgKBwAWiwgKBwAWiwgKBwAWiwgKBwAWiwgKBwAWiwgKBwAWiwgKBwANyogAAMsICgcADcqIAADLCAoHAA3KiAAAywgKBwANyogAAMsICgcADcqIAADLCAoHAA3KiAAAywgKBwANyogAAMsICgcABkgCQAVKiAAAywgKBwAGSAJABUqIAADLCAoHAAZIAkAFSogAAMsIAA1IAkAFSogAAMsIAA1IAkAFSogAAMsIAA1IAkAFSogAAMsIAA1IAkAFSogAAMsIAA1IAkAFSogAAMsIAA1IAkAFSogAAMsICsNACggCQAVKiAAAywgKw0AKCAJABUqIAADLCArDQAELiAABCAJABUqIAADLCArDQAELiAABCAJABUqIAADLCArDQAELiAABCAJABUqIAADLCArDQAELiAABCAJABUqIAADLCArDQAELiAABCAJABUqIAADLCArDQAELiAABCAJABUqIAAQKyAABC4gAAQgCQAVKiAAECsgAAQuIAAEIAkAFSogABArIAAELiAABCAJABUqIAAQKyAABC4gAAQgCQAVKiAAECsgAAQuIAAEIAkAFSogABArIAAELiAABCAJABUqIAAQKyAABC4gAAQgCQAVKiAAECsgAAQuIAAEIAk=","pattern":47} 13 | field1:{"type":"field","size":[175,88],"pos":[259,60],"locked":1,"show":"transparent","border":0,"value":{"text":["this deck contains a palette importer contraption that can understand the .hex palettes available on ","lospec.com","\n(up to 16 colors, naturally.)\n\nIt's also a handy regression test for round-tripping custom palettes, which is why it lives here!"],"font":["","",""],"arg":["","https://lospec.com/palette-list",""]}} 14 | 15 | {contraption:palImport} 16 | size:[137,100] 17 | margin:[0,0,0,0] 18 | description:"a tool for importing color palettes in the .hex format as used by lospec.com." 19 | image:"%%IMG2AIkAZAACAYUAAwEBDYUBAQABAQENhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQINhwECDYcBAg2HAQEAAQEBDYUBAQADAYUAAg==" 20 | {widgets} 21 | button1:{"type":"button","size":[60,20],"pos":[6,74],"script":"palImport.0p","text":"Import"} 22 | button2:{"type":"button","size":[60,20],"pos":[71,74],"script":"palImport.1p","text":"Apply"} 23 | p:{"type":"field","size":[125,62],"pos":[6,7],"scrollbar":1,"style":"code"} 24 | 25 | {script:palImport.0p} 26 | on click do 27 | p.text:read[] 28 | end 29 | {end} 30 | 31 | {script:palImport.1p} 32 | on color_dist a b do 33 | aa:"%2h%2h%2h" parse "%06h" format a 34 | bb:"%2h%2h%2h" parse "%06h" format b 35 | sum(aa-bb)^2 36 | end 37 | 38 | on find_closest i n do 39 | g:patterns[i] 40 | r:first n 41 | each v in n 42 | if color_dist[v g](x^2)+(y^2) 10 | t:x0+(x^2)-y^2 11 | y:y0+2*x*y 12 | x:t 13 | i:i+1 14 | end 15 | " .,-~:;=!*#$@"[12&.6*i] 16 | end 17 | 18 | each y in scale[30 -1 1] 19 | print[each x in scale[80 -2.5 1] mandelbrot[x y] end] 20 | end 21 | 22 | show[sys.workspace] 23 | -------------------------------------------------------------------------------- /examples/lilt/maze.lil: -------------------------------------------------------------------------------- 1 | # generate a maze via depth-first random walk 2 | # and render it as ASCII art 3 | 4 | on maze w h do 5 | used:up:lf:() 6 | on walk here do 7 | each delta in random[(list -1,0),(list 1,0),(list 0,-1),(list 0,1) -4] 8 | there:(w,h)&(0,0)|here+delta 9 | if !used[there] 10 | if delta[0]<0 lf[here ]:1 end 11 | if delta[0]>0 lf[there]:1 end 12 | if delta[1]<0 up[here ]:1 end 13 | if delta[1]>0 up[there]:1 end 14 | used[there]:1 15 | walk[there] 16 | end 17 | end 18 | end 19 | walk[0,0] 20 | 21 | # display the maze 22 | each y in range h 23 | print[each x in range w ("+--","+ ")[up[x,y]] end,"+"] 24 | print[each x in range w ("| "," ")[lf[x,y]] end,"|"] 25 | end 26 | print[(1+w*3) take "+--"] 27 | end 28 | 29 | maze[20 8] 30 | -------------------------------------------------------------------------------- /examples/lilt/mines.lil: -------------------------------------------------------------------------------- 1 | # Command-line minesweeper in Lil 2 | 3 | local mines :0 # {0,1} image; is there a mine here? 4 | local cover :0 # {0,1} image; is this tile covered? 5 | local around:0 # [0-9] image; adjacent mine count 6 | local deltas:flip (list 0,0) drop (3 cross 3)-1 7 | 8 | on setup size mine_count do 9 | mines:image[size] 10 | cover:image[size].map[() 1] 11 | sys.seed:sys.ms 12 | mines.pixels:random[mine_count>range prod size (-prod size)] 13 | horiz:mines.copy[] 14 | .merge["+" mines.copy[].translate[-1,0]] 15 | .merge["+" mines.copy[].translate[ 1,0]] 16 | around:horiz.copy[] 17 | .merge["+" horiz.copy[].translate[0,-1]] 18 | .merge["+" horiz.copy[].translate[0, 1]] 19 | end 20 | 21 | on display do 22 | cm:range mines.size[0] 23 | print["\n %s\n %s\n" floor .1*cm 10%cm] 24 | each row ri in mines.pixels 25 | print[("%02i " format ri),each mine ci in row 26 | if cover[ci,ri] "#" 27 | elseif mine "*" 28 | else "_" unless around[ci,ri] 29 | end 30 | end] 31 | end 32 | print[""] 33 | end 34 | 35 | on reveal here do 36 | if cover[here] 37 | cover[here]:0 38 | if !around[here] reveal @ flip here+deltas end 39 | end 40 | end 41 | 42 | on finish outcome do 43 | cover.map[() 0] 44 | display[] 45 | print["You %s!" outcome] 46 | exit[0] 47 | end 48 | 49 | if !5~count args 50 | print["Usage: lilt mines.lil width height mines_count"] 51 | else 52 | setup[0+args@2,3 0+args[4]] 53 | while 1 54 | display[] 55 | pos:"%i,%i" parse input["x,y > "] 56 | reveal[pos] 57 | if mines[pos] finish.lose 58 | elseif mines.pixels~cover.pixels finish.win 59 | end 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /examples/lilt/podcasts.lil: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env lilt 2 | # An excruciatingly simple podcast fetcher based on Lilt's XML parser. 3 | 4 | #feedUrl: "https://feeds.simplecast.com/wjQvYtdl" 5 | feedUrl: "https://arraycast.com/episodes?format=rss" 6 | 7 | print["fetching feed from %s..." feedUrl] 8 | shell["wget -q %s -O feed.xml" format feedUrl] 9 | feed:read["feed.xml"] 10 | shell["rm feed.xml"] 11 | xml:first readxml[feed] 12 | 13 | # RSS XML has a very sparse and heavily nested structure. 14 | # let's begin by flattening it out and making it easier to traverse: 15 | on squash x do 16 | on delist x do if 1=count x first x else x end end 17 | on gather x do if 1=count x squash[first x] else raze squash[x] end end 18 | on union x do 19 | r:() 20 | each c in x.children 21 | r[c.tag]:(() unless r[c.tag]),list gather[c.children] 22 | end 23 | r:delist @ r 24 | end 25 | on squash x do 26 | case.dict:on _ x do 27 | rr[x.tag]:if x.children 28 | r:if 1=count x.children squash[first x.children] else union[x] end 29 | if "string"~typeof r r else x.attr,r end 30 | else 31 | x.attr 32 | end 33 | end 34 | case.string:on _ x do x end 35 | case.list :on _ x do delist[squash @ x] end 36 | case[typeof x][x] 37 | end 38 | squash[x] 39 | end 40 | 41 | # RSS fields often contain fragments of HTML- 42 | # collapse these into simple text runs: 43 | on htmlText x do 44 | on flatten x do 45 | "" fuse if x.children 46 | flatten @ x.children 47 | else 48 | x 49 | end 50 | end 51 | "" fuse flatten @ readxml[x] 52 | end 53 | 54 | # RSS pubdates use the extremely inconvenient 55 | # RFC 822 date-time format, like "Wed, 02 Oct 2002 08:00:00 EST" 56 | # rearrange it into something we can sort on lexicographically: 57 | on pubDateToISO x do 58 | m:("|" split "Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec") dict 1+range 12 59 | each v in x 60 | p:"%*s, %[day]i %[month]s %[year]i %[hour]i:%[minute]i:%[second]i" parse v 61 | "%p" format p.month:m[p.month] 62 | end 63 | end 64 | 65 | # do some munging on the feed to get the parts we care about: 66 | root:squash[xml] 67 | doc:root.rss.channel 68 | data:select 69 | title 70 | pubdate:pubDateToISO[pubdate] 71 | file:(list "%s.mp3") format "%s:" parse title 72 | url:"%s?" parse enclosure..url 73 | from table doc.item 74 | 75 | # present a command-line UI: 76 | print["\n%s\n%s\n%s\n" doc.title ((count doc.title)take"=") htmlText[doc.description]] 77 | print["found %i episodes." (count data)] 78 | data:5 limit select where !file in dir["."].name orderby pubdate desc from data 79 | print["found %i unfetched recent episodes.\n" (count data)] 80 | each row in rows data 81 | if "y"~"%1l" parse input["fetch %s? (y/n) " row.title] 82 | print["fetching %s..." row.url] 83 | start:sys.ms 84 | shell["wget --no-clobber -q %s -O \"%s\"" format row.url,row.file] 85 | print["completed in %f seconds." (sys.ms-start)/1000] 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /icon_128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/icon_128x128.png -------------------------------------------------------------------------------- /icon_192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/icon_192x192.png -------------------------------------------------------------------------------- /icon_256x256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/icon_256x256.png -------------------------------------------------------------------------------- /icon_32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/icon_32x32.png -------------------------------------------------------------------------------- /icon_512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/icon_512x512.png -------------------------------------------------------------------------------- /icon_64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/icon_64x64.png -------------------------------------------------------------------------------- /images/wings.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/images/wings.gif -------------------------------------------------------------------------------- /js/danger.js: -------------------------------------------------------------------------------- 1 | 2 | // potentially unsafe/nonportable scripting APIs 3 | 4 | function js_to_lil(x){ 5 | if(x==null||x==undefined)return NIL 6 | if(deck_is(x)||card_is(x)||widget_is(x))return x 7 | if('number'==typeof x)return lmn(x) 8 | if('string'==typeof x)return lms(clchars(x)) 9 | if(Array.isArray(x))return lml(x.slice(0).map(js_to_lil)) 10 | if('object'==typeof x){ 11 | if(Object.getPrototypeOf(x)==Uint8Array.prototype)return array_make(x.length,'u8',0,x) 12 | return lmd(Object.keys(x).map(js_to_lil),Object.values(x).map(js_to_lil)) 13 | } 14 | if('function'==typeof x)return lmnat(args=>js_to_lil(x.apply(null,args.map(lil_to_js)))) 15 | return NIL 16 | } 17 | function lil_to_js(x){ 18 | if(deck_is(x)||card_is(x)||widget_is(x))return x 19 | if(lin(x))return ln(x) 20 | if(lis(x))return ls(x) 21 | if(lil(x))return ll(x).slice(0).map(lil_to_js) 22 | if(lid(x))return x.k.reduce((r,k,i)=>{r[ls(k)]=lil_to_js(x.v[i]);return r},{}) 23 | if(array_is(x))return x.slice?null: x.data 24 | if(lion(x))return (...args)=>{ 25 | // note that this ignores quota, and can therefore lock up Decker if misused! 26 | const p=lmblk();blk_lit(p,x),blk_lit(p,js_to_lil(args)),blk_op(p,op.CALL) 27 | const e=lmenv();pushstate(e),issue(e,p);while(running())runop();const r=arg();popstate() 28 | return lil_to_js(r) 29 | } 30 | return null 31 | } 32 | interface_danger=lmi((self,i,x)=>{ 33 | if(ikey(i,'js'))return lmnat(args=>{ 34 | try{ 35 | let r=args[0]==undefined?null:eval(ls(args[0])) 36 | if(('function'==typeof r)&&(args.length>1)){r=r.apply(null,args.slice(1).map(lil_to_js))} 37 | return js_to_lil(r) 38 | }catch(e){console.log('danger.js[] error',e);return NIL} 39 | }) 40 | return x?x:NIL 41 | },'danger') 42 | ext_add_constant=(k,v)=>{ext_constants[k]=js_to_lil(v)} 43 | endanger=_=>{ext_constants.danger=interface_danger} 44 | if(DANGEROUS)endanger() 45 | 46 | -------------------------------------------------------------------------------- /js/decker.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 15 | -------------------------------------------------------------------------------- /js/repl.js: -------------------------------------------------------------------------------- 1 | 2 | readBinaryFile=path=>{try{const b=require('fs').readFileSync(path);return array_make(b.length,'u8',0,new Uint8Array(b))}catch(e){return array_make(0,'u8',0)}} 3 | writeBinaryFile=(path,x)=>require('fs').writeFileSync(path,Buffer.from(x.data)) 4 | readTextFile=path=>clchars(require('fs').readFileSync(path,{encoding:'utf8'}).replace(/\uFEFF/g, '')) 5 | writeTextFile=(path,text)=>require('fs').writeFileSync(path,text,{encoding:'utf8'}) 6 | go_notify=(deck,x,t)=>{/*console.log('go notify',x,t)*/} 7 | field_notify=(field)=>{/*console.log('field notify',x,t)*/} 8 | const env=lmenv() 9 | n_play=([x])=>NIL 10 | n_show=z=>{console.log(z.map(x=>show(x,z.length==1)).join(' '));return z[0]} 11 | n_print=x=>{console.log(ls(x.length>1?dyad.format(x[0],lml(x.slice(1))):x[0]));return NIL} 12 | n_alert =([x])=>ONE 13 | n_save =([x])=>NIL 14 | n_open =( )=>lms('') 15 | n_panic =( )=>NIL 16 | is_fullscreen=_=>0 17 | set_fullscreen=_=>0 18 | run =prog=>{pushstate(env),issue(env,prog);while(running())runop();const r=arg();return popstate(),r} 19 | env.local('read',lmnat(([x,y])=> 20 | y&&ls(y)=='array'?readBinaryFile(ls(x)): 21 | ls(x).toLowerCase().endsWith('.gif')?readgif(readBinaryFile(ls(x)).data,ls(y)): 22 | ls(x).toLowerCase().endsWith('.deck')?deck_read(readTextFile(ls(x))): 23 | lms(readTextFile(ls(x))) 24 | )) 25 | env.local('newdeck',lmnat(([x])=>deck_read(lis(x)?ls(x):''))) 26 | env.local('write',lmnat(([x,y])=> 27 | array_is(y)?writeBinaryFile(ls(x),y): 28 | deck_is(y)?writeTextFile(ls(x),deck_write(y,/\.html$/i.test(ls(x)))): 29 | writeTextFile(ls(x),ls(y)) 30 | )) 31 | env.local('exit',lmnat(([x])=>process.exit(ln(x)))) 32 | env.local('print',lmnat(n_print)) 33 | env.local('show',lmnat(n_show)) 34 | env.local('shell',lmnat(([x])=>{ 35 | let o='',e=0;try{o=require('node:child_process').execSync(ls(x))}catch(err){o=err.stdout.toString(),e=err.status} 36 | return lmd(['exit','out'].map(lms),[lmn(e),lms(o)]) 37 | })) 38 | env.local('dir',lmnat(([x])=>{ 39 | const path=x?ls(x):'.', fs=require('fs'), pt=require('path') 40 | const rd=[],rn=[],rt=[],r=lmt();tab_set(r,'dir',rd),tab_set(r,'name',rn),tab_set(r,'type',rt) 41 | fs.readdirSync(path).map(name=>{ 42 | rd.push(lmn(fs.lstatSync(`${path}/${name}`).isDirectory())) 43 | rn.push(lms(name)),rt.push(lms(pt.extname(name))) 44 | });return r 45 | })) 46 | env.local('random',lmnat(n_random)) 47 | env.local('array',lmnat(n_array)) 48 | env.local('image',lmnat(n_image)) 49 | env.local('sound',lmnat(n_sound)) 50 | env.local('eval',lmnat(n_eval)) 51 | env.local('writecsv',lmnat(n_writecsv)) 52 | env.local('readcsv',lmnat(n_readcsv)) 53 | env.local('writexml',lmnat(n_writexml)) 54 | env.local('readxml',lmnat(n_readxml)) 55 | env.local('args',lml(process.argv.slice(1).map(lms))) 56 | constants(env) 57 | if(process.argv.length>=3){ 58 | try{run(parse(readTextFile(process.argv[2])))} 59 | catch(e){console.error('x' in e?`(${e.r+1}:${e.c+1}) ${e.x}`:e),process.exit(1)} 60 | process.exit(0) 61 | } 62 | rl=require('readline').createInterface({input:process.stdin,output:process.stdout,prompt:' '});rl.prompt() 63 | rl.on('close',_=>(console.log('\n'),process.exit(0))) 64 | rl.on('line',line=>{ 65 | try{if(line.trim()!=0){const x=run(parse(line.trim()));env.local('_',x);console.log(show(x,true))}} 66 | catch(e){console.error('x' in e?`${' '.repeat(e.c+1)}^\n${e.x}\n`:e)} 67 | rl.prompt() 68 | }) 69 | -------------------------------------------------------------------------------- /scripts/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # install lilt, decker and lil support resources 3 | 4 | DESTDIR=${DESTDIR:=""} 5 | PREFIX=${PREFIX:="/usr/local"} 6 | INSTALLDIR="${DESTDIR}${PREFIX}/bin/" 7 | 8 | # install binaries 9 | if [[ ! -f "./c/build/lilt" ]]; then 10 | echo "skipping lilt. no binary found. build first with 'make lilt'" 11 | fi 12 | if [ -f "./c/build/lilt" ]; then 13 | echo "copying lilt to ${INSTALLDIR}..." 14 | mkdir -p "${INSTALLDIR}" 15 | install -C ./c/build/lilt "${INSTALLDIR}lilt" 16 | fi 17 | if [[ ! -f "./c/build/decker" ]]; then 18 | echo "skipping decker. no binary found. build first with 'make decker'" 19 | fi 20 | if [ -f "./c/build/decker" ]; then 21 | echo "copying decker to ${INSTALLDIR}..." 22 | mkdir -p "${INSTALLDIR}" 23 | install -C ./c/build/decker "${INSTALLDIR}decker" 24 | fi 25 | 26 | # install syntax profiles 27 | SUBLIME=~/Library/Application\ Support/Sublime\ Text/ 28 | if [ -d "$SUBLIME" ]; then 29 | echo "installing lil syntax profile for Sublime Text 4 (mac)..." 30 | mkdir -p "${SUBLIME}/Packages" 31 | cp -r syntax/Lil.tmbundle "${SUBLIME}/Packages" 32 | else 33 | SUBLIME=~/Library/Application\ Support/Sublime\ Text\ 3/ 34 | if [ -d "$SUBLIME" ]; then 35 | echo "installing lil syntax profile for Sublime Text 3 (mac)..." 36 | mkdir -p "${SUBLIME}/Packages" 37 | cp -r syntax/Lil.tmbundle "${SUBLIME}/Packages" 38 | fi 39 | fi 40 | SUBLIME=~/.config/sublime-text-3/ 41 | if [ -d "$SUBLIME" ]; then 42 | echo "installing lil syntax profile for Sublime Text (linux)..." 43 | mkdir -p "${SUBLIME}/Packages" 44 | cp -r syntax/Lil.tmbundle "${SUBLIME}/Packages" 45 | fi 46 | 47 | VIM=~/.vim/ 48 | if [ -d "$VIM" ]; then 49 | echo "installing lil syntax profile for vim in ${VIM}..." 50 | mkdir -p "${VIM}ftdetect" 51 | mkdir -p "${VIM}syntax" 52 | cp syntax/vim/ftdetect/lil.vim "${VIM}ftdetect/lil.vim" 53 | cp syntax/vim/syntax/lil.vim "${VIM}syntax/lil.vim" 54 | fi 55 | 56 | echo "done." 57 | -------------------------------------------------------------------------------- /scripts/lildoc.lil: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # 3 | # LilDoc 4 | # 5 | # A Lil implementation of a sufficient subset of a markdown processor 6 | # to render all of Decker and Lil's documentation, 7 | # with a few handy customizations. 8 | # 9 | ############################################################################### 10 | 11 | styles:" 12 | body{margin:1em 5em 5em 3em;} 13 | h1,h2,figcaption{font-family:Helvetica Neue,Arial,sans-serif;} 14 | h1{color:#21183c;font-weight:700;} 15 | h2{color:#21183c;font-weight:300;margin-top:1.5em;} 16 | pre,code{background-color:Gainsboro;tab-size:2;font-size:large;} 17 | pre{margin:0 .5em;padding:.5em;border:1px solid #aaa;border-radius:5px;} 18 | li{margin:1em 0;} 19 | table{margin:0 .5em;border-collapse:collapse;border:1px solid #aaa;} 20 | td{padding:5px;}th{padding:10px;border-bottom:1px solid #aaa;background-color:Gainsboro;} 21 | td,th:not(:first-child){border-left:1px solid #aaa;} 22 | figure{display:block;text-align:center;} 23 | .TOC li{margin:0;} 24 | 25 | pre.lil{background:#272822;color:#f8f8f2;padding:.5em;border:1px;} 26 | pre.lil .comment {color:#75715e;} 27 | pre.lil .string {color:#e6db74;} 28 | pre.lil .escape {color:#ae81ff;} 29 | pre.lil .keyword {color:#f92672;} 30 | pre.lil .verb {color:#66d9ef;} 31 | pre.lil .call {color:#66d9ef;} 32 | pre.lil .funcname{color:#a6e22e;} 33 | " 34 | 35 | ############################################################################### 36 | # 37 | # Lil syntax highlighter: 38 | # 39 | ############################################################################### 40 | 41 | keywords:"|"split"while|each|send|on|if|elseif|else|end|do|with|local|select|extract|update|insert|into|from|where|by|orderby|asc|desc" 42 | monads:"|"split"floor|cos|sin|tan|exp|ln|sqrt|unit|mag|heading|sum|prod|raze|max|min|count|first|last|keys|range|list|typeof|flip|rows|cols|table" 43 | dyads:"|"split"split|fuse|dict|take|drop|limit|in|unless|join|cross|parse|format|like|window|fill" 44 | verbs:monads,dyads 45 | escapes:"\\n","\\\"","\\\\" 46 | 47 | # tokenize the input file into an rtext-compatible table 48 | # this gives us several useful output options, 49 | # and also makes it easy to coalesce adjacent runs together: 50 | 51 | on chunk type x do 52 | insert text font arg with x type "" end 53 | end 54 | 55 | on highlight_lil text do 56 | r:rtext.cat[] i:fn:0 57 | while i\n\n" format styles),markdown[file] 323 | r:write[dst result] 324 | if !r error["unable to write to %s!" dst] end 325 | end 326 | exit[0] 327 | 328 | -------------------------------------------------------------------------------- /scripts/resources.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # bundle the HTML/JS of the web-decker implementation as a c header file, 3 | # for inclusion with the native versions of decker and lilt. 4 | 5 | set -e 6 | 7 | DECK=$1 8 | if test -z "$DECK" ; then 9 | echo "Missing source deck">&2 10 | exit 1 11 | fi 12 | 13 | DST=c/resources.h 14 | 15 | printf "%s\n" "// auto-generated from web-decker source!" > $DST 16 | xxd -i js/lil.js >> $DST 17 | xxd -i js/danger.js >> $DST 18 | xxd -i js/decker.html >> $DST 19 | xxd -i js/decker.js >> $DST 20 | xxd -i $DECK >> $DST 21 | -------------------------------------------------------------------------------- /scripts/test_interpreter.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # integration tests for lil based on 3 | # a collection of blessed reference outputs and error logs. 4 | 5 | INTERPRETER=$1 6 | echo "running tests against ${INTERPRETER}..." 7 | 8 | positive_test() { 9 | rm -rf temp.out 10 | rm -rf temp.err 11 | $INTERPRETER "$1" >> temp.out 2>> temp.err 12 | ec=$? 13 | if [ -s temp.err ]; then 14 | echo "errors running test ${1}:" 15 | cat temp.err 16 | exit 1 17 | elif [ $ec != 0 ]; then 18 | echo "crash running test ${1}." 19 | exit 1 20 | elif [ ! -f temp.out ]; then 21 | echo "no output for ${1}." 22 | exit 1 23 | elif ! cmp -s temp.out $2; then 24 | echo "reference input doesn't match for ${1}:" 25 | diff --strip-trailing-cr $2 temp.out 26 | exit 1 27 | fi 28 | } 29 | 30 | negative_test() { 31 | rm -rf temp.out 32 | rm -rf temp.err 33 | $INTERPRETER "$1" >> temp.out 2>> temp.err 34 | ec=$? 35 | if ! diff -q --strip-trailing-cr temp.err $2; then 36 | echo "reference error doesn't match for ${1}:" 37 | echo "expected:" 38 | cat $2 39 | echo "got:" 40 | cat temp.err 41 | exit 1 42 | fi 43 | } 44 | 45 | for filename in tests/*.lil; do 46 | if [ -f ${filename%.*}.out ]; then 47 | positive_test $filename ${filename%.*}.out 48 | elif [ -f ${filename%.*}.err ]; then 49 | negative_test $filename ${filename%.*}.err 50 | else 51 | echo "no reference file found for test ${filename}!" 52 | exit 1 53 | fi 54 | done 55 | echo "all interpreter tests passed." 56 | rm -rf temp.out 57 | rm -rf temp.err 58 | -------------------------------------------------------------------------------- /scripts/uninstall.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # uninstall lilt, and lil support resources 3 | 4 | DESTDIR="" 5 | PREFIX="/usr/local" 6 | INSTALLDIR="${DESTDIR}${PREFIX}/bin/" 7 | 8 | sudo rm -f "${INSTALLDIR}lilt" 9 | sudo rm -f "${INSTALLDIR}decker" 10 | 11 | rm -rf ~/Library/Application\ Support/Sublime\ Text/Packages/Lil.tmbundle 12 | rm -rf ~/Library/Application\ Support/Sublime\ Text\ 3/Packages/Lil.tmbundle 13 | rm -rf ~/.config/sublime-text-3/Lil.tmbundle 14 | 15 | rm -f ~/.vim/ftdetect/lil.vim 16 | rm -f ~/.vim/syntax/lil.vim 17 | 18 | echo "done." 19 | -------------------------------------------------------------------------------- /scripts/web_decker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | # build the browser version of decker 3 | 4 | set -e 5 | 6 | SRC=${1?"Missing source deck"} 7 | DST=${2?"Missing destination"} 8 | VERSION=${3?"Missing version number"} 9 | DANGER=${4:-0} 10 | 11 | echo -e "\n" >> $DST 14 | cat js/decker.html >> $DST 15 | echo -e "" >> $DST 22 | 23 | echo "built web-decker: ${DST}" 24 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | with import {}; 2 | stdenv.mkDerivation { 3 | name = "decker-build-env"; 4 | buildInputs = [ unixtools.xxd SDL2 SDL2_image ]; 5 | } 6 | -------------------------------------------------------------------------------- /syntax/Lil.tmbundle/Deck.sublime-syntax: -------------------------------------------------------------------------------- 1 | %YAML 1.2 2 | --- 3 | name: Deck 4 | scope: source.deck 5 | 6 | file_extensions: 7 | - deck 8 | 9 | contexts: 10 | main: 11 | - match: ^\# 12 | scope: comment.deck 13 | push: 14 | - meta_scope: comment.line.deck 15 | - match: \n 16 | pop: true 17 | 18 | - match: ^\{(deck|sounds|fonts|widgets|data)\} 19 | scope: markup.heading 20 | 21 | - match: "^\\{card:([^\\}]+)\\}" 22 | scope: markup.heading 23 | captures: 24 | 1: entity.name.section 25 | 26 | - match: "^\\{script(:([^\\}]+))?\\}" 27 | scope: markup.heading 28 | captures: 29 | 1: meta.annotation.identifier 30 | embed: scope:source.lil 31 | escape: ^\{end\} 32 | escape_captures: 33 | 0: markup.heading 34 | 35 | - match: "^\\{module:([^\\}]+)\\}" 36 | scope: markup.heading 37 | captures: 38 | 1: meta.annotation.identifier 39 | 40 | - match: "^\\{contraption:([^\\}]+)\\}" 41 | scope: markup.heading 42 | captures: 43 | 1: meta.annotation.identifier 44 | -------------------------------------------------------------------------------- /syntax/Lil.tmbundle/Preferences/Comments.tmPreferences: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | name 6 | Comments 7 | scope 8 | source.lil 9 | settings 10 | 11 | shellVariables 12 | 13 | 14 | name 15 | TM_COMMENT_START 16 | value 17 | # 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /syntax/Lil.tmbundle/Syntaxes/Lil.tmLanguage: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | fileTypes 6 | 7 | lil 8 | 9 | name 10 | Lil 11 | patterns 12 | 13 | 14 | comment 15 | Single line comments 16 | match 17 | \#.* 18 | name 19 | comment.line.number-sign.lil 20 | 21 | 22 | comment 23 | Control flow keywords 24 | match 25 | (?<=^|[^a-zA-Z_?\d.])(?:if|else|elseif|end|while|each|send|on|do|select|extract|update|insert|into|from|where|by|orderby|asc|desc|with|local)(?=$|[^a-zA-Z_?\d]) 26 | name 27 | keyword.control.lil 28 | 29 | 30 | comment 31 | Primitive operators 32 | match 33 | (?<=^|[^a-zA-Z_?\d.])(?:floor|cos|sin|tan|exp|ln|sqrt|sum|prod|min|max|raze|count|first|last|range|keys|list|table|rows|cols|split|fuse|cat|dict|take|drop|in|at|join|cross|parse|format|typeof|unless|flip|limit|mag|unit|heading|like|window|fill)(?=$|[^a-zA-Z_?\d]) 34 | name 35 | support.function.lil 36 | 37 | 38 | comment 39 | Subroutine Calls 40 | match 41 | ([a-zA-Z_?][a-zA-Z_?\d]*)\s*(?=\[) 42 | name 43 | support.function.call.lil 44 | 45 | 46 | comment 47 | Subroutine Defs 48 | match 49 | (?<=on)\s+([a-zA-Z_?][a-zA-Z_?\d.]*) 50 | name 51 | entity.name.function.lil 52 | 53 | 54 | comment 55 | String Literals 56 | begin 57 | " 58 | end 59 | " 60 | name 61 | string.quoted.double.lil 62 | patterns 63 | 64 | 65 | match 66 | \\[n\\"] 67 | name 68 | constant.character.escape.lil 69 | 70 | 71 | 72 | 73 | scopeName 74 | source.lil 75 | uuid 76 | 08550b99-efd5-40f6-a6b6-0b812d0d33e1 77 | 78 | 79 | -------------------------------------------------------------------------------- /syntax/Lil.tmbundle/info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | description 6 | Lil 7 | name 8 | Lil 9 | uuid 10 | 08550b99-efd5-40f6-a6b6-0b812d0d33e1 11 | 12 | 13 | -------------------------------------------------------------------------------- /syntax/codemirror/lilmode.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Lil Syntax mode for CodeMirror: 3 | * https://codemirror.net 4 | **/ 5 | 6 | CodeMirror.defineMode('lil',(config,options)=>{ 7 | const KEYWORDS={ 8 | while:1,each:1,send:1,on:1,if:1,elseif:1,else:1,end:1,do:1,with:1,local:1,select:1,extract:1,update:1,insert:1, 9 | into:1,from:1,where:1,by:1,orderby:1,asc:1,desc:1, 10 | } 11 | const VERBS={ 12 | // monads 13 | floor:1,cos:1,sin:1,tan:1,exp:1,ln:1,sqrt:1,unit:1,mag:1,heading:1,sum:1,prod:1,raze:1,max:1,min:1,count:1, 14 | first:1,last:1,keys:1,range:1,list:1,typeof:1,flip:1,rows:1,cols:1,table:1, 15 | // dyads 16 | split:1,fuse:1,dict:1,take:1,drop:1,limit:1,in:1,unless:1,join:1,cross:1,parse:1,format:1,like:1,window:1,fill:1, 17 | } 18 | return{ 19 | lineComment:'# ', 20 | startState:_=>({mode:0}), 21 | copyState:x=>({mode:x.mode}), 22 | token:(stream,state)=>{ 23 | // lil supports multiline strings, which require some special handling in codemirror: 24 | if(state.mode=='str'){ 25 | if(stream.match('"')){state.mode=0;return'string'} 26 | if(stream.match(/\\[\\n"]/))return'atom' // valid escape sequence 27 | if(stream.match('\\'))return'string' // bogus escape sequence 28 | stream.match(/^[^"\\]*/);return'string' 29 | } 30 | if(stream.match('"')){state.mode='str';return'string'} 31 | // line comments, keywords/builtins, function definitions: 32 | if(stream.match('#')||stream.match(/[\t\n\r ]+\//))return stream.skipToEnd(),'comment' 33 | const n=stream.match(/^[a-zA-Z_?][a-zA-Z0-9_?]*/) 34 | if(state.mode=='fname'&&n){state.mode=0;return 'property'} 35 | if(n=='on'){state.mode='fname'} 36 | if(KEYWORDS[n])return 'keyword' 37 | if(VERBS[n])return 'variable-2' 38 | // calls/indexing: 39 | if(n&&stream.match(/^[ \t]*\[/,false))return 'variable-2' 40 | if(!n)stream.next() 41 | return null 42 | } 43 | } 44 | }) 45 | -------------------------------------------------------------------------------- /syntax/emacs/lil-mode.el: -------------------------------------------------------------------------------- 1 | ; lil-mode for emacs 2 | ; (c) 2024 tangentstorm. 3 | ; available for use under the MIT license 4 | (require 'generic-x) 5 | (defun wds (xs) 6 | (string-join 7 | (list "\\<\\(" 8 | (string-join (mapcar 'symbol-name xs) "\\|") 9 | "\\)\\>") "")) 10 | (define-generic-mode 'lil-mode 11 | '("#") ; comments 12 | '("while" "end" "if" "each" "else" "elseif" "on" "do" "in" 13 | "send" "local" 14 | "extract" "select" "update" "insert" 15 | "where" "by" "orderby" "asc" "desc" "group" "from") 16 | `(("^#.*" . 'font-lock-comment-face) 17 | ("[-!+*,&|/%^<>=~@:]" . 'font-lock-builtin-face) 18 | (,(wds '(floor cos sin tan exp ln sqrt 19 | sum prod raze min max 20 | typeof count first last 21 | range keys list flip 22 | rows cols table mag heading unit 23 | split fuse like dict take window 24 | drop limit in unless join cross 25 | parse format fill)) 26 | . 'font-lock-builtin-face) 27 | (,(wds '(show panic print play go transition brush 28 | sleep array image sound newdeck eval random 29 | readcsv writecsv readxml writexml 30 | alert read write input error dir 31 | path exit shell import)) 32 | . 'font-lock-function-name-face) 33 | ("\\<[0-9]+\\>" . 'font-lock-constant-face)) 34 | '("\\.lil$") ; which files 35 | nil ; other functions 36 | "mode for editing lil files") 37 | -------------------------------------------------------------------------------- /syntax/vim/ftdetect/lil.vim: -------------------------------------------------------------------------------- 1 | au BufRead,BufNewFile *.lil set filetype=lil 2 | -------------------------------------------------------------------------------- /syntax/vim/syntax/lil.vim: -------------------------------------------------------------------------------- 1 | " Vim syntax file 2 | " Language: Lil (Decker) 3 | " Filenames: *.lil 4 | " Maintainer: John Earnest 5 | 6 | if exists("b:current_syntax") 7 | finish 8 | endif 9 | 10 | syn match lilComment "#.*$" 11 | syn match lilEsc contained /\\[n"\\]/ 12 | syn region lilStr start=/"/ end=/"/ skip=/\\"/ contains=lilEsc 13 | 14 | syn match lilName contained /[_?a-zA-Z][_?a-zA-Z0-9]*/ 15 | syn keyword lilOn on nextgroup=lilName skipwhite 16 | 17 | syn keyword lilControl if else elseif end while each send do select extract update insert into from where by orderby asc desc with local 18 | syn keyword lilPrims floor cos sin tan exp ln sqrt count sum prod min max raze first last range keys list table rows cols mag unit heading 19 | syn keyword lilPrims split fuse cat dict take drop in at join cross parse format typeof unless flip limit like window fill 20 | 21 | hi def link lilComment Comment 22 | hi def link lilName Function 23 | hi def link lilEsc SpecialChar 24 | hi def link lilStr String 25 | hi def link lilOn Statement 26 | hi def link lilControl Repeat 27 | hi def link lilPrims Operator 28 | -------------------------------------------------------------------------------- /tests/amend.lil: -------------------------------------------------------------------------------- 1 | 2 | # exercise various quirks and surprises in amending (indexed assignment) 3 | 4 | # simple bare assignment (not an amend): 5 | print["bare:"] 6 | zami:999 7 | show[zami] 8 | 9 | # simple indexed assignment: 10 | print["\nindex:"] 11 | foo.bar:"A" 12 | show[foo] 13 | show[foo.quux:"B"] 14 | show[foo] 15 | 16 | # note: what looks like subscripting 17 | # on a function is actually application: 18 | on func x do 19 | ":" fuse x,x 20 | end 21 | print["\nsubscript:"] 22 | show[func.baz] 23 | 24 | # amending a bare function result: 25 | on get x do 26 | print["fetch foo..."] 27 | foo 28 | end 29 | print["\nvia function:"] 30 | show[get[].bar:"D"] # anonymous amend of 'foo.bar' 31 | show[get] # we *must not* reassign the source 32 | show[foo] # nor should we modify foo in place or reassign it 33 | 34 | # amending an indexed function result: 35 | contain.key:get 36 | print["\nvia indexed function:"] 37 | show[contain] 38 | show[contain.key[].bar:"C"] # anonymous amend of 'foo.bar' 39 | show[contain] # we *must not* reassign the source 40 | show[foo] # nor should we modify foo in place or reassign it 41 | contain.key:37 show[contain] # reassign a member that happens to be a function 42 | 43 | # amending a bare function result that is an interface: 44 | on getsys do 45 | print["access sys..."] 46 | sys 47 | end 48 | sys.seed:9876 49 | print["\ninterface via function:"] 50 | show[getsys[].seed] # accessing an interface member via a function call 51 | show[getsys[].seed:12345] # amending an interface member via a function call 52 | show[sys.seed getsys] # make sure the write happened and we didn't blow away the function 53 | 54 | # amending tables gets moderately complicated! 55 | print["\namending tables:"] 56 | t:insert fruit price amount with 57 | "apple" 1.00 1 58 | "cherry" 0.35 15 59 | "banana" 0.75 2 60 | "durian" 2.99 5 61 | "elderberry" 0.92 1 62 | end 63 | 64 | # these two specific alternatives are equivalent, following naturally from indexing: 65 | a:t 66 | a.fruit[2]:"golfball" # amend single cell by column, row 67 | show[a] 68 | b:t 69 | b[2].fruit:"dagger" # amend single cell by row, column 70 | show[b] 71 | 72 | # column-wise stuff: 73 | # the common-sense rule is we ONLY modify the contents of the indicated column. 74 | c:t 75 | c.amount:99,88,77,66,55 # amend entire column with a list: replace old column 76 | show[c] 77 | d:t 78 | d.zoot:11,22,33,44,55 # amend in novel column: creates a new column! 79 | show[d] 80 | d:t 81 | d.zoot:11,22,33 # amend in short column: extends with nils 82 | show[d] 83 | d:t 84 | d.zoot:11,22,33,44,55,66,77 # amend in long column: truncate to current rowcount 85 | show[d] 86 | d:t 87 | d.zoot:123 # spread non-lists when amending columns 88 | d.zami:"foof" # (see above) 89 | show[d] 90 | 91 | # row-wise stuff: 92 | # as above, the common-sense rule is we ONLY modify the contents of the indicated row. 93 | # unlike amending elements of a list, we don't handle any in-between or off-the-end row indices to insert rows. 94 | e:t 95 | val:("amount","fruit")dict(12.3,"rat") 96 | e[1]:val # amend row with a dictionary (which may not contain all keys, leaving missing entries unchanged) 97 | show[e] 98 | e:t 99 | e[-1].fruit:"orange" # amending row at an invalid index is ignored 100 | e[ 5].fruit:"kumquat" # (see above) 101 | show[e~t] 102 | e:t 103 | e[3].bologna:99999 # amending row at invalid column names is likewise ignored 104 | show[e~t] 105 | e:t 106 | e[3]:"five" # spread non-dicts when amending rows (this will *rarely* make sense to do!!!) 107 | show[e] 108 | -------------------------------------------------------------------------------- /tests/amend.out: -------------------------------------------------------------------------------- 1 | bare: 2 | 999 3 | 4 | index: 5 | {"bar":"A"} 6 | {"bar":"A","quux":"B"} 7 | {"bar":"A","quux":"B"} 8 | 9 | subscript: 10 | "baz:baz" 11 | 12 | via function: 13 | fetch foo... 14 | {"bar":"D","quux":"B"} 15 | on get x do ... end 16 | {"bar":"A","quux":"B"} 17 | 18 | via indexed function: 19 | {"key":on get x do ... end} 20 | fetch foo... 21 | {"bar":"C","quux":"B"} 22 | {"key":on get x do ... end} 23 | {"bar":"A","quux":"B"} 24 | {"key":37} 25 | 26 | interface via function: 27 | access sys... 28 | 9876 29 | access sys... 30 | 12345 31 | 12345 on getsys do ... end 32 | 33 | amending tables: 34 | +--------------+-------+--------+ 35 | | fruit | price | amount | 36 | +--------------+-------+--------+ 37 | | "apple" | 1 | 1 | 38 | | "cherry" | 0.35 | 15 | 39 | | "golfball" | 0.75 | 2 | 40 | | "durian" | 2.99 | 5 | 41 | | "elderberry" | 0.92 | 1 | 42 | +--------------+-------+--------+ 43 | +--------------+-------+--------+ 44 | | fruit | price | amount | 45 | +--------------+-------+--------+ 46 | | "apple" | 1 | 1 | 47 | | "cherry" | 0.35 | 15 | 48 | | "dagger" | 0.75 | 2 | 49 | | "durian" | 2.99 | 5 | 50 | | "elderberry" | 0.92 | 1 | 51 | +--------------+-------+--------+ 52 | +--------------+-------+--------+ 53 | | fruit | price | amount | 54 | +--------------+-------+--------+ 55 | | "apple" | 1 | 99 | 56 | | "cherry" | 0.35 | 88 | 57 | | "banana" | 0.75 | 77 | 58 | | "durian" | 2.99 | 66 | 59 | | "elderberry" | 0.92 | 55 | 60 | +--------------+-------+--------+ 61 | +--------------+-------+--------+------+ 62 | | fruit | price | amount | zoot | 63 | +--------------+-------+--------+------+ 64 | | "apple" | 1 | 1 | 11 | 65 | | "cherry" | 0.35 | 15 | 22 | 66 | | "banana" | 0.75 | 2 | 33 | 67 | | "durian" | 2.99 | 5 | 44 | 68 | | "elderberry" | 0.92 | 1 | 55 | 69 | +--------------+-------+--------+------+ 70 | +--------------+-------+--------+------+ 71 | | fruit | price | amount | zoot | 72 | +--------------+-------+--------+------+ 73 | | "apple" | 1 | 1 | 11 | 74 | | "cherry" | 0.35 | 15 | 22 | 75 | | "banana" | 0.75 | 2 | 33 | 76 | | "durian" | 2.99 | 5 | nil | 77 | | "elderberry" | 0.92 | 1 | nil | 78 | +--------------+-------+--------+------+ 79 | +--------------+-------+--------+------+ 80 | | fruit | price | amount | zoot | 81 | +--------------+-------+--------+------+ 82 | | "apple" | 1 | 1 | 11 | 83 | | "cherry" | 0.35 | 15 | 22 | 84 | | "banana" | 0.75 | 2 | 33 | 85 | | "durian" | 2.99 | 5 | 44 | 86 | | "elderberry" | 0.92 | 1 | 55 | 87 | +--------------+-------+--------+------+ 88 | +--------------+-------+--------+------+--------+ 89 | | fruit | price | amount | zoot | zami | 90 | +--------------+-------+--------+------+--------+ 91 | | "apple" | 1 | 1 | 123 | "foof" | 92 | | "cherry" | 0.35 | 15 | 123 | "foof" | 93 | | "banana" | 0.75 | 2 | 123 | "foof" | 94 | | "durian" | 2.99 | 5 | 123 | "foof" | 95 | | "elderberry" | 0.92 | 1 | 123 | "foof" | 96 | +--------------+-------+--------+------+--------+ 97 | +--------------+-------+--------+ 98 | | fruit | price | amount | 99 | +--------------+-------+--------+ 100 | | "apple" | 1 | 1 | 101 | | "rat" | 0.35 | 12.3 | 102 | | "banana" | 0.75 | 2 | 103 | | "durian" | 2.99 | 5 | 104 | | "elderberry" | 0.92 | 1 | 105 | +--------------+-------+--------+ 106 | 1 107 | 1 108 | +--------------+--------+--------+ 109 | | fruit | price | amount | 110 | +--------------+--------+--------+ 111 | | "apple" | 1 | 1 | 112 | | "cherry" | 0.35 | 15 | 113 | | "banana" | 0.75 | 2 | 114 | | "five" | "five" | "five" | 115 | | "elderberry" | 0.92 | 1 | 116 | +--------------+--------+--------+ 117 | -------------------------------------------------------------------------------- /tests/at.lil: -------------------------------------------------------------------------------- 1 | 2 | # test suite for the @ pseudo-operator. 3 | 4 | # @ performs multi-indexing 5 | show[(11,22,33,44) @ 0,1,0,2,0] 6 | 7 | # and dictionary extraction 8 | show[(11,22,33,44) @ ("a","b") dict 2,0] 9 | 10 | # in general, `x @ y` is equivalent to `each v in y x[v] end` 11 | on step x y z do 12 | (x,y,z) 13 | end 14 | show[step @ ("a","b") dict 11,22] 15 | 16 | # but note that we do not actually shadow a local v! 17 | local v:100 18 | show[(on tmp x do x+v end) @ 11,22,33] 19 | 20 | # ...with the exception that `x` is evaluated only once: 21 | on fetch do 22 | print["fetched"] 23 | on apply x do 24 | "applied <%s>" format x 25 | end 26 | end 27 | show[fetch[] @ 10+range 3] 28 | 29 | # chained application 30 | on foo x do 31 | print["foo %j" x] 32 | x+500,1000 33 | end 34 | on bar x do 35 | print["bar %j" x] 36 | 200,x 37 | end 38 | show[ bar @ range 3] 39 | show[foo @ bar @ range 3] 40 | 41 | # mapping with monadic primitives 42 | show[count @ "one","two","three","four"] 43 | show[last @ ((list 11,22),(list 33,44,55))] 44 | show[last @ (11,22,33,44) dict "one","two","three","four"] 45 | 46 | # chained @ with monadic primitives 47 | show[count @ @ ((list "one","two"),(list "three","four"))] 48 | show[first @ @ ((list "one","two"),(list "three","four"))] 49 | show[count @ ((list(list "one","two"),(list "three","four","five")),(list(list "six","seven")))] 50 | show[count @ @ ((list(list "one","two"),(list "three","four","five")),(list(list "six","seven")))] 51 | show[count @ @ @ ((list(list "one","two"),(list "three","four","five")),(list(list "six","seven")))] 52 | 53 | # chained @ with monadic functions 54 | on fc x do (first x),count x end 55 | show[fc @ ((list(list "one","two"),(list "three","four","five")),(list(list "six","seven")))] 56 | show[fc @ @ ((list(list "one","two"),(list "three","four","five")),(list(list "six","seven")))] 57 | show[fc @ @ @ ((list(list "one","two"),(list "three","four","five")),(list(list "six","seven")))] 58 | -------------------------------------------------------------------------------- /tests/at.out: -------------------------------------------------------------------------------- 1 | (11,22,11,33,11) 2 | {"a":33,"b":11} 3 | {"a":(11,nil,nil),"b":(22,nil,nil)} 4 | (111,122,133) 5 | fetched 6 | ("applied <10>","applied <11>","applied <12>") 7 | bar 0 8 | bar 1 9 | bar 2 10 | ((200,0),(200,1),(200,2)) 11 | bar 0 12 | bar 1 13 | bar 2 14 | foo [200,0] 15 | foo [200,1] 16 | foo [200,2] 17 | ((700,1000),(700,1001),(700,1002)) 18 | (3,3,5,4) 19 | (22,55) 20 | {11:"e",22:"o",33:"e",44:"r"} 21 | ((3,3),(5,4)) 22 | (("o","t"),("t","f")) 23 | (2,1) 24 | ((2,3),(2)) 25 | (((3,3),(5,4,4)),((3,5))) 26 | (("one","two",2),("six","seven",1)) 27 | ((("one",2),("three",3)),(("six",2))) 28 | (((("o",3),("t",3)),(("t",5),("f",4),("f",4))),((("s",3),("s",5)))) 29 | -------------------------------------------------------------------------------- /tests/badlit.lil: -------------------------------------------------------------------------------- 1 | 2 | print["string literal with ‘single’ curly-quotes, 3 | a tab: , an accented letter: ñ 4 | and a horrifying abomination:☺"] 5 | -------------------------------------------------------------------------------- /tests/badlit.out: -------------------------------------------------------------------------------- 1 | string literal with 'single' curly-quotes, 2 | a tab: , an accented letter: ñ 3 | and a horrifying abomination:� 4 | -------------------------------------------------------------------------------- /tests/big.lil: -------------------------------------------------------------------------------- 1 | # test large number handling 2 | 3 | # note: lil numbers are ultimately backed by doubles, which are nothing but trouble. 4 | # this test is not necessarily about *accuracy*, it is about *consistency*. 5 | 6 | on testnum x do 7 | show[x] 8 | show[fmt:"%j" format x] 9 | show[fus:"" fuse x] 10 | show[fmt~fus] 11 | end 12 | 13 | # a very cool number 14 | testnum[-3.1337] 15 | 16 | # a pretty big number (bigger than an int, for example) 17 | testnum[100000000000] 18 | 19 | # the biggest precise int in an ieee-754 20 | testnum[9007199254740992] 21 | 22 | -------------------------------------------------------------------------------- /tests/big.out: -------------------------------------------------------------------------------- 1 | -3.1337 2 | "-3.1337" 3 | "-3.1337" 4 | 1 5 | 100000000000 6 | "100000000000" 7 | "100000000000" 8 | 1 9 | 9007199254740992 10 | "9007199254740992" 11 | "9007199254740992" 12 | 1 13 | -------------------------------------------------------------------------------- /tests/bits.lil: -------------------------------------------------------------------------------- 1 | 2 | # test suite for the bits interface. 3 | show[bits] 4 | 5 | # basic bitwise ops: 6 | show[bits.and[12 10]] 7 | show[bits.or [12 10]] 8 | 9 | # bitwise ops conform: 10 | show[bits.xor[15 (range 16)]] 11 | 12 | # bitwise ops reduce: 13 | show[bits.or[32 16 8 4 2 1]] 14 | 15 | # bitwise ops reduce a single arg: 16 | show[bits.or[(32,16,8,4,2,1)]] 17 | 18 | # properly handle lengths 0 and 1: 19 | show[bits.and[5]] 20 | show[bits.and[()]~nil] 21 | show[bits.and[]~nil] 22 | 23 | # bounds 24 | show[bits.or[2^8,16,24,30,31]] 25 | -------------------------------------------------------------------------------- /tests/bits.out: -------------------------------------------------------------------------------- 1 | 2 | 8 3 | 14 4 | (15,14,13,12,11,10,9,8,7,6,5,4,3,2,1,0) 5 | 63 6 | 63 7 | 5 8 | 1 9 | 1 10 | 3238068480 11 | -------------------------------------------------------------------------------- /tests/closure.lil: -------------------------------------------------------------------------------- 1 | 2 | # lexical closure experiments. 3 | 4 | # assignments don't leak out of their enclosing scope: 5 | on foo do 6 | y:22 7 | show[y] # 22 8 | end 9 | foo[] 10 | show[y~nil] # nil 11 | 12 | # but if you've declared a variable outside, 13 | # it can be written from inside: 14 | q:999 15 | on foo do 16 | q:888 17 | show[q] # 888 18 | end 19 | foo[] 20 | show[q] # 888 21 | 22 | # nested functions see their parent's locals: 23 | on foo do 24 | z:33 25 | on bar do 26 | show[z] # 33 27 | end 28 | bar[] 29 | end 30 | foo[] 31 | 32 | # functions passed into functions retain their lexical bindings, 33 | # cannot see the bindings of their caller, 34 | # and do not modify their caller's bindings: 35 | g:333 36 | on quux x do 37 | z:99 38 | w:88 39 | x[77] 40 | show[z] # 99 41 | end 42 | on zami x do 43 | z:23 44 | show[z,w,g,x] # 23,nil,333,77 45 | end 46 | quux[zami] 47 | 48 | on nest x do 49 | on nest x do # this must not shadow its parent declaration! 50 | show[5*x] 51 | end 52 | show[20*x] 53 | end 54 | nest[100] 55 | nest[100] 56 | 57 | on counter do 58 | x:100 59 | on inc do # this closes over an environment that is otherwise torn down when counter returns... 60 | r:x 61 | x:x+1 62 | r 63 | end 64 | end 65 | c:counter[] 66 | show[c[]] 67 | show[c[]] 68 | d:counter[] 69 | show[d[]] 70 | show[c[]] 71 | 72 | # demonstrate send's "uplevel" semantics: 73 | on boop x do 74 | print["It is I, the outer boop: %s" x] 75 | end 76 | on beep do 77 | on boop do 78 | print["It is I, the inner boop."] 79 | send boop[25] # this is not recursive! 80 | send thisFunctionDoesNotExist[] 81 | end 82 | boop[] 83 | end 84 | beep[] 85 | 86 | # along different code paths, locals may be assigned (or not assigned) 87 | # in a different order. this must not impact their retrieval: 88 | on saddle x do 89 | if x 90 | saddle_a:33 91 | else 92 | saddle_b:66 93 | end 94 | saddle_a,saddle_b 95 | end 96 | show[saddle[1]] 97 | show[saddle[0]] 98 | 99 | # explicitly create a local, to avoid clobbering outer definitions: 100 | duplicate:"Alpha" 101 | on func do 102 | local duplicate:"Beta" 103 | show[duplicate] # "Beta" 104 | show[local temp:"Chained"] # locals return their right expression, just like any other plain assignment 105 | end 106 | func[] 107 | show[duplicate] # "Alpha" 108 | 109 | # variadic function declarations: 110 | on func ...x do 111 | show["variadic" x] 112 | end 113 | func[] 114 | func[11] 115 | func[22 "B"] 116 | show[keys func] 117 | -------------------------------------------------------------------------------- /tests/closure.out: -------------------------------------------------------------------------------- 1 | 22 2 | 1 3 | 888 4 | 888 5 | 33 6 | (23,nil,333,77) 7 | 99 8 | 2000 9 | 2000 10 | 100 11 | 101 12 | 100 13 | 102 14 | It is I, the inner boop. 15 | It is I, the outer boop: 25 16 | (33,nil) 17 | (nil,66) 18 | "Beta" 19 | "Chained" 20 | "Alpha" 21 | "variadic" () 22 | "variadic" (11) 23 | "variadic" (22,"B") 24 | ("...x") 25 | -------------------------------------------------------------------------------- /tests/cond.lil: -------------------------------------------------------------------------------- 1 | show[if 1 11 end] # clause permutations 2 | show[if 0 11 end] 3 | show[if 1 11 else 22 end] 4 | show[if 0 11 else 22 end] 5 | show[if 1 11 elseif 0 22 end] 6 | show[if 0 11 elseif 1 22 end] 7 | show[if 0 11 elseif 0 22 end] 8 | show[if 1 11 elseif 0 22 else 33 end] 9 | show[if 0 11 elseif 1 22 else 33 end] 10 | show[if 0 11 elseif 0 22 else 33 end] 11 | show[if 1 11 elseif 0 22 elseif 0 33 end] 12 | show[if 0 11 elseif 1 22 elseif 0 33 end] 13 | show[if 0 11 elseif 0 22 elseif 1 33 end] 14 | show[if 0 11 elseif 0 22 elseif 0 33 end] 15 | show[if 1 11 elseif 0 22 elseif 0 33 else 44 end] 16 | show[if 0 11 elseif 1 22 elseif 0 33 else 44 end] 17 | show[if 0 11 elseif 0 22 elseif 1 33 else 44 end] 18 | show[if 0 11 elseif 0 22 elseif 0 33 else 44 end] 19 | show[if 1 end] # empty bodies 20 | show[if 0 else end] 21 | show[if 0 elseif 1 else end] 22 | show[if 0 elseif 0 else end] 23 | show[if 1 11 else if 0 22 else 33 end end] # nesting 24 | show[if 0 11 else if 1 22 else 33 end end] 25 | show[if 0 11 else if 0 22 else 33 end end] 26 | show[if 1 if 1 11 else 22 end else 33 end] 27 | show[if 1 if 0 11 else 22 end else 33 end] 28 | show[if 0 if 1 11 else 22 end else 33 end] 29 | 30 | show[if nil "yes" else "nope" end] # nil is falsey 31 | show[if !nil "yes" else "nope" end] 32 | -------------------------------------------------------------------------------- /tests/cond.out: -------------------------------------------------------------------------------- 1 | 11 2 | 3 | 11 4 | 22 5 | 11 6 | 22 7 | 8 | 11 9 | 22 10 | 33 11 | 11 12 | 22 13 | 33 14 | 15 | 11 16 | 22 17 | 33 18 | 44 19 | 20 | 21 | 22 | 23 | 11 24 | 22 25 | 33 26 | 11 27 | 22 28 | 33 29 | "nope" 30 | "yes" 31 | -------------------------------------------------------------------------------- /tests/conform.lil: -------------------------------------------------------------------------------- 1 | # conforming between atoms, lists, and dictionaries. 2 | 3 | # monadic: 4 | show[floor (55.3)] 5 | show[floor (1.1,2.2,-3.3)] 6 | show[floor ("A","B") dict 11.2,33.9] 7 | show[unit "ABCD" dict pi*(0,.5,1,1.5,2)] 8 | 9 | # dyadic, the basics: 10 | show[11+22] 11 | show[100+(22,33)] 12 | show[(11,22)+100] 13 | show[(11,22)+(100,200)] 14 | 15 | # jagged lists keep the left arg's length: 16 | show[(11,22,33)+(100,200)] 17 | show[(11,22)+(100,200,300)] 18 | 19 | # dict-dict takes the union of keys: 20 | x:("White","Brown","Speckled")dict 10,34,27 21 | y:("White","Speckled","Brown")dict 5, 3, 8 22 | z:("Brown","White","Blue" )dict 9,13,35 23 | show[x+y] # prefer left order 24 | show[x+z] # zero-fill unmatched 25 | 26 | # atom-dict spreads the atom to every dict element: 27 | show[x+100] 28 | show[200+x] 29 | 30 | # dict-nondict / nondict-dict likewise spreads: 31 | d:("alpha","beta")dict (list 11,22),(list 33,44) 32 | show[(100,200)*d] 33 | show[d*(100,200)] 34 | 35 | # reducers reflect dict conforming 36 | show[sum (list x),(list y),(list z)] 37 | -------------------------------------------------------------------------------- /tests/conform.out: -------------------------------------------------------------------------------- 1 | 55 2 | (1,2,-4) 3 | {"A":11,"B":33} 4 | {"A":(1,0),"B":(0,1),"C":(-1,0),"D":(-0,-1)} 5 | 33 6 | (122,133) 7 | (111,122) 8 | (111,222) 9 | (111,222,133) 10 | (111,222) 11 | {"White":15,"Brown":42,"Speckled":30} 12 | {"White":23,"Brown":43,"Speckled":27,"Blue":35} 13 | {"White":110,"Brown":134,"Speckled":127} 14 | {"White":210,"Brown":234,"Speckled":227} 15 | {"alpha":(1100,4400),"beta":(3300,8800)} 16 | {"alpha":(1100,4400),"beta":(3300,8800)} 17 | {"White":28,"Brown":51,"Speckled":30,"Blue":35} 18 | -------------------------------------------------------------------------------- /tests/convert.lil: -------------------------------------------------------------------------------- 1 | 2 | # data conversion tests 3 | 4 | t.a:11,22,33 5 | t.b:0,1,0 t.t:("one,two","three\nfour","five") 6 | t:table t 7 | show[c:writecsv[t "fbss"]] 8 | show[readcsv[c "fbss"]] 9 | show[writecsv[table range 5]] # rectangular table(!) 10 | 11 | t2:insert foo bar with 12 | "A" -2.953 13 | "B" 99.8 14 | end 15 | show[writecsv[t2 "sc"]] 16 | 17 | show[readcsv["foo,bar\nA,-$2.953\nB,$99.8" "sc"]] # currency casts 18 | show[readcsv["A,B\none,-12.3\ntwo,45.999" "si"]] # integer casts 19 | show[readcsv["A,B\none,-12.3\ntwo,45.999"]] # unspecified datatypes 20 | show[readcsv["A,B\none\ntwo,45.999"]] # pad ragged column data 21 | show[readcsv["A,B,C\none,11,true\ntwo,12,true" "si"]] # trim extra columns 22 | show[readcsv["A,B\none,-12.3\ntwo,45.999\n"]] # ignore a trailing newline 23 | show[readcsv["A,B\none,-12.3\ntwo,45.999\n" "_i"]] # explicitly drop column 24 | show[readcsv["A,B\none,-12.3\ntwo,45.999" "sisi"]] # pad out missing columns 25 | show[readcsv[""]] # no headers or data 26 | show[readcsv["first,second,third"]] # headers only 27 | show[readcsv["first,second,third\n "]] # headers only, with whitespace 28 | show[readcsv["a,b\n,99\n,"]] # ragged last cell, no spec 29 | show[readcsv["a,b\n,99\n," "si"]] # ragged last cell, spec 30 | 31 | # custom delimiters 32 | t3:insert foo bar with "C|D" 789 into t2 33 | show[writecsv[t3 "sc" "|"]] 34 | show[readcsv["first|second|third\n11|22|33" "fff" "|"]] 35 | 36 | # special column types 37 | show[t4:readcsv["number,fruit,bool\n1,Apple,1\n2,Banana,0" "ILB"]] 38 | show[writecsv[t4 "ILB"]] 39 | 40 | # allow spaces in column names 41 | show[t5:readcsv["Multi Word,another\n-1337.5,\"quoted string\""]] 42 | show[writecsv[t5]] 43 | 44 | # handle 'j' columns 45 | show[writecsv[insert name value with 46 | "Alpha" 11,22 47 | "Beta" 33,44 48 | "Gamma" 55,66 49 | end "sj"]] 50 | show[readcsv["name,value\nAlpha,\"[11,22]\"\nBeta,\"[33,44]\"\nGamma,\"[55,66]\"" "sj"]] 51 | -------------------------------------------------------------------------------- /tests/convert.out: -------------------------------------------------------------------------------- 1 | "a,b,t,c4\n11,false,\"one,two\",\n22,true,\"three\nfour\",\n33,false,five," 2 | +----+---+---------------+----+ 3 | | a | b | t | c4 | 4 | +----+---+---------------+----+ 5 | | 11 | 0 | "one,two" | "" | 6 | | 22 | 1 | "three\nfour" | "" | 7 | | 33 | 0 | "five" | "" | 8 | +----+---+---------------+----+ 9 | "value\n0\n1\n2\n3\n4" 10 | "foo,bar\nA,-$2.95\nB,$99.80" 11 | +-----+--------+ 12 | | foo | bar | 13 | +-----+--------+ 14 | | "A" | -2.953 | 15 | | "B" | 99.8 | 16 | +-----+--------+ 17 | +-------+-----+ 18 | | A | B | 19 | +-------+-----+ 20 | | "one" | -12 | 21 | | "two" | 45 | 22 | +-------+-----+ 23 | +-------+----------+ 24 | | A | B | 25 | +-------+----------+ 26 | | "one" | "-12.3" | 27 | | "two" | "45.999" | 28 | +-------+----------+ 29 | +-------+----------+ 30 | | A | B | 31 | +-------+----------+ 32 | | "one" | nil | 33 | | "two" | "45.999" | 34 | +-------+----------+ 35 | +-------+----+ 36 | | A | B | 37 | +-------+----+ 38 | | "one" | 11 | 39 | | "two" | 12 | 40 | +-------+----+ 41 | +-------+----------+ 42 | | A | B | 43 | +-------+----------+ 44 | | "one" | "-12.3" | 45 | | "two" | "45.999" | 46 | +-------+----------+ 47 | +-----+ 48 | | B | 49 | +-----+ 50 | | -12 | 51 | | 45 | 52 | +-----+ 53 | +-------+-----+-----+-----+ 54 | | A | B | c2 | c3 | 55 | +-------+-----+-----+-----+ 56 | | "one" | -12 | nil | nil | 57 | | "two" | 45 | nil | nil | 58 | +-------+-----+-----+-----+ 59 | ++ 60 | || 61 | ++ 62 | +-------+--------+-------+ 63 | | first | second | third | 64 | +-------+--------+-------+ 65 | +-------+--------+-------+ 66 | | first | second | third | 67 | +-------+--------+-------+ 68 | | "" | nil | nil | 69 | +-------+--------+-------+ 70 | +----+------+ 71 | | a | b | 72 | +----+------+ 73 | | "" | "99" | 74 | | "" | "" | 75 | +----+------+ 76 | +----+-----+ 77 | | a | b | 78 | +----+-----+ 79 | | "" | 99 | 80 | | "" | nil | 81 | +----+-----+ 82 | "foo|bar\nA|-$2.95\nB|$99.80\n\"C|D\"|$789.00" 83 | +-------+--------+-------+ 84 | | first | second | third | 85 | +-------+--------+-------+ 86 | | 11 | 22 | 33 | 87 | +-------+--------+-------+ 88 | +--------+----------+------+ 89 | | number | fruit | bool | 90 | +--------+----------+------+ 91 | | 1 | "Apple" | 1 | 92 | | 2 | "Banana" | 0 | 93 | +--------+----------+------+ 94 | "number,fruit,bool\n1,Apple,true\n2,Banana,false" 95 | +------------+-----------------+ 96 | | Multi Word | another | 97 | +------------+-----------------+ 98 | | "-1337.5" | "quoted string" | 99 | +------------+-----------------+ 100 | "Multi Word,another\n-1337.5,quoted string" 101 | "name,value\nAlpha,\"[11,22]\"\nBeta,\"[33,44]\"\nGamma,\"[55,66]\"" 102 | +---------+---------+ 103 | | name | value | 104 | +---------+---------+ 105 | | "Alpha" | (11,22) | 106 | | "Beta" | (33,44) | 107 | | "Gamma" | (55,66) | 108 | +---------+---------+ 109 | -------------------------------------------------------------------------------- /tests/dom/a-blessed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tests/dom/a-blessed.gif -------------------------------------------------------------------------------- /tests/dom/a-opaque.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tests/dom/a-opaque.gif -------------------------------------------------------------------------------- /tests/dom/a.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tests/dom/a.gif -------------------------------------------------------------------------------- /tests/dom/a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tests/dom/a.png -------------------------------------------------------------------------------- /tests/dom/a4.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tests/dom/a4.wav -------------------------------------------------------------------------------- /tests/dom/ab-blessed.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tests/dom/ab-blessed.gif -------------------------------------------------------------------------------- /tests/dom/ab-opt.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tests/dom/ab-opt.gif -------------------------------------------------------------------------------- /tests/dom/arrays.lil: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # 3 | # Array Interface Integration Suite 4 | # 5 | ####################################### 6 | 7 | on assert_file text want got do 8 | s:shell["cmp %s %s" format want,got] 9 | if count s.out 10 | print["test failed: %s" text] 11 | print["binary file '%s' does not match '%s'" format got,want] 12 | exit[1] 13 | end 14 | shell["rm %s" format got] 15 | end 16 | on assert_base text want got disp do 17 | #print["%s..." text] 18 | if !want~got 19 | print["test failed: %s" text] 20 | print["expected:"] disp[want] 21 | print["got:"] disp[got] 22 | exit[1] 23 | end 24 | end 25 | on assert text want got do 26 | assert_base[text want got show] 27 | end 28 | 29 | ####################################### 30 | # 31 | # Tests 32 | # 33 | ####################################### 34 | 35 | a:array[4 "i16l"] 36 | assert["array from scratch" "array" (typeof a)] 37 | assert["array cast" "i16l" a.cast] 38 | assert["array size" 4 a.size] 39 | a[3]:99 40 | assert["array scalar r/w" 99 a[3]] 41 | a[1,3]:333 42 | assert["array scalar spread" (0,333,333,333) a[0,4]] 43 | a[0,4]:11,22,33,44,55 44 | assert["array vector r/w" (11,22,33,44) a[0,4]] 45 | assert["array left OOB" 0 a[-1]] 46 | 47 | assert["array encode" "%%DAT5CwAWACEALAA=" a.encoded] 48 | dec:array["%%DAT5CwAWACEALAA="] 49 | assert["array decode" ("i16l",list 11,22,33,44) dec.cast,list dec[0,4]] 50 | 51 | s:a.slice[1,2] 52 | assert["array slice read" ("i16l",2,0,33) (s.cast,s.size,s.here,s[1])] 53 | s[0]:77 54 | assert["array slice writethru" 77 a[1]] 55 | s[2]:88 56 | assert["array slice oob write" 44 a[3]] 57 | 58 | c:a.copy[1,5] 59 | assert["array copy read" ("i16l",5,0,list 77,33,44,0) (c.cast,c.size,c.here,list c[0,4])] 60 | c[0]:103 61 | assert["array copy no writethru" 77 a[1]] 62 | 63 | a[0]:"ABCDEFG" 64 | assert["array char write" (65,77) a[0,2]] 65 | a[0,4]:"ABCDEFG" 66 | assert["array string write" (65,66,67,68) a[0,4]] 67 | a.cast:"i8" 68 | assert["array splayed chars" (65,0,66,0) a[0,4]] 69 | a.cast:"char" 70 | a[0,4]:"ABCDEFG" 71 | assert["array string read" "ABCD" a[0,4]] 72 | a.cast:"i8" 73 | a[0,4]:(0,10,65,13) 74 | a.cast:"char" 75 | assert["array read denormal chars" "�\nA" a[0,4]] 76 | 77 | a:array[2 "i16l"] 78 | a[0,2]:"AB" 79 | a.size:5 80 | assert["array resize bigger" (65,66,0,0,0) a[0,a.size]] 81 | a.size:1 82 | assert["array resize smaller" (list 65) a[0,a.size]] 83 | b:a.slice[0,2] b.size:5 84 | assert["array slices can't resize" (1,2) (a.size,b.size)] 85 | 86 | a:array[4 "i32b"] # need capacity for the biggest-size cast! 87 | on limits cast nums do 88 | a.cast:cast 89 | o:0,count nums 90 | a[o]:nums 91 | assert[("array %s r/w limits" format cast) nums a[o]] 92 | end 93 | limits["u8" 0,1,255 ] 94 | limits["i8" -128,0,1,127 ] 95 | limits["u16b" 0,1,65535 ] 96 | limits["u16l" 0,1,65535 ] 97 | limits["i16b" -32768,0,1,32767 ] 98 | limits["i16l" -32768,0,1,32767 ] 99 | limits["u32b" 0,1,4294967295] 100 | limits["u32l" 0,1,4294967295] 101 | limits["i32b" -2147483648,0,1,2147483647] 102 | limits["i32l" -2147483648,0,1,2147483647] 103 | 104 | bin:read["tests/dom/a.gif" "array"] 105 | assert["read array from file" (86,"array") (bin.size,typeof bin)] 106 | write["temp.gif" bin] 107 | assert_file["write file from array" "tests/dom/a.gif" "temp.gif"] 108 | 109 | gif.magic :"char",6 # "GIF89a" magic number. 110 | gif.size :"u16l",2 # (width,height) in pixels. 111 | gif.gct.present:1 # flag: is there a global colortable? 112 | gif.gct.res :3 # color resolution (almost always 7; 8-bits per channel). 113 | gif.gct.sorted :1 # flag: are the colors sorted by importance? (almost always zero). 114 | gif.gct.size :3 # number of entries in the global colortable. 115 | gif.background :"u8" # index of the background color. 116 | gif.aspect :"u8" # pixel aspect ratio (almost always zero). 117 | 118 | rgif.magic :"GIF89a" 119 | rgif.size :8,9 120 | rgif.gct.present:1 121 | rgif.gct.res :7 122 | rgif.gct.sorted :0 123 | rgif.gct.size :0 124 | rgif.background :1 125 | rgif.aspect :0 126 | 127 | assert["array read struct" rgif bin.struct[gif]] 128 | cpy:array[0] 129 | cpy.struct[gif rgif] 130 | assert["array write struct" bin.slice[0,13].encoded cpy.encoded] 131 | cpy.struct["u8",6 (11,22,33,44,55,66)] 132 | assert["array write here" 19 cpy.here] 133 | assert["array write field" (11,22,33,44,55,66) cpy[13,6]] 134 | cpy.here:0 135 | assert["array read field" "GIF89a" cpy.struct["char",6]] 136 | 137 | blob:array["%%DAT08J+SqQ=="] 138 | a:array[0 "u16l"] 139 | a.cat["TEXT"] # 4 bytes 140 | a.cat[345,9000] # 4 bytes (as u16s) 141 | a.cat[blob] # 4 bytes 142 | b:array[0 "u16l"].cat["TEXT"].cat[345,9000].cat[blob] 143 | c:array[0 "u16l"].cat["TEXT" 345,9000 blob] 144 | assert["array cat" "%%DAT3VEVYVFkBKCPwn5Kp" a.encoded] 145 | assert["array cat, chained" "%%DAT3VEVYVFkBKCPwn5Kp" b.encoded] 146 | assert["array cat, multi" "%%DAT3VEVYVFkBKCPwn5Kp" c.encoded] 147 | assert["array cat payload" "%%DAT38J+SqQ==" c.slice[4].encoded] 148 | 149 | mem:array[16 "u8"] 150 | ds:mem.slice[8,2 "i32l"] 151 | ds[0]:123 152 | assert["array slice offset" (123,123,0,0,0,2) (ds[0],mem[8,4],ds.size)] 153 | dc:mem.copy[8,2 "i32l"] 154 | assert["array copy offset" (123,2) (ds[0],ds.size)] 155 | 156 | ####################################### 157 | # 158 | # Wrap Up 159 | # 160 | ####################################### 161 | 162 | if 0~assert 163 | print["internal error; environment corruption!"] 164 | else 165 | print["all array tests passed."] 166 | end 167 | -------------------------------------------------------------------------------- /tests/dom/b.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tests/dom/b.gif -------------------------------------------------------------------------------- /tests/dom/badchars.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | script:"0" 4 | 5 | {script:0} 6 | text with “double” and ‘single’ curly-quotes, 7 | a tab: , an accented letter: ñ 8 | and a horrifying abomination:☺ 9 | 10 | {end} 11 | -------------------------------------------------------------------------------- /tests/dom/bare.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[320,240] 5 | locked:0 6 | 7 | {card:home} 8 | {widgets} 9 | pusher:{"type":"button","text":"Push Me!","style":"rect","size":[40,60],"pos":[10,35],"locked":1,"script":"nuffing","show":"transparent","value":1} 10 | outfield:{"type":"field","border":1,"value":"some text"} 11 | expenses:{"type":"grid","format":"i$","value":{"a":[11,22],"b":[33,44]}} 12 | frame:{"type":"canvas","size":[10,8],"image":"%%IMG2AAoACAJQ","pattern":2} 13 | -------------------------------------------------------------------------------- /tests/dom/bare.deck.html: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[320,240] 5 | locked:0 6 | 7 | {card:home} 8 | {widgets} 9 | pusher:{"type":"button","text":"Push Me!","style":"rect","size":[40,60],"pos":[10,35],"locked":1,"script":"nuffing","show":"transparent"} 10 | outfield:{"type":"field","border":1} 11 | expenses:{"type":"grid","format":"i$"} 12 | frame:{"type":"canvas","size":[10,8]} 13 | {values} 14 | pusher:{"value":1} 15 | outfield:{"value":"some text"} 16 | expenses:{"value":{"a":[11,22],"b":[33,44]}} 17 | frame:{"image":"%%IMG2AAoACAJQ","pattern":2} -------------------------------------------------------------------------------- /tests/dom/barenums.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[512,342] 5 | 6 | {card:0} 7 | 8 | {card:55} 9 | {widgets} 10 | button1:{"type":"button","pos":[219,128],"text":"Fine"} 11 | 12 | {card:2} 13 | -------------------------------------------------------------------------------- /tests/dom/bom.txt: -------------------------------------------------------------------------------- 1 | Some ASCII contents after the bom! -------------------------------------------------------------------------------- /tests/dom/construction.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tests/dom/construction.gif -------------------------------------------------------------------------------- /tests/dom/contraptions.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[512,342] 5 | 6 | {card:home} 7 | {widgets} 8 | a:{"type":"contraption","size":[150,50],"pos":[0,0],"def":"spinner","widgets":{"down":{},"up":{},"value":{},"step":{}}} 9 | b:{"type":"contraption","size":[150,50],"pos":[0,0],"def":"spinner","widgets":{"down":{},"up":{},"value":{"value":"42"},"step":{}}} 10 | 11 | {contraption:spinner} 12 | size:[150,50] 13 | margin:[0,0,0,0] 14 | description:"unbounded numeric picker" 15 | script:"spinner.0p" 16 | template:"on change do\n \nend" 17 | attributes:{"name":["value","step"],"label":["Value","Step"],"type":["number","number"]} 18 | {widgets} 19 | down:{"type":"button","size":[60,20],"pos":[0,0],"script":"spinner.1p","text":"<"} 20 | up:{"type":"button","size":[60,20],"pos":[0,0],"script":"spinner.2p","text":">"} 21 | value:{"type":"field","size":[100,20],"pos":[0,0],"locked":1,"value":"0"} 22 | step:{"type":"field","size":[100,20],"pos":[0,0],"show":"none","value":"1"} 23 | 24 | {script:spinner.0p} 25 | 26 | on set_value x do value.text:0+x end 27 | on get_value do 0+value.text end 28 | on set_step x do step.text:0+x end 29 | on get_step do 0+step.text end 30 | {end} 31 | 32 | {script:spinner.1p} 33 | on click do 34 | value.text:value.text-step.text 35 | end 36 | {end} 37 | 38 | {script:spinner.2p} 39 | on click do 40 | value.text:value.text+step.text 41 | end 42 | {end} 43 | 44 | -------------------------------------------------------------------------------- /tests/dom/cursed.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[512,342] 5 | script:"__proto__" 6 | 7 | {script:__proto__} 8 | 2+3 9 | {end} 10 | 11 | {card:home} 12 | 13 | {card:__proto__} 14 | {widgets} 15 | but:{"type":"button","size":[60,20],"pos":[0,0],"text":"click meeee"} 16 | 17 | {module:__proto__} 18 | description:"a deeply cursed module." 19 | {script} 20 | 21 | {end} 22 | 23 | {contraption:__proto__} 24 | size:[100,100] 25 | margin:[0,0,0,0] 26 | description:"a deeply cursed contraption." 27 | 28 | -------------------------------------------------------------------------------- /tests/dom/decomposed.txt: -------------------------------------------------------------------------------- 1 | …ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĒēĘęĪīıŁłŃńŌōŐőŒœŚśŠšŪūŰűŸŹźŻżŽžȘșȚțẞ¡¿«»€° -------------------------------------------------------------------------------- /tests/dom/import.lil: -------------------------------------------------------------------------------- 1 | # import[] test 2 | 3 | on triple x do 4 | x,x,x 5 | end 6 | 7 | alpha:"abcdefghijklmnopqrstuvwxyz" 8 | beta:2+3 9 | -------------------------------------------------------------------------------- /tests/dom/logger.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[512,342] 5 | 6 | {card:home} 7 | {widgets} 8 | push:{"type":"button","script":0} 9 | pull:{"type":"grid"} 10 | 11 | {script:0} 12 | logger.put["second"] 13 | logger.put["third"] 14 | pull.value:logger.get[] 15 | {end} 16 | 17 | {module:logger} 18 | description:"a utility module for logging" 19 | {data} 20 | version:1.01 21 | log:<"time":[0,1],"message":["zeroth","first"]> 22 | zero:null 23 | 24 | {script} 25 | log:data.log 26 | 27 | mod.put:on _ x do 28 | log:insert time message with 29 | count log # would be sys.now, but that's a PITA for testing 30 | x 31 | into log 32 | data.log:log 33 | log 34 | end 35 | 36 | mod.get:on _ do 37 | log 38 | end 39 | {end} 40 | -------------------------------------------------------------------------------- /tests/dom/logger2.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[512,342] 5 | 6 | {card:home} 7 | {widgets} 8 | push:{"type":"button","size":[60,20],"pos":[0,0],"script":"home.0"} 9 | pull:{"type":"grid","size":[100,50],"pos":[0,0],"value":{"time":[0,1,2,3],"message":["zeroth","first","second","third"]}} 10 | 11 | {script:home.0} 12 | logger.put["second"] 13 | logger.put["third"] 14 | pull.value:logger.get[] 15 | {end} 16 | 17 | {module:logger} 18 | description:"a utility module for logging" 19 | {data} 20 | log:<"time":[0,1,2,3],"message":["zeroth","first","second","third"]> 21 | {script} 22 | log:data.log 23 | 24 | mod.put:on _ x do 25 | log:insert time message with 26 | count log # would be sys.now, but that's a PITA for testing 27 | x 28 | into log 29 | data.log:log 30 | log 31 | end 32 | 33 | mod.get:on _ do 34 | log 35 | end 36 | {end} 37 | 38 | -------------------------------------------------------------------------------- /tests/dom/module.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[512,342] 5 | 6 | {card:home} 7 | 8 | {module:trivial} 9 | description:"a trivial module" 10 | {script} 11 | ("a","b")dict 22,33 12 | {end} 13 | 14 | -------------------------------------------------------------------------------- /tests/dom/quotes.txt: -------------------------------------------------------------------------------- 1 | text with “double” and ‘single’ curly-quotes, 2 | a tab: , an accented letter: ñ 3 | and a horrifying abomination:☺ 4 | -------------------------------------------------------------------------------- /tests/dom/sink1.deck: -------------------------------------------------------------------------------- 1 | {deck} 2 | version:1 3 | card:0 4 | size:[512,342] 5 | locked:1 6 | script:"0" 7 | name:"everything but the sink" 8 | author:"just some guy" 9 | patterns:"%%IMG0AAgA4AAAAAAAAAAA//////////8LDA0ODxAREiBAgMEiHAgQgAAIAIAACAD/d//d/3f/3XEiF49HInT4iFAgAgWIiIiIACIAiAAiAHfdd9133XfdQIAACAQCACABAQOESDAMAogiiCKIIogiqlWqVapVqlWABEAIASACEMAMjbEwAxvYqgCqAKoAqgD/Vf9V/1X/Vf8A/wD/AP8AqqqqqqqqqqpEiBEiRIgRIt27d+7du3fuQIABAgQIECC/f/79+/fv3wgAqgAIAIgAj493mPj4d4mqAIgUIkGIALCwsL8Av7+w" 10 | animations:[[10,20,5],[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,0,0,0,0,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47,47],[18,18,20,19,19,20],[0,0,0,0,1,1,1,1]] 11 | 12 | {script:0} 13 | show["<{s}script> is a perfectly valid string in Lil."] 14 | {end} 15 | 16 | {fonts} 17 | dingus:"%%FNT1AgMBQQKAQIA=" 18 | 19 | {sounds} 20 | short:"%%SND0fn+AgYI=" 21 | 22 | {card:home} 23 | {widgets} 24 | output:{"type":"field","size":[100,20],"pos":[0,0],"value":"A Sentence"} 25 | richval:{"type":"field","size":[100,20],"pos":[0,0],"font":"mono","value":{"text":["Words","i"],"font":["mono",""],"arg":["","%%IMG2AAIAAhcE"]}} 26 | go:{"type":"button","size":[60,20],"pos":[0,0]} 27 | widget with {c}, {l} and {r} in the name:{"type":"button","size":[60,20],"pos":[0,0]} 28 | 29 | {card:other} 30 | {widgets} 31 | drawring:{"type":"canvas","size":[30,30],"pos":[0,0],"image":"%%IMG0AB4AHgABAAAAAQAAAAOAAAAHgAAAB8AAAA/AAAAf4AAAP+AAAD/wAAB/8AAA//gAAf/4AAH//AAD//wAB//+AA///gAD//8AAP//AAA//4AAD/+AAAP/wAAA/8AAAD/gAAAf4AAAB/AAAAHwAAAAeAAAABgAAAAEAAAAAA=="} 32 | things:{"type":"grid","size":[100,50],"pos":[0,0],"value":{"a":[11,22],"b":["One","Two"]}} 33 | 34 | -------------------------------------------------------------------------------- /tests/dom/test_roundtrip.lil: -------------------------------------------------------------------------------- 1 | # check whether all example decks will round-trip intact via this DOM implementation 2 | 3 | basedir:"examples/decks/" 4 | 5 | on roundtrip path do 6 | d:read[path] 7 | write["temp.deck" d] 8 | s:shell["cmp -s temp.deck %s" format path] # exit code { 0:matched, 1:differed, 2:error } 9 | if s.exit 10 | print["round trip for %s failed:" path] 11 | print[shell["diff temp.deck %s | cut -c 1-80" format path].out] 12 | exit[1] 13 | end 14 | shell["rm temp.deck"] 15 | end 16 | 17 | each name in extract name where type=".deck" from dir["examples/decks/"] 18 | roundtrip["%s%s" format basedir,name] 19 | end 20 | 21 | print["all roundtrip tests passed."] 22 | -------------------------------------------------------------------------------- /tests/err_badchar.err: -------------------------------------------------------------------------------- 1 | (4:3) Invalid character '`'. 2 | -------------------------------------------------------------------------------- /tests/err_badchar.lil: -------------------------------------------------------------------------------- 1 | 2 | on foo do 3 | # an invalid character: 4 | ` 5 | end 6 | -------------------------------------------------------------------------------- /tests/err_endless.err: -------------------------------------------------------------------------------- 1 | (4:7) Expected 'end' for block. 2 | -------------------------------------------------------------------------------- /tests/err_endless.lil: -------------------------------------------------------------------------------- 1 | 2 | 3 | on whatever x do 4 | 2 + 3 -------------------------------------------------------------------------------- /tests/err_escape.err: -------------------------------------------------------------------------------- 1 | (2:22) Invalid escape character '\p' in string. 2 | -------------------------------------------------------------------------------- /tests/err_escape.lil: -------------------------------------------------------------------------------- 1 | 2 | show "this escape: \p is bogus." 3 | -------------------------------------------------------------------------------- /tests/err_nodir.err: -------------------------------------------------------------------------------- 1 | (2:29) Expected 'asc' or 'desc'. 2 | -------------------------------------------------------------------------------- /tests/err_nodir.lil: -------------------------------------------------------------------------------- 1 | 2 | select foo orderby zami from bar 3 | 2 + 3 4 | -------------------------------------------------------------------------------- /tests/err_preelse.err: -------------------------------------------------------------------------------- 1 | (2:23) Expected 'end'. 2 | -------------------------------------------------------------------------------- /tests/err_preelse.lil: -------------------------------------------------------------------------------- 1 | 2 | if 1 11 else 22 elseif 0 33 end 3 | -------------------------------------------------------------------------------- /tests/err_reservedname.err: -------------------------------------------------------------------------------- 1 | (1:10) 'select' is a keyword, and cannot be used for a function name. 2 | -------------------------------------------------------------------------------- /tests/err_reservedname.lil: -------------------------------------------------------------------------------- 1 | on select x do 2 | 2 + 2 3 | end 4 | -------------------------------------------------------------------------------- /tests/err_unterm.err: -------------------------------------------------------------------------------- 1 | (6:1) Expected name, but found the end of the script. 2 | -------------------------------------------------------------------------------- /tests/err_unterm.lil: -------------------------------------------------------------------------------- 1 | 2 | # missing a 'do' to separate args from body: 3 | on foobar x y 4 | quux zami 5 | 6 | -------------------------------------------------------------------------------- /tests/err_variadic.err: -------------------------------------------------------------------------------- 1 | (2:22) Variadic functions must take exactly one named argument. 2 | -------------------------------------------------------------------------------- /tests/err_variadic.lil: -------------------------------------------------------------------------------- 1 | 2 | on foo ...baz quux do 3 | # variadic functions should only take one argument. 4 | end 5 | -------------------------------------------------------------------------------- /tests/eval.lil: -------------------------------------------------------------------------------- 1 | 2 | # parse error 3 | show[eval["\n99`5"]] 4 | 5 | # inappropriate keyword 6 | show[eval["if 1 do 2 else 3 end"].error] 7 | 8 | # cursed identifiers 9 | show[eval["__proto__:toString"]] 10 | 11 | # empty expression 12 | show[eval[""]] 13 | 14 | # simple expression 15 | show[eval["1+2"]] 16 | 17 | # chaining extraction 18 | show[eval["2+3"].value] 19 | 20 | # reference passed-in bindings 21 | foo.a:100 22 | foo.b:200 23 | show[eval["a+b" foo]] 24 | 25 | # capture new bindings 26 | show[eval["foo:69"]] 27 | 28 | # external bindings don't leak in 29 | show[eval["foo"]] 30 | 31 | # internal bindings don't leak out 32 | c:444 33 | show[eval["c:42 11+22"]] 34 | show[c] 35 | 36 | # passed-in binding dict doesn't mutate 37 | show[eval["c:999" foo]] 38 | show[foo] 39 | 40 | # parser edge case: ending on a keyword 41 | show[eval["on foo do 99 end"]] 42 | 43 | # functions built in an eval[] retain the correct closure 44 | inside:eval["on one x do 100*x end on two x do one[3*x] end"].value 45 | show[inside[5]] 46 | 47 | outside.const:300 48 | inside:eval["on one x do const*x end on two x do one[3+x] end" outside].value 49 | show[inside[4]] 50 | 51 | # if we know what we're doing, we can opt into executing in the caller's scope 52 | globalish:9998 53 | t:eval["localish:10+globalish globalish:777" () 1] 54 | show[t localish globalish] 55 | -------------------------------------------------------------------------------- /tests/eval.out: -------------------------------------------------------------------------------- 1 | {"value":nil,"vars":{},"error":"Invalid character '`'.","errorpos":(1,3)} 2 | "'do' is a keyword, and cannot be used for a variable name." 3 | {"value":nil,"vars":{"__proto__":nil}} 4 | {"value":nil,"vars":{}} 5 | {"value":3,"vars":{}} 6 | 5 7 | {"value":300,"vars":{"a":100,"b":200}} 8 | {"value":69,"vars":{"foo":69}} 9 | {"value":nil,"vars":{}} 10 | {"value":33,"vars":{"c":42}} 11 | 444 12 | {"value":999,"vars":{"a":100,"b":200,"c":999}} 13 | {"a":100,"b":200} 14 | {"value":on foo do ... end,"vars":{"foo":on foo do ... end}} 15 | 1500 16 | 2100 17 | {"value":777,"vars":{"localish":10008}} nil 777 18 | -------------------------------------------------------------------------------- /tests/interfaces.lil: -------------------------------------------------------------------------------- 1 | 2 | # test the basic/universal interfaces, 3 | # to make sure the mechanisms are working: 4 | 5 | show[sys] 6 | 7 | sys.seed:5 8 | show[sys.seed] 9 | 10 | show[random[100 10]] 11 | sys.seed:5 12 | show[random[100 10]] 13 | 14 | # while we're here, gently test the RNG: 15 | show[random[100]] 16 | show[random["ABC"]] 17 | show[random[11,22,33]] 18 | show[random["ABCDE" -5]] 19 | show[ 20 | typeof random["ABC" 1] 21 | typeof random[insert a with 11 22 33 end] 22 | ] 23 | show[random[] random[] random[]] 24 | show[random[0 -5] random[0 5]] 25 | show[random[() -2] random[() 2]] 26 | 27 | # accessing an interface via a container 28 | # should work the same as if it is a global: 29 | sys.seed:99 30 | wrap.sys:sys 31 | show[wrap] 32 | show[wrap.sys.seed] 33 | 34 | # same deal for assignment into an interface 35 | # inside a container: 36 | wrap.sys.seed:66 37 | show[wrap] 38 | show[wrap.sys.seed] 39 | show[sys.seed] 40 | 41 | # match and equals support reference 42 | # equality for interfaces: 43 | show[(sys~sys),(sys~0),(sys~"")] 44 | show[sys=(sys,22,0,sys,"")] 45 | 46 | # in general, bogus indexing produces nil 47 | show[sys.foobarf] 48 | 49 | # assigning through a bogus index returns the arg 50 | show[sys.foobarf:"quux"] 51 | 52 | # all interfaces respond to `.type` identically to `typeof`: 53 | show[sys.type] 54 | -------------------------------------------------------------------------------- /tests/interfaces.out: -------------------------------------------------------------------------------- 1 | 2 | 5 3 | (49,28,0,27,49,72,80,46,65,22) 4 | (49,28,0,27,49,72,80,46,65,22) 5 | 62 6 | "B" 7 | 11 8 | ("D","A","E","B","C") 9 | "list" "dict" 10 | 0.917802 0.74243 0.900731 11 | (nil,nil,nil,nil,nil) (0,0,0,0,0) 12 | (nil,nil) (nil,nil) 13 | {"sys":} 14 | 99 15 | {"sys":} 16 | 66 17 | 66 18 | (1,0,0) 19 | (1,0,0,1,0) 20 | 21 | "quux" 22 | "system" 23 | -------------------------------------------------------------------------------- /tests/like.lil: -------------------------------------------------------------------------------- 1 | # tests for the 'like' glob-matching operator 2 | 3 | show[("a","") like "" ] # empty patterns match ONLY empty input strings 4 | show[("a","") like () ] # no patterns never match 5 | show["" like "a" ] # empty input strings don't match patterns with literals 6 | show[("apple","orange") like "apple" ] # plain literals 7 | show["a`*.b" like "a```*`.b" ] # escaped literals 8 | show["ab`" like "ab`" ] # unterminated escapes act like a literal ` (!) 9 | show[("apple","axxle","axle","abzle","axxxle") like "a..le" ] # single-char wildcards 10 | show[("(123)-45","(009)-00","(01a)-90","(012)-0z") like "(###)-##" ] # digit wildcards 11 | show[("a*ZbXc","a*ZZbZc","a*ZbZZc","abc") like "a`*.b.c" ] # wildcards AND escaped literals 12 | show[("B123","123","B12") like ".123" ] # leading . 13 | show[("9123","123","912") like "#123" ] # leading # 14 | show[("123","A123","AB123","23") like "*123" ] # leading * 15 | show[("ABCD","ABxCD","ABxxCD","ABxCxD","ABD","ABCDx") like "AB*CD" ] # middle * 16 | show[("ABC","ABCx","ABCxx","AB") like "ABC*" ] # ending * 17 | show["" like "*" ] # empty string matches * (!) 18 | show[("ab","axxb","axxxxb","a","b") like "a***b" ] # tolerate sequential *s (which are redundant) 19 | show[("abc","zb") like ("ab","a.c")] # OR together pattern results 20 | show["abc" like ("ab","a.c")] # (see above) 21 | show["abc" like ("","abc" )] # empty patterns short-circuit 22 | -------------------------------------------------------------------------------- /tests/like.out: -------------------------------------------------------------------------------- 1 | (0,1) 2 | (0,0) 3 | 0 4 | (1,0) 5 | 1 6 | 1 7 | (1,1,0,1,0) 8 | (1,1,0,0) 9 | (1,0,0,0) 10 | (1,0,0) 11 | (1,0,0) 12 | (1,1,1,0) 13 | (1,1,1,0,0,0) 14 | (1,1,1,0) 15 | 1 16 | (1,1,1,0,0) 17 | (1,0) 18 | 1 19 | 1 20 | -------------------------------------------------------------------------------- /tests/mode.lil: -------------------------------------------------------------------------------- 1 | 2 | # one algorithm, written four ways: 3 | 4 | # imperative 5 | on mode1 a do 6 | r:() 7 | each x in a 8 | r[x]:1+r[x] 9 | end 10 | bk:0 bc:0 11 | each c k in r 12 | if c>bc bc:c bk:k end 13 | end 14 | bk 15 | end 16 | show[mode1["ABBCABBA"]] 17 | show[mode1[1,2,2,3,4,2,1]] 18 | 19 | # hybrid 20 | on mode2 a do 21 | r:() 22 | each x in a 23 | r[x]:1+r[x] 24 | end 25 | first extract key orderby value desc from r 26 | end 27 | show[mode2["ABBCABBA"]] 28 | show[mode2[1,2,2,3,4,2,1]] 29 | 30 | # monolithic query 31 | on mode3 a do 32 | first extract key orderby value desc from 33 | select key:first value value:count value by value from a 34 | end 35 | show[mode3["ABBCABBA"]] 36 | show[mode3[1,2,2,3,4,2,1]] 37 | 38 | # factored queries 39 | on counts x do select key:first value value:count value by value from x end 40 | on hist x do select key value orderby value desc from x end 41 | on mode4 x do first hist[counts[x]].key end 42 | show[mode4["ABBCABBA"]] 43 | show[mode4[1,2,2,3,4,2,1]] 44 | -------------------------------------------------------------------------------- /tests/mode.out: -------------------------------------------------------------------------------- 1 | "B" 2 | 2 3 | "B" 4 | 2 5 | "B" 6 | 2 7 | "B" 8 | 2 9 | -------------------------------------------------------------------------------- /tests/nil.lil: -------------------------------------------------------------------------------- 1 | 2 | # test suite for nils 3 | 4 | print["identity:"] 5 | show["type" typeof nil] 6 | show["falsiness" if nil "truthy" else "falsey" end] 7 | show["unlessiness" ("replace" unless "") ("replace" unless 0) ("replace" unless nil)] 8 | show["match self" nil ~ nil] 9 | show["spread (right)" nil = 11,nil,22,nil,33] 10 | show["spread (left)" (44,55,nil,66,nil) = nil] 11 | show["nil vs nothing" nil = nil,zilch,0,"",list ()] 12 | show["nil compare" (nil < 0) (nil > 0) (nil < 1) (nil > 1)] 13 | show["uninitialized var is" uninitialized] 14 | show["default body of func" (on f do end)[]] 15 | show["default body of each" each in 11,22,33 end] 16 | show["default body of while" while 0 end] 17 | show["default body of eval" eval[""].value] 18 | show["filter in list" nil take 11,nil,22,nil,33] 19 | show["filter out list" nil drop 11,nil,22,nil,33] 20 | show["filter in dict" nil take ("A",nil,"B","C") dict (11,22,nil,44)] 21 | show["filter out dict" nil drop ("A",nil,"B","C") dict (11,22,nil,44)] 22 | show["filter in multi dict" (nil,"A") take ("A",nil,"B","C") dict (11,22,nil,44)] 23 | show["filter out multi dict" (nil,"A") drop ("A",nil,"B","C") dict (11,22,nil,44)] 24 | 25 | print["\ncoercion:"] 26 | show["to num" 0+nil] 27 | show["to str" "fuse" format "[",nil,"]"] 28 | show["to tab" select from nil] 29 | 30 | print["\noutdexing:"] 31 | show["firsts" (first nil) (first ()) (first "")] 32 | show["lasts" (last nil) (last ()) (last "")] 33 | show["outdex str" "A"[0] "A"[-1] "A"[1] "A"[5]] 34 | show["outdex list" (11,22)[0] (11,22)[-1] (11,22)[2] (11,22)[5]] 35 | show["outdex dict" ("ab"dict 11,22)["a"] ("ab"dict 11,22)["c"] ("ab"dict 11,22)[nil] ("ab"dict 11,22)[0]] 36 | show["outdex tab" (insert a b with 11 22 end)["c"] (insert a b with 11 22 end)[-1] (insert a b with 11 22 end)[5]] 37 | img:image[3,4] 38 | show["outdex image" img[0,0] img[-1,0] img[1,-1] img[3,4]] 39 | snd:sound[3] 40 | show["outdex sound" snd[0] snd[-1] snd[3] snd[5] snd[-2,3] snd[2,4]] 41 | show["nindex str" ("ABC" )[nil]] 42 | show["nindex lst" (11,22,33 )[nil]] 43 | show["nindex dic" ("AB" dict 11,22)[nil]] 44 | show["nindex set str" ("ABC" )[nil]:55] 45 | show["nindex set lst" (11,22,33 )[nil]:55] 46 | show["nindex set dic" ("AB" dict 11,22)[nil]:55] 47 | 48 | print["\npadding:"] 49 | show["amend column" (insert a b with 11 22 33 44 end).c:list 55] 50 | show["flip list pad" flip (list 11,22),(list 44,55,66)] 51 | show["dict pad" "ABC" dict 11,22] 52 | show["take pad" (3 take 0) (3 take ()) (3 take "")] 53 | 54 | print["\nparseformat:"] 55 | show["parse null" "%j%i" parse "null23"] 56 | show["format null" "%j%i" format nil,23] 57 | show["parse invalid" ("%i" parse "ape")] 58 | show["parse missing csv col" readcsv["one,two,three\nA,11,98.6\nB,22,\nC,33," "sif"]] 59 | 60 | print["\nfill:"] 61 | show["fill empties" (99 fill ()) (99 fill "") (99 fill () dict ()) (99 fill table nil)] 62 | show["fill scalars" (33 fill nil) (34 fill 0) (35 fill "a")] 63 | show["fill lists" (36 fill 12,nil,13,nil) (37 fill "%J" parse "[[11,null,33],[44,55],null,[66,null]]")] 64 | show["fill dicts" (38 fill "ABC" dict 11,22)] 65 | show["fill tabs" (39 fill insert a b with 11 22 33 nil nil nil end)] 66 | -------------------------------------------------------------------------------- /tests/nil.out: -------------------------------------------------------------------------------- 1 | identity: 2 | "type" "nil" 3 | "falsiness" "falsey" 4 | "unlessiness" "" 0 "replace" 5 | "match self" 1 6 | "spread (right)" (0,1,0,1,0) 7 | "spread (left)" (0,0,1,0,1) 8 | "nil vs nothing" (1,1,0,0,()) 9 | "nil compare" 0 0 1 0 10 | "uninitialized var is" nil 11 | "default body of func" nil 12 | "default body of each" (nil,nil,nil) 13 | "default body of while" nil 14 | "default body of eval" nil 15 | "filter in list" (nil,nil) 16 | "filter out list" (11,22,33) 17 | "filter in dict" {nil:22} 18 | "filter out dict" {"A":11,"B":nil,"C":44} 19 | "filter in multi dict" {"A":11,nil:22} 20 | "filter out multi dict" {"B":nil,"C":44} 21 | 22 | coercion: 23 | "to num" 0 24 | "to str" "fuse" 25 | "to tab" insert with end 26 | 27 | outdexing: 28 | "firsts" nil nil nil 29 | "lasts" nil nil nil 30 | "outdex str" "A" "" "" "" 31 | "outdex list" 11 nil nil nil 32 | "outdex dict" 11 nil nil nil 33 | "outdex tab" nil nil nil 34 | "outdex image" 0 nil nil nil 35 | "outdex sound" 0 nil nil nil (nil,nil,0) (0,nil,nil,nil) 36 | "nindex str" "A" 37 | "nindex lst" 11 38 | "nindex dic" nil 39 | "nindex set str" {0:"A",1:"B",2:"C",nil:55} 40 | "nindex set lst" {0:11,1:22,2:33,nil:55} 41 | "nindex set dic" {"A":11,"B":22,nil:55} 42 | 43 | padding: 44 | "amend column" insert a b c with 11 22 55 33 44 nil end 45 | "flip list pad" ((11,44),(22,55),(nil,66)) 46 | "dict pad" {"A":11,"B":22,"C":nil} 47 | "take pad" (0,0,0) (nil,nil,nil) "" 48 | 49 | parseformat: 50 | "parse null" (nil,23) 51 | "format null" "null23" 52 | "parse invalid" nil 53 | "parse missing csv col" insert one two three with "A" 11 98.6 "B" 22 nil "C" 33 nil end 54 | 55 | fill: 56 | "fill empties" () "" {} insert with end 57 | "fill scalars" 33 0 "a" 58 | "fill lists" (12,36,13,36) ((11,37,33),(44,55),37,(66,37)) 59 | "fill dicts" {"A":11,"B":22,"C":38} 60 | "fill tabs" insert a b with 11 22 33 39 39 39 end 61 | -------------------------------------------------------------------------------- /tests/nocode.lil: -------------------------------------------------------------------------------- 1 | # just a comment -------------------------------------------------------------------------------- /tests/nocode.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tests/nocode.out -------------------------------------------------------------------------------- /tests/parseformat.out: -------------------------------------------------------------------------------- 1 | parse: 2 | (1,0,1,3) 3 | (1,0,0,0) 4 | (1,5) 5 | (0,1,0,0) 6 | (123,-5) 7 | (123,45,678) 8 | (171,171,171,171) 9 | (4207849487,4294967295) 10 | (2,5,1) 11 | (-3,2,0.3,5,0.997,11) 12 | (23,-3.5,420.69) 13 | (23,-3.5,420.69) 14 | (12.34,56.7) 15 | (2,6,1) 16 | ("one","two","three") 17 | ("ABC","DE") 18 | (65,66,67,68,69) 19 | (176,145,33) 20 | (57,55) 21 | (57) 22 | (1,0,1) 23 | "two" 24 | ("ONE","TWO","THREE","ẞẞÑÑ") 25 | ("one","two","three","ßßññ") 26 | "str\n" 27 | (nil,0) 28 | (nil,0) 29 | (nil,0) 30 | ("aGood_Nam3?",1) 31 | (nil,0) 32 | (("ab",1),("a",1),(nil,0)) 33 | ("debug",5) 34 | (1609556697,20) 35 | ({"year":2021,"month":1,"day":2,"hour":3,"minute":4,"second":57},1) 36 | (0,0) 37 | ({"year":0,"month":0,"day":0,"hour":0,"minute":0,"second":0},0) 38 | (1,2,10,0) 39 | (1,nil,nil,6,0) 40 | ("","A","AAAA") 41 | "ABBABA" 42 | ("TEXTUNTIL","BTEXT") 43 | "AAABBB" 44 | (0,0,1) 45 | ("AB","","AB") 46 | ("CD","","") 47 | " BC" 48 | ("","A","A") 49 | format: 50 | "one" 51 | "one" 52 | "ab%cd" 53 | "true,true,true,false" 54 | "42,-342,0" 55 | "4294967295" 56 | "ab AB 0" 57 | "-123.456:333" 58 | "-123.456/333.00" 59 | "$3.50,-$93.990" 60 | "3.50,-93.990" 61 | "one [two]" 62 | "one:TWO" 63 | "pięćßß:SZEŚĆẞẞ" 64 | "onb anylength" 65 | "onb anylength" 66 | "ABC" 67 | "ñÑ!" 68 | "A\n��C" 69 | "BC" 70 | " A" 71 | "A" 72 | "\"a\\n\\\"b\"" 73 | "fooBar" 74 | "2021-01-02T03:04:57Z" 75 | "2024-08-07T12:34:56Z" 76 | "2025-08-07T12:34:56Z" 77 | "2021-02-03T04:05:58Z" 78 | "1984-05-25T00:00:00Z" 79 | "0|1970-01-01T00:00:00Z|||42" 80 | " AB 10003" 81 | "00AB 10003" 82 | ":left : right:" 83 | "FACEB00F ffffffff" 84 | "2021-01-02" 85 | "longle,gright" 86 | " A|ABC|CDE" 87 | "A |ABC|ABC" 88 | "%34i" 89 | tabular: 90 | +----------+-------+-----+ 91 | | name | price | amt | 92 | +----------+-------+-----+ 93 | | "apple " | 1 | 1 | 94 | | "cherry" | 0.35 | 15 | 95 | | "banana" | 0.75 | 2 | 96 | +----------+-------+-----+ 97 | ("foo","bar") 98 | (12,0) 99 | named: 100 | {"banana":11,"apple":22} 101 | {"":"fruity"} 102 | {0:111,"n":"222",2:333} 103 | ({"one":34,"two":56},{"one":78,"two":99}) 104 | {} 105 | {} 106 | {"name":"two"} 107 | "fruit 22" 108 | "234" 109 | "111 222 333" 110 | "[333,444] [111,222]" 111 | ("333,111","444,222") 112 | "333,111\n444,222" 113 | "<>:0" 114 | "multiplicity:multiplicity" 115 | "0 0" 116 | json: 117 | "null" 118 | "42" 119 | "[11,22,33]" 120 | "\"string\\n with\\\"/escapes\\\\!\"" 121 | "\"<\\/script < \\/script < \\/ script\"" 122 | "{\"foo\":999}" 123 | "[{\"value\":11},{\"value\":22},{\"value\":33}]" 124 | "null" 125 | (1,nil,0) 126 | ((1,0,1),17) 127 | ((1,0,1),17) 128 | ((1,0),11) 129 | -23.4 130 | 23900 131 | ((12,13,nil),7) 132 | "foob" 133 | "A\\nB\\C/D\"" 134 | "DAB" 135 | "a b c\n1 2 3 4" 136 | "A B" 137 | "AB" 138 | {"a":11,"b":22} 139 | ({"a":23},8) 140 | ({"a":23},8) 141 | {1:5,2:99} 142 | {"foo":22,"bar":33} 143 | {"foo":22,"ba'r":33} 144 | "foobaz" 145 | {11:22} 146 | {11:22,33:44} 147 | ("A",nil,nil) 148 | ("A",12,"3") 149 | ("A"," 00","4") 150 | (nil,0) 151 | love: 152 | "{[11,22]:\"one\",[33,44]:\"two\"}" 153 | "<\"a\":[11,33],\"b\":[22,44]>" 154 | "%%IMG0AAMABAAAAAA=" 155 | "null" 156 | {(11,22):"one",(33,44):"two"} 157 | +----+----+ 158 | | a | b | 159 | +----+----+ 160 | | 11 | 22 | 161 | | 33 | 44 | 162 | +----+----+ 163 | ("%%IMG0AAMABAAAAAA=","%%IMG0AAMABAAAAAA=") 164 | deckroman: 165 | "��������� \n�������������������� !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~…ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõöøùúûüýþÿĀāĂ㥹ĆćĒēĘęĪīıŁłŃńŌōŐőŒœŚśŠšŪūŰűŸŹźŻżŽžȘșȚțẞ¡¿«»€°���������������" 166 | (127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234) 167 | "aŰbẞc" 168 | "a\nb" 169 | recursive: 170 | (11,"two") 171 | ("one.mp3","two.mp3","three.mp3") 172 | "011:022:033" 173 | ("(list 1, 2, 3)","(list 4, 5, 6)") 174 | "(list 1, 2, 3),\n(list 4, 5, 6)" 175 | ("U - 11","V - 22") 176 | "U - 11\nV - 22" 177 | -------------------------------------------------------------------------------- /tests/puzzles/top10.lil: -------------------------------------------------------------------------------- 1 | # codereport top 10 2 | # see https://github.com/codereport/top10/ 3 | 4 | px:pn:0 5 | on problem x name do 6 | px:x pn:name 7 | end 8 | on assert x y do 9 | if !x~y 10 | print["error in problem %i (%s)" px pn] 11 | print["got: %j\nexpected: %j" x y] 12 | exit[1] 13 | end 14 | end 15 | 16 | # some Q primitives / K compositions 17 | on reverse x do x @ (-1+count x)-range count x end # k: |: 18 | on such x do extract index where value from x end # k: &: 19 | on maxs x do r:0 each v in x r:r|v end end # k: |\ 20 | on deltas x do (first x), (1 drop x)-x end # k: -': 21 | on differ x do 1 ,!(1 drop x)=x end # k: !=': 22 | 23 | problem[1 "rain water"] 24 | on f1 x do 25 | sum mag@x-maxs[x]&reverse[maxs[reverse[x]]] 26 | end 27 | assert[f1[0,1,0,2,1,0,1,3,2,1,2,1] 6] 28 | assert[f1[4,2,0,3,2,5 ] 9] 29 | 30 | problem[2 "max consecutive ones"] 31 | on f2 x do 32 | r:0 max each v in x r:v*v+r end 33 | end 34 | assert[f2[1,1,0,1,1,1] 3] 35 | assert[f2[1,0,1,1,0,1] 2] 36 | 37 | problem[3 "longest continuous increasing subsequence"] 38 | on f3 x do 39 | r:0 1+max each v in(1 drop x)>x r:v*v+r end 40 | end 41 | assert[f3[1,3,5,4,7] 3] 42 | assert[f3[2,2,2,2,2] 1] 43 | 44 | problem[4 "maximum subarray sum"] 45 | on f4 x do 46 | r:0 max each v in x r:v|v+r end 47 | end 48 | assert[f4[-2,1,-3,4,-1,2,1,-5,4] 6] 49 | assert[f4[list 1 ] 1] 50 | assert[f4[5,4,-1,7,8 ] 23] 51 | 52 | problem[5 "sushi for two"] 53 | on f5 x do 54 | t:deltas[such[differ[x]],count x] 2*max(1 drop t)&t 55 | end 56 | assert[f5[2,2,2,1,1,2,2 ] 4] 57 | assert[f5[1,2,1,2,1,2 ] 2] 58 | assert[f5[2,2,1,1,1,2,2,2,2] 6] 59 | 60 | problem[6 "maximum gap"] 61 | on f6 x do 62 | max extract(1 drop value)-value orderby value asc from x 63 | end 64 | assert[f6[3,6,9,1] 3] 65 | assert[f6[list 10] 0] 66 | 67 | problem[7 "maximum gap count"] 68 | on f7 x do 69 | t:extract(1 drop value)-value orderby value asc from x sum t=max t 70 | end 71 | assert[f7[2,5,8,1] 2] 72 | assert[f7[list 3 ] 0] 73 | 74 | problem[8 "three consecutive odds"] 75 | on f8 x do 76 | r:0 !3>max each v in 2%x r:v*v+r end 77 | end 78 | assert[f8[2,6,4,1 ] 0] 79 | assert[f8[1,2,34,3,4,5,7,23,12] 1] 80 | 81 | problem[9 "skyline"] 82 | on f9 x do 83 | count maxs[x]dict 0 84 | end 85 | assert[f9[5,5,2,10,3,15,10] 3] 86 | assert[f9[4,3,1 ] 1] 87 | assert[f9[1,2,3,4,5 ] 5] 88 | 89 | problem[10 "ocean view"] 90 | on f10 x do 91 | such[reverse[deltas[maxs[reverse[x]]]]] 92 | end 93 | assert[f10[4,2,3,1] 0,2,3 ] 94 | assert[f10[4,3,2,1] 0,1,2,3] 95 | assert[f10[1,3,2,4] list 3 ] 96 | assert[f10[2,2,2,2] list 3 ] 97 | 98 | # implementation notes: 99 | # 1: Lil doesn't have 'abs', but it has magnitude (mag); this is equivalent for the scalar case, hence the @. 100 | # 2, 3, 4, 6, 8: Lil doesn't have scan, so we must use an each loop with an explicit carried variable. 101 | # 3, 5, 6, 7: Lil conforming over ragged vectors permits the (1 drop x) op x pattern to substitute for 'eachprior'. 102 | # 5, 10: Lil doesn't have 'where' as a primitive; it's integrated into query forms. (not ideal for code-golf!) 103 | # 9: Lil doesn't have 'distinct', but we can use the dictionary construction (dict) as a substitute. 104 | -------------------------------------------------------------------------------- /tests/queries.lil: -------------------------------------------------------------------------------- 1 | 2 | # even more dedicated query tests 3 | 4 | # multiple orderby: 5 | t1:insert name id with 6 | "Banana" 22 7 | "Durian" 11 8 | "Cherry" 33 9 | "Apple" 55 10 | "Apple" 44 11 | "Banana" 88 12 | end 13 | show[select name id orderby name asc orderby id desc from t1] 14 | 15 | # handling singles, empties: 16 | show[extract first value by value from 11,22,11] 17 | show[extract first value by value from 11] 18 | show[extract first value by value from ()] 19 | 20 | # tolerate undersized column expression results: 21 | show[extract id by () from t1] 22 | show[extract id where () from t1] 23 | show[extract id orderby () asc from t1] 24 | show[extract id by 7 from t1] 25 | show[extract id where 7 from t1] 26 | show[extract id orderby 7 asc from t1] 27 | show["evens" extract id where 1,0 from t1] 28 | show["odds" extract id where 0,1 from t1] 29 | 30 | # misc update scenarios: 31 | show[update name:"Best %s" format name where gindex=0 orderby id desc by name from t1] 32 | show[update name:"Best %s" format name where gindex=0 where 10 from denormal] 75 | show[first extract column from denormal] 76 | 77 | # group, then regroup 78 | t2:table("a","b","c")dict( 79 | (list "" split "ABABABABABABAB"), 80 | (list "" split "12341234123412"), 81 | (list "" split "abcdefghijklmn") 82 | ) 83 | show[select (first b) list group by b from t2] 84 | show[select where gindex=0 by b from t2] 85 | show[select where gindex=0 by a where gindex=0 by b from t2] 86 | 87 | # multiple where clauses for progressive narrowing 88 | tested:() 89 | on track x do tested:tested,count x x end 90 | show[select where track[b]="1" where track[a]="A" from t2] 91 | show[tested] 92 | 93 | # gindex is effectively "reset" at each clause: 94 | show[select a b gindex orderby a asc from t2] 95 | show[select a b gindex where a="A" from t2] 96 | 97 | # group with no rows: 98 | show[select by a from 0 take t2] 99 | show[select by a where a="Z" from t2] 100 | 101 | # insert edge-cases: 102 | show[insert with into 0] # no column names, empty 103 | show[insert with 42 end] # no column names, single 104 | show[insert a b c with 11 22 33 44 end] # ragged value count -> pad with nil 105 | show[insert a "quoted name" with 55 66 77 88 end] # quoted column names 106 | show[insert a a with "one" 11 "two" 22 end] # repeat column names: take the last one 107 | 108 | show[insert __proto__ with 11 22 end] # cursed column names 109 | -------------------------------------------------------------------------------- /tests/queries.out: -------------------------------------------------------------------------------- 1 | +----------+----+ 2 | | name | id | 3 | +----------+----+ 4 | | "Apple" | 55 | 5 | | "Apple" | 44 | 6 | | "Banana" | 88 | 7 | | "Banana" | 22 | 8 | | "Cherry" | 33 | 9 | | "Durian" | 11 | 10 | +----------+----+ 11 | (11,22) 12 | (11) 13 | () 14 | (22,11,33,55,44,88) 15 | () 16 | (22,11,33,55,44,88) 17 | (22,11,33,55,44,88) 18 | (22,11,33,55,44,88) 19 | (22,11,33,55,44,88) 20 | "evens" (22,33,44) 21 | "odds" (11,55,88) 22 | +---------------+----+ 23 | | name | id | 24 | +---------------+----+ 25 | | "Banana" | 22 | 26 | | "Best Durian" | 11 | 27 | | "Best Cherry" | 33 | 28 | | "Best Apple" | 55 | 29 | | "Apple" | 44 | 30 | | "Best Banana" | 88 | 31 | +---------------+----+ 32 | +---------------+----+ 33 | | name | id | 34 | +---------------+----+ 35 | | "Banana" | 22 | 36 | | "Durian" | 11 | 37 | | "Cherry" | 33 | 38 | | "Best Apple" | 55 | 39 | | "Apple" | 44 | 40 | | "Best Banana" | 88 | 41 | +---------------+----+ 42 | +----------+----+------+ 43 | | name | id | best | 44 | +----------+----+------+ 45 | | "Banana" | 22 | nil | 46 | | "Durian" | 11 | nil | 47 | | "Cherry" | 33 | nil | 48 | | "Apple" | 55 | 1 | 49 | | "Apple" | 44 | nil | 50 | | "Banana" | 88 | 1 | 51 | +----------+----+------+ 52 | (3,1,1,2,2,2) 53 | (1,3,3,2,2) 54 | (5,-1,4,4,-6,-6,1,1,1) 55 | +--------+ 56 | | name | 57 | +--------+ 58 | | "Sam" | 59 | | "Sara" | 60 | +--------+ 61 | +----------+-------+ 62 | | name | index | 63 | +----------+-------+ 64 | | "Alice" | 0 | 65 | | "Sam" | 1 | 66 | | "Sara" | 3 | 67 | | "Thomas" | 2 | 68 | | "Walter" | 4 | 69 | +----------+-------+ 70 | +----------+--------------+ 71 | | name | job | 72 | +----------+--------------+ 73 | | "Alice" | "Developer" | 74 | | "Sara" | "Developer" | 75 | | "Thomas" | "Developer" | 76 | | "Sam" | "Sales" | 77 | | "Walter" | "Accounting" | 78 | +----------+--------------+ 79 | +----------+--------------+ 80 | | name | job | 81 | +----------+--------------+ 82 | | "Walter" | "Accounting" | 83 | | "Alice" | "Developer" | 84 | | "Sara" | "Developer" | 85 | | "Thomas" | "Developer" | 86 | | "Sam" | "Sales" | 87 | +----------+--------------+ 88 | +----------+--------------+ 89 | | employed | job | 90 | +----------+--------------+ 91 | | 3 | "Developer" | 92 | | 1 | "Sales" | 93 | | 1 | "Accounting" | 94 | +----------+--------------+ 95 | {"v":("Accounting","Developer","Sales"),"i":(2,0,1)} 96 | ("Accounting","Sales","Developer") 97 | ((0,3,4),(1,2),(5)) 98 | (("A","B","C"),("D","E","F"),("G","H","I")) 99 | ("A","B","C") 100 | ("A") 101 | () 102 | +---------+----------+ 103 | | a | b | 104 | +---------+----------+ 105 | | "Alice" | "Joan" | 106 | | "Alice" | "Oscar" | 107 | | "Joan" | "Oscar" | 108 | | "Alice" | "Thomas" | 109 | | "Joan" | "Thomas" | 110 | | "Oscar" | "Thomas" | 111 | +---------+----------+ 112 | +---------------+-------+ 113 | | with "escapes | count | 114 | +---------------+-------+ 115 | | 1 | "B" | 116 | | 2 | "C" | 117 | +---------------+-------+ 118 | +---------------+-------+-------+--------+-------+ 119 | | with "escapes | count | index | gindex | group | 120 | +---------------+-------+-------+--------+-------+ 121 | | 0 | "A" | 0 | 0 | 0 | 122 | | 1 | "B" | 1 | 1 | 0 | 123 | | 2 | "C" | 2 | 2 | 0 | 124 | +---------------+-------+-------+--------+-------+ 125 | +-----+-----------+ 126 | | c0 | c1 | 127 | +-----+-----------+ 128 | | "1" | (0,0,0,0) | 129 | | "2" | (1,1,1,1) | 130 | | "3" | (2,2,2) | 131 | | "4" | (3,3,3) | 132 | +-----+-----------+ 133 | +-----+-----+-----+ 134 | | a | b | c | 135 | +-----+-----+-----+ 136 | | "A" | "1" | "a" | 137 | | "B" | "2" | "b" | 138 | | "A" | "3" | "c" | 139 | | "B" | "4" | "d" | 140 | +-----+-----+-----+ 141 | +-----+-----+-----+ 142 | | a | b | c | 143 | +-----+-----+-----+ 144 | | "A" | "1" | "a" | 145 | | "B" | "2" | "b" | 146 | +-----+-----+-----+ 147 | +-----+-----+-----+ 148 | | a | b | c | 149 | +-----+-----+-----+ 150 | | "A" | "1" | "a" | 151 | | "A" | "1" | "e" | 152 | | "A" | "1" | "i" | 153 | | "A" | "1" | "m" | 154 | +-----+-----+-----+ 155 | (14,7) 156 | +-----+-----+--------+ 157 | | a | b | gindex | 158 | +-----+-----+--------+ 159 | | "A" | "1" | 0 | 160 | | "A" | "3" | 1 | 161 | | "A" | "1" | 2 | 162 | | "A" | "3" | 3 | 163 | | "A" | "1" | 4 | 164 | | "A" | "3" | 5 | 165 | | "A" | "1" | 6 | 166 | | "B" | "2" | 7 | 167 | | "B" | "4" | 8 | 168 | | "B" | "2" | 9 | 169 | | "B" | "4" | 10 | 170 | | "B" | "2" | 11 | 171 | | "B" | "4" | 12 | 172 | | "B" | "2" | 13 | 173 | +-----+-----+--------+ 174 | +-----+-----+--------+ 175 | | a | b | gindex | 176 | +-----+-----+--------+ 177 | | "A" | "1" | 0 | 178 | | "A" | "3" | 1 | 179 | | "A" | "1" | 2 | 180 | | "A" | "3" | 3 | 181 | | "A" | "1" | 4 | 182 | | "A" | "3" | 5 | 183 | | "A" | "1" | 6 | 184 | +-----+-----+--------+ 185 | +---+---+---+ 186 | | a | b | c | 187 | +---+---+---+ 188 | +---+---+---+ 189 | | a | b | c | 190 | +---+---+---+ 191 | +-------+ 192 | | value | 193 | +-------+ 194 | +-------+ 195 | | value | 196 | +-------+ 197 | | 42 | 198 | +-------+ 199 | +----+-----+-----+ 200 | | a | b | c | 201 | +----+-----+-----+ 202 | | 11 | 22 | 33 | 203 | | 44 | nil | nil | 204 | +----+-----+-----+ 205 | +----+-------------+ 206 | | a | quoted name | 207 | +----+-------------+ 208 | | 55 | 66 | 209 | | 77 | 88 | 210 | +----+-------------+ 211 | +----+ 212 | | a | 213 | +----+ 214 | | 11 | 215 | | 22 | 216 | +----+ 217 | +-----------+ 218 | | __proto__ | 219 | +-----------+ 220 | | 11 | 221 | | 22 | 222 | +-----------+ 223 | -------------------------------------------------------------------------------- /tests/recursion.lil: -------------------------------------------------------------------------------- 1 | 2 | 3 | on addrec x y do 4 | if x>0 5 | 1+addrec[x-1 y] 6 | else 7 | y 8 | end 9 | end 10 | 11 | on addtail x y do 12 | if x>0 13 | addtail[x-1 y+1] 14 | else 15 | y 16 | end 17 | end 18 | 19 | print["non-tail recursion"] 20 | print[addrec[3 5]] 21 | 22 | print["tail recursion: shallow"] 23 | print[addtail[3 5]] 24 | 25 | print["deepest observed stack: %i" sys.workspace.depth] 26 | 27 | print["tail recursion: deep"] 28 | print[addtail[800 5]] 29 | 30 | print["deepest observed stack: %i" sys.workspace.depth] 31 | 32 | print["false tail-calls"] 33 | # this is a minimized example from a more elaborate 34 | # program posing problems. while loops create scopes, 35 | # they must not be treated as a possible site for a tail call: 36 | on poison do 37 | y:0 # if a tail call, this modifies the outer scope! 38 | end 39 | each y in range 3 40 | each x in list 1 poison[] end 41 | print[y] 42 | end 43 | # should print 44 | # 0 45 | # 1 46 | # 2 47 | 48 | on factrec n a do 49 | # somewhat overly verbose tail-recursive factorial, 50 | # demonstrating that ifelse interacts with TCE properly: 51 | if n=0 a 52 | elseif n=1 factrec[n-1 n*a] 53 | elseif n=2 factrec[n-1 n*a] 54 | else factrec[n-1 n*a] end 55 | end 56 | 57 | print[factrec[12 1]] 58 | print["deepest observed stack: %i" sys.workspace.depth] 59 | -------------------------------------------------------------------------------- /tests/recursion.out: -------------------------------------------------------------------------------- 1 | non-tail recursion 2 | 8 3 | tail recursion: shallow 4 | 8 5 | deepest observed stack: 6 6 | tail recursion: deep 7 | 805 8 | deepest observed stack: 6 9 | false tail-calls 10 | 0 11 | 1 12 | 2 13 | 479001600 14 | deepest observed stack: 6 15 | -------------------------------------------------------------------------------- /tests/rtext.lil: -------------------------------------------------------------------------------- 1 | 2 | # test suite for the rtext interface 3 | 4 | rt1:insert font arg text with 5 | "menu" "" "Hello" 6 | "" "" "how about " 7 | "mono" "" "some" 8 | "" "" " salsa?" 9 | end 10 | rt2:insert text arg font with 11 | " never " "" "" 12 | "mind." "the_brain_page" "" 13 | end 14 | 15 | show[rtext.end] 16 | show[rtext.len[rt1]] 17 | show[rtext.len[table ()]] 18 | show[rtext.make["simple"]] 19 | show[rtext.make["more" "menu" 23]] 20 | 21 | show[rtext.get[rt1 6]] 22 | show[rtext.get[rt1 18]] 23 | show[rtext.get[rt1 -5]] 24 | show[rtext.get[rt1 1000]] 25 | 26 | show[rtext.index[]] # bogus args 1 27 | show[rtext.index["one\ntwo\nthree" ]] # bogus args 2 28 | show[rtext.index["one\ntwo\nthree" 1,0 ]] # line number 29 | show[rtext.index["one\ntwo\nthree" 2,1 ]] # line, column 30 | show[rtext.index["one\ntwo\nthree" 4,0 ]] # pos clamp line number 31 | show[rtext.index["one\ntwo\nthree" -4,0]] # neg clamp line number 32 | show[rtext.index["one\ntwo\nthree" 1,6 ]] # pos clamp row number 33 | show[rtext.index["one\ntwo\nthree" 2,-5]] # neg clamp row number 34 | 35 | show[rtext.cat[insert text with "single" end]]# cast 36 | show[rtext.cat[rt1 rt2]] # catenate 37 | show[rtext.cat["one" rt2 "another"]] # catenate w/ strings 38 | show[rtext.cat[image[3,4]]] # cast to inline image 39 | 40 | show[rtext.string[rt1 4,19]] 41 | show[rtext.span[rt1 8,12]] # onechunk 42 | show[rtext.span[rt1 4,19]] # multichunk 43 | show[rtext.span[rtext.make[""] 0,0 ]] # empty start 44 | show[rtext.span[rtext.make[""] 0,rtext.end]] # empty end 45 | 46 | d1:rtext.cat[ 47 | rtext.make["first|" "f0"] 48 | rtext.make["second|third" "f1"] 49 | rtext.make["|fourth" "f2"] 50 | ] 51 | d2:rtext.cat[ 52 | rtext.make["first one\n\nsecond" "f1"] 53 | rtext.make[" one" "f2"] 54 | rtext.make["\n\nthird one\n\n" "f3"] 55 | ] 56 | show[rtext.split[]] 57 | show[rtext.split["bogus"]] 58 | show[rtext.split["" d1]] 59 | show[first rtext.split["not present" d1]] 60 | print["--d1--"] each v in rtext.split["|" d1] show[v] end 61 | print["--d2--"] each v in rtext.split["\n\n" d2] show[v] end 62 | 63 | norm:select text font arg from rt1 64 | show[rt1~rtext.replace[rt1]] # bogus args 65 | show[rtext.cat[rt1]~rtext.replace[rt1 "" "NEVER"]] # empty key 66 | show[norm~rtext.replace[rt1 "bogus" "AAAAAAAA"]] # no matches 67 | show[rtext.replace[rt1 "salsa" "pineapple"]] # simple a->b 68 | show[rtext.replace[rt1 "sa?" "{C}"]] # trailing replacement 69 | show[rtext.replace[rt1 "l" "{L}"]] # adjacent replacement 70 | show[rtext.replace[rt1 ("Hello","about") "?!?"]] # multiple keys, one value 71 | show[rtext.replace[rt1 ("Hello","about") ("{A}","{B}")]] # multiple keys, multiple values 72 | show[rtext.replace[rt1 "about" rtext.make["ABOUT" "menu" "http://about.com"]]] # rtext replacement 73 | show[rtext.replace["Some some somE none" "some" "Q" 1]] # case-insensitive 74 | 75 | show[rtext.find[]] # bogus args 1 76 | show[rtext.find[rt1]] # bogus args 2 77 | show[rtext.find["ABC" ""]] # empty key 78 | show[rtext.find[rt1 "how"]] # find word 79 | show[rtext.find["one two one one" "one"]] # find words 80 | show[rtext.find["one two three" "nope"]] # no match 81 | show[rtext.find["eye\nbee\nsea" "\n"]] # find newlines 82 | show[rtext.find["one two one three one" ("two","three")]] # multiple keys 83 | show[rtext.find["one a two A A three a" "a" 0]] # case-sensitive 84 | show[rtext.find["one a two A A three a" "a" 1]] # case-insensitive 85 | show[rtext.find["aaaaaa" "aaa"]] # matches don't overlap 86 | -------------------------------------------------------------------------------- /tests/rtext.out: -------------------------------------------------------------------------------- 1 | 2147483647 2 | 26 3 | 0 4 | +----------+------+-----+ 5 | | text | font | arg | 6 | +----------+------+-----+ 7 | | "simple" | "" | "" | 8 | +----------+------+-----+ 9 | +--------+--------+------+ 10 | | text | font | arg | 11 | +--------+--------+------+ 12 | | "more" | "menu" | "23" | 13 | +--------+--------+------+ 14 | 1 15 | 2 16 | 0 17 | -1 18 | 0 19 | 0 20 | 4 21 | 9 22 | 13 23 | 0 24 | 7 25 | 8 26 | +----------+------+-----+ 27 | | text | font | arg | 28 | +----------+------+-----+ 29 | | "single" | "" | "" | 30 | +----------+------+-----+ 31 | +------------------+--------+------------------+ 32 | | text | font | arg | 33 | +------------------+--------+------------------+ 34 | | "Hello" | "menu" | "" | 35 | | "how about " | "" | "" | 36 | | "some" | "mono" | "" | 37 | | " salsa? never " | "" | "" | 38 | | "mind." | "" | "the_brain_page" | 39 | +------------------+--------+------------------+ 40 | +--------------+------+------------------+ 41 | | text | font | arg | 42 | +--------------+------+------------------+ 43 | | "one never " | "" | "" | 44 | | "mind." | "" | "the_brain_page" | 45 | | "another" | "" | "" | 46 | +--------------+------+------------------+ 47 | +------+------+---------+ 48 | | text | font | arg | 49 | +------+------+---------+ 50 | | "i" | "" | | 51 | +------+------+---------+ 52 | "ohow about some" 53 | +------+-----+--------+ 54 | | font | arg | text | 55 | +------+-----+--------+ 56 | | "" | "" | " abo" | 57 | +------+-----+--------+ 58 | +--------+-----+--------------+ 59 | | font | arg | text | 60 | +--------+-----+--------------+ 61 | | "menu" | "" | "o" | 62 | | "" | "" | "how about " | 63 | | "mono" | "" | "some" | 64 | +--------+-----+--------------+ 65 | +------+------+-----+ 66 | | text | font | arg | 67 | +------+------+-----+ 68 | +------+------+-----+ 69 | | text | font | arg | 70 | +------+------+-----+ 71 | () 72 | () 73 | () 74 | +----------------+------+-----+ 75 | | text | font | arg | 76 | +----------------+------+-----+ 77 | | "first|" | "f0" | "" | 78 | | "second|third" | "f1" | "" | 79 | | "|fourth" | "f2" | "" | 80 | +----------------+------+-----+ 81 | --d1-- 82 | +---------+------+-----+ 83 | | text | font | arg | 84 | +---------+------+-----+ 85 | | "first" | "f0" | "" | 86 | +---------+------+-----+ 87 | +----------+------+-----+ 88 | | text | font | arg | 89 | +----------+------+-----+ 90 | | "second" | "f1" | "" | 91 | +----------+------+-----+ 92 | +---------+------+-----+ 93 | | text | font | arg | 94 | +---------+------+-----+ 95 | | "third" | "f1" | "" | 96 | +---------+------+-----+ 97 | +----------+------+-----+ 98 | | text | font | arg | 99 | +----------+------+-----+ 100 | | "fourth" | "f2" | "" | 101 | +----------+------+-----+ 102 | --d2-- 103 | +-------------+------+-----+ 104 | | text | font | arg | 105 | +-------------+------+-----+ 106 | | "first one" | "f1" | "" | 107 | +-------------+------+-----+ 108 | +----------+------+-----+ 109 | | text | font | arg | 110 | +----------+------+-----+ 111 | | "second" | "f1" | "" | 112 | | " one" | "f2" | "" | 113 | +----------+------+-----+ 114 | +-------------+------+-----+ 115 | | text | font | arg | 116 | +-------------+------+-----+ 117 | | "third one" | "f3" | "" | 118 | +-------------+------+-----+ 119 | +------+------+-----+ 120 | | text | font | arg | 121 | +------+------+-----+ 122 | 1 123 | 1 124 | 1 125 | +---------------+--------+-----+ 126 | | text | font | arg | 127 | +---------------+--------+-----+ 128 | | "Hello" | "menu" | "" | 129 | | "how about " | "" | "" | 130 | | "some" | "mono" | "" | 131 | | " pineapple?" | "" | "" | 132 | +---------------+--------+-----+ 133 | +--------------+--------+-----+ 134 | | text | font | arg | 135 | +--------------+--------+-----+ 136 | | "Hello" | "menu" | "" | 137 | | "how about " | "" | "" | 138 | | "some" | "mono" | "" | 139 | | " sal{C}" | "" | "" | 140 | +--------------+--------+-----+ 141 | +--------------+--------+-----+ 142 | | text | font | arg | 143 | +--------------+--------+-----+ 144 | | "He" | "menu" | "" | 145 | | "{L}{L}" | "" | "" | 146 | | "o" | "menu" | "" | 147 | | "how about " | "" | "" | 148 | | "some" | "mono" | "" | 149 | | " sa{L}sa?" | "" | "" | 150 | +--------------+--------+-----+ 151 | +---------------+--------+-----+ 152 | | text | font | arg | 153 | +---------------+--------+-----+ 154 | | "?!?how ?!? " | "" | "" | 155 | | "some" | "mono" | "" | 156 | | " salsa?" | "" | "" | 157 | +---------------+--------+-----+ 158 | +---------------+--------+-----+ 159 | | text | font | arg | 160 | +---------------+--------+-----+ 161 | | "{A}how {B} " | "" | "" | 162 | | "some" | "mono" | "" | 163 | | " salsa?" | "" | "" | 164 | +---------------+--------+-----+ 165 | +-----------+--------+--------------------+ 166 | | text | font | arg | 167 | +-----------+--------+--------------------+ 168 | | "Hello" | "menu" | "" | 169 | | "how " | "" | "" | 170 | | "ABOUT" | "menu" | "http://about.com" | 171 | | " " | "" | "" | 172 | | "some" | "mono" | "" | 173 | | " salsa?" | "" | "" | 174 | +-----------+--------+--------------------+ 175 | +--------------+------+-----+ 176 | | text | font | arg | 177 | +--------------+------+-----+ 178 | | "Q Q Q none" | "" | "" | 179 | +--------------+------+-----+ 180 | () 181 | () 182 | ((0,0),(1,1),(2,2)) 183 | ((5,8)) 184 | ((0,3),(8,11),(12,15)) 185 | () 186 | ((3,4),(7,8)) 187 | ((4,7),(12,17)) 188 | ((4,5),(20,21)) 189 | ((4,5),(10,11),(12,13),(20,21)) 190 | ((0,3),(3,6)) 191 | -------------------------------------------------------------------------------- /tests/smalltests.lil: -------------------------------------------------------------------------------- 1 | 2 | # very simple test cases 3 | # for early stages of bringing up an interpreter 4 | 5 | # unary, binary, order of operations 6 | show[-25] # -25 7 | show[3*5-4] # 3 8 | show[(3*5)-4] # 11 9 | show[1,2,3] # (1,2,3) 10 | 11 | # string literals 12 | show["Some Text\nWith accented chars: ñ"] 13 | 14 | # conditionals and fallthrough 15 | show[if 1<3 222 else 333 end] # 222 16 | show[if 1>3 222 else 333 end] # 333 17 | show[if 1>4 222 end ] # nil 18 | 19 | # last item in block 20 | show[if 1 2+2 3*5 end ] # 15 21 | 22 | # simple set and get 23 | show[a:234] # 234 24 | show[100+a] # 334 25 | 26 | # indexed set and get 27 | show[b.foo.bar:"quuz"] # {"foo":{"bar":"quuz"}} 28 | show[b.foo ] # {"bar":"quuz"} 29 | show[b.foo.bar ] # "quuz" 30 | 31 | # basic while loop 32 | i:1 show[while i<7 show[i] i:i*2 end] 33 | # 1 34 | # 2 35 | # 4 36 | # 8 37 | 38 | # basic each loop 39 | show[each x in 3,5,7 show[100+x] 50+x end] 40 | # 103 41 | # 105 42 | # 107 43 | # (53,55,57) 44 | 45 | # iteration over a dictionary 46 | each v k i in ("foo","bar") dict 11,22 show[v,k,i] end 47 | # (11,"foo",0) 48 | # (22,"bar",1) 49 | 50 | # basic definition and call 51 | show[on plustwice x y do x+y,y end] 52 | show[plustwice[3 500]] # (503,503) 53 | 54 | # insert from nothing 55 | show[t:insert name job with 56 | "Sara" "Programmer" 57 | "Eric" "Programmer" 58 | "Laura" "IT" 59 | "Alice" "Manager" 60 | end] 61 | #+---------+--------------+ 62 | #| name | job | 63 | #+---------+--------------+ 64 | #| "Sara" | "Programmer" | 65 | #| "Eric" | "Programmer" | 66 | #| "Laura" | "IT" | 67 | #| "Alice" | "Manager" | 68 | #+---------+--------------+ 69 | 70 | # select 71 | show[select name where job="Programmer" orderby name asc from t] 72 | #+--------+ 73 | #| name | 74 | #+--------+ 75 | #| "Eric" | 76 | #| "Sara" | 77 | #+--------+ 78 | 79 | # update 80 | show[update dep_count:count job by job from t] 81 | #+---------+--------------+-----------+ 82 | #| name | job | dep_count | 83 | #+---------+--------------+-----------+ 84 | #| "Sara" | "Programmer" | 2 | 85 | #| "Eric" | "Programmer" | 2 | 86 | #| "Laura" | "IT" | 1 | 87 | #| "Alice" | "Manager" | 1 | 88 | #+---------+--------------+-----------+ 89 | 90 | # extract 91 | show[extract name orderby name asc from t] 92 | #("Alice","Eric","Laura","Sara") 93 | show[extract value by value from "ABBABC"] 94 | #("A","A","B","B","B","C") 95 | -------------------------------------------------------------------------------- /tests/smalltests.out: -------------------------------------------------------------------------------- 1 | -25 2 | 3 3 | 11 4 | (1,2,3) 5 | "Some Text\nWith accented chars: ñ" 6 | 222 7 | 333 8 | 9 | 15 10 | 234 11 | 334 12 | {"foo":{"bar":"quuz"}} 13 | {"bar":"quuz"} 14 | "quuz" 15 | 1 16 | 2 17 | 4 18 | 8 19 | 103 20 | 105 21 | 107 22 | (53,55,57) 23 | (11,"foo",0) 24 | (22,"bar",1) 25 | on plustwice x y do ... end 26 | (503,503) 27 | +---------+--------------+ 28 | | name | job | 29 | +---------+--------------+ 30 | | "Sara" | "Programmer" | 31 | | "Eric" | "Programmer" | 32 | | "Laura" | "IT" | 33 | | "Alice" | "Manager" | 34 | +---------+--------------+ 35 | +--------+ 36 | | name | 37 | +--------+ 38 | | "Eric" | 39 | | "Sara" | 40 | +--------+ 41 | +---------+--------------+-----------+ 42 | | name | job | dep_count | 43 | +---------+--------------+-----------+ 44 | | "Sara" | "Programmer" | 2 | 45 | | "Eric" | "Programmer" | 2 | 46 | | "Laura" | "IT" | 1 | 47 | | "Alice" | "Manager" | 1 | 48 | +---------+--------------+-----------+ 49 | ("Alice","Eric","Laura","Sara") 50 | ("A","A","B","B","B","C") 51 | -------------------------------------------------------------------------------- /tests/spread.lil: -------------------------------------------------------------------------------- 1 | 2 | # test double-dot "spread" indexing 3 | 4 | # dictionaries 5 | d1:("a","b")dict "one","two" 6 | d2:("a","b")dict "three","four" 7 | d3:("a","b")dict "five","six" 8 | ds:(list d1),(list d2),(list d3) 9 | show[ds..a] # extract a column 10 | show[ds..a:"new"] # amend-each dict 11 | show[ds] # spreading assignments don't modify the source object; 12 | ds:ds..a:"new" show[ds] # ...unless you have an explicit leading assign. 13 | 14 | # lists 15 | ls:(list 11,22,33,44),(list 55,66,77,88) 16 | show[ls.[2]] # extract a column 17 | 18 | # lists-of-tables 19 | t1:table "ABC" cross "DE" 20 | t2:table "MN" cross "Q" 21 | ts:(list t1),(list t2) 22 | show[ts..c1] # extract columns 23 | show[ts..c1[1]] # extract cell from each column 24 | 25 | # tables 26 | show[t1.[1]] # extract the first row 27 | 28 | # nesting 29 | show[((range 3)..a:99)..b:32] 30 | 31 | # old versions of lil accidentally shadowed a local 'x': 32 | local x:5 33 | show[ds..a:x] 34 | -------------------------------------------------------------------------------- /tests/spread.out: -------------------------------------------------------------------------------- 1 | ("one","three","five") 2 | ({"a":"new","b":"two"},{"a":"new","b":"four"},{"a":"new","b":"six"}) 3 | ({"a":"one","b":"two"},{"a":"three","b":"four"},{"a":"five","b":"six"}) 4 | ({"a":"new","b":"two"},{"a":"new","b":"four"},{"a":"new","b":"six"}) 5 | (33,77) 6 | (("D","D","D","E","E","E"),("Q","Q")) 7 | ("D","Q") 8 | ("B","D") 9 | ({"a":99,"b":32},{"a":99,"b":32},{"a":99,"b":32}) 10 | ({"a":5,"b":"two"},{"a":5,"b":"four"},{"a":5,"b":"six"}) 11 | -------------------------------------------------------------------------------- /tests/xml.lil: -------------------------------------------------------------------------------- 1 | 2 | on header name do 3 | print["%s:\n%s" name (1+count name)take"="] 4 | end 5 | 6 | # ----- format ------------- 7 | 8 | header["lists"] 9 | print["%s\n" writexml["one","two","three" 1]] 10 | 11 | header["string escaping"] 12 | print["%s\n" writexml["An escaped ." 1]] 13 | 14 | header["self-closing tag with attrs"] 15 | foo.tag:"a" 16 | foo.attr.href:"http://beyondloom.com" 17 | foo.attr.target:"_blank" 18 | print[writexml[foo 1]] 19 | 20 | header["child tags"] 21 | foo.children[0].tag:"baz" 22 | foo.children[1]:"plain text" 23 | foo.children[2].tag:"quux" 24 | print[writexml[foo 1]] 25 | 26 | header["nested tags"] 27 | foo.children[0].children[0].tag:"zami" 28 | foo.children[0].children[0].attr.boof:99 29 | print[writexml[foo 1]] 30 | 31 | header["no formatting"] 32 | print[writexml[foo]] 33 | 34 | header["smuggling in arrays"] 35 | print[writexml["string with < text and a literal <.",array[].cat["string with < entity and a literal <."]]] 36 | 37 | # ----- parse -------------- 38 | 39 | header["simple tags"] 40 | show[readxml[" "]] 41 | 42 | header["nested tags + body text"] 43 | show[readxml["text"]] 44 | 45 | header["collapse whitespace"] 46 | show[readxml["some words\n and so on"]] 47 | 48 | header["decode entities"] 49 | show[readxml["fish & chips!"]] 50 | 51 | header["mixed body text"] 52 | show[readxml["some text bolded or italicized"]] 53 | 54 | header["attrs"] 55 | show[readxml[""]] 56 | 57 | header["comments"] 58 | show[readxml["text bolded"]] 59 | 60 | header["prolog"] 61 | show[readxml[" "]] 62 | 63 | header["cdata"] 64 | show[readxml[" text!]]>"]] 65 | 66 | header["unterminated cdata"] 67 | show[readxml[""]] 71 | 72 | header["empty"] 73 | show[readxml[" "]] 74 | 75 | # ----- squash -------------- 76 | 77 | # transform rss-style XML into a more compact structure 78 | # and along the way, demonstrate how much of a pain in the ass 79 | # it is to do nontrivial XML rewrites... 80 | 81 | on squash x do 82 | on delist x do if 1=count x first x else x end end 83 | on gather x do list if 1=count x squash[first x] else raze squash[x] end end 84 | on union x do 85 | r:() 86 | each c in x.children 87 | r[c.tag]:(() unless r[c.tag]),gather[c.children] 88 | end 89 | r:delist @ r 90 | end 91 | on squash x do 92 | case.dict:on _ x do 93 | rr[x.tag]:if x.children 94 | r:if 1=count x.children squash[first x.children] else union[x] end 95 | if "string"~typeof r r else x.attr,r end 96 | else 97 | x.attr 98 | end 99 | end 100 | case.string:on _ x do x end 101 | case.list :on _ x do delist[squash @ x] end 102 | case[typeof x][x] 103 | end 104 | squash[x] 105 | end 106 | 107 | print[""] 108 | header["fun with squashing"] 109 | show[squash[readxml["ChannelNameen"]]] 110 | 111 | show[squash[readxml[""]]] 112 | 113 | show[squash[readxml["ChannelNameen"]]] 114 | 115 | show[squash[readxml["FirstSecond"]]] 116 | 117 | show[squash[readxml["FirstSecond"]]] 118 | 119 | show[squash[readxml["Firstgoogle.comSecondexample.com"]]] 120 | -------------------------------------------------------------------------------- /tests/xml.out: -------------------------------------------------------------------------------- 1 | lists: 2 | ====== 3 | onetwothree 4 | 5 | string escaping: 6 | ================ 7 | An escaped </tag>. 8 | 9 | self-closing tag with attrs: 10 | ============================ 11 | 12 | 13 | child tags: 14 | =========== 15 | 16 | 17 | plain text 18 | 19 | 20 | 21 | nested tags: 22 | ============ 23 | 24 | 25 | 26 | 27 | plain text 28 | 29 | 30 | 31 | no formatting: 32 | ============== 33 | plain text 34 | smuggling in arrays: 35 | ==================== 36 | string with &lt; text and a literal <.string with < entity and a literal <. 37 | simple tags: 38 | ============ 39 | ({"tag":"foo","attr":{},"children":()},{"tag":"bar","attr":{},"children":()}) 40 | nested tags + body text: 41 | ======================== 42 | ({"tag":"foo","attr":{},"children":({"tag":"bar","attr":{},"children":("text")})}) 43 | collapse whitespace: 44 | ==================== 45 | ("some words and so on") 46 | decode entities: 47 | ================ 48 | ("fish & chips!") 49 | mixed body text: 50 | ================ 51 | ("some text ",{"tag":"b","attr":{},"children":("bolded")}," or ",{"tag":"i","attr":{},"children":("italicized")}) 52 | attrs: 53 | ====== 54 | ({"tag":"foo","attr":{"zero":1,"one":"2","two":"tw\"o","three":"thr'ee","four":"a&b"},"children":()}) 55 | comments: 56 | ========= 57 | ("text ",{"tag":"b","attr":{},"children":("bolded")}) 58 | prolog: 59 | ======= 60 | ({"tag":"boop","attr":{},"children":()}) 61 | cdata: 62 | ====== 63 | ({"tag":"foo","attr":{},"children":("some text!")}) 64 | unterminated cdata: 65 | =================== 66 | ({"tag":"foo","attr":{},"children":("some text")}) 67 | no tag name: 68 | ============ 69 | ({"tag":"","attr":{},"children":()}) 70 | empty: 71 | ====== 72 | (" ") 73 | 74 | fun with squashing: 75 | =================== 76 | {"channel":{"title":"ChannelName","language":"en"}} 77 | {"rss":{"version":"2.0"}} 78 | {"rss":{"version":"2.0","channel":{"title":"ChannelName","language":"en"}}} 79 | {"channel":{"item":({"title":"First"},{"title":"Second"})}} 80 | ({"item":{"title":"First"}},{"item":{"title":"Second"}}) 81 | {"channel":{"item":({"title":"First","url":"google.com"},{"title":"Second","url":"example.com"})}} 82 | -------------------------------------------------------------------------------- /tools/awk/Readme.md: -------------------------------------------------------------------------------- 1 | Lila: A Lil interpreter in Awk 2 | ============================== 3 | Lila is an interpreter for the [Lil](http://beyondloom.com/tools/trylil.html) programming language implemented (mostly) in POSIX Awk. To learn more, see [the blog post](http://beyondloom.com/blog/lila.html) describing this project. 4 | 5 | *Note: As of Decker v1.54, Lila is no longer actively maintained and kept in sync with newer revisions of Lil; if there's demand, I may resume work on this sub-project.* 6 | 7 | Invoked with a `.lil` filename, Lila will execute the script and exit. If no filename is provided, it will instead run in an interactive "[REPL](https://en.wikipedia.org/wiki/Read–eval–print_loop_)" mode which can be terminated with Ctrl+C. Using the optional `rlwrap` utility as an intermediary provides a much nicer REPL experience, with line-editing and command history: 8 | ``` 9 | % rlwrap awk -f lila.awk 10 | range 10 11 | (0,1,2,3,4,5,6,7,8,9) 12 | 13 | select index*2 value from "ABCD" 14 | +-------+-------+ 15 | | index | value | 16 | +-------+-------+ 17 | | 0 | "A" | 18 | | 2 | "B" | 19 | | 4 | "C" | 20 | | 6 | "D" | 21 | +-------+-------+ 22 | 23 | 24 | % cat demo.lil 25 | print["Goodbye, cruel world."] 26 | 27 | % awk -f lila.awk demo.lil 28 | Goodbye, cruel world. 29 | % 30 | ``` 31 | 32 | You can run Lila against an appropriate subset of Decker's test suite from the top directory of this repository: 33 | ``` 34 | % make testawk 35 | running tests against awk -f tools/awk/lila.awk... 36 | all interpreter tests passed. 37 | all array tests passed. 38 | all image tests passed. 39 | all integration tests passed. 40 | ``` 41 | 42 | Lila has been tested against several Awk implementations: 43 | 44 | - `awk` 20200816 45 | - `awk` 20070501 46 | - `mawk` v1.3.4 47 | - `goawk` v1.29.0 48 | - `gawk` v5.3.0 (requires the additional `-b` flag to disable locale-specific character handling) 49 | - `gawk` v4.0.1 (see above) 50 | 51 | Lila includes a large subset of the standard library provided with [Lilt](http://beyondloom.com/decker/lilt.html), with the following omissions, limitations, and caveats: 52 | 53 | - `readdeck[]`, `writedeck[]`, and the interfaces that make up the [Decker](http://beyondloom.com/decker/) document model are not present. 54 | - `sound[]` and the `sound` interface are not present. 55 | - `read[]` and `write[]` do not support import or export of `.WAV` audio or `.GIF` images. 56 | - `write[]` always returns `1`. 57 | - `shell[]` always reports exit codes as `-1`. 58 | - `path[]` is not present. 59 | - `sys.ms` only provides per-second granularity. 60 | - `sys.platform` reports `"awk"`. 61 | 62 | Lila shells out to common utilities for functionality that is not possible (portably) in pure Awk: 63 | 64 | - `od` for reading raw bytes from files. 65 | - `ls` to enumerate directory contents for `dir[]`. 66 | - `date` for performing some date-time formatting. 67 | -------------------------------------------------------------------------------- /tools/twine/Readme.md: -------------------------------------------------------------------------------- 1 | Ply: A Decker-Compatible Twine Story Format 2 | =========================================== 3 | [Twine](https://twinery.org) offers an extensible universe of "[story formats](https://twinery.org/cookbook/introduction/story_formats.html)" with their own markup and semantics. Most of the popular story formats include support for web browser features- like embedded JavaScript and CSS styling- which preclude full support for them within Decker. 4 | 5 | In the interest of offering a lowest common denominator, I have defined an excruciatingly simple story format called _Ply_: 6 | 7 | - Text surrounded by `*asterisks*` is *bolded*. 8 | - Text surrounded by `` `backticks` `` is `fixed-width`. 9 | - Text surrounded by curly braces `{2+3}` is a fragment of Lil source code, which is evaluated and inserted in-place. 10 | - Links match Harlowe syntax: `[[Link Label->Destination Passage]]`. 11 | 12 | You can reference a compiled version of this story format suitable for loading into Twine at: 13 | ``` 14 | https://beyondloom.com/decker/ply/format.js 15 | ``` 16 | 17 | To build this story format from source, you will need Lilt: 18 | ``` 19 | lilt build_ply.lil 20 | ``` 21 | 22 | For more information, see [the twee module](https://beyondloom.com/decker/twee.html). 23 | -------------------------------------------------------------------------------- /tools/twine/build_ply.lil: -------------------------------------------------------------------------------- 1 | # generate the Ply story mode for Twine: 2 | 3 | lib:read["../../js/lil.js"] 4 | template:"{{LIL_INTEPRETER}}" split read["template.html"] 5 | compiled:"" fuse template[0],lib,template[1] 6 | 7 | payload.name :"Ply" 8 | payload.version :"1.0.3" 9 | payload.author :"John Earnest" 10 | payload.description:"A Decker-compatible Story Format" 11 | payload.url :"https://beyondloom/decker/ply/" 12 | payload.image :"logo.gif" 13 | payload.license :"MIT" 14 | payload.proofing :0 15 | payload.source :compiled 16 | write["format.js" "window.storyFormat(%j);\n" format payload] 17 | -------------------------------------------------------------------------------- /tools/twine/logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JohnEarnest/Decker/b09e460494568dca170975e1d216783d005790e0/tools/twine/logo.gif -------------------------------------------------------------------------------- /tools/twine/template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{STORY_TITLE}} 4 | 5 | 6 |
7 |
8 | 9 | 10 | 13 | 162 | -------------------------------------------------------------------------------- /x-decker.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Decker document 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /xdg-install.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # install application icon 4 | xdg-icon-resource install --novendor --context apps --size 32 icon_32x32.png decker 5 | xdg-icon-resource install --novendor --context apps --size 64 icon_64x64.png decker 6 | xdg-icon-resource install --novendor --context apps --size 128 icon_128x128.png decker 7 | xdg-icon-resource install --novendor --context apps --size 192 icon_192x192.png decker 8 | xdg-icon-resource install --novendor --context apps --size 256 icon_256x256.png decker 9 | xdg-icon-resource install --novendor --context apps --size 512 icon_512x512.png decker 10 | 11 | # install mime type icon 12 | xdg-icon-resource install --context mimetypes --size 32 icon_32x32.png x-decker 13 | xdg-icon-resource install --context mimetypes --size 64 icon_64x64.png x-decker 14 | xdg-icon-resource install --context mimetypes --size 128 icon_128x128.png x-decker 15 | xdg-icon-resource install --context mimetypes --size 192 icon_192x192.png x-decker 16 | xdg-icon-resource install --context mimetypes --size 256 icon_256x256.png x-decker 17 | xdg-icon-resource install --context mimetypes --size 512 icon_512x512.png x-decker 18 | 19 | # register mime type 20 | xdg-mime install x-decker.xml 21 | 22 | # register application launcher 23 | xdg-desktop-menu install --novendor Decker.desktop 24 | --------------------------------------------------------------------------------