From 839b376d73c7caf2d3aabf72fc33e3caa4172741 Mon Sep 17 00:00:00 2001 From: Simon Forman Date: Sat, 15 Jan 2022 17:23:11 -0800 Subject: [PATCH] Bring in the Prolog impl. --- implementations/Prolog/CHANGELOG | 56 + implementations/Prolog/README | 211 + .../docs/dev-guide/partial-reduction.md | 7 + .../YUNO-color-arrows-right-like-this.PNG | Bin 0 -> 47123 bytes .../docs/notes/YUNO-color-arrows-right.PNG | Bin 0 -> 73742 bytes .../Prolog/docs/notes/abs-def-vs-func | 310 + .../Prolog/docs/notes/minimal-basis | 4 + .../Prolog/docs/notes/on-square-spiral.md | 225 + .../Prolog/docs/notes/semantics.md | 61 + .../reference/FORMAT-Functor-Reference.md | 39 + .../Prolog/docs/reference/FuncRef.html | 353 + .../docs/reference/Functor-Reference.md | 355 + .../Prolog/docs/reference/Makefile | 3 + implementations/Prolog/docs/reference/app1.md | 26 + implementations/Prolog/docs/reference/b.md | 33 + .../Prolog/docs/reference/binary.md | 28 + .../Prolog/docs/reference/bleah.txt | 20 + .../Prolog/docs/reference/ccons.md | 24 + implementations/Prolog/docs/reference/cons.md | 27 + .../Prolog/docs/reference/foobar.txt | 98 + implementations/Prolog/docs/reference/i.md | 22 + .../Prolog/docs/reference/infra.md | 29 + .../Prolog/docs/reference/not_negative.md | 24 + .../Prolog/docs/reference/nullary.md | 37 + .../Prolog/docs/reference/ternary.md | 29 + .../Prolog/docs/reference/unary.md | 27 + .../Prolog/docs/reference/uncons.md | 25 + implementations/Prolog/docs/reference/x.md | 18 + .../Prolog/docs/user-guide/user-guide.md | 167 + implementations/Prolog/issues/boolean-prefix | 21 + implementations/Prolog/issues/disenstacken | 2 + .../Prolog/issues/document-all-functions | 132 + implementations/Prolog/issues/funcs-nooooo | 18 + .../Prolog/issues/interpolation-literal | 13 + implementations/Prolog/jd.dot | 306 + implementations/Prolog/jd.dot.svg | 2671 +++++++ implementations/Prolog/source/agl.PNG | Bin 0 -> 63042 bytes implementations/Prolog/source/b-joy.pl | 228 + .../Prolog/source/bleah.code-workspace | 14 + implementations/Prolog/source/canhazmd.pl | 45 + implementations/Prolog/source/client/elm.json | 24 + .../Prolog/source/client/index.html | 6127 +++++++++++++++++ .../Prolog/source/client/src/Main.elm | 116 + implementations/Prolog/source/defs.txt | 115 + implementations/Prolog/source/derp.pl | 130 + implementations/Prolog/source/derp.py | 40 + implementations/Prolog/source/fuuu.pl | 527 ++ .../Prolog/source/gen-defs+funcs.pl | 759 ++ implementations/Prolog/source/gen-defs.pl | 320 + implementations/Prolog/source/gen-funcs.pl | 462 ++ implementations/Prolog/source/joy2dot.pl | 153 + implementations/Prolog/source/joy2py.pl | 919 +++ implementations/Prolog/source/joy_defs.dot | 46 + implementations/Prolog/source/joy_defs.png | Bin 0 -> 176538 bytes implementations/Prolog/source/more-notes.txt | 114 + implementations/Prolog/source/nerdsniped.txt | 62 + implementations/Prolog/source/notes.txt | 317 + implementations/Prolog/source/thun.pl | 2817 ++++++++ implementations/Prolog/source/thun_compile.pl | 295 + implementations/Prolog/test/test_thun.pl | 32 + 60 files changed, 19083 insertions(+) create mode 100644 implementations/Prolog/CHANGELOG create mode 100644 implementations/Prolog/README create mode 100644 implementations/Prolog/docs/dev-guide/partial-reduction.md create mode 100644 implementations/Prolog/docs/notes/YUNO-color-arrows-right-like-this.PNG create mode 100644 implementations/Prolog/docs/notes/YUNO-color-arrows-right.PNG create mode 100644 implementations/Prolog/docs/notes/abs-def-vs-func create mode 100644 implementations/Prolog/docs/notes/minimal-basis create mode 100644 implementations/Prolog/docs/notes/on-square-spiral.md create mode 100644 implementations/Prolog/docs/notes/semantics.md create mode 100644 implementations/Prolog/docs/reference/FORMAT-Functor-Reference.md create mode 100644 implementations/Prolog/docs/reference/FuncRef.html create mode 100644 implementations/Prolog/docs/reference/Functor-Reference.md create mode 100644 implementations/Prolog/docs/reference/Makefile create mode 100644 implementations/Prolog/docs/reference/app1.md create mode 100644 implementations/Prolog/docs/reference/b.md create mode 100644 implementations/Prolog/docs/reference/binary.md create mode 100644 implementations/Prolog/docs/reference/bleah.txt create mode 100644 implementations/Prolog/docs/reference/ccons.md create mode 100644 implementations/Prolog/docs/reference/cons.md create mode 100644 implementations/Prolog/docs/reference/foobar.txt create mode 100644 implementations/Prolog/docs/reference/i.md create mode 100644 implementations/Prolog/docs/reference/infra.md create mode 100644 implementations/Prolog/docs/reference/not_negative.md create mode 100644 implementations/Prolog/docs/reference/nullary.md create mode 100644 implementations/Prolog/docs/reference/ternary.md create mode 100644 implementations/Prolog/docs/reference/unary.md create mode 100644 implementations/Prolog/docs/reference/uncons.md create mode 100644 implementations/Prolog/docs/reference/x.md create mode 100644 implementations/Prolog/docs/user-guide/user-guide.md create mode 100644 implementations/Prolog/issues/boolean-prefix create mode 100644 implementations/Prolog/issues/disenstacken create mode 100644 implementations/Prolog/issues/document-all-functions create mode 100644 implementations/Prolog/issues/funcs-nooooo create mode 100644 implementations/Prolog/issues/interpolation-literal create mode 100644 implementations/Prolog/jd.dot create mode 100644 implementations/Prolog/jd.dot.svg create mode 100644 implementations/Prolog/source/agl.PNG create mode 100644 implementations/Prolog/source/b-joy.pl create mode 100644 implementations/Prolog/source/bleah.code-workspace create mode 100644 implementations/Prolog/source/canhazmd.pl create mode 100644 implementations/Prolog/source/client/elm.json create mode 100644 implementations/Prolog/source/client/index.html create mode 100644 implementations/Prolog/source/client/src/Main.elm create mode 100644 implementations/Prolog/source/defs.txt create mode 100644 implementations/Prolog/source/derp.pl create mode 100644 implementations/Prolog/source/derp.py create mode 100644 implementations/Prolog/source/fuuu.pl create mode 100644 implementations/Prolog/source/gen-defs+funcs.pl create mode 100644 implementations/Prolog/source/gen-defs.pl create mode 100644 implementations/Prolog/source/gen-funcs.pl create mode 100644 implementations/Prolog/source/joy2dot.pl create mode 100644 implementations/Prolog/source/joy2py.pl create mode 100644 implementations/Prolog/source/joy_defs.dot create mode 100644 implementations/Prolog/source/joy_defs.png create mode 100644 implementations/Prolog/source/more-notes.txt create mode 100644 implementations/Prolog/source/nerdsniped.txt create mode 100644 implementations/Prolog/source/notes.txt create mode 100644 implementations/Prolog/source/thun.pl create mode 100644 implementations/Prolog/source/thun_compile.pl create mode 100644 implementations/Prolog/test/test_thun.pl diff --git a/implementations/Prolog/CHANGELOG b/implementations/Prolog/CHANGELOG new file mode 100644 index 0000000..d7d5c8c --- /dev/null +++ b/implementations/Prolog/CHANGELOG @@ -0,0 +1,56 @@ + + +----------------------------------------- +Unreleased + +Added: + +- Some documentation for a few functors. +- A start on regression tests. + +Fixed: +- Blanks should parse as null expression. + + +----------------------------------------- +[-10.0.0] - 2020-2-1 + +Initial re-release as a Prolog project. See https://joypy.osdn.io/ for +the original Python 2 project. + +Added: + +- Parser & Grammar. +- Semantics (evaluation function.) +- Many functions, combinators, & definitions. +- Compiler to Prolog. +- (Unfinished) Compiler to machine code. +- Expand/Contract Definitions. +- Formatter (Joy expressions to strings.) +- Partial Reducer (transforms Prolog rules for greater efficiency.) + +Removed: + +- All the Python code. +- All the Jupyter notebooks. (I want to rework the content anyway. The + originals will remain up on the web at the same URLs (I think the web + hosting of OSDN allows for setting up 301s though, in case I want to move + them and redirect.)) +- All the GNU Prolog portage ( https://osdn.net/projects/joypy/scm/hg/Joypy/tree/tip/thun/gnu-prolog/ ) + This is another area where i want to rework the content. It's kind of + cool to compile Prolog to native code so easily. + + + + + +Appendix A: Types of changes + +See https://keepachangelog.com/en/1.0.0/ + +- 'Added' for new features. +- 'Changed' for changes in existing functionality. +- 'Deprecated' for soon-to-be removed features. +- 'Removed' for now removed features. +- 'Fixed' for any bug fixes. +- 'Security' in case of vulnerabilities. diff --git a/implementations/Prolog/README b/implementations/Prolog/README new file mode 100644 index 0000000..eb47c4f --- /dev/null +++ b/implementations/Prolog/README @@ -0,0 +1,211 @@ + _____ _ + |_ _| |_ _ _ _ _ + | | | ' \ || | ' \ + |_| |_||_\_,_|_||_| + +Thun + +"...as simple as possible, but no simpler." + +A dialect of Joy. + +Version -10.0.0. (Version -10 in case I want to change the names of some +functions before the first "real" release, and I'm more-or-less using +https://semver.org/ ) + +This project started life as part of a Python project called at first +Joypy but then later renamed (after someone claimed the name on PyPI +before me) to Thun in honor of Manfred Von Thun who created Joy. While +creating a type-inference system for it I realized that it would be much +easier and more flexible to do it in Prolog. In fact, the Prolog code +(using SWI Prolog) is so much more elegant than the Python version that, +combined with the recent deprecation of Python 2, it convinced me to +switch "whole-hog" to Prolog. (You can find the original project at: +https://joypy.osdn.io/ ) + + ___ _ ___ _ + | __|_ ____ _ _ __ _ __| |___ / __|___ __| |___ + | _|\ \ / _` | ' \| '_ \ / -_) | (__/ _ \/ _` / -_) + |___/_\_\__,_|_|_|_| .__/_\___| \___\___/\__,_\___| + |_| +Here is an example of Joy code: + + [[[abs]ii <=][[<>][pop !-]||]&&][[!-][[++]][[--]]ifte dip][[pop !-][--][++]ifte]ifte + +It might seem unreadable but with a little familiarity it becomes just as +legible as any other notation. Some layout helps: + + [ [[abs] ii <=] + [ + [<>] [pop !-] || + ] && + ] + [[ !-] [[++]] [[--]] ifte dip] + [[pop !-] [--] [++] ifte ] + ifte + +This function accepts two integers on the stack and increments or +decrements one of them such that the new pair of numbers is the next +coordinate pair in a square spiral (like the kind used to construct an +Ulam Spiral). For more information see docs\notes\on-square-spiral.md + + ___ _ _ _ _ _ + | __| _ _ _ __| |_(_)___ _ _ __ _| (_) |_ _ _ + | _| || | ' \/ _| _| / _ \ ' \/ _` | | | _| || | + |_| \_,_|_||_\__|\__|_\___/_||_\__,_|_|_|\__|\_, | + __ _ _ _ __| | | _ \_ _ _ _ _ __ ___ ___ |__/ + / _` | ' \/ _` | | _/ || | '_| '_ \/ _ (_- cd source + + PS C:\Users\sforman\Desktop\src\PROLOG\Thun\source> swipl thun.pl + + Welcome to SWI-Prolog (threaded, 64 bits, version 8.1.4-33-gf5970a6e0) + SWI-Prolog comes with ABSOLUTELY NO WARRANTY. This is free software. + Please run ?- license. for legal details. + + For online help and background, visit http://www.swi-prolog.org + For built-in help, use ?- help(Topic). or ?- apropos(Word). + + 1 ?- + +Creating a better user interface is a big part of the "meta-project" +here, if you will. Thun/Joy is the language, the UI is the real project. +I'm thinking a server with multiple clients (web, native) just because +Prolog isn't fabulous at the nitty-gritty of UIs. There are other logic +languages that have e.g. reactive rules. Kowalski has a great thing: LPS +(Logic Production Systems) http://lps.doc.ic.ac.uk/ + +Anyhooo, for now there's not even a REPL. Use Prolog's. + +TODO: Explain the top-level predicates, with examples, this can be put in +the docs directory... + + ___ _ + | \ _____ _____| |___ _ __ ___ _ _ + | |) / -_) V / -_) / _ \ '_ \/ -_) '_| + |___/\___|\_/\___|_\___/ .__/\___|_| _ _ + | \ ___ __ _ _ _ __|_|__ _ _| |_ __ _| |_(_)___ _ _ + | |) / _ \/ _| || | ' \/ -_) ' \ _/ _` | _| / _ \ ' \ + |___/\___/\__|\_,_|_|_|_\___|_||_\__\__,_|\__|_\___/_||_| + +Developer Documentation + +Since there aren't yet any user-facing clients this is pretty much all +developer documentation. Until the UI side of the project is re-worked +this will mostly be of interest to people who are into formal semanics of +programming languages. + +If you already know Prolog then the code should be pretty simple and +straightforward. The partial reducer is documented in "The Art of +Prolog" so I won't repeat that here. Other than that there just really +isn't anything to crunchy in there. The signal virtual of Joy is it's +simplicity after all. + +Adding syntax should be avoided. At some point I'll likely add more +types, and maybe subtype relations between them. Maybe add the ability +to "tag" types from Joy itself (i.e. the ordered binary tree functions.) + + ___ _ _ _ _ _ + / __|___ _ _| |_ _ _(_) |__ _ _| |_(_)___ _ _ ___ + | (__/ _ \ ' \ _| '_| | '_ \ || | _| / _ \ ' \(_-< + \___\___/_||_\__|_| |_|_.__/\_,_|\__|_\___/_||_/__/ + +Contributions + +Well, aren't you sweet! GPL, docs please, "Be excellent to each other." + + ___ _ _ _ ___ _ _ + | _ )_ _(_) |__| | / __| |_ __ _| |_ _ _ ___ + | _ \ || | | / _` | \__ \ _/ _` | _| || (_-< + |___/\_,_|_|_\__,_| |___/\__\__,_|\__|\_,_/__/ + +Build Status + +No build, no status. diff --git a/implementations/Prolog/docs/dev-guide/partial-reduction.md b/implementations/Prolog/docs/dev-guide/partial-reduction.md new file mode 100644 index 0000000..0f5363b --- /dev/null +++ b/implementations/Prolog/docs/dev-guide/partial-reduction.md @@ -0,0 +1,7 @@ +The `source/gen-*.pl` files are created by `partial_reduce_thun/0`. I'm +just messing around with it at the moment. In theory the reduced forms +would be more efficient, but there's no pressure to improve performance +yet, and I'm not e.g. feeding the output to GNU Prolog to compile to +machine code, eh? + +It's just neat to see what the reducer makes of it. \ No newline at end of file diff --git a/implementations/Prolog/docs/notes/YUNO-color-arrows-right-like-this.PNG b/implementations/Prolog/docs/notes/YUNO-color-arrows-right-like-this.PNG new file mode 100644 index 0000000000000000000000000000000000000000..93edd1d31d668b61378706bd402fa47f6b38e5e1 GIT binary patch literal 47123 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 z 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 O^-Nw(u1v-#@P7f!tqFht literal 0 HcmV?d00001 diff --git a/implementations/Prolog/docs/notes/YUNO-color-arrows-right.PNG b/implementations/Prolog/docs/notes/YUNO-color-arrows-right.PNG new file mode 100644 index 0000000000000000000000000000000000000000..b715bb3ae187cb2b310d6ed795494b4b54cda19d GIT binary patch literal 73742 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 B=xP7} literal 0 HcmV?d00001 diff --git a/implementations/Prolog/docs/notes/abs-def-vs-func b/implementations/Prolog/docs/notes/abs-def-vs-func new file mode 100644 index 0000000..ce2ee07 --- /dev/null +++ b/implementations/Prolog/docs/notes/abs-def-vs-func @@ -0,0 +1,310 @@ +% With abs as a definition: abs ::= dup 0 < [] [neg] branch + +?- joy(`[abs] ii <=`, [int(A), int(C)], [bool(B)]). + +% Eight solutions: + +B = false, +A in 0..sup, +A#==C, +A+1#=_67204, +C in 0..sup, +C+1#=_67252, +_67252 in 1..sup, +_67204 in 1..sup ; + + +B = false, +A in inf.. -1, +_67892+A#=0, +A+1#=_67912, +_67892 in 1..sup, +_67892#==C, +C in 0..sup, +C+1#=_67834, +_67834 in 1..sup, +_67744 in inf..0 ; + + +B = false, +A in 0..sup, +A#=<_67850+ -1, +A+1#=_67870, +_67850 in 1..sup, +_67850+C#=0, +C in inf.. -1, +C+1#=_67966, +_67966 in inf..0, +_67870 in 1..sup ; + + +B = true, +A in 1..sup, +A#>=_67762, +A+1#=_67780, +_67762 in 1..sup, +_67762+C#=0, +C in inf.. -1, +C+1#=_67876, +_67876 in inf..0, +_67780 in 1..sup ; + + +B = false, +A in inf.. -1, +_68746+A#=0, +A+1#=_68766, +_68746 in 1..sup, +_68746#=<_68818+ -1, +_68818 in 2..sup, +_68818+C#=0, +C in inf.. -2, +C+1#=_68910, +_68910 in inf..0, +_68766 in inf..0 ; + + +B = true, +A in inf.. -1, +_68258+A#=0, +A+1#=_68278, +_68258 in 1..sup, +_68258#>=_68326, +_68326 in 1..sup, +_68326+C#=0, +C in inf.. -1, +C+1#=_68416, +_68416 in inf..0, +_68278 in inf..0 ; + + +false. + + +% If we add a function rule for it using CLP(FD)...: + + func(abs, [int(A)|S], [int(B)|S]) :- B #= abs(A). + +?- joy(`[abs] ii <=`, [int(A), int(C)], [bool(B)]). + +% We get eighteen solutions! Egad. + +B = false, +_7784#=abs(A), +_7784 in 0..sup, +_7784#=<_7836+ -1, +_7836 in 1..sup, +_7836#=abs(C), +C in inf.. -1\/1..sup ; + + +B = true, +_6512#=abs(A), +_6512 in 0..sup, +_6512#>=_6560, +_6560 in 0..sup, +_6560#=abs(C) ; + + +B = false, +A in 0..sup, +A#=<_8820+ -1, +A+1#=_8840, +_8820 in 1..sup, +_8820#=abs(C), +C in inf.. -1\/1..sup, +_8840 in 1..sup ; + + +B = true, +A in 0..sup, +A#>=_7544, +A+1#=_7562, +_7544 in 0..sup, +_7544#=abs(C), +_7562 in 1..sup ; + + +B = false, +A in inf.. -1, +_9354+A#=0, +A+1#=_9374, +_9354 in 1..sup, +_9354#=<_9426+ -1, +_9426 in 2..sup, +_9426#=abs(C), +C in inf.. -2\/2..sup, +_9374 in inf..0 ; + + +B = true, +A in inf.. -1, +_8082+A#=0, +A+1#=_8102, +_8082 in 1..sup, +_8082#>=_8150, +_8150 in 0..sup, +_8150#=abs(C), +_8102 in inf..0 ; + + +B = false, +_7686#=abs(A), +_7686 in 0..sup, +_7686#==C, +C in 0..sup, +C+1#=_7608, +_7608 in 1..sup ; + + +B = false, +A in 0..sup, +A#==C, +A+1#=_8568, +C in 0..sup, +C+1#=_8616, +_8616 in 1..sup, +_8568 in 1..sup ; + + +B = false, +A in inf.. -1, +_9256+A#=0, +A+1#=_9276, +_9256 in 1..sup, +_9256#==C, +C in 0..sup, +C+1#=_9198, +_9198 in 1..sup, +_9108 in inf..0 ; + + +B = false, +_8178#=abs(A), +_8178 in 0..sup, +_8178#=<_8230+ -1, +_8230 in 1..sup, +_8230+C#=0, +C in inf.. -1, +C+1#=_8322, +_8322 in inf..0 ; + + +B = true, +A in inf.. -1\/1..sup, +_9272#=abs(A), +_9272 in 1..sup, +_9272#>=_9320, +_9320 in 1..sup, +_9320+C#=0, +C in inf.. -1, +C+1#=_9410, +_9410 in inf..0 ; + + +B = false, +A in 0..sup, +A#=<_9214+ -1, +A+1#=_9234, +_9214 in 1..sup, +_9214+C#=0, +C in inf.. -1, +C+1#=_9330, +_9330 in inf..0, +_9234 in 1..sup ; + + +B = true, +A in 1..sup, +A#>=_9126, +A+1#=_9144, +_9126 in 1..sup, +_9126+C#=0, +C in inf.. -1, +C+1#=_9240, +_9240 in inf..0, +_9144 in 1..sup ; + + +B = false, +A in inf.. -1, +_10110+A#=0, +A+1#=_10130, +_10110 in 1..sup, +_10110#=<_10182+ -1, +_10182 in 2..sup, +_10182+C#=0, +C in inf.. -2, +C+1#=_10274, +_10274 in inf..0, +_10130 in inf..0 ; + + +B = true, +A in inf.. -1, +_9622+A#=0, +A+1#=_9642, +_9622 in 1..sup, +_9622#>=_9690, +_9690 in 1..sup, +_9690+C#=0, +C in inf.. -1, +C+1#=_9780, +_9780 in inf..0, +_9642 in inf..0 ; + + +false. diff --git a/implementations/Prolog/docs/notes/minimal-basis b/implementations/Prolog/docs/notes/minimal-basis new file mode 100644 index 0000000..8d7a875 --- /dev/null +++ b/implementations/Prolog/docs/notes/minimal-basis @@ -0,0 +1,4 @@ +Talk about minimal basis, Kirby (sp?) has found a basis involving a +combinator he calls 'cake'... + +Branch, Loop, Sequence, Parallel. diff --git a/implementations/Prolog/docs/notes/on-square-spiral.md b/implementations/Prolog/docs/notes/on-square-spiral.md new file mode 100644 index 0000000..2d1ece7 --- /dev/null +++ b/implementations/Prolog/docs/notes/on-square-spiral.md @@ -0,0 +1,225 @@ + ___ _ ___ _ + | __|_ ____ _ _ __ _ __| |___ / __|___ __| |___ + | _|\ \ / _` | ' \| '_ \ / -_) | (__/ _ \/ _` / -_) + |___/_\_\__,_|_|_|_| .__/_\___| \___\___/\__,_\___| + |_| +# On the Square Spiral Example Code + +Here is the example of Joy code from the `README` file: + + [[[abs]ii <=][[<>][pop !-]||]&&][[!-][[++]][[--]]ifte dip][[pop !-][--][++]ifte]ifte + +It might seem unreadable but with a little familiarity it becomes just as +legible as any other notation. Some layout helps: + + [ [[abs] ii <=] + [ + [<>] [pop !-] || + ] && + ] + [[ !-] [[++]] [[--]] ifte dip] + [[pop !-] [--] [++] ifte ] + ifte + +This function accepts two integers on the stack and increments or +decrements one of them such that the new pair of numbers is the next +coordinate pair in a square spiral (like the kind used to construct an +Ulam Spiral). + + +## Original Form + +It's adapted from the [original code on StackOverflow](https://stackoverflow.com/questions/398299/looping-in-a-spiral/31864777#31864777): + +> If all you're trying to do is generate the first N points in the spiral +> (without the original problem's constraint of masking to an N x M +> region), the code becomes very simple: + + void spiral(const int N) + { + int x = 0; + int y = 0; + for(int i = 0; i < N; ++i) + { + cout << x << '\t' << y << '\n'; + if(abs(x) <= abs(y) && (x != y || x >= 0)) + x += ((y >= 0) ? 1 : -1); + else + y += ((x >= 0) ? -1 : 1); + } + } + +> The trick is that you can compare x and y to determine what side of the +> square you're on, and that tells you what direction to move in. + + +## Translation to Joy + +I'm going to make a function that take two ints (`x` and `y`) and +generates the next pair, we'll turn it into a generator later using the +`x` combinator. + +### First Boolean Predicate + +We need a function that computes `abs(x) <= abs(y)`, we can use `ii` to +apply `abs` in parallel (eventually) to both values and then compare them +with `<=`: + + [abs] ii <= + +I've defined two short-circuiting Boolean combinators `&&` and `||` that +each accept two quoted predicate programs, run the first, and +conditionally run the second only if required (to compute the final +Boolean value). They run their predicate arguments `nullary`. Given +those, we can define `x != y || x >= 0` as: + + [<>] [pop 0 >=] || + +And `(abs(x) <= abs(y) && (x != y || x >= 0))` as: + + [[abs] ii <=] [[<>] [pop 0 >=] ||] && + +It's a little rough, but, as I say, with a little familiarity it becomes +legible. + +### The Increment / Decrement Branches + +Turning to the branches of the main `if` statement: + + x += ((y >= 0) ? 1 : -1); + +Rewrite as a hybrid (pseudo-code) `ifte` expression: + + [y >= 0] [x += 1] [X -= 1] ifte + +Change each C phrase to Joy code: + + [0 >=] [[++] dip] [[--] dip] ifte + +Factor out the dip from each branch: + + [0 >=] [[++]] [[--]] ifte dip + +Similar logic applies to the other branch: + + y += ((x >= 0) ? -1 : 1); + + [x >= 0] [y -= 1] [y += 1] ifte + + [pop 0 >=] [--] [++] ifte + + +## Putting the Pieces Together + +We can assemble the three functions we just defined in quotes and give +them them to the `ifte` combinator. With some arrangement to show off +the symmetry of the two branches, we have: + + [[[abs] ii <=] [[<>] [pop !-] ||] &&] + [[ !-] [[++]] [[--]] ifte dip] + [[pop !-] [--] [++] ifte ] + ifte + +As I was writing this up I realized that, since the `&&` combinator +doesn't consume the stack (below its quoted args), I can unquote the +predicate, swap the branches, and use the `branch` combinator instead of +`ifte`: + + [[abs] ii <=] [[<>] [pop !-] ||] && + [[pop !-] [--] [++] ifte ] + [[ !-] [[++]] [[--]] ifte dip] + branch + + +## Turning it into a Generator with `x` + +It can be used with the x combinator to make a kind of generator for +spiral square coordinates. + + +We can use `codireco` to make a generator + + codireco ::= cons dip rest cons + +It will look like this: + + [value [F] codireco] + +Here's a trace of how it works: + + [0 [dup ++] codireco] . x + [0 [dup ++] codireco] . 0 [dup ++] codireco + [0 [dup ++] codireco] 0 . [dup ++] codireco + [0 [dup ++] codireco] 0 [dup ++] . codireco + [0 [dup ++] codireco] 0 [dup ++] . cons dip rest cons + [0 [dup ++] codireco] [0 dup ++] . dip rest cons + . 0 dup ++ [0 [dup ++] codireco] rest cons + 0 . dup ++ [0 [dup ++] codireco] rest cons + 0 0 . ++ [0 [dup ++] codireco] rest cons + 0 1 . [0 [dup ++] codireco] rest cons + 0 1 [0 [dup ++] codireco] . rest cons + 0 1 [[dup ++] codireco] . cons + 0 [1 [dup ++] codireco] . + +But first we have to change the `spiral_next` function to work on a +quoted pair of integers, and leave a copy of the pair on the stack. +From: + + y x spiral_next + --------------------- + y' x' + +to: + + [x y] [spiral_next] infra + ------------------------------- + [x' y'] + +So our generator is: + + [[x y] [dup [spiral_next] infra] codireco] + +Or rather: + + [[0 0] [dup [spiral_next] infra] codireco] + +There is a function `make_generator` that will build the generator for us +out of the value and stepper function: + + [0 0] [dup [spiral_next] infra] make_generator + ---------------------------------------------------- + [[0 0] [dup [spiral_next] infra] codireco] + +Here it is in action: + + ?- joy(`[[0 0] [dup [spiral_next] infra] codireco] x x x x pop`, [], _So), + | joy_terms_to_string(_So, S). + + _So = [list([int(-1), int(0)]), list([int(-1), int(1)]), list([int(0), int(1)]), list([int(0), int(0)])], + + S = "[-1 0] [-1 1] [0 1] [0 0]" . + +Four `x` combinators, four pairs of coordinates. + + +## Conclusion + +So that's an example of Joy code. It's a straightforward translation of +the original. It's a little long for a single definition, you might +break it up like so: + + _spn_P ::= [[abs] ii <=] [[<>] [pop !-] ||] && + + _spn_T ::= [ !-] [[++]] [[--]] ifte dip + _spn_E ::= [pop !-] [--] [++] ifte + + spiral_next ::= _spn_P [_spn_E] [_spn_T] branch + +This way it's easy to see that the function is a branch with two +quasi-symmetrical paths. + +We then used this function to make a simple generator of coordinate +pairs, where the next pair in the series can be generated at any time by +using the `x` combinator on the generator (which is just a quoted +expression containing a copy of the current pair and the "stepper +function" to generate the next pair from that.) \ No newline at end of file diff --git a/implementations/Prolog/docs/notes/semantics.md b/implementations/Prolog/docs/notes/semantics.md new file mode 100644 index 0000000..96d39e8 --- /dev/null +++ b/implementations/Prolog/docs/notes/semantics.md @@ -0,0 +1,61 @@ +# list-structured memory + +[In SICP, section 5.3, "Storage Allocation and Garbage Collection"](https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-33.html#%_sec_5.3): + +> In order to simplify the discussion, we will assume that our register + machines can be equipped with a list-structured memory, in which the + basic operations for manipulating list-structured data are primitive. + +So they bunt to an abstraction and then implement that abstraction as a +separate problem. Makes sense. I see no reason not to adopt the design +described here. + +------------------ + +# Machine Ints vs BigNums + +Already there is a problem in the semantics. SWI Prolog integers can be +larger than machine words, which in the RISC CPU are thirty-two bits. +(GNU Prolog uses machine words for its integers). THe main options are: + +1. Implements "BigNums" for Wirth RISC. + +2. Adjust the semantics of Thun to reflect the modular arithmetic of + machine words and native machine integer math operations. + +3. ... something else. + +------------------ + +# specialized versions of `branch` and `ifte` + +THere's another semantic wrinkle with branches and Boolean values. +Namely, the CPU provides the condition and the offset in one instruction +whereas Joy has them separated. I have been thinking about introducing +specialized versions of `branch` as primitives: + + =branch + >branch + =branch + <>branch + +Or maybe: + + =? + >? + =? + <>? + +Anyway, it would be pretty easy to detect simple cases of the split +pattern and convert them automatically, but the programmer could use them +directly whenever it made sense. + + > [F] [T] branch ==> [F] [T] >branch + +Probably specialized versions of `ifte` would be useful as well. + +------------------ diff --git a/implementations/Prolog/docs/reference/FORMAT-Functor-Reference.md b/implementations/Prolog/docs/reference/FORMAT-Functor-Reference.md new file mode 100644 index 0000000..f3f3b25 --- /dev/null +++ b/implementations/Prolog/docs/reference/FORMAT-Functor-Reference.md @@ -0,0 +1,39 @@ + +-------------------- + +Get a list of currently defined functors with: + + ?- joy(`words`, [], [Words]), maplist(writeln, Words). + + + + + +FORMAT: + +-------------------- + +## Name + +[Basis] Function | Combinator + +Summary + +Gentzen diagram. + +### Definition + +if not basis. + +### Derivation + +if not basis. + +### Source + +if basis + +### Discussion + + +### Crosslinks \ No newline at end of file diff --git a/implementations/Prolog/docs/reference/FuncRef.html b/implementations/Prolog/docs/reference/FuncRef.html new file mode 100644 index 0000000..b984927 --- /dev/null +++ b/implementations/Prolog/docs/reference/FuncRef.html @@ -0,0 +1,353 @@ + + + + + + + Functor-Reference + + + + + +

Functor Reference

+

Version -10.0.0

+

Each function, combinator, or definition should be documented here.

+
+

!-

+

“not negative”

+

(Function, Boolean Predicate)

+

Integer on top of stack is replaced by Boolean value indicating whether it is non-negative.

+
    N !-
+-----------  N < 0
+   false
+
+   N !-
+----------  N >= 0
+   true
+

Definition

+
0 >=
+
+

app1

+

“apply one”

+

(Combinator)

+

Given a quoted program on TOS and anything as the second stack item run the program without disturbing the stack and replace the two args with the first result of the program.

+
         ... x [Q] app1
+---------------------------------
+   ... [x ...] [Q] infra first
+

Definition

+
nullary popd
+

Discussion

+

Just a specialization of nullary really. Its parallelizable cousins are more useful.

+
+

b

+

(Combinator)

+

Run two quoted programs

+
   [P] [Q] b
+---------------
+      P Q
+

Definition

+
[i] dip i
+

Derivation

+
[P] [Q] b
+[P] [Q] [i] dip i
+[P] i [Q] i
+ P    [Q] i
+ P     Q
+

Discussion

+

This combinator comes in handy.

+ +

dupdip ii

+
+

binary

+

(Combinator)

+

Run a quoted program using exactly two stack values and leave the first item of the result on the stack.

+
   ... y x [P] binary
+-----------------------
+        ... A
+

Definition

+
unary popd
+

Discussion

+

Runs any other quoted function and returns its first result while consuming exactly two items from the stack.

+ +

nullary ternary unary

+
+

ccons

+

(Function)

+

Given two items and a list, append the items to the list to make a new list.

+
   B A [...] ccons
+---------------------
+      [B A ...]
+

Definition

+
cons cons
+

Discussion

+

Does cons twice.

+ +

cons

+
+

cons

+

(Basis Function)

+

Given an item and a list, append the item to the list to make a new list.

+
   A [...] cons
+------------------
+     [A ...]
+

Source

+
func(cons, [list(A), B|S], [list([B|A])|S]).
+

Discussion

+

Cons is a venerable old function from Lisp. It doesn’t inspect the item but it will not cons onto a non-list. It’s inverse operation is called uncons.

+ +

ccons uncons

+
+

i

+

(Basis Combinator)

+

Append a quoted expression onto the pending expression.

+
   [Q] i
+-----------
+    Q
+

Source

+
combo(i, [list(P)|S], S, Ei, Eo) :- append(P, Ei, Eo).
+

Discussion

+

This is probably the fundamental combinator. You wind up using it in all kinds of places (for example, the x combinator can be defined as dup i.)

+
+

infra

+

(Combinator)

+

Accept a quoted program and a list on the stack and run the program with the list as its stack. Does not affect the stack (below the list.)

+
   ... [a b c] [Q] infra
+---------------------------
+    c b a Q [...] swaack
+

Definition

+
swons swaack [i] dip swaack
+

Discussion

+

This is one of the more useful combinators. It allows a quoted expression to serve as a stack for a program, effectively running it in a kind of “pocket universe”. If the list represents a datastructure then infra lets you work on its internal structure.

+ +

swaack

+
+

nullary

+

(Combinator)

+

Run a quoted program without using any stack values and leave the first item of the result on the stack.

+
   ... [P] nullary
+---------------------
+        ... A
+

Definition

+
[stack] dip infra first
+

Derivation

+
... [P] nullary
+... [P] [stack] dip infra first
+... stack [P] infra first
+... [...] [P] infra first
+... [A ...] first
+...  A
+

Discussion

+

A very useful function that runs any other quoted function and returns it’s first result without disturbing the stack (under the quoted program.)

+ +

unary binary ternary

+
+

ternary

+

(Combinator)

+

Run a quoted program using exactly three stack values and leave the first item of the result on the stack.

+
   ... z y x [P] unary
+-------------------------
+         ... A
+

Definition

+
binary popd
+

Discussion

+

Runs any other quoted function and returns its first result while consuming exactly three items from the stack.

+ +

binary nullary unary

+
+

unary

+

(Combinator)

+

Run a quoted program using exactly one stack value and leave the first item of the result on the stack.

+
   ... x [P] unary
+---------------------
+       ... A
+

Definition

+
nullary popd
+

Discussion

+

Runs any other quoted function and returns its first result while consuming exactly one item from the stack.

+ +

binary nullary ternary

+
+

uncons

+

(Basis Function)

+

Removes an item from a list and leaves it on the stack under the rest of the list. You cannot uncons an item from an empty list.

+
   [A ...] uncons
+--------------------
+      A [...]
+

Source

+
func(uncons, Si, So) :- func(cons, So, Si).
+

Discussion

+

This is the inverse of cons.

+ +

cons

+
+

x

+

(Combinator)

+
   [F] x
+-----------
+   [F] F
+

Definition

+
dup i
+

Discussion

+

The x combinator …

+ + diff --git a/implementations/Prolog/docs/reference/Functor-Reference.md b/implementations/Prolog/docs/reference/Functor-Reference.md new file mode 100644 index 0000000..ffa76c5 --- /dev/null +++ b/implementations/Prolog/docs/reference/Functor-Reference.md @@ -0,0 +1,355 @@ +# Functor Reference + +Version -10.0.0 + +Each function, combinator, or definition should be documented here. + +-------------------- + +## !- + +"not negative" + +(Function, Boolean Predicate) + +Integer on top of stack is replaced by Boolean value indicating whether +it is non-negative. + + N !- + ----------- N < 0 + false + + N !- + ---------- N >= 0 + true + + +### Definition + + 0 >= + +-------------------- + +## app1 + +"apply one" + +(Combinator) + +Given a quoted program on TOS and anything as the second stack item run +the program without disturbing the stack and replace the two args with +the first result of the program. + + ... x [Q] app1 + --------------------------------- + ... [x ...] [Q] infra first + +### Definition + + nullary popd + +### Discussion + +Just a specialization of `nullary` really. Its parallelizable cousins +are more useful. + + +-------------------- + +## b + +(Combinator) + +Run two quoted programs + + [P] [Q] b + --------------- + P Q + +### Definition + + [i] dip i + +### Derivation + + [P] [Q] b + [P] [Q] [i] dip i + [P] i [Q] i + P [Q] i + P Q + +### Discussion + +This combinator comes in handy. + +### Crosslinks + +[dupdip](#dupdip) +[ii](#ii) + +-------------------- + +## binary + +(Combinator) + +Run a quoted program using exactly two stack values and leave the first +item of the result on the stack. + + ... y x [P] binary + ----------------------- + ... A + +### Definition + + unary popd + +### Discussion + +Runs any other quoted function and returns its first result while +consuming exactly two items from the stack. + +### Crosslinks + +[nullary](#nullary) +[ternary](#ternary) +[unary](#unary) + +-------------------- + +## ccons + +(Function) + +Given two items and a list, append the items to the list to make a new list. + + B A [...] ccons + --------------------- + [B A ...] + +### Definition + + cons cons + +### Discussion + +Does `cons` twice. + +### Crosslinks + +[cons](#cons) + +-------------------- + +## cons + +(Basis Function) + +Given an item and a list, append the item to the list to make a new list. + + A [...] cons + ------------------ + [A ...] + +### Source + + func(cons, [list(A), B|S], [list([B|A])|S]). + +### Discussion + +Cons is a venerable old function from Lisp. It doesn't inspect the item +but it will not cons onto a non-list. It's inverse operation is called +`uncons`. + +### Crosslinks + +[ccons](#ccons) +[uncons](#uncons) + +-------------------- + +## i + +(Basis Combinator) + +Append a quoted expression onto the pending expression. + + + [Q] i + ----------- + Q + +### Source + + combo(i, [list(P)|S], S, Ei, Eo) :- append(P, Ei, Eo). + +### Discussion + +This is probably the fundamental combinator. You wind up using it in all +kinds of places (for example, the `x` combinator can be defined as `dup i`.) + +-------------------- + +## infra + +(Combinator) + +Accept a quoted program and a list on the stack and run the program with +the list as its stack. Does not affect the stack (below the list.) + + ... [a b c] [Q] infra + --------------------------- + c b a Q [...] swaack + +### Definition + + swons swaack [i] dip swaack + + +### Discussion + +This is one of the more useful combinators. It allows a quoted +expression to serve as a stack for a program, effectively running it in a +kind of "pocket universe". If the list represents a datastructure then +`infra` lets you work on its internal structure. + +### Crosslinks + +[swaack](#swaack) + +-------------------- + +## nullary + +(Combinator) + +Run a quoted program without using any stack values and leave the first item of the result on the stack. + + ... [P] nullary + --------------------- + ... A + +### Definition + + [stack] dip infra first + +### Derivation + + ... [P] nullary + ... [P] [stack] dip infra first + ... stack [P] infra first + ... [...] [P] infra first + ... [A ...] first + ... A + +### Discussion + +A very useful function that runs any other quoted function and returns +it's first result without disturbing the stack (under the quoted +program.) + +### Crosslinks + +[unary](#unary) +[binary](#binary) +[ternary](#ternary) + +-------------------- + +## ternary + +(Combinator) + + +Run a quoted program using exactly three stack values and leave the first +item of the result on the stack. + + ... z y x [P] unary + ------------------------- + ... A + +### Definition + + binary popd + +### Discussion + +Runs any other quoted function and returns its first result while +consuming exactly three items from the stack. + +### Crosslinks + +[binary](#binary) +[nullary](#nullary) +[unary](#unary) + +-------------------- + +## unary + +(Combinator) + +Run a quoted program using exactly one stack value and leave the first item of the result on the stack. + + ... x [P] unary + --------------------- + ... A + +### Definition + + nullary popd + +### Discussion + +Runs any other quoted function and returns its first result while +consuming exactly one item from the stack. + +### Crosslinks + +[binary](#binary) +[nullary](#nullary) +[ternary](#ternary) + +-------------------- + +## uncons + +(Basis Function) + +Removes an item from a list and leaves it on the stack under the rest of +the list. You cannot `uncons` an item from an empty list. + + [A ...] uncons + -------------------- + A [...] + +### Source + + func(uncons, Si, So) :- func(cons, So, Si). + +### Discussion + +This is the inverse of `cons`. + +### Crosslinks + +[cons](#cons) + +-------------------- + +## x + +(Combinator) + + [F] x + ----------- + [F] F + +### Definition + + dup i + +### Discussion + +The `x` combinator ... + diff --git a/implementations/Prolog/docs/reference/Makefile b/implementations/Prolog/docs/reference/Makefile new file mode 100644 index 0000000..8b16eab --- /dev/null +++ b/implementations/Prolog/docs/reference/Makefile @@ -0,0 +1,3 @@ + +all: + pandoc -s --toc --toc-depth=2 --ascii Functor-Reference.md -o FuncRef.html diff --git a/implementations/Prolog/docs/reference/app1.md b/implementations/Prolog/docs/reference/app1.md new file mode 100644 index 0000000..95dbe5f --- /dev/null +++ b/implementations/Prolog/docs/reference/app1.md @@ -0,0 +1,26 @@ +-------------------- + +## app1 + +"apply one" + +(Combinator) + +Given a quoted program on TOS and anything as the second stack item run +the program without disturbing the stack and replace the two args with +the first result of the program. + + ... x [Q] app1 + --------------------------------- + ... [x ...] [Q] infra first + +### Definition + + nullary popd + +### Discussion + +Just a specialization of `nullary` really. Its parallelizable cousins +are more useful. + + diff --git a/implementations/Prolog/docs/reference/b.md b/implementations/Prolog/docs/reference/b.md new file mode 100644 index 0000000..37647a1 --- /dev/null +++ b/implementations/Prolog/docs/reference/b.md @@ -0,0 +1,33 @@ +-------------------- + +## b + +(Combinator) + +Run two quoted programs + + [P] [Q] b + --------------- + P Q + +### Definition + + [i] dip i + +### Derivation + + [P] [Q] b + [P] [Q] [i] dip i + [P] i [Q] i + P [Q] i + P Q + +### Discussion + +This combinator comes in handy. + +### Crosslinks + +[dupdip](#dupdip) +[ii](#ii) + diff --git a/implementations/Prolog/docs/reference/binary.md b/implementations/Prolog/docs/reference/binary.md new file mode 100644 index 0000000..182da5b --- /dev/null +++ b/implementations/Prolog/docs/reference/binary.md @@ -0,0 +1,28 @@ +-------------------- + +## binary + +(Combinator) + +Run a quoted program using exactly two stack values and leave the first +item of the result on the stack. + + ... y x [P] binary + ----------------------- + ... A + +### Definition + + unary popd + +### Discussion + +Runs any other quoted function and returns its first result while +consuming exactly two items from the stack. + +### Crosslinks + +[nullary](#nullary) +[ternary](#ternary) +[unary](#unary) + diff --git a/implementations/Prolog/docs/reference/bleah.txt b/implementations/Prolog/docs/reference/bleah.txt new file mode 100644 index 0000000..10f82f6 --- /dev/null +++ b/implementations/Prolog/docs/reference/bleah.txt @@ -0,0 +1,20 @@ +The problem is twofold: + +1.) Programming is very young, and has been in a growth phase since it's inception. I forget the exact numbers, but *half* of *all* programmers have been doing it for less than *five years*, and that has been true for many decades. + +Because there has been no "shakedown" phase (a "correction" in financial jargon), and because the influx of newbies ("Eternal September") has washed out any attempts at *better* programming ("Mother of All Demos", Nelson's "Dream Machine", etc., on the one hand and e.g. Ada on the other, all of it pretty much ignored in the mainstream machines and software available today. How many people have heard of Jef Raskin, let alone read "Humane Interface"? etc...) because of these things we should not expect programming to be at the level of engineering. We are in the Alchemy phase, not Chemistry. + + +2.) Choice of notation. Without going into a long rant, our fundamental mathematical tools for programming are grotesque. (It would be shocking if the first thing out of the primordial ooze was the perfect programming notation, eh? In point of fact we have Turing Machines, Lambda Calculus, and what was the other one? Anyway, they're clunky.) + +For binary Boolean circuits the notation in "Laws of Form" by George Spencer-Brown is the most elegant and parsimonious: + + AA == A + ((A)) == A + A(AB) == A(B) + +That is a complete system of binary Boolean logic that is more efficient than other notations. (For instance, de Morgan's law doesn't apply: you can convert a formula into it's dual, reduce, and recover the original formula. (Ergo, conventional notation has (non-useful) redundancy.)) + +For orchestrating binary Boolean logic circuits into what we call "programs" the best notation is something called "Joy". It was invented by a philosopher! It has the best aspects of Lisp and Forth. It handles concurrency in a very simple and tractable way. It delivers on the promise of Backus' FP to enable mathematical *algebraic* derivations of algorithms/programs. Etc... + +Anyhow, I'm preparing a demo of Joy, with UI influenced by psycho-ergonomic considerations, that can be compiled down to the logic circuits (and e.g. burned onto an FPGA or whatever.) A new model of computer architecture is implied, using latching sort-nets to allow for dynamic reconfiguration of what amounts to dataflow on the level of the CPU. (No more Von Neumann bottleneck.) \ No newline at end of file diff --git a/implementations/Prolog/docs/reference/ccons.md b/implementations/Prolog/docs/reference/ccons.md new file mode 100644 index 0000000..0afb708 --- /dev/null +++ b/implementations/Prolog/docs/reference/ccons.md @@ -0,0 +1,24 @@ +-------------------- + +## ccons + +(Function) + +Given two items and a list, append the items to the list to make a new list. + + B A [...] ccons + --------------------- + [B A ...] + +### Definition + + cons cons + +### Discussion + +Does `cons` twice. + +### Crosslinks + +[cons](#cons) + diff --git a/implementations/Prolog/docs/reference/cons.md b/implementations/Prolog/docs/reference/cons.md new file mode 100644 index 0000000..cfc19aa --- /dev/null +++ b/implementations/Prolog/docs/reference/cons.md @@ -0,0 +1,27 @@ +-------------------- + +## cons + +(Basis Function) + +Given an item and a list, append the item to the list to make a new list. + + A [...] cons + ------------------ + [A ...] + +### Source + + func(cons, [list(A), B|S], [list([B|A])|S]). + +### Discussion + +Cons is a venerable old function from Lisp. It doesn't inspect the item +but it will not cons onto a non-list. It's inverse operation is called +`uncons`. + +### Crosslinks + +[ccons](#ccons) +[uncons](#uncons) + diff --git a/implementations/Prolog/docs/reference/foobar.txt b/implementations/Prolog/docs/reference/foobar.txt new file mode 100644 index 0000000..d24d6cb --- /dev/null +++ b/implementations/Prolog/docs/reference/foobar.txt @@ -0,0 +1,98 @@ +Implement modular arithmetic semantics for Joy, or + +Implement 'BigNums' for Oberon RISC, or + +Implement mod arith in Joy and use that to implement BigNums in mod-Joy, +then partial reduce/eval etc. to get BigNums for RISC? + + + +OR, let division operator make rationals!? + + + + + +I'm using DCG w/ the lists as machine code, and explicitly passing the +environment around; If I put the machine code into the environment I can +just pass that around and define an asm//n DCG for writing machine code. + + + +Parsing and Compiling Using Prolog + + 1. Introduction + 2. Parsing + 2.1 Bottom-Up + 2.2 Top-Down + 2.3 Recursive Descent + 3. Syntax-Directed Translation + 4. M-Grammars and DCGs + 5. Grammar Properties + 6. Lexical Scanners And Parser Generation + 7. Code Generation + 7.1 Generating Code from Polish + 7.2 Generating Code from Trees + 7.3 A Machine-Independent Algorithm for Code Generation + 7.4 Code Generation from a Labelled Tree + 8. Optimizations + 8.1 Compile-Time Evaluation + 8.2 Peephole Optimization + 9. Using Proposed Extension + 10. Final Remarks + + + + + +type Item + = Integer Int + | Symbol String + | Boolean Bool + + + + +The Web UI + +- Docs for each function + - Crosslinks to other funcs in PatLang style +- "Forge" for creating new funcs + - Type-checking + - Examples + - Docs + - Domain/Applicability + - Mathematical Aspects (Cat Theory) +- Evaluation Contexts + - Stage + - Spreadsheet/Grid + - Factory + - Dataflow + - Hall of Records +- History + - Learning from + - Quoting from + - Changing/Deleting (sometimes you gotta) +- Publishing/Sharing + - Registry of definition/functions + - Money? + + +https://thenewstack.io/rust-creator-graydon-hoare-recounts-the-history-of-compilers/ + + +I've been working (on and off, for years) on making a system inspired by that book and by Jef Raskin's "Humane Interface", et. el., and I'm convinced that software could be made bug-free and cheaply, however I have no hope of convincing other programmers. Instead, I'm going to take it directly to end users (and not tell them that they are learning to program until they already have, so as not to jinx it.) + +Trying to market to other developers would be an uphill battle, but normal people can use it to develop bug-free software easily and with minimal training (it's fun, like playing a video game). + +I should have a demo going in a week or two. I'm learning Elm lang right now to make a web UI for it. The original demo UI is in Python 2 and Tkinter, but I've transitioned to an implementation based on Prolog, where inter-op with TCL/Tk would be more trouble than simple putting a web server in front of it (not to go off on a tangent but search for "pengine" if you want to know more about Prolog-over-TCP.) + + +- - - - + +Graydon Hoare has a talk on the history of compilers but he doesn't mention Prolog once. I think it's possible he doesn't know about the research into logic programming and compilers. + + +"Parsing and Compiling Using Prolog" Jacques Cohen and Tim Hickey +ACM Transactions on Programming Languages and Systems 9(2):125-163 · April 1987 +DOI: 10.1145/22719.22946 · Source: DBLP diff --git a/implementations/Prolog/docs/reference/i.md b/implementations/Prolog/docs/reference/i.md new file mode 100644 index 0000000..8491757 --- /dev/null +++ b/implementations/Prolog/docs/reference/i.md @@ -0,0 +1,22 @@ +-------------------- + +## i + +(Basis Combinator) + +Append a quoted expression onto the pending expression. + + + [Q] i + ----------- + Q + +### Source + + combo(i, [list(P)|S], S, Ei, Eo) :- append(P, Ei, Eo). + +### Discussion + +This is probably the fundamental combinator. You wind up using it in all +kinds of places (for example, the `x` combinator can be defined as `dup i`.) + diff --git a/implementations/Prolog/docs/reference/infra.md b/implementations/Prolog/docs/reference/infra.md new file mode 100644 index 0000000..edea95a --- /dev/null +++ b/implementations/Prolog/docs/reference/infra.md @@ -0,0 +1,29 @@ +-------------------- + +## infra + +(Combinator) + +Accept a quoted program and a list on the stack and run the program with +the list as its stack. Does not affect the stack (below the list.) + + ... [a b c] [Q] infra + --------------------------- + c b a Q [...] swaack + +### Definition + + swons swaack [i] dip swaack + + +### Discussion + +This is one of the more useful combinators. It allows a quoted +expression to serve as a stack for a program, effectively running it in a +kind of "pocket universe". If the list represents a datastructure then +`infra` lets you work on its internal structure. + +### Crosslinks + +[swaack](#swaack) + diff --git a/implementations/Prolog/docs/reference/not_negative.md b/implementations/Prolog/docs/reference/not_negative.md new file mode 100644 index 0000000..7853a98 --- /dev/null +++ b/implementations/Prolog/docs/reference/not_negative.md @@ -0,0 +1,24 @@ +-------------------- + +## !- + +"not negative" + +(Function, Boolean Predicate) + +Integer on top of stack is replaced by Boolean value indicating whether +it is non-negative. + + N !- + ----------- N < 0 + false + + N !- + ---------- N >= 0 + true + + +### Definition + + 0 >= + diff --git a/implementations/Prolog/docs/reference/nullary.md b/implementations/Prolog/docs/reference/nullary.md new file mode 100644 index 0000000..b6eaeb4 --- /dev/null +++ b/implementations/Prolog/docs/reference/nullary.md @@ -0,0 +1,37 @@ +-------------------- + +## nullary + +(Combinator) + +Run a quoted program without using any stack values and leave the first item of the result on the stack. + + ... [P] nullary + --------------------- + ... A + +### Definition + + [stack] dip infra first + +### Derivation + + ... [P] nullary + ... [P] [stack] dip infra first + ... stack [P] infra first + ... [...] [P] infra first + ... [A ...] first + ... A + +### Discussion + +A very useful function that runs any other quoted function and returns +it's first result without disturbing the stack (under the quoted +program.) + +### Crosslinks + +[unary](#unary) +[binary](#binary) +[ternary](#ternary) + diff --git a/implementations/Prolog/docs/reference/ternary.md b/implementations/Prolog/docs/reference/ternary.md new file mode 100644 index 0000000..856b162 --- /dev/null +++ b/implementations/Prolog/docs/reference/ternary.md @@ -0,0 +1,29 @@ +-------------------- + +## ternary + +(Combinator) + + +Run a quoted program using exactly three stack values and leave the first +item of the result on the stack. + + ... z y x [P] unary + ------------------------- + ... A + +### Definition + + binary popd + +### Discussion + +Runs any other quoted function and returns its first result while +consuming exactly three items from the stack. + +### Crosslinks + +[binary](#binary) +[nullary](#nullary) +[unary](#unary) + diff --git a/implementations/Prolog/docs/reference/unary.md b/implementations/Prolog/docs/reference/unary.md new file mode 100644 index 0000000..ccd3ea4 --- /dev/null +++ b/implementations/Prolog/docs/reference/unary.md @@ -0,0 +1,27 @@ +-------------------- + +## unary + +(Combinator) + +Run a quoted program using exactly one stack value and leave the first item of the result on the stack. + + ... x [P] unary + --------------------- + ... A + +### Definition + + nullary popd + +### Discussion + +Runs any other quoted function and returns its first result while +consuming exactly one item from the stack. + +### Crosslinks + +[binary](#binary) +[nullary](#nullary) +[ternary](#ternary) + diff --git a/implementations/Prolog/docs/reference/uncons.md b/implementations/Prolog/docs/reference/uncons.md new file mode 100644 index 0000000..39f1d63 --- /dev/null +++ b/implementations/Prolog/docs/reference/uncons.md @@ -0,0 +1,25 @@ +-------------------- + +## uncons + +(Basis Function) + +Removes an item from a list and leaves it on the stack under the rest of +the list. You cannot `uncons` an item from an empty list. + + [A ...] uncons + -------------------- + A [...] + +### Source + + func(uncons, Si, So) :- func(cons, So, Si). + +### Discussion + +This is the inverse of `cons`. + +### Crosslinks + +[cons](#cons) + diff --git a/implementations/Prolog/docs/reference/x.md b/implementations/Prolog/docs/reference/x.md new file mode 100644 index 0000000..d956a4f --- /dev/null +++ b/implementations/Prolog/docs/reference/x.md @@ -0,0 +1,18 @@ +-------------------- + +## x + +(Combinator) + + [F] x + ----------- + [F] F + +### Definition + + dup i + +### Discussion + +The `x` combinator ... + diff --git a/implementations/Prolog/docs/user-guide/user-guide.md b/implementations/Prolog/docs/user-guide/user-guide.md new file mode 100644 index 0000000..d5181fe --- /dev/null +++ b/implementations/Prolog/docs/user-guide/user-guide.md @@ -0,0 +1,167 @@ +# User Guide + +There is no use interface as such. At the moment you just load the +`thun.pl` file into SWI Prolog and use some of these "top-level" +predicates to interact with it. + +------------------ + +## `joy/3` + + joy(InputString, StackIn, StackOut) + +### Evaluation + +Accepts a joy expression as a list of codes (in SWI Prolog you can use +backticks to quote a string literal and get codes): + + ?- joy(`+ *`, [int(2), int(3), int(10)], StackOut). + StackOut = [int(50)] ; + false. + + ?- joy(`2 3 + 10 *`, StackIn, StackOut). + StackOut = [int(50)|StackIn] ; + false. + +### Type Checking + +This predicate can also perform type checking: + + ?- joy(`2 [] +`, StackIn, StackOut). + false. + +### Type Inference + +And type inference with CLP(FD) constraints on integer operations and +comparisons: + + ?- joy(`+ *`, StackIn, StackOut). + StackIn = [int(_37782), int(_37792), int(_37802)|_37798], + StackOut = [int(_37824)|_37798], + _37782+_37792#=_37842, + _37842*_37802#=_37824 ; + false. + +------------------ + +## `joy_parse//1` + +If you just want to parse a string into a Joy expression use joy_parse//1 +DCG, like so: + + phrase(joy_parse(Expression), InputString) + +Or directly: + + joy_parse(Expression, InputString, []) + + +Example: + + ?- phrase(joy_parse(Expression), `1 [i]`). + Expression = [int(1), list([symbol(i)])] ; + false. + +------------------ + +## `thun/3` + +Once you have a (type-tagged) Joy exression as Prolog data-structure you +can use the `thun/3` predicate to evaluate it: + + thun(Expression, InputStack, OutputStack) + + +------------------ + +## `sjc/2` + + sjc(Name, InputString) + +Helper function to see to what Prolog code a given Joy expression would +compile. Give it a name (Prolog atom) and a list of code. + + ?- sjc(third, `third`). + func(third, [list([_, _, A|_])|B], [A|B]). + true ; + false. + + ?- sjc(ccons, `ccons`). + func(ccons, [list(C), B, A|D], [list([A, B|C])|D]). + true ; + false. + +Compilation captures CLP(FD) constraints: + + ?- sjc(+*, `+ *`). + func(+*, [int(D), int(E), int(B)|A], [int(C)|A]) :- + maplist(call, + [ clpfd:(F*B#=C), + clpfd:(D+E#=F) + ]). + true ; + false. + +------------------ + +## `show_joy_compile/2` + + show_joy_compile(Name, Expression) + +Same as `sjc/2` but you give it an already-parsed expression. + +------------------ + +## `joy_compile/2` + + joy_compile(Name, Expression) + +This actually asserts the new function definition into the Prolog rule +database. Use with care. + +At some point I'll probably add a build phase that tries to pre-compile +all definitions it can into Prolog rules but for now this is just +experimental. + +------------------ + +## `compiler/4` + + compiler(InputString, MachineCode, StackIn, StackOut) + +Experimental *and* unfinished, this predicate attempts to build a list of +terms representing machine code for the RISC CPU that Prof. Wirth has +specified for his Project Oberon. + +------------------ + +## `grow//0` & `shrink//0` + +These DCGs recursively unfold or fold definitions in a Joy expression: + + ?- phrase(grow, [symbol(third)], Out). + Out = [symbol(rest), symbol(rest), symbol(first)] ; + Out = [symbol(rest), symbol(rest), symbol(first)] ; + Out = [symbol(rest), symbol(second)] ; + Out = [symbol(third)]. + + ?- phrase(shrink, [symbol(rest), symbol(rest), symbol(first)], Out). + Out = [symbol(rrest), symbol(first)] ; + Out = [symbol(third)] ; + Out = [symbol(rest), symbol(second)] ; + Out = [symbol(rest), symbol(rest), symbol(first)]. + +They are more a proof-of-concept than useful at the moment. I imagine +that it might be possible to set up some kind of automated search through +all the variations to see if a more efficient form (after compiling with +whatever optimizations) can be found. + +------------------ + +## `joy_terms_to_string/2` + + joy_terms_to_string(Expr, String) + +Converts a Joy expression into a string (an actual string, not a list of +codes. See the [SWI Prolog manual for more information on strings](https://www.swi-prolog.org/pldoc/man?section=strings).) + diff --git a/implementations/Prolog/issues/boolean-prefix b/implementations/Prolog/issues/boolean-prefix new file mode 100644 index 0000000..3a36b5e --- /dev/null +++ b/implementations/Prolog/issues/boolean-prefix @@ -0,0 +1,21 @@ + ?- phrase(joy_parse(Expression), `truedat`). + Expression = [bool(true), symbol(dat)] . + +Oops! + +I knew about: + + ?- phrase(joy_parse(Expression), `23dat`). + Expression = [int(23), symbol(dat)] . + +should probably fix it too + +still want: + + ?- phrase(joy_parse(Expression), `[2[3]]`). + Expression = [list([int(2), list([int(3)])])] . + + ?- phrase(joy_parse(Expression), `[true[false]]`). + Expression = [list([bool(true), list([bool(false)])])] . + +...to work. That is, you shouldn't need spaces around '[' and ']'. diff --git a/implementations/Prolog/issues/disenstacken b/implementations/Prolog/issues/disenstacken new file mode 100644 index 0000000..3fa2cdf --- /dev/null +++ b/implementations/Prolog/issues/disenstacken @@ -0,0 +1,2 @@ +disenstacken is in the defs.txt +it needs to be updated to reflect the unstack<->disenstacken switch. \ No newline at end of file diff --git a/implementations/Prolog/issues/document-all-functions b/implementations/Prolog/issues/document-all-functions new file mode 100644 index 0000000..9dc8ff0 --- /dev/null +++ b/implementations/Prolog/issues/document-all-functions @@ -0,0 +1,132 @@ +Each function should have full documentation including examples, +deriviation, history, background, crosslinks (like "Pattern Language"), +rationale, details of implementation, etc. + +Still to do: + + % + && + * + + + ++ + - + -- + / + /% + < + <= + <> + = + > + >= + ? + abs + anamorphism + and + app2 + app3 + appN + at + average + bool + branch + clear + cleave + clop + codireco + concat + dinfrirst + dip + dipd + disenstacken + down_to_zero + drop + dup + dupd + dupdd + dupdip + dupdipd + empty? + enstacken + first + flatten + fork + fourth + gcd + genrec + grabN + grba + hypot + ifte + ii + infrst + list? + loop + make_generator + map + neg + not + of + one-or-more? + or + over + pam + pm + pop + popd + popdd + popop + popopd + popopdd + primrec + product + quoted + range + range_to_zero + rest + reverse + rolldown + rollup + rrest + run + second + shift + shunt + size + spiral_next + split_at + sqr + stack + step + step_zero + sum + swaack + swap + swons + take + third + times + tuck + unit + unquoted + unswons + while + words + || + +------- +Done: + + !- + app1 + b + binary + ccons + cons + i + infra + nullary + ternary + unary + uncons + x diff --git a/implementations/Prolog/issues/funcs-nooooo b/implementations/Prolog/issues/funcs-nooooo new file mode 100644 index 0000000..dce52d6 --- /dev/null +++ b/implementations/Prolog/issues/funcs-nooooo @@ -0,0 +1,18 @@ +?- sjc(add_twice, `[36 *] ii`). +func(add_twice, [int(B), int(D)|A], [int(C), int(E)|A]) :- + maplist(call, + [clpfd:(36*B#=C), clpfd:(36*D#=E)]). +true ; +func(add_twice, [symbol(swap), int(D), int(B)|A], [int(C), int(E)|A]) :- + maplist(call, + [clpfd:(36*B#=C), clpfd:(36*D#=E)]). +true ; +func(add_twice, [symbol(dup), int(D)|A], [int(C), int(B)|A]) :- + maplist(call, + [clpfd:(36*B#=C), clpfd:(36*D#=B)]). +true ; +func(add_twice, [symbol(pop), int(D), int(B)|A], [int(C)|A]) :- + maplist(call, + [clpfd:(36*B#=C), clpfd:(36*D#=_)]). +true . + diff --git a/implementations/Prolog/issues/interpolation-literal b/implementations/Prolog/issues/interpolation-literal new file mode 100644 index 0000000..b3ab9ab --- /dev/null +++ b/implementations/Prolog/issues/interpolation-literal @@ -0,0 +1,13 @@ + +What about a kind of interpolation literal? + + {} -> [] + \n -> nth stack item. + +So: + + a b c { \0 { \2 { \0 \1 }}} + --------------------------------- + [ c [ a [ c b ]]] + +And so on... diff --git a/implementations/Prolog/jd.dot b/implementations/Prolog/jd.dot new file mode 100644 index 0000000..6ceca42 --- /dev/null +++ b/implementations/Prolog/jd.dot @@ -0,0 +1,306 @@ +digraph joy_defs { + "--" -> "-"; + "?" -> "bool"; + "?" -> "dup"; + "&&" -> "branch"; + "&&" -> "dip"; + "&&" -> "nullary"; + "&&" -> "nulco"; + "++" -> "+"; + "||" -> "branch"; + "||" -> "dip"; + "||" -> "nullary"; + "||" -> "nulco"; + "!-" -> ">="; + "<{}" -> "swap"; + "<<{}" -> "rolldown"; + "abs" -> "branch"; + "abs" -> "neg"; + "abs" -> "<"; + "abs" -> "dup"; + "anamorphism" -> "genrec"; + "anamorphism" -> "swons"; + "anamorphism" -> "dip"; + "anamorphism" -> "swap"; + "anamorphism" -> "pop"; + "app1" -> "infrst"; + "app1" -> "grba"; + "app2" -> "ii"; + "app2" -> "cons"; + "app2" -> "infrst"; + "app2" -> "dip"; + "app2" -> "swap"; + "app2" -> "grba"; + "app3" -> "appN"; + "appN" -> "disenstacken"; + "appN" -> "map"; + "appN" -> "codi"; + "appN" -> "grabN"; + "at" -> "first"; + "at" -> "drop"; + "average" -> "/"; + "average" -> "cleave"; + "average" -> "size"; + "average" -> "sum"; + "b" -> "dip"; + "b" -> "i"; + "binary" -> "popd"; + "binary" -> "unary"; + "ccccons" -> "ccons"; + "ccons" -> "cons"; + "clear" -> "loop"; + "clear" -> "pop"; + "clear" -> "bool"; + "clear" -> "stack"; + "cleave" -> "popdd"; + "cleave" -> "fork"; + "clop" -> "popdd"; + "clop" -> "cleave"; + "codi" -> "dip"; + "codi" -> "cons"; + "codireco" -> "reco"; + "codireco" -> "codi"; + "dinfrirst" -> "infrst"; + "dinfrirst" -> "dip"; + "dipd" -> "codi"; + "dipd" -> "dip"; + "disenstacken" -> "pop"; + "disenstacken" -> "loop"; + "disenstacken" -> "uncons"; + "disenstacken" -> "?"; + "down_to_zero" -> "while"; + "down_to_zero" -> "--"; + "down_to_zero" -> "dup"; + "down_to_zero" -> ">"; + "drop" -> "times"; + "drop" -> "rest"; + "dupd" -> "dip"; + "dupd" -> "dup"; + "dupdd" -> "dipd"; + "dupdd" -> "dup"; + "dupdip" -> "dip"; + "dupdip" -> "dupd"; + "dupdipd" -> "dipd"; + "dupdipd" -> "dup"; + "enstacken" -> "dip"; + "enstacken" -> "clear"; + "enstacken" -> "stack"; + "flatten" -> "step"; + "flatten" -> "concat"; + "flatten" -> "<{}"; + "fork" -> "app2"; + "fork" -> "i"; + "fourth" -> "third"; + "fourth" -> "rest"; + "gcd" -> "pop"; + "gcd" -> "loop"; + "gcd" -> ">"; + "gcd" -> "dup"; + "gcd" -> "mod"; + "gcd" -> "tuck"; + "genrec" -> "ifte"; + "genrec" -> "concat"; + "genrec" -> "swons"; + "genrec" -> "nullary"; + "genrec" -> "ccccons"; + "genrec" -> "genrec"; + "grabN" -> "times"; + "grabN" -> "cons"; + "grabN" -> "<{}"; + "grba" -> "dip"; + "grba" -> "popd"; + "grba" -> "stack"; + "hypot" -> "sqrt"; + "hypot" -> "+"; + "hypot" -> "ii"; + "hypot" -> "sqr"; + "ifte" -> "branch"; + "ifte" -> "swap"; + "ifte" -> "dipd"; + "ifte" -> "nullary"; + "ii" -> "i"; + "ii" -> "dupdip"; + "ii" -> "dip"; + "infra" -> "dip"; + "infra" -> "i"; + "infra" -> "swaack"; + "infra" -> "swons"; + "infrst" -> "first"; + "infrst" -> "infra"; + "make_generator" -> "ccons"; + "make_generator" -> "codireco"; + "mod" -> "%"; + "neg" -> "-"; + "neg" -> "swap"; + "not" -> "branch"; + "nulco" -> "cons"; + "nulco" -> "nullary"; + "nullary" -> "dinfrirst"; + "nullary" -> "stack"; + "of" -> "at"; + "of" -> "swap"; + "pam" -> "map"; + "pam" -> "i"; + "pm" -> "clop"; + "pm" -> "-"; + "pm" -> "+"; + "popd" -> "dip"; + "popd" -> "pop"; + "popdd" -> "dipd"; + "popdd" -> "pop"; + "popop" -> "pop"; + "popopop" -> "popop"; + "popopop" -> "pop"; + "popopd" -> "dip"; + "popopd" -> "popop"; + "popopdd" -> "dipd"; + "popopdd" -> "popop"; + "product" -> "step"; + "product" -> "*"; + "product" -> "swap"; + "quoted" -> "dip"; + "quoted" -> "unit"; + "range" -> "anamorphism"; + "range" -> "dup"; + "range" -> "-"; + "range" -> "<="; + "range_to_zero" -> "infra"; + "range_to_zero" -> "down_to_zero"; + "range_to_zero" -> "unit"; + "reco" -> "cons"; + "reco" -> "rest"; + "rest" -> "infra"; + "rest" -> "pop"; + "reverse" -> "shunt"; + "reverse" -> "<{}"; + "roll>" -> "swapd"; + "roll>" -> "swap"; + "roll<" -> "swap"; + "roll<" -> "swapd"; + "rollup" -> "roll>"; + "rolldown" -> "roll<"; + "rrest" -> "rest"; + "run" -> "infra"; + "run" -> "<{}"; + "second" -> "first"; + "second" -> "rest"; + "shift" -> "dip"; + "shift" -> "swons"; + "shift" -> "uncons"; + "shunt" -> "step"; + "shunt" -> "swons"; + "size" -> "step_zero"; + "size" -> "++"; + "size" -> "pop"; + "spiral_next" -> "dip"; + "spiral_next" -> "ifte"; + "spiral_next" -> "--"; + "spiral_next" -> "++"; + "spiral_next" -> "&&"; + "spiral_next" -> "||"; + "spiral_next" -> "!-"; + "spiral_next" -> "pop"; + "spiral_next" -> "<>"; + "spiral_next" -> "<="; + "spiral_next" -> "ii"; + "spiral_next" -> "abs"; + "split_at" -> "clop"; + "split_at" -> "take"; + "split_at" -> "drop"; + "split_list" -> "clop"; + "split_list" -> "drop"; + "split_list" -> "reverse"; + "split_list" -> "take"; + "sqr" -> "*"; + "sqr" -> "dup"; + "stackd" -> "dip"; + "stackd" -> "stack"; + "step_zero" -> "step"; + "step_zero" -> "roll>"; + "sum" -> "step_zero"; + "sum" -> "+"; + "swapd" -> "dip"; + "swapd" -> "swap"; + "swons" -> "cons"; + "swons" -> "swap"; + "swoncat" -> "concat"; + "swoncat" -> "swap"; + "tailrec" -> "genrec"; + "tailrec" -> "i"; + "take" -> "pop"; + "take" -> "times"; + "take" -> "shift"; + "take" -> "roll>"; + "ternary" -> "popd"; + "ternary" -> "binary"; + "third" -> "second"; + "third" -> "rest"; + "tuck" -> "swapd"; + "tuck" -> "dup"; + "unary" -> "popd"; + "unary" -> "nullary"; + "uncons" -> "cleave"; + "uncons" -> "rest"; + "uncons" -> "first"; + "unit" -> "cons"; + "unquoted" -> "dip"; + "unquoted" -> "i"; + "unswons" -> "swap"; + "unswons" -> "uncons"; + "while" -> "loop"; + "while" -> "concat"; + "while" -> "dupdipd"; + "while" -> "nulco"; + "while" -> "swap"; + "x" -> "i"; + "x" -> "dup"; + "step" -> "x"; + "step" -> "_step0"; + "_step0" -> "branch"; + "_step0" -> "_stept"; + "_step0" -> "popopop"; + "_step0" -> "_step1"; + "_step1" -> "roll<"; + "_step1" -> "dipd"; + "_step1" -> "?"; + "_stept" -> "x"; + "_stept" -> "dip"; + "_stept" -> "dupdipd"; + "_stept" -> "dipd"; + "_stept" -> "uncons"; + "times" -> "x"; + "times" -> "_times0"; + "_times0" -> "branch"; + "_times0" -> "_timest"; + "_times0" -> "popopop"; + "_times0" -> "_times1"; + "_times1" -> "roll<"; + "_times1" -> "dipd"; + "_times1" -> ">"; + "_times1" -> "dup"; + "_timest" -> "x"; + "_timest" -> "dupdipd"; + "_timest" -> "dip"; + "_timest" -> "--"; + "map" -> "tailrec"; + "map" -> "dip"; + "map" -> "_mape"; + "map" -> "_map?"; + "map" -> "cons"; + "map" -> "_map0"; + "_map?" -> "not"; + "_map?" -> "bool"; + "_map?" -> "pop"; + "_mape" -> "reverse"; + "_mape" -> "popd"; + "_map0" -> "_map2"; + "_map0" -> "dipd"; + "_map0" -> "_map1"; + "_map1" -> "shift"; + "_map1" -> "stackd"; + "_map2" -> "swons"; + "_map2" -> "roll<"; + "_map2" -> "dipd"; + "_map2" -> "cons"; + "_map2" -> "infrst"; +} diff --git a/implementations/Prolog/jd.dot.svg b/implementations/Prolog/jd.dot.svg new file mode 100644 index 0000000..d01d0ce --- /dev/null +++ b/implementations/Prolog/jd.dot.svg @@ -0,0 +1,2671 @@ + + + + + + +joy_defs + + + +-- + +-- + + + +- + +- + + + +--->- + + + + + +? + +? + + + +bool + +bool + + + +?->bool + + + + + +dup + +dup + + + +?->dup + + + + + +&& + +&& + + + +branch + +branch + + + +&&->branch + + + + + +dip + +dip + + + +&&->dip + + + + + +nullary + +nullary + + + +&&->nullary + + + + + +nulco + +nulco + + + +&&->nulco + + + + + +stack + +stack + + + +nullary->stack + + + + + +dinfrirst + +dinfrirst + + + +nullary->dinfrirst + + + + + +nulco->nullary + + + + + +cons + +cons + + + +nulco->cons + + + + + +++ + +++ + + + ++ + ++ + + + +++->+ + + + + + +|| + +|| + + + +||->branch + + + + + +||->dip + + + + + +||->nullary + + + + + +||->nulco + + + + + +!- + +!- + + + +>= + +>= + + + +!-->>= + + + + + +<{} + +<{} + + + +swap + +swap + + + +<{}->swap + + + + + +<<{} + +<<{} + + + +rolldown + +rolldown + + + +<<{}->rolldown + + + + + +roll< + +roll< + + + +rolldown->roll< + + + + + +abs + +abs + + + +abs->dup + + + + + +abs->branch + + + + + +neg + +neg + + + +abs->neg + + + + + +< + +< + + + +abs->< + + + + + +neg->- + + + + + +neg->swap + + + + + +anamorphism + +anamorphism + + + +anamorphism->dip + + + + + +anamorphism->swap + + + + + +genrec + +genrec + + + +anamorphism->genrec + + + + + +swons + +swons + + + +anamorphism->swons + + + + + +pop + +pop + + + +anamorphism->pop + + + + + +genrec->nullary + + + + + +genrec->genrec + + + + + +genrec->swons + + + + + +ccccons + +ccccons + + + +genrec->ccccons + + + + + +concat + +concat + + + +genrec->concat + + + + + +ifte + +ifte + + + +genrec->ifte + + + + + +swons->swap + + + + + +swons->cons + + + + + +app1 + +app1 + + + +infrst + +infrst + + + +app1->infrst + + + + + +grba + +grba + + + +app1->grba + + + + + +first + +first + + + +infrst->first + + + + + +infra + +infra + + + +infrst->infra + + + + + +grba->dip + + + + + +popd + +popd + + + +grba->popd + + + + + +grba->stack + + + + + +app2 + +app2 + + + +app2->dip + + + + + +app2->swap + + + + + +app2->infrst + + + + + +app2->grba + + + + + +ii + +ii + + + +app2->ii + + + + + +app2->cons + + + + + +ii->dip + + + + + +i + +i + + + +ii->i + + + + + +dupdip + +dupdip + + + +ii->dupdip + + + + + +app3 + +app3 + + + +appN + +appN + + + +app3->appN + + + + + +disenstacken + +disenstacken + + + +appN->disenstacken + + + + + +map + +map + + + +appN->map + + + + + +codi + +codi + + + +appN->codi + + + + + +grabN + +grabN + + + +appN->grabN + + + + + +disenstacken->? + + + + + +disenstacken->pop + + + + + +loop + +loop + + + +disenstacken->loop + + + + + +uncons + +uncons + + + +disenstacken->uncons + + + + + +map->dip + + + + + +map->cons + + + + + +tailrec + +tailrec + + + +map->tailrec + + + + + +_mape + +_mape + + + +map->_mape + + + + + +_map? + +_map? + + + +map->_map? + + + + + +_map0 + +_map0 + + + +map->_map0 + + + + + +codi->dip + + + + + +codi->cons + + + + + +grabN-><{} + + + + + +grabN->cons + + + + + +times + +times + + + +grabN->times + + + + + +at + +at + + + +at->first + + + + + +drop + +drop + + + +at->drop + + + + + +drop->times + + + + + +rest + +rest + + + +drop->rest + + + + + +average + +average + + + +/ + +/ + + + +average->/ + + + + + +cleave + +cleave + + + +average->cleave + + + + + +size + +size + + + +average->size + + + + + +sum + +sum + + + +average->sum + + + + + +popdd + +popdd + + + +cleave->popdd + + + + + +fork + +fork + + + +cleave->fork + + + + + +size->++ + + + + + +size->pop + + + + + +step_zero + +step_zero + + + +size->step_zero + + + + + +sum->+ + + + + + +sum->step_zero + + + + + +b + +b + + + +b->dip + + + + + +b->i + + + + + +binary + +binary + + + +binary->popd + + + + + +unary + +unary + + + +binary->unary + + + + + +popd->dip + + + + + +popd->pop + + + + + +unary->nullary + + + + + +unary->popd + + + + + +ccons + +ccons + + + +ccccons->ccons + + + + + +ccons->cons + + + + + +clear + +clear + + + +clear->bool + + + + + +clear->pop + + + + + +clear->loop + + + + + +clear->stack + + + + + +popdd->pop + + + + + +dipd + +dipd + + + +popdd->dipd + + + + + +fork->app2 + + + + + +fork->i + + + + + +clop + +clop + + + +clop->cleave + + + + + +clop->popdd + + + + + +codireco + +codireco + + + +codireco->codi + + + + + +reco + +reco + + + +codireco->reco + + + + + +reco->cons + + + + + +reco->rest + + + + + +dinfrirst->dip + + + + + +dinfrirst->infrst + + + + + +dipd->dip + + + + + +dipd->codi + + + + + +uncons->first + + + + + +uncons->cleave + + + + + +uncons->rest + + + + + +down_to_zero + +down_to_zero + + + +down_to_zero->-- + + + + + +down_to_zero->dup + + + + + +while + +while + + + +down_to_zero->while + + + + + +> + +> + + + +down_to_zero->> + + + + + +while->nulco + + + + + +while->swap + + + + + +while->loop + + + + + +dupdipd + +dupdipd + + + +while->dupdipd + + + + + +while->concat + + + + + +x + +x + + + +times->x + + + + + +_times0 + +_times0 + + + +times->_times0 + + + + + +rest->pop + + + + + +rest->infra + + + + + +dupd + +dupd + + + +dupd->dup + + + + + +dupd->dip + + + + + +dupdd + +dupdd + + + +dupdd->dup + + + + + +dupdd->dipd + + + + + +dupdip->dip + + + + + +dupdip->dupd + + + + + +dupdipd->dup + + + + + +dupdipd->dipd + + + + + +enstacken + +enstacken + + + +enstacken->dip + + + + + +enstacken->clear + + + + + +enstacken->stack + + + + + +flatten + +flatten + + + +flatten-><{} + + + + + +step + +step + + + +flatten->step + + + + + +flatten->concat + + + + + +step->x + + + + + +_step0 + +_step0 + + + +step->_step0 + + + + + +fourth + +fourth + + + +fourth->rest + + + + + +third + +third + + + +fourth->third + + + + + +third->rest + + + + + +second + +second + + + +third->second + + + + + +gcd + +gcd + + + +gcd->dup + + + + + +gcd->pop + + + + + +gcd->loop + + + + + +gcd->> + + + + + +mod + +mod + + + +gcd->mod + + + + + +tuck + +tuck + + + +gcd->tuck + + + + + +%226432 + +%1079413561 + + + +mod->%0 + + + + + +tuck->dup + + + + + +swapd + +swapd + + + +tuck->swapd + + + + + +ifte->branch + + + + + +ifte->nullary + + + + + +ifte->swap + + + + + +ifte->dipd + + + + + +hypot + +hypot + + + +hypot->+ + + + + + +hypot->ii + + + + + +sqrt + +sqrt + + + +hypot->sqrt + + + + + +sqr + +sqr + + + +hypot->sqr + + + + + +sqr->dup + + + + + +* + +* + + + +sqr->* + + + + + +infra->dip + + + + + +infra->swons + + + + + +infra->i + + + + + +swaack + +swaack + + + +infra->swaack + + + + + +make_generator + +make_generator + + + +make_generator->ccons + + + + + +make_generator->codireco + + + + + +not + +not + + + +not->branch + + + + + +of + +of + + + +of->swap + + + + + +of->at + + + + + +pam + +pam + + + +pam->map + + + + + +pam->i + + + + + +pm + +pm + + + +pm->- + + + + + +pm->+ + + + + + +pm->clop + + + + + +popop + +popop + + + +popop->pop + + + + + +popopop + +popopop + + + +popopop->pop + + + + + +popopop->popop + + + + + +popopd + +popopd + + + +popopd->dip + + + + + +popopd->popop + + + + + +popopdd + +popopdd + + + +popopdd->dipd + + + + + +popopdd->popop + + + + + +product + +product + + + +product->swap + + + + + +product->step + + + + + +product->* + + + + + +quoted + +quoted + + + +quoted->dip + + + + + +unit + +unit + + + +quoted->unit + + + + + +unit->cons + + + + + +range + +range + + + +range->- + + + + + +range->dup + + + + + +range->anamorphism + + + + + +<= + +<= + + + +range-><= + + + + + +range_to_zero + +range_to_zero + + + +range_to_zero->down_to_zero + + + + + +range_to_zero->infra + + + + + +range_to_zero->unit + + + + + +reverse + +reverse + + + +reverse-><{} + + + + + +shunt + +shunt + + + +reverse->shunt + + + + + +shunt->swons + + + + + +shunt->step + + + + + +roll> + +roll> + + + +roll>->swap + + + + + +roll>->swapd + + + + + +swapd->dip + + + + + +swapd->swap + + + + + +roll<->swap + + + + + +roll<->swapd + + + + + +rollup + +rollup + + + +rollup->roll> + + + + + +rrest + +rrest + + + +rrest->rest + + + + + +run + +run + + + +run-><{} + + + + + +run->infra + + + + + +second->first + + + + + +second->rest + + + + + +shift + +shift + + + +shift->dip + + + + + +shift->swons + + + + + +shift->uncons + + + + + +step_zero->step + + + + + +step_zero->roll> + + + + + +spiral_next + +spiral_next + + + +spiral_next->-- + + + + + +spiral_next->&& + + + + + +spiral_next->dip + + + + + +spiral_next->++ + + + + + +spiral_next->|| + + + + + +spiral_next->!- + + + + + +spiral_next->abs + + + + + +spiral_next->pop + + + + + +spiral_next->ii + + + + + +spiral_next->ifte + + + + + +spiral_next-><= + + + + + +<> + +<> + + + +spiral_next-><> + + + + + +split_at + +split_at + + + +split_at->drop + + + + + +split_at->clop + + + + + +take + +take + + + +split_at->take + + + + + +take->pop + + + + + +take->times + + + + + +take->roll> + + + + + +take->shift + + + + + +split_list + +split_list + + + +split_list->drop + + + + + +split_list->clop + + + + + +split_list->reverse + + + + + +split_list->take + + + + + +stackd + +stackd + + + +stackd->dip + + + + + +stackd->stack + + + + + +swoncat + +swoncat + + + +swoncat->swap + + + + + +swoncat->concat + + + + + +tailrec->genrec + + + + + +tailrec->i + + + + + +ternary + +ternary + + + +ternary->binary + + + + + +ternary->popd + + + + + +unquoted + +unquoted + + + +unquoted->dip + + + + + +unquoted->i + + + + + +unswons + +unswons + + + +unswons->swap + + + + + +unswons->uncons + + + + + +x->dup + + + + + +x->i + + + + + +_step0->branch + + + + + +_step0->popopop + + + + + +_stept + +_stept + + + +_step0->_stept + + + + + +_step1 + +_step1 + + + +_step0->_step1 + + + + + +_stept->dip + + + + + +_stept->dipd + + + + + +_stept->uncons + + + + + +_stept->dupdipd + + + + + +_stept->x + + + + + +_step1->? + + + + + +_step1->dipd + + + + + +_step1->roll< + + + + + +_times0->branch + + + + + +_times0->popopop + + + + + +_timest + +_timest + + + +_times0->_timest + + + + + +_times1 + +_times1 + + + +_times0->_times1 + + + + + +_timest->-- + + + + + +_timest->dip + + + + + +_timest->dupdipd + + + + + +_timest->x + + + + + +_times1->dup + + + + + +_times1->dipd + + + + + +_times1->> + + + + + +_times1->roll< + + + + + +_mape->popd + + + + + +_mape->reverse + + + + + +_map?->bool + + + + + +_map?->pop + + + + + +_map?->not + + + + + +_map0->dipd + + + + + +_map2 + +_map2 + + + +_map0->_map2 + + + + + +_map1 + +_map1 + + + +_map0->_map1 + + + + + +_map2->swons + + + + + +_map2->infrst + + + + + +_map2->cons + + + + + +_map2->dipd + + + + + +_map2->roll< + + + + + +_map1->shift + + + + + +_map1->stackd + + + + + diff --git a/implementations/Prolog/source/agl.PNG b/implementations/Prolog/source/agl.PNG new file mode 100644 index 0000000000000000000000000000000000000000..ba9a729f79203dcd69a4e1ed86e15afbaa15f949 GIT binary patch literal 63042 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%0Df)2zpqZ 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%0D3vI+xk1(IX8UnG?y3yQGeySFZIqptHx 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 z. + +A version of joy with just lists and symbols, data structures are +logical expressions in LoF notation, optionally organised in lists. +No distinction is made syntactically or semantically between lists-as-forumla +and lists-as-containers, 'tis done by usage. + + [] as zero / false + [[]] as true (1 in Peano arith) + + ((A)(B)) OR + A B AND + ((A) B) B IMPLIES A + + (A(B)) ((A)B) EQUIV (A IMPLIES B) AND (B IMPLIES A) + ((A(B)) ((A)B)) XOR + + + + + _ _ ( ( )) (( ) ) _ + o _ (o( )) ((o) ) o + _ o ( (o)) (( )o) o + o o (o(o)) ((o)o) _ + + _ _ ( ) o + o _ (o) _ + _ o ( )o o + o o (o)o o + + _ _ (( ) ) _ + o _ ((o) ) o + _ o (( )o) _ + o o ((o)o) _ + + + +*/ + +joy(InputString, StackIn, StackOut) :- + phrase(joy_parse(Expression), InputString), !, + thun(Expression, StackIn, StackOut). + +joy_parse([J|Js]) --> blanks, joy_term(J), blanks, joy_parse(Js). +joy_parse([]) --> blanks. +joy_term(list(J)) --> "[", !, joy_parse(J), "]". +joy_term(symbol(S)) --> symbol(S). +symbol(C) --> chars(Chars), !, {atom_string(C, Chars)}. +chars([Ch|Rest]) --> char(Ch), chars(Rest). +chars([Ch]) --> char(Ch). +char(Ch) --> [Ch], {Ch \== 91, Ch \== 93, code_type(Ch, graph)}. + +thun([], S, S). +% thun(E, Si, _) :- show_it(E, Si), fail. % To visualize the evaluation. +thun([Term|E], S0, S) :- thun(Term, E, S0, S). + +thun(list(L), Expr, S0, S) :- thun(Expr, [list(L)|S0], S). +thun(symbol(Name), Expr0, S0, S) :- + ( def(Name, Body), append(Body, Expr0, Expr), S1=S0 + ; func(Name, S0, S1), Expr0=Expr + ; combo(Name, S0, S1, Expr0, Expr) + ), thun(Expr, S1, S). + +show_it(E, Si) :- + joy_terms_to_string(E, Es), + is_list(Si), reverse(Si, Is), + joy_terms_to_string(Is, Sis), + write(Sis), write(' . '), writeln(Es). + +% joy_terms_to_string(So, S) + +func(void, [A|S], [B|S]) :- void(A, B). +% func(or, [A, B|S], [[[B], [A]]|S]). +% func(and, [A, B|S], [[[B, A]]|S]). +func(swap, [A, B|S], [B, A|S]). +func(dup, [A|S], [A, A|S]). +func(pop, [_|S], S ). +func(cons, [list(A), B |S], [list([B|A])|S]). +func(concat, [list(A), list(B)|S], [list(C)|S]) :- append(B, A, C). +func(flatten, [list(A)|S], [list(B)|S]) :- flatten(A, B). +func(swaack, [list(R)|S], [list(S)|R]). +func(stack, S , [list(S)|S]). +func(clear, _ , []). +func(first, [list([X|_])|S], [ X |S]). +func(rest, [list([_|X])|S], [list(X)|S]). +func(unit, [X|S], [list([X])|S]). +func(rolldown, [A, B, C|S], [B, C, A|S]). +func(dupd, [A, B|S], [A, B, B|S]). +func(over, [A, B|S], [B, A, B|S]). +func(tuck, [A, B|S], [A, B, A|S]). +func(shift, [list([B|A]), list(C)|D], [list(A), list([B|C])|D]). +func(rollup, Si, So) :- func(rolldown, So, Si). +func(uncons, Si, So) :- func(cons, So, Si). + +combo(i, [list(P)|S], S, Ei, Eo) :- append(P, Ei, Eo). +combo(dip, [list(P), X|S], S, Ei, Eo) :- append(P, [X|Ei], Eo). +combo(dipd, [list(P), X, Y|S], S, Ei, Eo) :- append(P, [Y, X|Ei], Eo). +combo(dupdip, [list(P), X|S], [X|S], Ei, Eo) :- append(P, [X|Ei], Eo). +combo(branch, [list(T), list(_), list([list([])])|S], S, Ei, Eo) :- append(T, Ei, Eo). +combo(branch, [list(_), list(F), list([]) |S], S, Ei, Eo) :- append(F, Ei, Eo). +combo(loop, [list(_), list([]) |S], S, E, E ). +combo(loop, [list(B), list([list([])])|S], S, Ei, Eo) :- append(B, [list(B), symbol(loop)|Ei], Eo). +combo(step, [list(_), list([])|S], S, E, E ). +combo(step, [list(P), list([X|Z])|S], [X|S], Ei, Eo) :- append(P, [list(Z), list(P), symbol(step)|Ei], Eo). +combo(times, [list(_), list([]) |S], S, E, E ). +combo(times, [list(P), list([list([])])|S], S, Ei, Eo) :- append(P, Ei, Eo). +combo(times, [list(P), list([list(L )])|S], S, Ei, Eo) :- + L \= [], append(P, [list(L), list(P), symbol(times)|Ei], Eo). +combo(genrec, [R1, R0, Then, If|S], [Else, Then, If|S], E, [symbol(ifte)|E]) :- + append(R0, [list([If, Then, R0, R1, symbol(genrec)])|R1], Else). +combo(map, [list(_), list([])|S], [list([])|S], E, E ) :- !. +combo(map, [list(P), list(List)|S], [list(Mapped), list([])|S], E, [symbol(infra)|E]) :- + prepare_mapping(list(P), S, List, Mapped). + +prepare_mapping(Pl, S, In, Out) :- prepare_mapping(Pl, S, In, [], Out). + +prepare_mapping( _, _, [], Out, Out) :- !. +prepare_mapping( Pl, S, [T|In], Acc, Out) :- + prepare_mapping(Pl, S, In, [list([T|S]), Pl, symbol(infrst)|Acc], Out). + + +term_expansion(def(Def), def(Name, Body)) :- + phrase(joy_parse([symbol(Name)|Body]), Def), + % Don't let defs "shadow" functions or combinators. + \+ ( func(Name, _, _) ; combo(Name, _, _, _, _) ). + +% def(``). +def(`and duo unit`). +def(`app2 [grba swap grba swap] dip [infrst] cons ii`). +def(`b [i] dip i`). +def(`cleave fork popdd`). +def(`clop cleave popdd`). +def(`duo unit cons`). +def(`fba [xor xor void] [[and] [xor and] fork or void] clop popdd`). +def(`fork [i] app2`). +def(`grba [stack popd] dip`). +def(`ii [dip] dupdip i`). +def(`infra swons swaack [i] dip swaack`). +def(`infrst infra first`). +def(`or [unit] ii duo`). +def(`popd [pop] dip`). +def(`popdd [pop] dipd`). +def(`popop pop pop`). +def(`swons swap cons`). +def(`uncons-pair [uncons] dip unswons rolldown`). +def(`unswons uncons swap`). +def(`xor [unit] ii [cons] [swap cons] clop duo`). + + +format_joy_expression( V ) --> { var(V), ! }, "...". +format_joy_expression(symbol(S)) --> !, { atom_codes(S, Codes) }, Codes. +format_joy_expression( list(J)) --> "[", format_joy_terms(J), "]". +format_joy_terms( []) --> []. +format_joy_terms( [T]) --> format_joy_expression(T), !. +format_joy_terms([T|Ts]) --> format_joy_expression(T), " ", format_joy_terms(Ts). +joy_terms_to_string(Expr, String) :- + format_joy_terms(Expr, Codes, []), + string_codes(String, Codes). + +/* Reduce arithmetic formula to Mark or Void */ + +void( list([]), list([]) ) :- !. +void(list([list([])]), list([list([])])) :- !. +void(list([ A |_]), list([list([])])) :- void(A, list([]) ), !. +void(list([ A |S]), V ) :- void(A, list([list([])])), void(list(S), V). + + +symbols(E, S) :- symbols(E, [], S). + +symbols(symbol(S)) --> seen_sym(S), !. +symbols(symbol(S)), [S] --> []. +symbols( list([])) --> []. +symbols(list([T|Tail])) --> symbols(T), symbols(list(Tail)). + +seen_sym(Term, List, List) :- member(Term, List). + +fooooo :- forall(def(Symbol, Body), + ( + symbols(list(Body), Deps), + forall(member(Dep, Deps), + ( + write(Symbol), + write(" -> "), + write(Dep), + writeln(";") + ) + ) + ) +). + + +/* + + + +ᴀ?- joy(`[] [ [] [[]] [] ] [or] step void`, Si, So), !, joy_terms_to_string(So, S). +Si = [], +So = [list([list([])])], +S = "[[]]". + +?- joy(`[[]] [ [[]] [[]] [[]] [[]] ] [and] step void`, Si, So), !, joy_terms_to_string(So, S). +Si = [], +So = [list([list([])])], +S = "[[]]". + + + */ \ No newline at end of file diff --git a/implementations/Prolog/source/bleah.code-workspace b/implementations/Prolog/source/bleah.code-workspace new file mode 100644 index 0000000..f804ba3 --- /dev/null +++ b/implementations/Prolog/source/bleah.code-workspace @@ -0,0 +1,14 @@ +{ + "folders": [ + { + "path": "C:\\Users\\sforman\\Desktop\\src\\PROLOG\\Thun" + }, + { + "path": "C:\\Users\\sforman\\Desktop\\src\\PROLOG\\yrad-nettles" + }, + { + "path": "C:\\Users\\sforman\\Desktop\\src\\PROLOG\\prolog-markdown" + } + ], + "settings": {} +} \ No newline at end of file diff --git a/implementations/Prolog/source/canhazmd.pl b/implementations/Prolog/source/canhazmd.pl new file mode 100644 index 0000000..7695384 --- /dev/null +++ b/implementations/Prolog/source/canhazmd.pl @@ -0,0 +1,45 @@ +:- use_module(library(md/md_parse)). + +fn("C:/Users/sforman/Desktop/src/PROLOG/Thun/docs/reference/Functor-Reference.md"). + +do(X) :- + fn(Fn), + md_parse_file(Fn, Blocks), + split_on_hr([_|X], Blocks), !. % Ignore the header for now + + +% Split a list of HTML stuff into sublists on
tags. +split_on_hr([Thing|Rest], Blocks) :- append(Thing, [hr([])|Tail], Blocks), !, split_on_hr(Rest, Tail). +split_on_hr(Blocks, Blocks). + + +bar([h2(Name)|_]) :- writeln(Name). + + +fooober(Name, [preable(Preamble)|Sections]) --> [h2(Name)], parts(Preamble), sections(Sections). + +sections([S|Rest]) --> section(S), sections(Rest). +sections([]) --> []. + +section(definition(Stuff)) --> [h3("Definition")], parts(Stuff). +section(derivation(Stuff)) --> [h3("Derivation")], parts(Stuff). +section(source(Stuff)) --> [h3("Source")], parts(Stuff). +section(discussion(Stuff)) --> [h3("Discussion")], parts(Stuff). +section(crosslinks(Stuff)) --> [h3("Crosslinks")], parts(Stuff). + +parts([P|Ps]) --> part(P), !, parts(Ps). +parts([]) --> []. + +part(p(P)) --> [p(P)]. +part(pre(P)) --> [pre(P)]. + +% ... --> [] | [_], ... . + +/* + +?- do([_, _, X|_]), fooober(Name, Docs, X, _), !. +X = [h2("b"), p([\["(Combinator)"]]), p([\["Run two quoted programs"]]), pre(code(" [P] [Q] b\n---------------\n P Q")), h3("Definition"), pre(code("[i] dip i")), h3("Derivation"), pre(code(...)), h3(...)|...], +Name = "b", +Docs = [preable([p([\["(Combinator)"]]), p([\["Run two quoted programs"]]), pre(code(" [P] [Q] b\n---------------\n P Q"))]), definition([pre(code("[i] dip i"))]), derivation([pre(code("[P] [Q] b\n[P] [Q] [i] dip i\n[P] i [Q] i\n P [Q] i\n P Q"))]), discussion([p([\[...]])]), crosslinks([p([a(..., ...)|...])])]. + + */ \ No newline at end of file diff --git a/implementations/Prolog/source/client/elm.json b/implementations/Prolog/source/client/elm.json new file mode 100644 index 0000000..1208d3d --- /dev/null +++ b/implementations/Prolog/source/client/elm.json @@ -0,0 +1,24 @@ +{ + "type": "application", + "source-directories": [ + "src" + ], + "elm-version": "0.19.1", + "dependencies": { + "direct": { + "elm/browser": "1.0.2", + "elm/core": "1.0.4", + "elm/html": "1.0.0", + "elm/url": "1.0.0" + }, + "indirect": { + "elm/json": "1.1.3", + "elm/time": "1.0.0", + "elm/virtual-dom": "1.0.2" + } + }, + "test-dependencies": { + "direct": {}, + "indirect": {} + } +} diff --git a/implementations/Prolog/source/client/index.html b/implementations/Prolog/source/client/index.html new file mode 100644 index 0000000..6d58003 --- /dev/null +++ b/implementations/Prolog/source/client/index.html @@ -0,0 +1,6127 @@ + + + + + Main + + + + + +

+
+
+
+
+
\ No newline at end of file
diff --git a/implementations/Prolog/source/client/src/Main.elm b/implementations/Prolog/source/client/src/Main.elm
new file mode 100644
index 0000000..1a94815
--- /dev/null
+++ b/implementations/Prolog/source/client/src/Main.elm
@@ -0,0 +1,116 @@
+module Main exposing (main)
+
+import Browser
+import Browser.Navigation as Nav
+import Html exposing (a, b, li, text, ul, Html)
+import Html.Attributes exposing (href)
+import Url
+import Url.Parser exposing (Parser, parse, string, s, ())
+
+-- MAIN
+main : Program () Model Msg
+main =
+  Browser.application
+    { init = init
+    , view = view
+    , update = update
+    , subscriptions = subscriptions
+    , onUrlChange = UrlChanged
+    , onUrlRequest = LinkClicked
+    }
+
+-- MODEL
+type alias Model =
+  { key : Nav.Key
+  , url : Url.Url
+  }
+
+
+init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
+init _ url key =
+  -- ignore flags arg
+  ( Model key url, Cmd.none )
+
+-- UPDATE
+type Msg
+  = LinkClicked Browser.UrlRequest
+  | UrlChanged Url.Url
+
+
+update : Msg -> Model -> ( Model, Cmd Msg )
+update msg model =
+  case msg of
+    LinkClicked urlRequest ->
+      case urlRequest of
+        Browser.Internal url ->
+          -- Don't clutter browser history if the user clicks links to
+          -- the current URL.
+          if url == model.url then
+            ( model, Cmd.none )
+          else
+            ( model, Nav.pushUrl model.key (Url.toString url) )
+
+        Browser.External href ->
+          ( model, Nav.load href )
+
+    UrlChanged url ->
+      ( { model | url = url }
+      , Cmd.none
+      )
+
+-- SUBSCRIPTIONS
+
+subscriptions : Model -> Sub Msg
+subscriptions _ =
+  Sub.none
+
+-- VIEW
+
+view : Model -> Browser.Document Msg
+view model =
+  case parse docFunct model.url of
+    Nothing ->
+      viewGeneric (Url.toString model.url)
+    Just functor_name ->
+      viewFunctorDocs functor_name
+
+
+viewGeneric : String -> Browser.Document Msg
+viewGeneric current =
+  { title = "URL Interceptor: " ++ current
+  , body =
+      [ text "The current URL is: "
+      , b [] [ text current ]
+      , ul []
+          [ viewLink "Home" "/home"
+          , viewLink "Profile" "/profile"
+          , viewLink "Cent" "/reviews/the-century-of-the-self"
+          , viewLink "Pub" "/reviews/public-opinion"
+          , viewLink "cons" "/doc/functors/cons"
+          ]
+      ]
+  }
+
+
+viewFunctorDocs : String -> Browser.Document Msg
+viewFunctorDocs functor_name =
+  { title = "Reference: " ++ functor_name
+  , body =
+      [ text "Reference documentation for "
+      , b [] [ text functor_name ]
+      , ul []
+          [ viewLink "Home" "/home"
+          , viewLink "cons" "/doc/functors/cons"
+          ]
+      ]
+  }
+
+
+viewLink : String -> String -> Html msg
+viewLink link_text path =
+  li [] [ a [ href path ] [ text link_text ] ]
+
+
+docFunct : Parser (String -> a) a
+docFunct =
+  s "doc"  s "functors"  string
diff --git a/implementations/Prolog/source/defs.txt b/implementations/Prolog/source/defs.txt
new file mode 100644
index 0000000..5a40424
--- /dev/null
+++ b/implementations/Prolog/source/defs.txt
@@ -0,0 +1,115 @@
+-- 1 -
+? dup bool
+&& nulco [nullary [false]] dip branch
+++ 1 +
+|| nulco [nullary] dip [true] branch
+!- 0 >=
+<{} [] swap
+<<{} [] rolldown
+abs dup 0 < [] [neg] branch
+anamorphism [pop []] swap [dip swons] genrec
+app1 grba infrst
+app2 [grba swap grba swap] dip [infrst] cons ii
+app3 3 appN
+appN [grabN] codi map disenstacken
+at drop first
+average [sum] [size] cleave /
+b [i] dip i
+binary unary popd
+ccccons ccons ccons
+ccons cons cons
+clear stack bool [pop stack bool] loop
+cleave fork popdd
+clop cleave popdd
+codi cons dip
+codireco codi reco
+dinfrirst dip infrst
+dipd [dip] codi
+disenstacken ? [uncons ?] loop pop
+down_to_zero [0 >] [dup --] while
+drop [rest] times
+dupd [dup] dip
+dupdd [dup] dipd
+dupdip dupd dip
+dupdipd dup dipd
+enstacken stack [clear] dip
+flatten <{} [concat] step
+fork [i] app2
+fourth rest third
+gcd true [tuck mod dup 0 >] loop pop
+genrec [[genrec] ccccons] nullary swons concat ifte
+grabN <{} [cons] times
+grba [stack popd] dip
+hypot [sqr] ii + sqrt
+ifte [nullary] dipd swap branch
+ii [dip] dupdip i
+infra swons swaack [i] dip swaack
+infrst infra first
+make_generator [codireco] ccons
+mod %
+neg 0 swap -
+not [true] [false] branch
+nulco [nullary] cons
+nullary [stack] dinfrirst
+of swap at
+pam [i] map
+pm [+] [-] clop
+popd [pop] dip
+popdd [pop] dipd
+popop pop pop
+popopop pop popop
+popopd [popop] dip
+popopdd [popop] dipd
+product 1 swap [*] step
+quoted [unit] dip
+range [0 <=] [1 - dup] anamorphism
+range_to_zero unit [down_to_zero] infra
+reco rest cons
+rest [pop] infra
+reverse <{} shunt
+roll> swap swapd
+roll< swapd swap
+rollup roll>
+rolldown roll<
+rrest rest rest
+run <{} infra
+second rest first
+shift uncons [swons] dip
+shunt [swons] step
+size [pop ++] step_zero
+spiral_next [[[abs] ii <=] [[<>] [pop !-] ||] &&] [[!-] [[++]] [[--]] ifte dip] [[pop !-] [--] [++] ifte] ifte
+split_at [drop] [take] clop
+split_list [take reverse] [drop] clop
+sqr dup *
+stackd [stack] dip
+step_zero 0 roll> step
+sum [+] step_zero
+swapd [swap] dip
+swons swap cons
+swoncat swap concat
+tailrec [i] genrec
+take [] roll> [shift] times pop
+ternary binary popd
+third rest second
+tuck dup swapd
+unary nullary popd
+uncons [first] [rest] cleave
+unit [] cons
+unquoted [i] dip
+unswons uncons swap
+while swap nulco dupdipd concat loop
+x dup i
+step [_step0] x
+_step0 _step1 [popopop] [_stept] branch
+_step1 [?] dipd roll<
+_stept [uncons] dipd [dupdipd] dip x
+times [_times0] x
+_times0 _times1 [popopop] [_timest] branch
+_times1 [dup 0 >] dipd roll<
+_timest [[--] dip dupdipd] dip x
+map [_map0] cons [[] [_map?] [_mape]] dip tailrec
+_map? pop bool not
+_mape popd reverse
+_map0 [_map1] dipd _map2
+_map1 stackd shift
+_map2 [infrst] cons dipd roll< swons
diff --git a/implementations/Prolog/source/derp.pl b/implementations/Prolog/source/derp.pl
new file mode 100644
index 0000000..998de2b
--- /dev/null
+++ b/implementations/Prolog/source/derp.pl
@@ -0,0 +1,130 @@
+% :− module(autodiff2, [mul/3, add/3, pow/3, exp/2, log/2, deriv/3, 2back/1, compile/0]).
+% :− use_module(library(chr)).
+% :− chr_constraint add(?, ?, −), mul(?, ?, −), log(−, −), exp(−, −), pow(+, −, −), 5deriv(?, −, ?), agg(?, −), acc(?, −), acc(−), go, compile.
+
+% mul(0.0,_,Y) ⇔ Y=0.0.
+
+:- module(autodiff, [mul/3, add/3, pow/3, exp/2, llog/2, log/2, deriv/3, back/1, compile/0
+                    ,derivs/3, taylor/4]).
+/**  Reverse mode automatic differentiation
+
+    This module implements a CHR-based approach to reverse-mode automatic differentiation
+    by providing a set of CHR constraints representing arithmetic operators, such as
+    add/3 and mul/3, a constraint deriv/3 to request the derivative of one variable with
+    respect to another, back/1 to initiate derivative back-propagation, and compile/0 to
+    reduce arithmetic constraints to frozen goals for numeric computations.
+    
+    The idea is that the arithmetic constraints are used to build up a representation of
+    a computation graph in the constraint store, with variables in the graph represented by
+    Prolog variables in the store. Then, deriv/3, back/1 and compile/0 must be used in that
+    order to get numeric results, eg:
+    ==
+    ?- foldl(mul,[X1,X2,X3],1.0,Prod), maplist(deriv(Prod),[X1,X2,X3],[D1,D2,D3]),
+       back(Prod), compile, [X1,X2,X3]=[2.0,3.0,4.0].
+    ==
+    Copyright (C) Samer Abdallah, 2017.
+    All rights reserved.
+*/
+:- use_module(library(chr)).
+
+:- chr_constraint add(?,?,-), mul(?,?,-), llog(-,-), log(-,-), exp(-,-), pow(+,-,-),
+                  deriv(?,-,?), agg(?,-), acc(?,-), acc(-), go, compile.
+
+% operations interface with simplifications
+mul(0.0,_,Y) <=> Y=0.0.
+mul(_,0.0,Y) <=> Y=0.0.
+mul(1.0,X,Y) <=> Y=X.
+mul(X,1.0,Y) <=> Y=X.
+mul(X,Y,Z1) \ mul(X,Y,Z2) <=> Z1=Z2.
+pow(1,X,Y) <=> Y=X.
+pow(0,_,Y) <=> Y=1.
+add(0.0,X,Y) <=> Y=X.
+add(X,0.0,Y) <=> Y=X.
+add(X,Y,Z1) \ add(X,Y,Z2) <=> Z1=Z2.
+
+%% back(Y:float) is det.
+%  Initiatiate derivative back-propagation starting from a variable Y. 
+%  Starting with deriv(Y,Y,1.0), this inserts constraints into the store
+%  representing derivatives dY/dX for all variables reachable by traversing
+%  the computation graph backwards from Y, that is all variables that 
+%  contribute to the computation of Y. Once this back-propagation is complete,
+%  then (using go/0) all the deriv/3 constraints are removed and the constraints 
+%  representing the aggregation of the derivatives (acc/1 and agg/2) are processed 
+%  to reduce them to a collection of arithmetic constraints representing the 
+%  computation. This means that the derivatives can themselves be differentiated 
+%  further if desired.
+%  
+%  This process computes ALL the derivatives travelling backwards from Y, but
+%  the caller must pick out which derivatives are to be made available to the
+%  rest of the program by inserting deriv/3 constraints BEFORE calling back/1.
+%
+%  If Y is not a variable, nothing happens. 
+back(Y) :- var(Y) -> deriv(Y,Y,1.0), go; true.
+
+go \ deriv(_,_,_) <=> true.
+go \ acc(DX) <=> acc(0.0,DX).
+go <=> true.
+
+acc(S1,X), agg(Z,X) <=> add(Z,S1,S2), acc(S2,X).
+acc(S,X) <=> S=X.
+
+%% deriv(Y:float,--X:float,D:float) is det.
+%  CHR constraint meaning 'the derivative of Y with respect to X is D'.
+%  It serves two purposes. Firstly, it causes a recursive back-propagation
+%  of derivatives from X to all nodes backward-reachable from X. Secondly,
+%  when used before back/1, it provides access to computed derivatives via
+%  the third argument.
+deriv(L,X,DX) \ deriv(L,X,DX1) <=> DX=DX1.
+deriv(L,_,DX) <=> ground(L) | DX=0.0.
+deriv(_,_,DX) ==> var(DX) | acc(DX).
+deriv(L,Y,DY), pow(K,X,Y)   ==> deriv(L,X,DX), pow_contrib(K,X,DY,Z), agg(Z,DX).
+deriv(L,Y,DY), exp(X,Y)     ==> deriv(L,X,DX), mul(Y,DY,T), agg(T,DX).
+deriv(L,Y,DY), log(X,Y)     ==> deriv(L,X,DX), pow(-1,X,RX), mul(RX,DY,T), agg(T,DX).
+deriv(L,Y,DY), add(X1,X2,Y) ==> maplist(add_contrib(L,DY),[X1,X2]).
+deriv(L,Y,DY), mul(X1,X2,Y) ==> maplist(mul_contrib(L,DY),[X1,X2],[X2,X1]).
+deriv(L,Y,DY), agg(X1,Y)    ==> add_contrib(L,DY,X1).
+
+pow_contrib(K,X,DY,Z)   :- K1 is K - 1, KK is float(K), pow(K1,X,XpowK1), mul(KK,XpowK1,W), mul(DY,W,Z).
+mul_contrib(L,DY,X1,X2) :- var(X1) -> deriv(L,X1,DX1), mul(X2,DY,T1), agg(T1,DX1); true.
+add_contrib(L,DY,X1)    :- var(X1) -> deriv(L,X1,DX1), agg(DY,DX1); true.
+
+acc(X) \ acc(X) <=> true.
+
+%% compile is det.
+%  When this constraint is inserted into the store, it causes any
+%  arithmetic constraints (add/3, mul/3 etc) to be converted into
+%  frozen evaluations, which will yield numeric answers as soon as
+%  their arguments are sufficiently grounded. NB. the computation 
+%  graph is destroyed! Use this after back/1 has been used as many
+%  times as desired to get any derivatives of interest.
+compile \ add(X,Y,Z) <=> delay(X+Y,Z).
+compile \ mul(X,Y,Z) <=> delay(X*Y,Z).
+compile \ add(X,Y,Z) <=> delay(X+Y,Z).
+compile \ log(X,Y)   <=> delay(log(X),Y).
+compile \ exp(X,Y)   <=> delay(exp(X),Y).
+compile \ pow(K,X,Y) <=> delay(X**K,Y).
+compile <=> true.
+
+delay(Expr,Res) :- when(ground(Expr), Res is Expr).
+
+% ------------ multiple derivatives and Taylor series ----------
+
+%% derivs(Y:float,X:float,Ds:list(float)) is det.
+%  Unifies Ds with a list of variables representing derivatives
+%  Y with respect to X, starting with the zeroth order Y itself,
+%  followed by dY/dX, d(dY/dX)/dX, etc.
+derivs(Y,X,[Y|Ds]) :- foldl(d(X),Ds,Y,_).
+d(X,DYDX,Y,DYDX) :- deriv(Y,X,DYDX), back(Y).
+
+%% taylor(+N:nonneg, X:float, Y:float, -Cs:list(float)) is det.
+%  Compute coefficients of the Taylor series expansion of Y
+%  as a function of X, by computing derivatives at X=0.0.
+%  NB. constraint store representation of the computation graph
+%  is destroyed in the process!
+taylor(N,X,Y,Cs0) :-
+   length(Ds,N), derivs(Y,X,Ds),
+   compile, X=0.0,
+   numlist(1,N,Is),
+   foldl(nth_deriv_coeff,Is,Ds,Cs,1.0,_).
+
+nth_deriv_coeff(I,D,C,P1,P2) :- P2 is P1*I, C is D/P1.
\ No newline at end of file
diff --git a/implementations/Prolog/source/derp.py b/implementations/Prolog/source/derp.py
new file mode 100644
index 0000000..2a96c9c
--- /dev/null
+++ b/implementations/Prolog/source/derp.py
@@ -0,0 +1,40 @@
+
+
+def step(stack, expression, dictionary):
+    (program, (seq, stack)) = stack
+    while seq:
+        item, seq = seq
+        stack = joy((item, stack), program, dictionary)[0]
+    return stack, expression, dictionary
+
+
+def step_jit(stack, expression, dictionary):
+    (program, (seq, stack)) = stack
+    try:
+        p = joyc(program)
+    except:
+        return step(stack, expression, dictionary)
+    while seq:
+        item, seq = seq
+        stack = p((item, stack), (), dictionary)[0]
+    return stack, expression, dictionary
+
+
+def step_exact_semantics(stack, expression, dictionary):
+    (program, (seq, stack)) = stack
+    if seq:
+        item, seq = seq
+        stack = item, stack
+        expression = concat(program, (seq, (program, (expression))))
+    return stack, expression, dictionary
+
+
+
+# [+] step
+def fn(stack, expression, dictionary):
+    (s1, (i1, stack)) = stack
+    while s1:
+        (i2, s1) = s1
+        i3 = i2 + i1
+        (i1, stack) = (i3, stack)
+    return (i1, stack), expression, dictionary
diff --git a/implementations/Prolog/source/fuuu.pl b/implementations/Prolog/source/fuuu.pl
new file mode 100644
index 0000000..d34180c
--- /dev/null
+++ b/implementations/Prolog/source/fuuu.pl
@@ -0,0 +1,527 @@
+f, [foo] --> [bar].  % f([bar|A], [foo|A]).
+g, [bas] --> [quo].  % g([quo|A], [bas|A]).
+
+end, [end] --> [].   % end(A, [end|A]).
+
+
+k --> f, g, end.
+% k(A, B) :- f(A, C), g(C, D), end(D, B).
+
+/* So I DON'T know what I was doing.
+
+f replaces foo with bar and then passes the whole enchilada on to the
+next predicate.  I guess I somehow thought it was building an output list
+or something?
+
+ */
+
+/*
+
+?- gronk("fn", `[swap] [] branch `).
+
+def fn(stack, expression, dictionary):
+    (v1, (v2, (v3, stack))) = stack
+    if v1:
+        stack = (v2, (v3, stack))
+    else:
+        stack = (v3, (v2, stack))
+    return stack, expression, dictionary
+
+
+
+?- gronk("fn", `[swap] [] branch pop`).
+
+def fn(stack, expression, dictionary):
+    (v1, (v2, (v3, stack))) = stack
+    if v1:
+        (v4, stack) = (v2, (v3, stack))
+    else:
+        (v4, stack) = (v3, (v2, stack))
+    return stack, expression, dictionary
+
+
+
+?- gronk("fn", `over over > [swap] [] branch pop`).
+
+def fn(stack, expression, dictionary):
+    (v1, (v2, stack)) = stack
+    v3 = v2 > v1
+    if v3:
+        (v4, stack) = (v1, (v2, stack))
+    else:
+        (v4, stack) = (v2, (v1, stack))
+    return stack, expression, dictionary
+
+
+
+Here's a case where factoring the pop to after the branch results in
+inefficient code.  (Compare the function below to the versions above.  It
+doesn't create and then immediately discard a v4 variable.)
+
+?- gronk("fn", `[swap pop] [pop] branch`).
+
+def fn(stack, expression, dictionary):
+    (v1, (v2, (v3, stack))) = stack
+    if v1:
+        stack = (v3, stack)
+    else:
+        stack = (v2, stack)
+    return stack, expression, dictionary
+
+
+ */
+/*
+
+gronk_fn_list([symbol(*)], [int(A),int(A)|B], StackOut, [tab,"return ",stack_to_python(StackOut),", expression, dictionary",nl], CGTail, 1)
+
+
+def fn(stack, expression, dictionary):
+    tos = True
+    while tos:
+        (v1, (v2, stack)) = stack
+        v3 = v2 % v1
+        tos = v3 > 0
+        stack = (v3, (v1, stack))
+    (v4, stack) = stack
+    return stack, expression, dictionary
+
+
+Close, but broken.  THe boundaries between blocks are too permeable.
+
+?- gronk("fn", `true [>] loop`).
+
+def fn(stack, expression, dictionary):
+    (v1, (v2, stack)) = stack
+    tos = True
+    while tos:
+        v3 = v1 > v2
+        tos = v3
+    return stack, expression, dictionary
+
+
+
+
+gronk_fn_list(
+    [symbol(*)],
+    [int(A),int(A)|B],
+    StackOut,
+    [tab,"return ",stack_to_python(StackOut),", expression, dictionary",nl],
+    CGTail,
+    1
+    ).
+
+
+
+
+
+
+
+?- gronk("fn", `stack`).
+
+def fn(stack, expression, dictionary):
+    stack = stack
+    return ((), stack), expression, dictionary
+
+SHould be
+
+?- gronk("fn", `stack`).
+
+def fn(stack, expression, dictionary):
+    return (stack, stack), expression, dictionary
+
+
+
+Okay then...
+
+?- gronk("fn", `over over + stack dup`).
+
+def fn(stack, expression, dictionary):
+    (i1, (i2, stack)) = stack
+    v1 = i2 + i1
+    (v2, stack) = ((v1, (i1, (i2, stack))), (v1, (i1, (i2, stack))))
+    return (v2, (v2, stack)), expression, dictionary
+
+
+*/
+
+/*
+
+
+gronk_fn_body([int(A), int(B)|S], StackOut, IndentLevel, [symbol(Sym)|D], E) :-
+    [symbol(Sym)|D]=[symbol(Sym)|F],
+    bin_math_op(Sym, Op),
+    G=F,
+    gronk_fn_body([int(C)|S],
+                  StackOut,
+                  IndentLevel,
+                  G,
+                  H),
+    E=[tabs(IndentLevel), term_to_python(C), " = ", term_to_python(A), Op, term_to_python(B), nl|H].
+
+gronk_fn_body([int(A), int(B)|S], StackOut, IndentLevel, [symbol(Sym)|D], E) :-
+    [symbol(Sym)|D]=[symbol(Sym)|F],
+    bin_bool_op(Sym, Op),
+    G=F,
+    gronk_fn_body([bool(C)|S],
+                  StackOut,
+                  IndentLevel,
+                  G,
+                  H),
+    E=[tabs(IndentLevel), term_to_python(C), " = ", term_to_python(A), Op, term_to_python(B), nl|H].
+
+gronk_fn_body(S, S, _, A, [tab, "return ", stack_to_python(S), ", expression, dictionary", nl|A]).
+
+
+Yeah, that can't be right...  I'm basically in "How did this ever work?" territory.
+
+
+
+
+
+
+
+
+?- gronk("fn", `+ +`).
+
+def fn(stack, expression, dictionary):
+    (v1, (v2, (v3, stack))) = stack
+    v4 = v1 + v2
+    v5 = v4 + v3
+    return (v5, stack), expression, dictionary
+
+
+?- gronk("fn", `+ * - div mod`).
+
+def fn(stack, expression, dictionary):
+    (v1, (v2, (v3, (v4, (v5, (v6, stack)))))) = stack
+    v7 = v1 + v2
+    v8 = v7 * v3
+    v9 = v8 - v4
+    v10 = v9 // v5
+    v11 = v10 % v6
+    return (v11, stack), expression, dictionary
+
+
+
+
+
+
+
+
+?- gronk_fn("name", [symbol(+)], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S).
+
+def name(stack, expression, dictionary):
+    (v1, (v2, stack)) = stack
+    stack = (v3, stack)
+    return stack, expression, dictionary
+    v3 = v1 + v2
+
+Reversing the order reversed the output...  I wish i knew what I was
+doing... :)
+
+?- gronk_fn("name", [symbol(+)], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S).
+
+def name(stack, expression, dictionary):
+    (v1, (v2, stack)) = stack
+    v3 = v1 + v2
+    stack = (v3, stack)
+    return stack, expression, dictionary
+
+
+?- gronk_fn("name", [symbol(+), symbol(+)], Out), code_gen(Out, A, []), !, string_codes(S, A), writeln(""), writeln(S).
+
+def name(stack, expression, dictionary):
+    (v1, (v2, (v3, stack))) = stack
+    v4 = v1 + v2
+    v5 = v4 + v3
+    stack = (v5, stack)
+    return stack, expression, dictionary
+
+Whatever, it works now.
+
+ */
+
+
+
+/*
+
+?- gronk_fn("name", [], [], Out), code_gen(Out, In, []).
+Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, "return stack, expression, dictionary", nl],
+In = "def name(stack, expressio...nary
+".
+
+?- listing(cg).
+cg(A, D) :-
+    A=[C|B],
+    cg(B, E),
+    phrase(C, D, E).
+cg(A, A).
+
+?- gronk_fn("name", [], [], Out), cg(Out,C).
+Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, "return stack, expression, dictionary", nl],
+C = "def name(stack, expressio...nary
+" ;
+Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, "return stack, expression, dictionary", nl],
+C = [100, 101, 102, 32, 110, 97, 109, 101, 40|...] .
+
+?- phrase((gronk_fn("name", []), cg), [], Out).
+Out = "def name(stack, expressio...nary
+" ;
+Out = [100, 101, 102, 32, 110, 97, 109, 101, 40|...] ;
+Out = [100, 101, 102, 32, 110, 97, 109, 101, 40|...] ;
+Out = [100, 101, 102, 32, 110, 97, 109, 101, 40|...] ;
+Out = [100, 101, 102, 32, 110, 97, 109, 101, 40|...] ;
+Out = [100, 101, 102, 32, 110, 97, 109, 101, "(stack, expression, dictionary):"|...] ;
+Out = [100, 101, 102, 32, "name", "(stack, expression, dictionary):", nl, tab, "return stack, expression, dictionary"|...] .
+
+Bleah.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+?- gronk_fn("name", [symbol(+)], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S).
+
+def name(stack, expression, dictionary):
+    (v1, (v2, stack)) = stack
+    stack = (v3, stack)
+    return stack, expression, dictionary
+    v3 = v1 + v2
+
+
+Almost, but not quite.  The assignment is happening after the return call!
+
+
+
+=-=-=-=--=-=-=-=-==-=-
+
+?- gronk_fn("name", [], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S).
+
+def name(stack, expression, dictionary):
+    stack = stack
+    stack = stack
+    return stack, expression, dictionary
+
+Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, stack_to_python([]), " = stack", nl, tab|...],
+A = "def name(stack, expressio...nary
+",
+S = "def name(stack, expression, dictionary):\n    stack = stack\n    stack = stack\n    return stack, expression, dictionary\n" .
+
+?- gronk_fn("name", [symbol(+)], Out), writeln(Out).
+[def ,name,(stack, expression, dictionary):,nl,tab,stack_to_python([int(_274090),int(_274100)|_274096]), = stack,nl,tab,stack = ,stack_to_python([int(_274110)|_274096]),nl,tab,return stack, expression, dictionary,nl,tabs(1),term_to_python(_274110), = ,term_to_python(_274090), + ,term_to_python(_274100),nl]
+Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, stack_to_python([int(_274090), int(...)|...]), " = stack", nl, tab|...] .
+
+?- gronk_fn("name", [symbol(+)], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S).
+
+def name(stack, expression, dictionary):
+    (v1, (v2, stack)) = stack
+    stack = (v3, stack)
+    return stack, expression, dictionary
+    v3 = v1 + v2
+
+Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, stack_to_python([int(v1), int(...)]), " = stack", nl, tab|...],
+A = "def name(stack, expressio...+ v2
+",
+S = "def name(stack, expression, dictionary):\n    (v1, (v2, stack)) = stack\n    stack = (v3, stack)\n    return stack, expression, dictionary\n    v3 = v1 + v2\n" .
+
+
+
+
+=-=-=-=--=-=-=-=-==-=-
+
+There we go...
+
+?- gronk_fn("name", [symbol(+)], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S).
+
+def name(stack, expression, dictionary):
+    (v1, (v2, stack)) = stack
+    v3 = v1 + v2
+    stack = (v3, stack)
+    return stack, expression, dictionary
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+?- do(`dup dup +`).
+
+(v5, stack) = stack
+stack = ((v5 + v5), (v5, stack))
+
+true .
+
+That's better.
+
+?- do(`[* / - + dup] [dup + over *] branch * * `).
+
+tos, stack = stack
+if tos:
+    (v16, (v17, stack)) = stack
+    stack = ((v17 * (v16 + v16)), (v17, stack))
+else:
+    (v18, (v19, (v20, (v21, (v22, stack))))) = stack
+    stack = (((v21 - (v20 // (v18 * v19))) + v22), (((v21 - (v20 // (v18 * v19))) + v22), stack))
+(v23, (v24, (v25, stack))) = stack
+stack = (((v23 * v24) * v25), stack)
+
+true .
+
+That's beautiful.
+
+
+Of course, if we carried through the expression for the stack...
+
+
+    tos, stack = stack
+    if tos:
+        (v16, (v17, stack)) = stack
+        (v23, (v24, (v25, stack))) = ((v17 * (v16 + v16)), (v17, stack))
+    else:
+        (v18, (v19, (v20, (v21, (v22, stack))))) = stack
+        (v23, (v24, (v25, stack))) = (((v21 - (v20 // (v18 * v19))) + v22), (((v21 - (v20 // (v18 * v19))) + v22), stack))
+    stack = (((v23 * v24) * v25), stack)
+
+we could assign the new variables directly from the previous stage,
+saving the packing and unpacking of the "stack" tuple.
+
+"Something to think about."
+
+
+With symbolic Booleans this works now (there were a lot of bugs but I
+don't know what they were.)
+
+?- do(`<= [+] [-] branch`).
+
+(v1, (v2, stack)) = stack
+stack = ((v2 <= v1), stack)
+tos, stack = stack
+if tos:
+    (v3, (v4, stack)) = stack
+    stack = ((v4 - v3), stack)
+else:
+    (v5, (v6, stack)) = stack
+    stack = ((v5 + v6), stack)
+
+true.
+
+
+
+Now we can compile GCD:
+
+?- do(`true [tuck % dup 0 >] loop pop`).
+
+stack = True, stack
+tos, stack = stack
+while tos:
+    (v9, (v10, stack)) = stack
+    stack = ((v10 % v9), ((v10 % v9), (v9, stack)))
+    stack = 0, stack
+    (v11, (v12, stack)) = stack
+    stack = ((v12 > v11), stack)
+    tos, stack = stack
+(v13, stack) = stack
+stack = stack
+
+true.
+
+
+It's not ideal, for example, it computes v10 % v9 twice.  :(
+
+We would like, e.g.:
+
+tos = True
+while tos:
+    (v9, (v10, stack)) = stack
+    vN = v10 % v9
+    stack = ((vN), ((vN), (v9, stack)))
+    (v11, (v12, stack)) = 0, stack
+    stack = ((v12 > v11), stack)
+    tos, stack = stack
+(v13, stack) = stack
+stack = stack
+
+
+tos = True
+while tos:
+    (v9, (v10, stack)) = stack
+    vN = v10 % v9
+    stack = ((vN), ((vN), (v9, stack)))
+    (v12, stack) = stack
+    stack = ((v12 > 0), stack)
+    tos, stack = stack
+(v13, stack) = stack
+
+
+tos = True
+while tos:
+    (v9, (v10, stack)) = stack
+    vN = v10 % v9
+    stack = ((vN), ((vN), (v9, stack)))
+    (v12, stack) = stack
+    tos = (v12 > 0)
+(v13, stack) = stack
+
+
+
+tos = True
+while tos:
+    (v9, (v10, stack)) = stack
+    vN = v10 % v9
+    (v12, stack) = ((vN), ((vN), (v9, stack)))
+    tos = (v12 > 0)
+(v13, stack) = stack
+
+
+
+
+tos = True
+while tos:
+    (v9, (v10, stack)) = stack
+    vN = v10 % v9
+    stack = (vN, (v9, stack))
+    tos = (vN > 0)
+(v13, stack) = stack
+
+Anyhow...  I could keep going but you get the idea.  The simple
+mechanical translation results in correct but inefficient code.
+I'm not too worried about it, this is great progress nonetheless, but it
+would be nice to tighten up that code gen.
+
+What's that "stack = stack" doing in there?
+
+
+
+
+
+do(`[[dup dup] [dup] branch dup [dup] loop dup] loop dup`).
+
+do(`[dup] [[dup dup dup] [dup dup] branch] branch`).
+
+
+*/
diff --git a/implementations/Prolog/source/gen-defs+funcs.pl b/implementations/Prolog/source/gen-defs+funcs.pl
new file mode 100644
index 0000000..27907fa
--- /dev/null
+++ b/implementations/Prolog/source/gen-defs+funcs.pl
@@ -0,0 +1,759 @@
+thun(int(A), [], B, [int(A)|B]).
+thun(int(C), [A|B], D, E) :-
+    thun(A, B, [int(C)|D], E).
+thun(bool(A), [], B, [bool(A)|B]).
+thun(bool(C), [A|B], D, E) :-
+    thun(A, B, [bool(C)|D], E).
+thun(list(A), [], B, [list(A)|B]).
+thun(list(C), [A|B], D, E) :-
+    thun(A, B, [list(C)|D], E).
+thun(symbol(--), A, C, D) :-
+    append([symbol(-)], A, B),
+    thun(int(1), B, C, D).
+thun(symbol(?), A, C, D) :-
+    append([symbol(bool)], A, B),
+    thun(symbol(dup), B, C, D).
+thun(symbol(&&), A, C, D) :-
+    append(
+           [ symbol(cons),
+             list([symbol(nullary), list([bool(false)])]),
+             symbol(dip),
+             symbol(branch)
+           ],
+           A,
+           B),
+    thun(list([symbol(nullary)]), B, C, D).
+thun(symbol(++), A, C, D) :-
+    append([symbol(+)], A, B),
+    thun(int(1), B, C, D).
+thun(symbol('||'), A, C, D) :-
+    append(
+           [ symbol(cons),
+             list([symbol(nullary)]),
+             symbol(dip),
+             list([bool(true)]),
+             symbol(branch)
+           ],
+           A,
+           B),
+    thun(list([symbol(nullary)]), B, C, D).
+thun(symbol('!-'), A, C, D) :-
+    append([symbol(>=)], A, B),
+    thun(int(0), B, C, D).
+thun(symbol(abs), A, C, D) :-
+    append([int(0), symbol(<), list([]), list([symbol(neg)]), symbol(branch)],
+           A,
+           B),
+    thun(symbol(dup), B, C, D).
+thun(symbol(anamorphism), A, C, D) :-
+    append([symbol(swap), list([symbol(dip), symbol(swons)]), symbol(genrec)],
+           A,
+           B),
+    thun(list([symbol(pop), list([])]), B, C, D).
+thun(symbol(app1), A, C, D) :-
+    append([symbol(infrst)], A, B),
+    thun(symbol(grba), B, C, D).
+thun(symbol(app2), A, C, D) :-
+    append([symbol(dip), list([symbol(infrst)]), symbol(cons), symbol(ii)],
+           A,
+           B),
+    thun(list([symbol(grba), symbol(swap), symbol(grba), symbol(swap)]),
+         B,
+         C,
+         D).
+thun(symbol(app3), A, C, D) :-
+    append([symbol(appN)], A, B),
+    thun(int(3), B, C, D).
+thun(symbol(appN), A, C, D) :-
+    append([symbol(cons), symbol(dip), symbol(map), symbol(disenstacken)],
+           A,
+           B),
+    thun(list([symbol(grabN)]), B, C, D).
+thun(symbol(at), A, C, D) :-
+    append([symbol(first)], A, B),
+    thun(symbol(drop), B, C, D).
+thun(symbol(average), A, C, D) :-
+    append([list([symbol(size)]), symbol(cleave), symbol(/)], A, B),
+    thun(list([symbol(sum), int(1), symbol('.0'), symbol(*)]),
+         B,
+         C,
+         D).
+thun(symbol(b), A, C, D) :-
+    append([symbol(dip), symbol(i)], A, B),
+    thun(list([symbol(i)]), B, C, D).
+thun(symbol(binary), A, C, D) :-
+    append([symbol(popd)], A, B),
+    thun(symbol(unary), B, C, D).
+thun(symbol(ccons), A, C, D) :-
+    append([symbol(cons)], A, B),
+    thun(symbol(cons), B, C, D).
+thun(symbol(cleave), A, C, D) :-
+    append([symbol(popdd)], A, B),
+    thun(symbol(fork), B, C, D).
+thun(symbol(clop), A, C, D) :-
+    append([symbol(popdd)], A, B),
+    thun(symbol(cleave), B, C, D).
+thun(symbol(codireco), A, C, D) :-
+    append([symbol(dip), symbol(rest), symbol(cons)], A, B),
+    thun(symbol(cons), B, C, D).
+thun(symbol(dinfrirst), A, C, D) :-
+    append([symbol(infrst)], A, B),
+    thun(symbol(dip), B, C, D).
+thun(symbol(disenstacken), A, C, D) :-
+    append([list([symbol(uncons), symbol(?)]), symbol(loop), symbol(pop)],
+           A,
+           B),
+    thun(symbol(?), B, C, D).
+thun(symbol(down_to_zero), A, C, D) :-
+    append([list([symbol(dup), symbol(--)]), symbol(while)], A, B),
+    thun(list([int(0), symbol(>)]), B, C, D).
+thun(symbol(drop), A, C, D) :-
+    append([symbol(times)], A, B),
+    thun(list([symbol(rest)]), B, C, D).
+thun(symbol(dupdd), A, C, D) :-
+    append([symbol(dipd)], A, B),
+    thun(list([symbol(dup)]), B, C, D).
+thun(symbol(dupdipd), A, C, D) :-
+    append([symbol(dipd)], A, B),
+    thun(symbol(dup), B, C, D).
+thun(symbol(enstacken), A, C, D) :-
+    append([list([symbol(clear)]), symbol(dip)], A, B),
+    thun(symbol(stack), B, C, D).
+thun(symbol(fork), A, C, D) :-
+    append([symbol(app2)], A, B),
+    thun(list([symbol(i)]), B, C, D).
+thun(symbol(fourth), A, C, D) :-
+    append([symbol(third)], A, B),
+    thun(symbol(rest), B, C, D).
+thun(symbol(gcd), A, C, D) :-
+    append(
+           [ list([symbol(tuck), symbol(mod), symbol(dup), int(0), symbol(>)]),
+             symbol(loop),
+             symbol(pop)
+           ],
+           A,
+           B),
+    thun(bool(true), B, C, D).
+thun(symbol(grabN), A, C, D) :-
+    append([symbol(swap), list([symbol(cons)]), symbol(times)], A, B),
+    thun(list([]), B, C, D).
+thun(symbol(grba), A, C, D) :-
+    append([symbol(dip)], A, B),
+    thun(list([symbol(stack), symbol(popd)]), B, C, D).
+thun(symbol(hypot), A, C, D) :-
+    append([symbol(ii), symbol(+), symbol(sqrt)], A, B),
+    thun(list([symbol(sqr)]), B, C, D).
+thun(symbol(ifte), A, C, D) :-
+    append([symbol(dipd), symbol(swap), symbol(branch)], A, B),
+    thun(list([symbol(nullary)]), B, C, D).
+thun(symbol(ii), A, C, D) :-
+    append([symbol(dupdip), symbol(i)], A, B),
+    thun(list([symbol(dip)]), B, C, D).
+thun(symbol(infra), A, C, D) :-
+    append([symbol(swaack), list([symbol(i)]), symbol(dip), symbol(swaack)],
+           A,
+           B),
+    thun(symbol(swons), B, C, D).
+thun(symbol(infrst), A, C, D) :-
+    append([symbol(first)], A, B),
+    thun(symbol(infra), B, C, D).
+thun(symbol(make_generator), A, C, D) :-
+    append([symbol(ccons)], A, B),
+    thun(list([symbol(codireco)]), B, C, D).
+thun(symbol(neg), A, C, D) :-
+    append([symbol(swap), symbol(-)], A, B),
+    thun(int(0), B, C, D).
+thun(symbol(not), A, C, D) :-
+    append([list([bool(false)]), symbol(branch)], A, B),
+    thun(list([bool(true)]), B, C, D).
+thun(symbol(nullary), A, C, D) :-
+    append([symbol(dinfrirst)], A, B),
+    thun(list([symbol(stack)]), B, C, D).
+thun(symbol(of), A, C, D) :-
+    append([symbol(at)], A, B),
+    thun(symbol(swap), B, C, D).
+thun(symbol(pam), A, C, D) :-
+    append([symbol(map)], A, B),
+    thun(list([symbol(i)]), B, C, D).
+thun(symbol(popd), A, C, D) :-
+    append([symbol(dip)], A, B),
+    thun(list([symbol(pop)]), B, C, D).
+thun(symbol(popdd), A, C, D) :-
+    append([symbol(dipd)], A, B),
+    thun(list([symbol(pop)]), B, C, D).
+thun(symbol(popop), A, C, D) :-
+    append([symbol(pop)], A, B),
+    thun(symbol(pop), B, C, D).
+thun(symbol(popopd), A, C, D) :-
+    append([symbol(dip)], A, B),
+    thun(list([symbol(popop)]), B, C, D).
+thun(symbol(popopdd), A, C, D) :-
+    append([symbol(dipd)], A, B),
+    thun(list([symbol(popop)]), B, C, D).
+thun(symbol(primrec), A, C, D) :-
+    append([symbol(genrec)], A, B),
+    thun(list([symbol(i)]), B, C, D).
+thun(symbol(product), A, C, D) :-
+    append([symbol(swap), list([symbol(*)]), symbol(step)], A, B),
+    thun(int(1), B, C, D).
+thun(symbol(quoted), A, C, D) :-
+    append([symbol(dip)], A, B),
+    thun(list([symbol(unit)]), B, C, D).
+thun(symbol(range), A, C, D) :-
+    append([list([int(1), symbol(-), symbol(dup)]), symbol(anamorphism)],
+           A,
+           B),
+    thun(list([int(0), symbol(<=)]), B, C, D).
+thun(symbol(range_to_zero), A, C, D) :-
+    append([list([symbol(down_to_zero)]), symbol(infra)], A, B),
+    thun(symbol(unit), B, C, D).
+thun(symbol(reverse), A, C, D) :-
+    append([symbol(swap), symbol(shunt)], A, B),
+    thun(list([]), B, C, D).
+thun(symbol(rrest), A, C, D) :-
+    append([symbol(rest)], A, B),
+    thun(symbol(rest), B, C, D).
+thun(symbol(run), A, C, D) :-
+    append([symbol(swap), symbol(infra)], A, B),
+    thun(list([]), B, C, D).
+thun(symbol(second), A, C, D) :-
+    append([symbol(first)], A, B),
+    thun(symbol(rest), B, C, D).
+thun(symbol(shunt), A, C, D) :-
+    append([symbol(step)], A, B),
+    thun(list([symbol(swons)]), B, C, D).
+thun(symbol(size), A, C, D) :-
+    append([symbol(swap), list([symbol(pop), symbol(++)]), symbol(step)],
+           A,
+           B),
+    thun(int(0), B, C, D).
+thun(symbol(spiral_next), A, C, D) :-
+    append(
+           [ list(
+                  [ list([symbol('!-')]),
+                    list([list([symbol(++)])]),
+                    list([list([symbol(--)])]),
+                    symbol(ifte),
+                    symbol(dip)
+                  ]),
+             list(
+                  [ list([symbol(pop), symbol('!-')]),
+                    list([symbol(--)]),
+                    list([symbol(++)]),
+                    symbol(ifte)
+                  ]),
+             symbol(ifte)
+           ],
+           A,
+           B),
+    thun(list(
+              [ list([list([symbol(abs)]), symbol(ii), symbol(<=)]),
+                list(
+                     [ list([symbol(<>)]),
+                       list([symbol(pop), symbol('!-')]),
+                       symbol('||')
+                     ]),
+                symbol(&&)
+              ]),
+         B,
+         C,
+         D).
+thun(symbol(split_at), A, C, D) :-
+    append([list([symbol(take)]), symbol(clop)], A, B),
+    thun(list([symbol(drop)]), B, C, D).
+thun(symbol(sqr), A, C, D) :-
+    append([symbol(*)], A, B),
+    thun(symbol(dup), B, C, D).
+thun(symbol(step_zero), A, C, D) :-
+    append([symbol('roll>'), symbol(step)], A, B),
+    thun(int(0), B, C, D).
+thun(symbol(sum), A, C, D) :-
+    append([symbol(swap), list([symbol(+)]), symbol(step)], A, B),
+    thun(int(0), B, C, D).
+thun(symbol(swons), A, C, D) :-
+    append([symbol(cons)], A, B),
+    thun(symbol(swap), B, C, D).
+thun(symbol(take), A, C, D) :-
+    append([symbol(rolldown), list([symbol(shift)]), symbol(times), symbol(pop)],
+           A,
+           B),
+    thun(list([]), B, C, D).
+thun(symbol(ternary), A, C, D) :-
+    append([symbol(popd)], A, B),
+    thun(symbol(binary), B, C, D).
+thun(symbol(third), A, C, D) :-
+    append([symbol(second)], A, B),
+    thun(symbol(rest), B, C, D).
+thun(symbol(unary), A, C, D) :-
+    append([symbol(popd)], A, B),
+    thun(symbol(nullary), B, C, D).
+thun(symbol(unquoted), A, C, D) :-
+    append([symbol(dip)], A, B),
+    thun(list([symbol(i)]), B, C, D).
+thun(symbol(unswons), A, C, D) :-
+    append([symbol(swap)], A, B),
+    thun(symbol(uncons), B, C, D).
+thun(symbol(while), A, C, D) :-
+    append(
+           [ list([symbol(nullary)]),
+             symbol(cons),
+             symbol(dup),
+             symbol(dipd),
+             symbol(concat),
+             symbol(loop)
+           ],
+           A,
+           B),
+    thun(symbol(swap), B, C, D).
+thun(symbol(x), A, C, D) :-
+    append([symbol(i)], A, B),
+    thun(symbol(dup), B, C, D).
+thun(symbol(words), [], A, [B|A]) :-
+    words(B).
+thun(symbol(words), [A|B], D, E) :-
+    words(C),
+    thun(A, B, [C|D], E).
+thun(symbol(swap), [], [B, A|C], [A, B|C]).
+thun(symbol(swap), [A|B], [D, C|E], F) :-
+    thun(A, B, [C, D|E], F).
+thun(symbol(dup), [], [A|B], [A, A|B]).
+thun(symbol(dup), [A|B], [C|D], E) :-
+    thun(A, B, [C, C|D], E).
+thun(symbol(pop), [], [_|A], A).
+thun(symbol(pop), [A|B], [_|C], D) :-
+    thun(A, B, C, D).
+thun(symbol(cons), [], [list(B), A|C], [list([A|B])|C]).
+thun(symbol(cons), [A|B], [list(D), C|E], F) :-
+    thun(A, B, [list([C|D])|E], F).
+thun(symbol(concat), [], [list(C), list(B)|A], [list(D)|A]) :-
+    append(B, C, D).
+thun(symbol(concat), [C|D], [list(B), list(A)|F], G) :-
+    append(A, B, E),
+    thun(C, D, [list(E)|F], G).
+thun(symbol(flatten), [], [list(B)|A], [list(C)|A]) :-
+    flatten(B, C).
+thun(symbol(flatten), [B|C], [list(A)|E], F) :-
+    flatten(A, D),
+    thun(B, C, [list(D)|E], F).
+thun(symbol(swaack), [], [list(B)|A], [list(A)|B]).
+thun(symbol(swaack), [A|B], [list(D)|C], E) :-
+    thun(A, B, [list(C)|D], E).
+thun(symbol(stack), [], A, [list(A)|A]).
+thun(symbol(stack), [A|B], C, D) :-
+    thun(A, B, [list(C)|C], D).
+thun(symbol(clear), [], _, []).
+thun(symbol(clear), [A|B], _, C) :-
+    thun(A, B, [], C).
+thun(symbol(first), [], [list([A|_])|B], [A|B]).
+thun(symbol(first), [A|B], [list([C|_])|D], E) :-
+    thun(A, B, [C|D], E).
+thun(symbol(rest), [], [list([_|A])|B], [list(A)|B]).
+thun(symbol(rest), [A|B], [list([_|C])|D], E) :-
+    thun(A, B, [list(C)|D], E).
+thun(symbol(unit), [], [A|B], [list([A])|B]).
+thun(symbol(unit), [A|B], [C|D], E) :-
+    thun(A, B, [list([C])|D], E).
+thun(symbol(rolldown), [], [C, A, B|D], [A, B, C|D]).
+thun(symbol(rolldown), [A|B], [E, C, D|F], G) :-
+    thun(A, B, [C, D, E|F], G).
+thun(symbol(dupd), [], [A, B|C], [A, B, B|C]).
+thun(symbol(dupd), [A|B], [C, D|E], F) :-
+    thun(A, B, [C, D, D|E], F).
+thun(symbol(over), [], [B, A|C], [A, B, A|C]).
+thun(symbol(over), [A|B], [D, C|E], F) :-
+    thun(A, B, [C, D, C|E], F).
+thun(symbol(tuck), [], [A, B|C], [A, B, A|C]).
+thun(symbol(tuck), [A|B], [C, D|E], F) :-
+    thun(A, B, [C, D, C|E], F).
+thun(symbol(shift), [], [list([B|A]), list(C)|D], [list(A), list([B|C])|D]).
+thun(symbol(shift), [A|B], [list([D|C]), list(E)|F], G) :-
+    thun(A,
+         B,
+         [list(C), list([D|E])|F],
+         G).
+thun(symbol(rollup), [], [B, C, A|D], [A, B, C|D]).
+thun(symbol(rollup), [A|B], [D, E, C|F], G) :-
+    thun(A, B, [C, D, E|F], G).
+thun(symbol(uncons), [], [list([B|A])|C], [list(A), B|C]).
+thun(symbol(uncons), [A|B], [list([D|C])|E], F) :-
+    thun(A, B, [list(C), D|E], F).
+thun(symbol(bool), [], [int(0)|A], [bool(false)|A]).
+thun(symbol(bool), [A|B], [int(0)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(bool), [], [list([])|A], [bool(false)|A]).
+thun(symbol(bool), [A|B], [list([])|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(bool), [], [bool(false)|A], [bool(false)|A]).
+thun(symbol(bool), [A|B], [bool(false)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(bool), [], [int(B)|A], [bool(true)|A]) :-
+    B#\=0.
+thun(symbol(bool), [B|C], [int(A)|D], E) :-
+    A#\=0,
+    thun(B, C, [bool(true)|D], E).
+thun(symbol(bool), [], [list([_|_])|A], [bool(true)|A]).
+thun(symbol(bool), [A|B], [list([_|_])|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol(bool), [], [bool(true)|A], [bool(true)|A]).
+thun(symbol(bool), [A|B], [bool(true)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol('empty?'), [], [list([])|A], [bool(true)|A]).
+thun(symbol('empty?'), [A|B], [list([])|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol('empty?'), [], [list([_|_])|A], [bool(false)|A]).
+thun(symbol('empty?'), [A|B], [list([_|_])|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol('list?'), [], [list(_)|A], [bool(true)|A]).
+thun(symbol('list?'), [A|B], [list(_)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol('list?'), [], [bool(_)|A], [bool(false)|A]).
+thun(symbol('list?'), [A|B], [bool(_)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol('list?'), [], [int(_)|A], [bool(false)|A]).
+thun(symbol('list?'), [A|B], [int(_)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol('list?'), [], [symbol(_)|A], [bool(false)|A]).
+thun(symbol('list?'), [A|B], [symbol(_)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol('one-or-more?'), [], [list([_|_])|A], [bool(true)|A]).
+thun(symbol('one-or-more?'), [A|B], [list([_|_])|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol('one-or-more?'), [], [list([])|A], [bool(false)|A]).
+thun(symbol('one-or-more?'), [A|B], [list([])|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(and), [], [bool(true), bool(true)|A], [bool(true)|A]).
+thun(symbol(and), [A|B], [bool(true), bool(true)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol(and), [], [bool(true), bool(false)|A], [bool(false)|A]).
+thun(symbol(and), [A|B], [bool(true), bool(false)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(and), [], [bool(false), bool(true)|A], [bool(false)|A]).
+thun(symbol(and), [A|B], [bool(false), bool(true)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(and), [], [bool(false), bool(false)|A], [bool(false)|A]).
+thun(symbol(and), [A|B], [bool(false), bool(false)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(or), [], [bool(true), bool(true)|A], [bool(true)|A]).
+thun(symbol(or), [A|B], [bool(true), bool(true)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol(or), [], [bool(true), bool(false)|A], [bool(true)|A]).
+thun(symbol(or), [A|B], [bool(true), bool(false)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol(or), [], [bool(false), bool(true)|A], [bool(true)|A]).
+thun(symbol(or), [A|B], [bool(false), bool(true)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol(or), [], [bool(false), bool(false)|A], [bool(false)|A]).
+thun(symbol(or), [A|B], [bool(false), bool(false)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(+), [], [int(C), int(D)|A], [int(B)|A]) :-
+    (   integer(B)
+    ->  (   integer(C),
+            integer(D)
+        ->  B=:=C+D
+        ;   E=B,
+            clpfd:clpfd_equal(E, C+D)
+        )
+    ;   integer(C),
+        integer(D)
+    ->  (   var(B)
+        ->  B is C+D
+        ;   E is C+D,
+            clpfd:clpfd_equal(B, E)
+        )
+    ;   clpfd:clpfd_equal(B, C+D)
+    ).
+thun(symbol(+), [E|F], [int(A), int(B)|G], H) :-
+    (   integer(C)
+    ->  (   integer(A),
+            integer(B)
+        ->  C=:=A+B
+        ;   D=C,
+            clpfd:clpfd_equal(D, A+B)
+        )
+    ;   integer(A),
+        integer(B)
+    ->  (   var(C)
+        ->  C is A+B
+        ;   D is A+B,
+            clpfd:clpfd_equal(C, D)
+        )
+    ;   clpfd:clpfd_equal(C, A+B)
+    ),
+    thun(E, F, [int(C)|G], H).
+thun(symbol(-), [], [int(D), int(C)|A], [int(B)|A]) :-
+    (   integer(B)
+    ->  (   integer(C),
+            integer(D)
+        ->  B=:=C-D
+        ;   E=B,
+            clpfd:clpfd_equal(E, C-D)
+        )
+    ;   integer(C),
+        integer(D)
+    ->  (   var(B)
+        ->  B is C-D
+        ;   E is C-D,
+            clpfd:clpfd_equal(B, E)
+        )
+    ;   clpfd:clpfd_equal(B, C-D)
+    ).
+thun(symbol(-), [E|F], [int(B), int(A)|G], H) :-
+    (   integer(C)
+    ->  (   integer(A),
+            integer(B)
+        ->  C=:=A-B
+        ;   D=C,
+            clpfd:clpfd_equal(D, A-B)
+        )
+    ;   integer(A),
+        integer(B)
+    ->  (   var(C)
+        ->  C is A-B
+        ;   D is A-B,
+            clpfd:clpfd_equal(C, D)
+        )
+    ;   clpfd:clpfd_equal(C, A-B)
+    ),
+    thun(E, F, [int(C)|G], H).
+thun(symbol(*), [], [int(C), int(D)|A], [int(B)|A]) :-
+    (   integer(B)
+    ->  (   integer(C),
+            integer(D)
+        ->  B=:=C*D
+        ;   E=B,
+            clpfd:clpfd_equal(E, C*D)
+        )
+    ;   integer(C),
+        integer(D)
+    ->  (   var(B)
+        ->  B is C*D
+        ;   E is C*D,
+            clpfd:clpfd_equal(B, E)
+        )
+    ;   clpfd:clpfd_equal(B, C*D)
+    ).
+thun(symbol(*), [E|F], [int(A), int(B)|G], H) :-
+    (   integer(C)
+    ->  (   integer(A),
+            integer(B)
+        ->  C=:=A*B
+        ;   D=C,
+            clpfd:clpfd_equal(D, A*B)
+        )
+    ;   integer(A),
+        integer(B)
+    ->  (   var(C)
+        ->  C is A*B
+        ;   D is A*B,
+            clpfd:clpfd_equal(C, D)
+        )
+    ;   clpfd:clpfd_equal(C, A*B)
+    ),
+    thun(E, F, [int(C)|G], H).
+thun(symbol(/), [], [int(D), int(C)|A], [int(B)|A]) :-
+    B#=C div D.
+thun(symbol(/), [C|D], [int(B), int(A)|F], G) :-
+    E#=A div B,
+    thun(C, D, [int(E)|F], G).
+thun(symbol('%'), [], [int(D), int(C)|A], [int(B)|A]) :-
+    (   integer(B)
+    ->  (   integer(C),
+            integer(D),
+            D=\=0
+        ->  B=:=C mod D
+        ;   E=B,
+            clpfd:clpfd_equal(E, C mod D)
+        )
+    ;   integer(C),
+        integer(D),
+        D=\=0
+    ->  (   var(B)
+        ->  B is C mod D
+        ;   E is C mod D,
+            clpfd:clpfd_equal(B, E)
+        )
+    ;   clpfd:clpfd_equal(B, C mod D)
+    ).
+thun(symbol('%'), [E|F], [int(B), int(A)|G], H) :-
+    (   integer(C)
+    ->  (   integer(A),
+            integer(B),
+            B=\=0
+        ->  C=:=A mod B
+        ;   D=C,
+            clpfd:clpfd_equal(D, A mod B)
+        )
+    ;   integer(A),
+        integer(B),
+        B=\=0
+    ->  (   var(C)
+        ->  C is A mod B
+        ;   D is A mod B,
+            clpfd:clpfd_equal(C, D)
+        )
+    ;   clpfd:clpfd_equal(C, A mod B)
+    ),
+    thun(E, F, [int(C)|G], H).
+thun(symbol('/%'), [], [int(D), int(C)|A], [int(B), int(E)|A]) :-
+    B#=C div D,
+    (   integer(E)
+    ->  (   integer(C),
+            integer(D),
+            D=\=0
+        ->  E=:=C mod D
+        ;   F=E,
+            clpfd:clpfd_equal(F, C mod D)
+        )
+    ;   integer(C),
+        integer(D),
+        D=\=0
+    ->  (   var(E)
+        ->  E is C mod D
+        ;   F is C mod D,
+            clpfd:clpfd_equal(E, F)
+        )
+    ;   clpfd:clpfd_equal(E, C mod D)
+    ).
+thun(symbol('/%'), [E|F], [int(B), int(A)|H], I) :-
+    ( G#=A div B,
+      (   integer(C)
+      ->  (   integer(A),
+              integer(B),
+              B=\=0
+          ->  C=:=A mod B
+          ;   D=C,
+              clpfd:clpfd_equal(D, A mod B)
+          )
+      ;   integer(A),
+          integer(B),
+          B=\=0
+      ->  (   var(C)
+          ->  C is A mod B
+          ;   D is A mod B,
+              clpfd:clpfd_equal(C, D)
+          )
+      ;   clpfd:clpfd_equal(C, A mod B)
+      )
+    ),
+    thun(E, F, [int(G), int(C)|H], I).
+thun(symbol(pm), [], [int(C), int(D)|A], [int(B), int(F)|A]) :-
+    (   integer(B)
+    ->  (   integer(C),
+            integer(D)
+        ->  B=:=C+D
+        ;   E=B,
+            clpfd:clpfd_equal(E, C+D)
+        )
+    ;   integer(C),
+        integer(D)
+    ->  (   var(B)
+        ->  B is C+D
+        ;   E is C+D,
+            clpfd:clpfd_equal(B, E)
+        )
+    ;   clpfd:clpfd_equal(B, C+D)
+    ),
+    (   integer(F)
+    ->  (   integer(D),
+            integer(C)
+        ->  F=:=D-C
+        ;   G=F,
+            clpfd:clpfd_equal(G, D-C)
+        )
+    ;   integer(D),
+        integer(C)
+    ->  (   var(F)
+        ->  F is D-C
+        ;   G is D-C,
+            clpfd:clpfd_equal(F, G)
+        )
+    ;   clpfd:clpfd_equal(F, D-C)
+    ).
+thun(symbol(pm), [G|H], [int(A), int(B)|I], J) :-
+    ( (   integer(C)
+      ->  (   integer(A),
+              integer(B)
+          ->  C=:=A+B
+          ;   D=C,
+              clpfd:clpfd_equal(D, A+B)
+          )
+      ;   integer(A),
+          integer(B)
+      ->  (   var(C)
+          ->  C is A+B
+          ;   D is A+B,
+              clpfd:clpfd_equal(C, D)
+          )
+      ;   clpfd:clpfd_equal(C, A+B)
+      ),
+      (   integer(E)
+      ->  (   integer(B),
+              integer(A)
+          ->  E=:=B-A
+          ;   F=E,
+              clpfd:clpfd_equal(F, B-A)
+          )
+      ;   integer(B),
+          integer(A)
+      ->  (   var(E)
+          ->  E is B-A
+          ;   F is B-A,
+              clpfd:clpfd_equal(E, F)
+          )
+      ;   clpfd:clpfd_equal(E, B-A)
+      )
+    ),
+    thun(G, H, [int(C), int(E)|I], J).
+thun(symbol(>), [], [int(C), int(B)|A], [E|A]) :-
+    B#>C#<==>D,
+    r_truth(D, E).
+thun(symbol(>), [D|E], [int(B), int(A)|G], H) :-
+    ( A#>B#<==>C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(<), [], [int(C), int(B)|A], [E|A]) :-
+    B#D,
+    r_truth(D, E).
+thun(symbol(<), [D|E], [int(B), int(A)|G], H) :-
+    ( A#C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(=), [], [int(C), int(B)|A], [E|A]) :-
+    B#=C#<==>D,
+    r_truth(D, E).
+thun(symbol(=), [D|E], [int(B), int(A)|G], H) :-
+    ( A#=B#<==>C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(>=), [], [int(C), int(B)|A], [E|A]) :-
+    B#>=C#<==>D,
+    r_truth(D, E).
+thun(symbol(>=), [D|E], [int(B), int(A)|G], H) :-
+    ( A#>=B#<==>C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(<=), [], [int(C), int(B)|A], [E|A]) :-
+    B#=D,
+    r_truth(D, E).
+thun(symbol(<=), [D|E], [int(B), int(A)|G], H) :-
+    ( A#=C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(<>), [], [int(C), int(B)|A], [E|A]) :-
+    B#\=C#<==>D,
+    r_truth(D, E).
+thun(symbol(<>), [D|E], [int(B), int(A)|G], H) :-
+    ( A#\=B#<==>C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(A), D, B, C) :-
+    combo(A, B, C, D, []).
+thun(symbol(A), C, B, G) :-
+    combo(A, B, F, C, [D|E]),
+    thun(D, E, F, G).
diff --git a/implementations/Prolog/source/gen-defs.pl b/implementations/Prolog/source/gen-defs.pl
new file mode 100644
index 0000000..dec6e2e
--- /dev/null
+++ b/implementations/Prolog/source/gen-defs.pl
@@ -0,0 +1,320 @@
+thun(int(A), [], B, [int(A)|B]).
+thun(int(C), [A|B], D, E) :-
+    thun(A, B, [int(C)|D], E).
+thun(bool(A), [], B, [bool(A)|B]).
+thun(bool(C), [A|B], D, E) :-
+    thun(A, B, [bool(C)|D], E).
+thun(list(A), [], B, [list(A)|B]).
+thun(list(C), [A|B], D, E) :-
+    thun(A, B, [list(C)|D], E).
+thun(symbol(--), A, C, D) :-
+    append([symbol(-)], A, B),
+    thun(int(1), B, C, D).
+thun(symbol(?), A, C, D) :-
+    append([symbol(bool)], A, B),
+    thun(symbol(dup), B, C, D).
+thun(symbol(&&), A, C, D) :-
+    append(
+           [ symbol(cons),
+             list([symbol(nullary), list([bool(false)])]),
+             symbol(dip),
+             symbol(branch)
+           ],
+           A,
+           B),
+    thun(list([symbol(nullary)]), B, C, D).
+thun(symbol(++), A, C, D) :-
+    append([symbol(+)], A, B),
+    thun(int(1), B, C, D).
+thun(symbol('||'), A, C, D) :-
+    append(
+           [ symbol(cons),
+             list([symbol(nullary)]),
+             symbol(dip),
+             list([bool(true)]),
+             symbol(branch)
+           ],
+           A,
+           B),
+    thun(list([symbol(nullary)]), B, C, D).
+thun(symbol('!-'), A, C, D) :-
+    append([symbol(>=)], A, B),
+    thun(int(0), B, C, D).
+thun(symbol(abs), A, C, D) :-
+    append([int(0), symbol(<), list([]), list([symbol(neg)]), symbol(branch)],
+           A,
+           B),
+    thun(symbol(dup), B, C, D).
+thun(symbol(anamorphism), A, C, D) :-
+    append([symbol(swap), list([symbol(dip), symbol(swons)]), symbol(genrec)],
+           A,
+           B),
+    thun(list([symbol(pop), list([])]), B, C, D).
+thun(symbol(app1), A, C, D) :-
+    append([symbol(infrst)], A, B),
+    thun(symbol(grba), B, C, D).
+thun(symbol(app2), A, C, D) :-
+    append([symbol(dip), list([symbol(infrst)]), symbol(cons), symbol(ii)],
+           A,
+           B),
+    thun(list([symbol(grba), symbol(swap), symbol(grba), symbol(swap)]),
+         B,
+         C,
+         D).
+thun(symbol(app3), A, C, D) :-
+    append([symbol(appN)], A, B),
+    thun(int(3), B, C, D).
+thun(symbol(appN), A, C, D) :-
+    append([symbol(cons), symbol(dip), symbol(map), symbol(disenstacken)],
+           A,
+           B),
+    thun(list([symbol(grabN)]), B, C, D).
+thun(symbol(at), A, C, D) :-
+    append([symbol(first)], A, B),
+    thun(symbol(drop), B, C, D).
+thun(symbol(average), A, C, D) :-
+    append([list([symbol(size)]), symbol(cleave), symbol(/)], A, B),
+    thun(list([symbol(sum), int(1), symbol('.0'), symbol(*)]),
+         B,
+         C,
+         D).
+thun(symbol(b), A, C, D) :-
+    append([symbol(dip), symbol(i)], A, B),
+    thun(list([symbol(i)]), B, C, D).
+thun(symbol(binary), A, C, D) :-
+    append([symbol(popd)], A, B),
+    thun(symbol(unary), B, C, D).
+thun(symbol(ccons), A, C, D) :-
+    append([symbol(cons)], A, B),
+    thun(symbol(cons), B, C, D).
+thun(symbol(cleave), A, C, D) :-
+    append([symbol(popdd)], A, B),
+    thun(symbol(fork), B, C, D).
+thun(symbol(clop), A, C, D) :-
+    append([symbol(popdd)], A, B),
+    thun(symbol(cleave), B, C, D).
+thun(symbol(codireco), A, C, D) :-
+    append([symbol(dip), symbol(rest), symbol(cons)], A, B),
+    thun(symbol(cons), B, C, D).
+thun(symbol(dinfrirst), A, C, D) :-
+    append([symbol(infrst)], A, B),
+    thun(symbol(dip), B, C, D).
+thun(symbol(disenstacken), A, C, D) :-
+    append([list([symbol(uncons), symbol(?)]), symbol(loop), symbol(pop)],
+           A,
+           B),
+    thun(symbol(?), B, C, D).
+thun(symbol(down_to_zero), A, C, D) :-
+    append([list([symbol(dup), symbol(--)]), symbol(while)], A, B),
+    thun(list([int(0), symbol(>)]), B, C, D).
+thun(symbol(drop), A, C, D) :-
+    append([symbol(times)], A, B),
+    thun(list([symbol(rest)]), B, C, D).
+thun(symbol(dupdd), A, C, D) :-
+    append([symbol(dipd)], A, B),
+    thun(list([symbol(dup)]), B, C, D).
+thun(symbol(dupdipd), A, C, D) :-
+    append([symbol(dipd)], A, B),
+    thun(symbol(dup), B, C, D).
+thun(symbol(enstacken), A, C, D) :-
+    append([list([symbol(clear)]), symbol(dip)], A, B),
+    thun(symbol(stack), B, C, D).
+thun(symbol(fork), A, C, D) :-
+    append([symbol(app2)], A, B),
+    thun(list([symbol(i)]), B, C, D).
+thun(symbol(fourth), A, C, D) :-
+    append([symbol(third)], A, B),
+    thun(symbol(rest), B, C, D).
+thun(symbol(gcd), A, C, D) :-
+    append(
+           [ list([symbol(tuck), symbol(mod), symbol(dup), int(0), symbol(>)]),
+             symbol(loop),
+             symbol(pop)
+           ],
+           A,
+           B),
+    thun(bool(true), B, C, D).
+thun(symbol(grabN), A, C, D) :-
+    append([symbol(swap), list([symbol(cons)]), symbol(times)], A, B),
+    thun(list([]), B, C, D).
+thun(symbol(grba), A, C, D) :-
+    append([symbol(dip)], A, B),
+    thun(list([symbol(stack), symbol(popd)]), B, C, D).
+thun(symbol(hypot), A, C, D) :-
+    append([symbol(ii), symbol(+), symbol(sqrt)], A, B),
+    thun(list([symbol(sqr)]), B, C, D).
+thun(symbol(ifte), A, C, D) :-
+    append([symbol(dipd), symbol(swap), symbol(branch)], A, B),
+    thun(list([symbol(nullary)]), B, C, D).
+thun(symbol(ii), A, C, D) :-
+    append([symbol(dupdip), symbol(i)], A, B),
+    thun(list([symbol(dip)]), B, C, D).
+thun(symbol(infra), A, C, D) :-
+    append([symbol(swaack), list([symbol(i)]), symbol(dip), symbol(swaack)],
+           A,
+           B),
+    thun(symbol(swons), B, C, D).
+thun(symbol(infrst), A, C, D) :-
+    append([symbol(first)], A, B),
+    thun(symbol(infra), B, C, D).
+thun(symbol(make_generator), A, C, D) :-
+    append([symbol(ccons)], A, B),
+    thun(list([symbol(codireco)]), B, C, D).
+thun(symbol(neg), A, C, D) :-
+    append([symbol(swap), symbol(-)], A, B),
+    thun(int(0), B, C, D).
+thun(symbol(not), A, C, D) :-
+    append([list([bool(false)]), symbol(branch)], A, B),
+    thun(list([bool(true)]), B, C, D).
+thun(symbol(nullary), A, C, D) :-
+    append([symbol(dinfrirst)], A, B),
+    thun(list([symbol(stack)]), B, C, D).
+thun(symbol(of), A, C, D) :-
+    append([symbol(at)], A, B),
+    thun(symbol(swap), B, C, D).
+thun(symbol(pam), A, C, D) :-
+    append([symbol(map)], A, B),
+    thun(list([symbol(i)]), B, C, D).
+thun(symbol(popd), A, C, D) :-
+    append([symbol(dip)], A, B),
+    thun(list([symbol(pop)]), B, C, D).
+thun(symbol(popdd), A, C, D) :-
+    append([symbol(dipd)], A, B),
+    thun(list([symbol(pop)]), B, C, D).
+thun(symbol(popop), A, C, D) :-
+    append([symbol(pop)], A, B),
+    thun(symbol(pop), B, C, D).
+thun(symbol(popopd), A, C, D) :-
+    append([symbol(dip)], A, B),
+    thun(list([symbol(popop)]), B, C, D).
+thun(symbol(popopdd), A, C, D) :-
+    append([symbol(dipd)], A, B),
+    thun(list([symbol(popop)]), B, C, D).
+thun(symbol(primrec), A, C, D) :-
+    append([symbol(genrec)], A, B),
+    thun(list([symbol(i)]), B, C, D).
+thun(symbol(product), A, C, D) :-
+    append([symbol(swap), list([symbol(*)]), symbol(step)], A, B),
+    thun(int(1), B, C, D).
+thun(symbol(quoted), A, C, D) :-
+    append([symbol(dip)], A, B),
+    thun(list([symbol(unit)]), B, C, D).
+thun(symbol(range), A, C, D) :-
+    append([list([int(1), symbol(-), symbol(dup)]), symbol(anamorphism)],
+           A,
+           B),
+    thun(list([int(0), symbol(<=)]), B, C, D).
+thun(symbol(range_to_zero), A, C, D) :-
+    append([list([symbol(down_to_zero)]), symbol(infra)], A, B),
+    thun(symbol(unit), B, C, D).
+thun(symbol(reverse), A, C, D) :-
+    append([symbol(swap), symbol(shunt)], A, B),
+    thun(list([]), B, C, D).
+thun(symbol(rrest), A, C, D) :-
+    append([symbol(rest)], A, B),
+    thun(symbol(rest), B, C, D).
+thun(symbol(run), A, C, D) :-
+    append([symbol(swap), symbol(infra)], A, B),
+    thun(list([]), B, C, D).
+thun(symbol(second), A, C, D) :-
+    append([symbol(first)], A, B),
+    thun(symbol(rest), B, C, D).
+thun(symbol(shunt), A, C, D) :-
+    append([symbol(step)], A, B),
+    thun(list([symbol(swons)]), B, C, D).
+thun(symbol(size), A, C, D) :-
+    append([symbol(swap), list([symbol(pop), symbol(++)]), symbol(step)],
+           A,
+           B),
+    thun(int(0), B, C, D).
+thun(symbol(spiral_next), A, C, D) :-
+    append(
+           [ list(
+                  [ list([symbol('!-')]),
+                    list([list([symbol(++)])]),
+                    list([list([symbol(--)])]),
+                    symbol(ifte),
+                    symbol(dip)
+                  ]),
+             list(
+                  [ list([symbol(pop), symbol('!-')]),
+                    list([symbol(--)]),
+                    list([symbol(++)]),
+                    symbol(ifte)
+                  ]),
+             symbol(ifte)
+           ],
+           A,
+           B),
+    thun(list(
+              [ list([list([symbol(abs)]), symbol(ii), symbol(<=)]),
+                list(
+                     [ list([symbol(<>)]),
+                       list([symbol(pop), symbol('!-')]),
+                       symbol('||')
+                     ]),
+                symbol(&&)
+              ]),
+         B,
+         C,
+         D).
+thun(symbol(split_at), A, C, D) :-
+    append([list([symbol(take)]), symbol(clop)], A, B),
+    thun(list([symbol(drop)]), B, C, D).
+thun(symbol(sqr), A, C, D) :-
+    append([symbol(*)], A, B),
+    thun(symbol(dup), B, C, D).
+thun(symbol(step_zero), A, C, D) :-
+    append([symbol('roll>'), symbol(step)], A, B),
+    thun(int(0), B, C, D).
+thun(symbol(sum), A, C, D) :-
+    append([symbol(swap), list([symbol(+)]), symbol(step)], A, B),
+    thun(int(0), B, C, D).
+thun(symbol(swons), A, C, D) :-
+    append([symbol(cons)], A, B),
+    thun(symbol(swap), B, C, D).
+thun(symbol(take), A, C, D) :-
+    append([symbol(rolldown), list([symbol(shift)]), symbol(times), symbol(pop)],
+           A,
+           B),
+    thun(list([]), B, C, D).
+thun(symbol(ternary), A, C, D) :-
+    append([symbol(popd)], A, B),
+    thun(symbol(binary), B, C, D).
+thun(symbol(third), A, C, D) :-
+    append([symbol(second)], A, B),
+    thun(symbol(rest), B, C, D).
+thun(symbol(unary), A, C, D) :-
+    append([symbol(popd)], A, B),
+    thun(symbol(nullary), B, C, D).
+thun(symbol(unquoted), A, C, D) :-
+    append([symbol(dip)], A, B),
+    thun(list([symbol(i)]), B, C, D).
+thun(symbol(unswons), A, C, D) :-
+    append([symbol(swap)], A, B),
+    thun(symbol(uncons), B, C, D).
+thun(symbol(while), A, C, D) :-
+    append(
+           [ list([symbol(nullary)]),
+             symbol(cons),
+             symbol(dup),
+             symbol(dipd),
+             symbol(concat),
+             symbol(loop)
+           ],
+           A,
+           B),
+    thun(symbol(swap), B, C, D).
+thun(symbol(x), A, C, D) :-
+    append([symbol(i)], A, B),
+    thun(symbol(dup), B, C, D).
+thun(symbol(A), [], B, C) :-
+    func(A, B, C).
+thun(symbol(A), [C|D], B, F) :-
+    func(A, B, E),
+    thun(C, D, E, F).
+thun(symbol(A), D, B, C) :-
+    combo(A, B, C, D, []).
+thun(symbol(A), C, B, G) :-
+    combo(A, B, F, C, [D|E]),
+    thun(D, E, F, G).
diff --git a/implementations/Prolog/source/gen-funcs.pl b/implementations/Prolog/source/gen-funcs.pl
new file mode 100644
index 0000000..4f3cbc3
--- /dev/null
+++ b/implementations/Prolog/source/gen-funcs.pl
@@ -0,0 +1,462 @@
+thun(int(A), [], B, [int(A)|B]).
+thun(int(C), [A|B], D, E) :-
+    thun(A, B, [int(C)|D], E).
+thun(bool(A), [], B, [bool(A)|B]).
+thun(bool(C), [A|B], D, E) :-
+    thun(A, B, [bool(C)|D], E).
+thun(list(A), [], B, [list(A)|B]).
+thun(list(C), [A|B], D, E) :-
+    thun(A, B, [list(C)|D], E).
+thun(symbol(A), C, F, G) :-
+    def(A, [D|B]),
+    append(B, C, E),
+    thun(D, E, F, G).
+thun(symbol(words), [], A, [B|A]) :-
+    words(B).
+thun(symbol(words), [A|B], D, E) :-
+    words(C),
+    thun(A, B, [C|D], E).
+thun(symbol(swap), [], [B, A|C], [A, B|C]).
+thun(symbol(swap), [A|B], [D, C|E], F) :-
+    thun(A, B, [C, D|E], F).
+thun(symbol(dup), [], [A|B], [A, A|B]).
+thun(symbol(dup), [A|B], [C|D], E) :-
+    thun(A, B, [C, C|D], E).
+thun(symbol(pop), [], [_|A], A).
+thun(symbol(pop), [A|B], [_|C], D) :-
+    thun(A, B, C, D).
+thun(symbol(cons), [], [list(B), A|C], [list([A|B])|C]).
+thun(symbol(cons), [A|B], [list(D), C|E], F) :-
+    thun(A, B, [list([C|D])|E], F).
+thun(symbol(concat), [], [list(C), list(B)|A], [list(D)|A]) :-
+    append(B, C, D).
+thun(symbol(concat), [C|D], [list(B), list(A)|F], G) :-
+    append(A, B, E),
+    thun(C, D, [list(E)|F], G).
+thun(symbol(flatten), [], [list(B)|A], [list(C)|A]) :-
+    flatten(B, C).
+thun(symbol(flatten), [B|C], [list(A)|E], F) :-
+    flatten(A, D),
+    thun(B, C, [list(D)|E], F).
+thun(symbol(swaack), [], [list(B)|A], [list(A)|B]).
+thun(symbol(swaack), [A|B], [list(D)|C], E) :-
+    thun(A, B, [list(C)|D], E).
+thun(symbol(stack), [], A, [list(A)|A]).
+thun(symbol(stack), [A|B], C, D) :-
+    thun(A, B, [list(C)|C], D).
+thun(symbol(clear), [], _, []).
+thun(symbol(clear), [A|B], _, C) :-
+    thun(A, B, [], C).
+thun(symbol(first), [], [list([A|_])|B], [A|B]).
+thun(symbol(first), [A|B], [list([C|_])|D], E) :-
+    thun(A, B, [C|D], E).
+thun(symbol(rest), [], [list([_|A])|B], [list(A)|B]).
+thun(symbol(rest), [A|B], [list([_|C])|D], E) :-
+    thun(A, B, [list(C)|D], E).
+thun(symbol(unit), [], [A|B], [list([A])|B]).
+thun(symbol(unit), [A|B], [C|D], E) :-
+    thun(A, B, [list([C])|D], E).
+thun(symbol(rolldown), [], [C, A, B|D], [A, B, C|D]).
+thun(symbol(rolldown), [A|B], [E, C, D|F], G) :-
+    thun(A, B, [C, D, E|F], G).
+thun(symbol(dupd), [], [A, B|C], [A, B, B|C]).
+thun(symbol(dupd), [A|B], [C, D|E], F) :-
+    thun(A, B, [C, D, D|E], F).
+thun(symbol(over), [], [B, A|C], [A, B, A|C]).
+thun(symbol(over), [A|B], [D, C|E], F) :-
+    thun(A, B, [C, D, C|E], F).
+thun(symbol(tuck), [], [A, B|C], [A, B, A|C]).
+thun(symbol(tuck), [A|B], [C, D|E], F) :-
+    thun(A, B, [C, D, C|E], F).
+thun(symbol(shift), [], [list([B|A]), list(C)|D], [list(A), list([B|C])|D]).
+thun(symbol(shift), [A|B], [list([D|C]), list(E)|F], G) :-
+    thun(A,
+         B,
+         [list(C), list([D|E])|F],
+         G).
+thun(symbol(rollup), [], [B, C, A|D], [A, B, C|D]).
+thun(symbol(rollup), [A|B], [D, E, C|F], G) :-
+    thun(A, B, [C, D, E|F], G).
+thun(symbol(uncons), [], [list([B|A])|C], [list(A), B|C]).
+thun(symbol(uncons), [A|B], [list([D|C])|E], F) :-
+    thun(A, B, [list(C), D|E], F).
+thun(symbol(bool), [], [int(0)|A], [bool(false)|A]).
+thun(symbol(bool), [A|B], [int(0)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(bool), [], [list([])|A], [bool(false)|A]).
+thun(symbol(bool), [A|B], [list([])|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(bool), [], [bool(false)|A], [bool(false)|A]).
+thun(symbol(bool), [A|B], [bool(false)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(bool), [], [int(B)|A], [bool(true)|A]) :-
+    B#\=0.
+thun(symbol(bool), [B|C], [int(A)|D], E) :-
+    A#\=0,
+    thun(B, C, [bool(true)|D], E).
+thun(symbol(bool), [], [list([_|_])|A], [bool(true)|A]).
+thun(symbol(bool), [A|B], [list([_|_])|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol(bool), [], [bool(true)|A], [bool(true)|A]).
+thun(symbol(bool), [A|B], [bool(true)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol('empty?'), [], [list([])|A], [bool(true)|A]).
+thun(symbol('empty?'), [A|B], [list([])|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol('empty?'), [], [list([_|_])|A], [bool(false)|A]).
+thun(symbol('empty?'), [A|B], [list([_|_])|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol('list?'), [], [list(_)|A], [bool(true)|A]).
+thun(symbol('list?'), [A|B], [list(_)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol('list?'), [], [bool(_)|A], [bool(false)|A]).
+thun(symbol('list?'), [A|B], [bool(_)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol('list?'), [], [int(_)|A], [bool(false)|A]).
+thun(symbol('list?'), [A|B], [int(_)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol('list?'), [], [symbol(_)|A], [bool(false)|A]).
+thun(symbol('list?'), [A|B], [symbol(_)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol('one-or-more?'), [], [list([_|_])|A], [bool(true)|A]).
+thun(symbol('one-or-more?'), [A|B], [list([_|_])|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol('one-or-more?'), [], [list([])|A], [bool(false)|A]).
+thun(symbol('one-or-more?'), [A|B], [list([])|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(and), [], [bool(true), bool(true)|A], [bool(true)|A]).
+thun(symbol(and), [A|B], [bool(true), bool(true)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol(and), [], [bool(true), bool(false)|A], [bool(false)|A]).
+thun(symbol(and), [A|B], [bool(true), bool(false)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(and), [], [bool(false), bool(true)|A], [bool(false)|A]).
+thun(symbol(and), [A|B], [bool(false), bool(true)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(and), [], [bool(false), bool(false)|A], [bool(false)|A]).
+thun(symbol(and), [A|B], [bool(false), bool(false)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(or), [], [bool(true), bool(true)|A], [bool(true)|A]).
+thun(symbol(or), [A|B], [bool(true), bool(true)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol(or), [], [bool(true), bool(false)|A], [bool(true)|A]).
+thun(symbol(or), [A|B], [bool(true), bool(false)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol(or), [], [bool(false), bool(true)|A], [bool(true)|A]).
+thun(symbol(or), [A|B], [bool(false), bool(true)|C], D) :-
+    thun(A, B, [bool(true)|C], D).
+thun(symbol(or), [], [bool(false), bool(false)|A], [bool(false)|A]).
+thun(symbol(or), [A|B], [bool(false), bool(false)|C], D) :-
+    thun(A, B, [bool(false)|C], D).
+thun(symbol(+), [], [int(C), int(D)|A], [int(B)|A]) :-
+    (   integer(B)
+    ->  (   integer(C),
+            integer(D)
+        ->  B=:=C+D
+        ;   E=B,
+            clpfd:clpfd_equal(E, C+D)
+        )
+    ;   integer(C),
+        integer(D)
+    ->  (   var(B)
+        ->  B is C+D
+        ;   E is C+D,
+            clpfd:clpfd_equal(B, E)
+        )
+    ;   clpfd:clpfd_equal(B, C+D)
+    ).
+thun(symbol(+), [E|F], [int(A), int(B)|G], H) :-
+    (   integer(C)
+    ->  (   integer(A),
+            integer(B)
+        ->  C=:=A+B
+        ;   D=C,
+            clpfd:clpfd_equal(D, A+B)
+        )
+    ;   integer(A),
+        integer(B)
+    ->  (   var(C)
+        ->  C is A+B
+        ;   D is A+B,
+            clpfd:clpfd_equal(C, D)
+        )
+    ;   clpfd:clpfd_equal(C, A+B)
+    ),
+    thun(E, F, [int(C)|G], H).
+thun(symbol(-), [], [int(D), int(C)|A], [int(B)|A]) :-
+    (   integer(B)
+    ->  (   integer(C),
+            integer(D)
+        ->  B=:=C-D
+        ;   E=B,
+            clpfd:clpfd_equal(E, C-D)
+        )
+    ;   integer(C),
+        integer(D)
+    ->  (   var(B)
+        ->  B is C-D
+        ;   E is C-D,
+            clpfd:clpfd_equal(B, E)
+        )
+    ;   clpfd:clpfd_equal(B, C-D)
+    ).
+thun(symbol(-), [E|F], [int(B), int(A)|G], H) :-
+    (   integer(C)
+    ->  (   integer(A),
+            integer(B)
+        ->  C=:=A-B
+        ;   D=C,
+            clpfd:clpfd_equal(D, A-B)
+        )
+    ;   integer(A),
+        integer(B)
+    ->  (   var(C)
+        ->  C is A-B
+        ;   D is A-B,
+            clpfd:clpfd_equal(C, D)
+        )
+    ;   clpfd:clpfd_equal(C, A-B)
+    ),
+    thun(E, F, [int(C)|G], H).
+thun(symbol(*), [], [int(C), int(D)|A], [int(B)|A]) :-
+    (   integer(B)
+    ->  (   integer(C),
+            integer(D)
+        ->  B=:=C*D
+        ;   E=B,
+            clpfd:clpfd_equal(E, C*D)
+        )
+    ;   integer(C),
+        integer(D)
+    ->  (   var(B)
+        ->  B is C*D
+        ;   E is C*D,
+            clpfd:clpfd_equal(B, E)
+        )
+    ;   clpfd:clpfd_equal(B, C*D)
+    ).
+thun(symbol(*), [E|F], [int(A), int(B)|G], H) :-
+    (   integer(C)
+    ->  (   integer(A),
+            integer(B)
+        ->  C=:=A*B
+        ;   D=C,
+            clpfd:clpfd_equal(D, A*B)
+        )
+    ;   integer(A),
+        integer(B)
+    ->  (   var(C)
+        ->  C is A*B
+        ;   D is A*B,
+            clpfd:clpfd_equal(C, D)
+        )
+    ;   clpfd:clpfd_equal(C, A*B)
+    ),
+    thun(E, F, [int(C)|G], H).
+thun(symbol(/), [], [int(D), int(C)|A], [int(B)|A]) :-
+    B#=C div D.
+thun(symbol(/), [C|D], [int(B), int(A)|F], G) :-
+    E#=A div B,
+    thun(C, D, [int(E)|F], G).
+thun(symbol('%'), [], [int(D), int(C)|A], [int(B)|A]) :-
+    (   integer(B)
+    ->  (   integer(C),
+            integer(D),
+            D=\=0
+        ->  B=:=C mod D
+        ;   E=B,
+            clpfd:clpfd_equal(E, C mod D)
+        )
+    ;   integer(C),
+        integer(D),
+        D=\=0
+    ->  (   var(B)
+        ->  B is C mod D
+        ;   E is C mod D,
+            clpfd:clpfd_equal(B, E)
+        )
+    ;   clpfd:clpfd_equal(B, C mod D)
+    ).
+thun(symbol('%'), [E|F], [int(B), int(A)|G], H) :-
+    (   integer(C)
+    ->  (   integer(A),
+            integer(B),
+            B=\=0
+        ->  C=:=A mod B
+        ;   D=C,
+            clpfd:clpfd_equal(D, A mod B)
+        )
+    ;   integer(A),
+        integer(B),
+        B=\=0
+    ->  (   var(C)
+        ->  C is A mod B
+        ;   D is A mod B,
+            clpfd:clpfd_equal(C, D)
+        )
+    ;   clpfd:clpfd_equal(C, A mod B)
+    ),
+    thun(E, F, [int(C)|G], H).
+thun(symbol('/%'), [], [int(D), int(C)|A], [int(B), int(E)|A]) :-
+    B#=C div D,
+    (   integer(E)
+    ->  (   integer(C),
+            integer(D),
+            D=\=0
+        ->  E=:=C mod D
+        ;   F=E,
+            clpfd:clpfd_equal(F, C mod D)
+        )
+    ;   integer(C),
+        integer(D),
+        D=\=0
+    ->  (   var(E)
+        ->  E is C mod D
+        ;   F is C mod D,
+            clpfd:clpfd_equal(E, F)
+        )
+    ;   clpfd:clpfd_equal(E, C mod D)
+    ).
+thun(symbol('/%'), [E|F], [int(B), int(A)|H], I) :-
+    ( G#=A div B,
+      (   integer(C)
+      ->  (   integer(A),
+              integer(B),
+              B=\=0
+          ->  C=:=A mod B
+          ;   D=C,
+              clpfd:clpfd_equal(D, A mod B)
+          )
+      ;   integer(A),
+          integer(B),
+          B=\=0
+      ->  (   var(C)
+          ->  C is A mod B
+          ;   D is A mod B,
+              clpfd:clpfd_equal(C, D)
+          )
+      ;   clpfd:clpfd_equal(C, A mod B)
+      )
+    ),
+    thun(E, F, [int(G), int(C)|H], I).
+thun(symbol(pm), [], [int(C), int(D)|A], [int(B), int(F)|A]) :-
+    (   integer(B)
+    ->  (   integer(C),
+            integer(D)
+        ->  B=:=C+D
+        ;   E=B,
+            clpfd:clpfd_equal(E, C+D)
+        )
+    ;   integer(C),
+        integer(D)
+    ->  (   var(B)
+        ->  B is C+D
+        ;   E is C+D,
+            clpfd:clpfd_equal(B, E)
+        )
+    ;   clpfd:clpfd_equal(B, C+D)
+    ),
+    (   integer(F)
+    ->  (   integer(D),
+            integer(C)
+        ->  F=:=D-C
+        ;   G=F,
+            clpfd:clpfd_equal(G, D-C)
+        )
+    ;   integer(D),
+        integer(C)
+    ->  (   var(F)
+        ->  F is D-C
+        ;   G is D-C,
+            clpfd:clpfd_equal(F, G)
+        )
+    ;   clpfd:clpfd_equal(F, D-C)
+    ).
+thun(symbol(pm), [G|H], [int(A), int(B)|I], J) :-
+    ( (   integer(C)
+      ->  (   integer(A),
+              integer(B)
+          ->  C=:=A+B
+          ;   D=C,
+              clpfd:clpfd_equal(D, A+B)
+          )
+      ;   integer(A),
+          integer(B)
+      ->  (   var(C)
+          ->  C is A+B
+          ;   D is A+B,
+              clpfd:clpfd_equal(C, D)
+          )
+      ;   clpfd:clpfd_equal(C, A+B)
+      ),
+      (   integer(E)
+      ->  (   integer(B),
+              integer(A)
+          ->  E=:=B-A
+          ;   F=E,
+              clpfd:clpfd_equal(F, B-A)
+          )
+      ;   integer(B),
+          integer(A)
+      ->  (   var(E)
+          ->  E is B-A
+          ;   F is B-A,
+              clpfd:clpfd_equal(E, F)
+          )
+      ;   clpfd:clpfd_equal(E, B-A)
+      )
+    ),
+    thun(G, H, [int(C), int(E)|I], J).
+thun(symbol(>), [], [int(C), int(B)|A], [E|A]) :-
+    B#>C#<==>D,
+    r_truth(D, E).
+thun(symbol(>), [D|E], [int(B), int(A)|G], H) :-
+    ( A#>B#<==>C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(<), [], [int(C), int(B)|A], [E|A]) :-
+    B#D,
+    r_truth(D, E).
+thun(symbol(<), [D|E], [int(B), int(A)|G], H) :-
+    ( A#C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(=), [], [int(C), int(B)|A], [E|A]) :-
+    B#=C#<==>D,
+    r_truth(D, E).
+thun(symbol(=), [D|E], [int(B), int(A)|G], H) :-
+    ( A#=B#<==>C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(>=), [], [int(C), int(B)|A], [E|A]) :-
+    B#>=C#<==>D,
+    r_truth(D, E).
+thun(symbol(>=), [D|E], [int(B), int(A)|G], H) :-
+    ( A#>=B#<==>C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(<=), [], [int(C), int(B)|A], [E|A]) :-
+    B#=D,
+    r_truth(D, E).
+thun(symbol(<=), [D|E], [int(B), int(A)|G], H) :-
+    ( A#=C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(<>), [], [int(C), int(B)|A], [E|A]) :-
+    B#\=C#<==>D,
+    r_truth(D, E).
+thun(symbol(<>), [D|E], [int(B), int(A)|G], H) :-
+    ( A#\=B#<==>C,
+      r_truth(C, F)
+    ),
+    thun(D, E, [F|G], H).
+thun(symbol(A), D, B, C) :-
+    combo(A, B, C, D, []).
+thun(symbol(A), C, B, G) :-
+    combo(A, B, F, C, [D|E]),
+    thun(D, E, F, G).
diff --git a/implementations/Prolog/source/joy2dot.pl b/implementations/Prolog/source/joy2dot.pl
new file mode 100644
index 0000000..bbd0e0f
--- /dev/null
+++ b/implementations/Prolog/source/joy2dot.pl
@@ -0,0 +1,153 @@
+/*
+
+    Copyright © 2018, 2019, 2020 Simon Forman
+
+    This file is part of Thun
+
+    Thun is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Thun is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Thun.  If not see .
+
+
+Run with e.g.:
+
+    $ swipl -g fooooo -g halt source/joy2dot.pl  > jd.dot
+
+
+*/
+:- use_module(library(dcg/basics)).
+:- dynamic def/2.
+
+
+joy_lex([tok(Token)|Ls]) --> chars(Token), !, joy_lex(Ls).
+joy_lex([  lbracket|Ls]) --> "[",          !, joy_lex(Ls).
+joy_lex([  rbracket|Ls]) --> "]",          !, joy_lex(Ls).
+
+joy_lex(Ls) --> [Space], {code_type(Space, space)}, !, joy_lex(Ls).
+
+joy_lex([]) --> [].
+
+
+joy_parse([J|Js]) --> joy_term(J), !, joy_parse(Js).
+joy_parse([]) --> [].
+
+joy_term(list(J)) --> [lbracket], !, joy_parse(J), [rbracket].
+joy_term(Atomic) --> [tok(Codes)], {joy_token(Atomic, Codes)}.
+
+joy_token(int(I), Codes) :- number(I, Codes, []), !.  % See dcg/basics.
+joy_token(bool(true), `true`) :- !.
+joy_token(bool(false), `false`) :- !.
+joy_token(symbol(S), Codes) :- atom_codes(S, Codes).
+
+
+text_to_expression(Text, Expression) :-
+    phrase(joy_lex(Tokens), Text), !,
+    phrase(joy_parse(Expression), Tokens).
+
+% Apologies for all the (green, I hope) cuts.  The strength of the Joy
+% syntax is that it's uninteresting.
+
+chars([Ch|Rest]) --> char(Ch), chars(Rest).
+chars([Ch])      --> char(Ch).
+
+char(Ch) --> [Ch], {Ch \== 0'[, Ch \== 0'], code_type(Ch, graph)}.
+
+
+
+joy_def(Codes) :-
+    text_to_expression(Codes, [symbol(Name)|Body]),
+    % writeln(Name),
+    assert_def(Name, Body).
+
+assert_defs(DefsFile) :-
+    read_file_to_codes(DefsFile, Codes, []),
+    lines(Codes, Lines),
+    maplist(joy_def, Lines).
+
+assert_def(Symbol, Body) :-
+        retractall(def(Symbol, _)),
+        assertz(def(Symbol, Body)).
+
+% Split on newline chars a list of codes into a list of lists of codes
+% one per line.  Helper function.
+lines([], []) :- !.
+lines(Codes, [Line|Lines]) :- append(Line, [0'\n|Rest], Codes), !, lines(Rest, Lines).
+lines(Codes, [Codes]).
+
+:- assert_defs("defs.txt").
+
+/*
+
+term_expansion(def(Def), def(Name, Body)) :-
+    text_to_expression(Def, [symbol(Name)|Body]).
+
+
+% def(``).
+def(`and duo unit`).
+def(`app2 [grba swap grba swap] dip [infrst] cons ii`).
+def(`b [i] dip i`).
+def(`cleave fork popdd`).
+def(`clop cleave popdd`).
+def(`duo unit cons`).
+def(`fba [xor xor void] [[and] [xor and] fork or void] clop popdd`).
+def(`fork [i] app2`).
+def(`grba [stack popd] dip`).
+def(`ii [dip] dupdip i`).
+def(`infra swons swaack [i] dip swaack`).
+def(`infrst infra first`).
+def(`or [unit] ii duo`).
+def(`popd [pop] dip`).
+def(`popdd [pop] dipd`).
+def(`popop pop pop`).
+def(`swons swap cons`).
+% def(`uncons-pair [uncons] dip unswons rolldown`).
+def(`unswons uncons swap`).
+def(`xor [unit] ii [cons] [swap cons] clop duo`).
+
+*/
+
+symbols(E, S) :- symbols(E, [], S).
+
+symbols(symbol(S))      --> seen_sym(S), !.
+symbols(symbol(S)), [S] --> [].
+symbols(  bool(_))      --> [].
+symbols(   int(_))      --> [].
+symbols(  list(L))      --> symbols(L).
+
+symbols([])             --> [].
+symbols([T|Tail])       --> symbols(T), symbols(Tail).
+
+seen_sym(Term, List, List) :- member(Term, List).
+
+write_sym(Symbol) :- write('"'), write(Symbol), write('"').
+
+fooooo :- 
+    writeln("digraph joy_defs {"),
+    % writeln("    rankdir=LR;"),
+    forall(
+        def(Symbol, Body),
+        (
+            symbols(list(Body), Deps),
+            forall(
+                member(Dep, Deps),
+                (
+                    write("    "),
+                    write_sym(Symbol),
+                    write(" -> "),
+                    write_sym(Dep),
+                    writeln(";")
+                )
+            )
+        )
+    ),
+    writeln("}"). 
+
diff --git a/implementations/Prolog/source/joy2py.pl b/implementations/Prolog/source/joy2py.pl
new file mode 100644
index 0000000..81ed8e5
--- /dev/null
+++ b/implementations/Prolog/source/joy2py.pl
@@ -0,0 +1,919 @@
+/*
+
+████████╗██╗  ██╗██╗   ██╗███╗   ██╗
+╚══██╔══╝██║  ██║██║   ██║████╗  ██║
+   ██║   ███████║██║   ██║██╔██╗ ██║
+   ██║   ██╔══██║██║   ██║██║╚██╗██║
+   ██║   ██║  ██║╚██████╔╝██║ ╚████║
+   ╚═╝   ╚═╝  ╚═╝ ╚═════╝ ╚═╝  ╚═══╝
+
+A dialect of Joy.  Version -10.0.0.
+
+    Copyright © 2018, 2019, 2020 Simon Forman
+
+    This file is part of Thun
+
+    Thun is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    Thun is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with Thun.  If not see .
+
+(Big fonts are from Figlet "ANSI Shadow" http://www.patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=formatter and "Small".)
+
+Thun is an implementation of a dialect of the Joy executable notation.
+
+Table of Contents
+    Parser & Grammar
+    Semantics
+        Functions
+        Combinators
+        Definitions
+    Compiler
+        to Prolog
+        to Machine Code
+    Meta-Programming
+        Expand/Contract Definitions
+        Formatter
+        Partial Reducer
+
+ */
+
+:- use_module(library(clpfd)).
+:- use_module(library(dcg/basics)).
+:- use_module(library(gensym)).
+:- dynamic func/3.
+:- dynamic def/2.
+
+
+/*
+An entry point.
+*/
+
+joy(InputString, StackIn, StackOut) :-
+    text_to_expression(InputString, Expression),
+    !,
+    thun(Expression, StackIn, StackOut).
+
+/*
+
+██████╗  █████╗ ██████╗ ███████╗███████╗██████╗        ██╗
+██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔════╝██╔══██╗       ██║
+██████╔╝███████║██████╔╝███████╗█████╗  ██████╔╝    ████████╗
+██╔═══╝ ██╔══██║██╔══██╗╚════██║██╔══╝  ██╔══██╗    ██╔═██╔═╝
+██║     ██║  ██║██║  ██║███████║███████╗██║  ██║    ██████║
+╚═╝     ╚═╝  ╚═╝╚═╝  ╚═╝╚══════╝╚══════╝╚═╝  ╚═╝    ╚═════╝
+
+ ██████╗ ██████╗  █████╗ ███╗   ███╗███╗   ███╗ █████╗ ██████╗
+██╔════╝ ██╔══██╗██╔══██╗████╗ ████║████╗ ████║██╔══██╗██╔══██╗
+██║  ███╗██████╔╝███████║██╔████╔██║██╔████╔██║███████║██████╔╝
+██║   ██║██╔══██╗██╔══██║██║╚██╔╝██║██║╚██╔╝██║██╔══██║██╔══██╗
+╚██████╔╝██║  ██║██║  ██║██║ ╚═╝ ██║██║ ╚═╝ ██║██║  ██║██║  ██║
+ ╚═════╝ ╚═╝  ╚═╝╚═╝  ╚═╝╚═╝     ╚═╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═╝
+
+The grammar of Joy is very simple.  A Joy expression is zero or more Joy
+terms (separated by blanks, see below) and terms can be
+integers, Booleans, quoted Joy expressions, or symbols (names of
+functions.)
+
+    joy ::= term*
+
+    term ::= integer | bool | '[' joy ']' | symbol
+
+    integer ::= [ '-' | '+' ] ('0'...'9')+
+    bool ::= 'true' | 'false'
+    symbol ::= char+
+
+    char ::= 
+
+There are a few wrinkles in the handling of blank space between terms
+because we want to be able to omit it around brackets:
+
+Valid expressions:
+
+    1 2 3
+    1[2]3
+    1 [ 2 ] 3
+    true
+    truedat  (a symbol prefixed with the name of a boolean)
+
+Invalid:
+
+    12three  (symbols can't start with numbers, and this shouldn't parse
+              as [12 three].)
+
+Symbols can be made of any non-blank characters except '['and ']' which
+are fully reserved for list literals (aka "quotes"). 'true' and 'false'
+would be valid symbols but they are reserved for Boolean literals.
+
+Integers are converted to Prolog integers, symbols and bools to Prolog
+atoms, and list literals to Prolog lists.
+
+For now strings are neglected in favor of lists of numbers.  (But there's
+no support for parsing string notation and converting to lists of ints.)
+
+First lex the stream of codes into tokens separated by square brackets
+or whitespace.  We keep the brackets and throw away the blanks.
+*/
+
+joy_lex([tok(Token)|Ls]) --> chars(Token), !, joy_lex(Ls).
+joy_lex([  lbracket|Ls]) --> "[",          !, joy_lex(Ls).
+joy_lex([  rbracket|Ls]) --> "]",          !, joy_lex(Ls).
+
+joy_lex(Ls) --> [Space], {code_type(Space, space)}, !, joy_lex(Ls).
+
+joy_lex([]) --> [].
+
+% Then parse the tokens converting them to Prolog values and building up
+% the list structures (if any.)
+
+joy_parse([J|Js]) --> joy_term(J), !, joy_parse(Js).
+joy_parse([]) --> [].
+
+joy_term(list(J)) --> [lbracket], !, joy_parse(J), [rbracket].
+joy_term(Atomic) --> [tok(Codes)], {joy_token(Atomic, Codes)}.
+
+joy_token(int(I), Codes) :- number(I, Codes, []), !.  % See dcg/basics.
+joy_token(bool(true), `true`) :- !.
+joy_token(bool(false), `false`) :- !.
+joy_token(symbol(S), Codes) :- atom_codes(S, Codes).
+
+
+text_to_expression(Text, Expression) :-
+    phrase(joy_lex(Tokens), Text), !,
+    phrase(joy_parse(Expression), Tokens).
+
+% Apologies for all the (green, I hope) cuts.  The strength of the Joy
+% syntax is that it's uninteresting.
+
+chars([Ch|Rest]) --> char(Ch), chars(Rest).
+chars([Ch])      --> char(Ch).
+
+char(Ch) --> [Ch], {Ch \== 0'[, Ch \== 0'], code_type(Ch, graph)}.
+
+
+/* Here is an example of Joy code:
+
+    [   [[abs] ii <=]
+        [
+            [<>] [pop !-] ||
+        ] &&
+    ]
+    [[    !-] [[++]] [[--]] ifte dip]
+    [[pop !-]  [--]   [++]  ifte    ]
+    ifte
+
+It probably seems unreadable but with a little familiarity it becomes
+just as legible as any other notation.  This function accepts two
+integers on the stack and increments or decrements one of them such that
+the new pair of numbers is the next coordinate pair in a square spiral
+(like that used to construct an Ulam Spiral).  It is adapted from the
+code in the answer here:
+
+https://stackoverflow.com/questions/398299/looping-in-a-spiral/31864777#31864777
+
+It can be used with the x combinator to make a kind of generator for
+spiral square coordinates.
+
+
+
+███████╗███████╗███╗   ███╗ █████╗ ███╗   ██╗████████╗██╗ ██████╗███████╗
+██╔════╝██╔════╝████╗ ████║██╔══██╗████╗  ██║╚══██╔══╝██║██╔════╝██╔════╝
+███████╗█████╗  ██╔████╔██║███████║██╔██╗ ██║   ██║   ██║██║     ███████╗
+╚════██║██╔══╝  ██║╚██╔╝██║██╔══██║██║╚██╗██║   ██║   ██║██║     ╚════██║
+███████║███████╗██║ ╚═╝ ██║██║  ██║██║ ╚████║   ██║   ██║╚██████╗███████║
+╚══════╝╚══════╝╚═╝     ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝   ╚═╝   ╚═╝ ╚═════╝╚══════╝
+
+The fundamental Joy relation involves an expression and two stacks.  One
+stack serves as input and the other as output.
+
+    thun(Expression, InputStack, OutputStack)
+
+The null expression (denoted by an empty Prolog list) is effectively an
+identity function and serves as the end-of-processing marker.  As a
+matter of efficiency (of Prolog) the thun/3 predicate picks off the first
+term of the expression (if any) and passes it to thun/4 which can then
+take advantage of Prolog indexing on the first term of a predicate. */
+
+thun([], S, S).
+thun([Term|E], Si, So) :- thun(Term, E, Si, So).
+
+/* The thun/4 predicate was originally written in terms of the thun/3
+predicate, which was very elegant, but prevented (I assume but have not
+checked) tail-call recursion.  In order to alleviate this, partial
+reduction is used to generate the actual thun/4 rules, see below.
+
+Original thun/4 code:
+
+thun(int(I),        E, Si, So) :- thun(E, [ int(I)|Si], So).
+thun(bool(B),       E, Si, So) :- thun(E, [bool(B)|Si], So).
+thun(list(L),       E, Si, So) :- thun(E, [list(L)|Si], So).
+thun(symbol(Def),   E, Si, So) :- def(Def, Body), append(Body, E, Eo), thun(Eo, Si, So).
+thun(symbol(Func),  E, Si, So) :- func(Func, Si, S),                   thun(E,  S,  So).
+thun(symbol(Combo), E, Si, So) :- combo(Combo, Si, S, E, Eo),          thun(Eo, S,  So).
+
+Integers, Boolean values, and lists are put onto the stack, symbols are
+dispatched to one of three kinds of processing: functions, combinators
+and definitions (see "defs.txt".) */
+
+thun(A,    [], S, [A|S]) :- var(A), !.
+thun(A, [T|E], S,   So)  :- var(A), !, thun(T, E, [A|S], So).
+
+% Literals turn out okay.
+
+thun(int(A),    [], B, [int(A)|B]).
+thun(int(C), [A|B], D, E) :- thun(A, B, [int(C)|D], E).
+
+thun(bool(A),    [], B, [bool(A)|B]).
+thun(bool(C), [A|B], D, E) :- thun(A, B, [bool(C)|D], E).
+
+thun(list(A),    [], B, [list(A)|B]).
+thun(list(C), [A|B], D, E) :- thun(A, B, [list(C)|D], E).
+
+% Partial reduction works for func/3 cases.
+
+thun(symbol(A),    [], B, C) :- func(A, B, C).
+thun(symbol(A), [C|D], B, F) :- func(A, B, E), thun(C, D, E, F).
+
+% Combinators look ok too.
+
+% thun(symbol(A), D, B, C) :- combo(A, B, C, D, []).
+% thun(symbol(A), C, B, G) :- combo(A, B, F, C, [D|E]), thun(D, E, F, G).
+
+% However, in this case, I think the original version will be more
+% efficient.
+
+thun(symbol(Combo), E, Si, So) :- combo(Combo, Si, S, E, Eo), thun(Eo, S, So).
+
+% In the reduced rules Prolog will redo all the work of the combo/5
+% predicate on backtracking through the second rule.  It will try
+% combo/5, which usually won't end in Eo=[] so the first rule fails, then
+% it will try combo/5 again in the second rule.  In the original form
+% after combo/5 has completed Prolog has computed Eo and can index on it
+% for thun/3.
+%
+% Neither functions nor definitions can affect the expression so this
+% consideration doesn't apply to those rules.  The unification of the
+% head clauses will distinguish the cases for them.
+
+% Definitions don't work though (See "Partial Reducer" section below.)
+% I hand-wrote the def/3 cases here.
+
+thun(symbol(D),     [], Si, So) :- def(D, [DH| E]), thun(DH, E, Si, So).
+thun(symbol(D), [H|E0], Si, So) :- def(D, [DH|DE]),
+     append(DE, [H|E0], E), /* ................. */ thun(DH, E, Si, So).
+
+% Partial reduction has been the subject of a great deal of research and
+% I'm sure there's a way to make definitions work, but it's beyond the
+% scope of the project at the moment.  It works well enough as-is that I'm
+% happy to manually write out two rules by hand.
+
+% Some error handling.
+
+thun(symbol(Unknown), _, _, _) :-
+    \+ def(Unknown, _),
+    \+ func(Unknown, _, _),
+    \+ combo(Unknown, _, _, _, _),
+    write("Unknown: "),
+    writeln(Unknown),
+    fail.
+
+/*
+
+███████╗██╗   ██╗███╗   ██╗ ██████╗████████╗██╗ ██████╗ ███╗   ██╗███████╗
+██╔════╝██║   ██║████╗  ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗  ██║██╔════╝
+█████╗  ██║   ██║██╔██╗ ██║██║        ██║   ██║██║   ██║██╔██╗ ██║███████╗
+██╔══╝  ██║   ██║██║╚██╗██║██║        ██║   ██║██║   ██║██║╚██╗██║╚════██║
+██║     ╚██████╔╝██║ ╚████║╚██████╗   ██║   ██║╚██████╔╝██║ ╚████║███████║
+╚═╝      ╚═════╝ ╚═╝  ╚═══╝ ╚═════╝   ╚═╝   ╚═╝ ╚═════╝ ╚═╝  ╚═══╝╚══════╝
+
+*/
+
+func(words, S, [Words|S]) :- words(Words).
+
+func(swap, [A, B|S],  [B, A|S]).
+func(dup,     [A|S],  [A, A|S]).
+func(pop,     [_|S],        S ).
+
+func(cons,   [list(A),      B |S], [list([B|A])|S]).
+func(concat, [list(A), list(B)|S],     [list(C)|S]) :- append(B, A, C).
+func(flatten,   [list(A)|S],   [list(B)|S]) :- flatten(A, B).
+func(swaack,    [list(R)|S],   [list(S)|R]).
+func(stack,              S ,   [list(S)|S]).
+func(clear,              _ ,            []).
+func(first, [list([X|_])|S],   [     X |S]).
+func(rest,  [list([_|X])|S],   [list(X)|S]).
+func(unit, [X|S], [list([X])|S]).
+
+func(rolldown, [A, B, C|S], [B, C, A|S]).
+func(dupd,        [A, B|S], [A, B, B|S]).
+func(over,        [A, B|S], [B, A, B|S]).
+func(tuck,        [A, B|S], [A, B, A|S]).
+func(dupdd, [A, B, C|D], [A, B, C, C|D]).
+
+% func(stackd, [A|B], [A, list(B)|B]).  % Doesn't compile.
+
+func(shift, [list([B|A]), list(C)|D], [list(A), list([B|C])|D]).
+
+func(rollup, Si, So) :- func(rolldown, So, Si).
+func(uncons, Si, So) :- func(cons, So, Si).
+
+func(bool, [     int(0)|S], [bool(false)|S]).
+func(bool, [   list([])|S], [bool(false)|S]).
+func(bool, [bool(false)|S], [bool(false)|S]).
+
+func(bool, [     int(N)|S], [bool(true)|S]) :- N #\= 0.
+func(bool, [list([_|_])|S], [bool(true)|S]).
+func(bool, [ bool(true)|S], [bool(true)|S]).
+% func(bool, [A|S], [bool(true)|S]) :- \+ func(bool, [A], [bool(false)]).
+
+func('empty?', [    list([])|S], [ bool(true)|S]).
+func('empty?', [ list([_|_])|S], [bool(false)|S]).
+
+func('list?', [  list(_)|S], [ bool(true)|S]).
+func('list?', [  bool(_)|S], [bool(false)|S]).
+func('list?', [   int(_)|S], [bool(false)|S]).
+func('list?', [symbol(_)|S], [bool(false)|S]).
+
+func('one-or-more?', [list([_|_])|S], [ bool(true)|S]).
+func('one-or-more?', [   list([])|S], [bool(false)|S]).
+
+func(and, [bool(true),   bool(true)|S], [ bool(true)|S]).
+func(and, [bool(true),  bool(false)|S], [bool(false)|S]).
+func(and, [bool(false),  bool(true)|S], [bool(false)|S]).
+func(and, [bool(false), bool(false)|S], [bool(false)|S]).
+
+func(or,  [bool(true),   bool(true)|S], [ bool(true)|S]).
+func(or,  [bool(true),  bool(false)|S], [ bool(true)|S]).
+func(or,  [bool(false),  bool(true)|S], [ bool(true)|S]).
+func(or,  [bool(false), bool(false)|S], [bool(false)|S]).
+
+func( + ,  [int(A), int(B)|S], [int(A + B)|S]).
+func( - ,  [int(A), int(B)|S], [int(B - A)|S]).
+func( * ,  [int(A), int(B)|S], [int(A * B)|S]).
+func( / ,  [int(A), int(B)|S], [int(B div A)|S]).
+func('%',  [int(A), int(B)|S], [int(B mod A)|S]).
+% func( + ,  [int(A), int(B)|S], [int(C)|S]) :- C #= A + B.
+% func( - ,  [int(A), int(B)|S], [int(C)|S]) :- C #= B - A.
+% func( * ,  [int(A), int(B)|S], [int(C)|S]) :- C #= A * B.
+% func( / ,  [int(A), int(B)|S], [int(C)|S]) :- C #= B div A.
+% func('%',  [int(A), int(B)|S], [int(C)|S]) :- C #= B mod A.
+
+func('/%', [int(A), int(B)|S], [int(B div A), int(B mod A)|S]).
+func( pm , [int(A), int(B)|S], [int(A + B), int(B - A)|S]).
+% func('/%', [int(A), int(B)|S], [int(C), int(D)|S]) :- C #= B div A, D #= B mod A.
+% func( pm , [int(A), int(B)|S], [int(C), int(D)|S]) :- C #= A + B,   D #= B - A.
+
+func(>,  [int(A), int(B)|S], [    bool(B > A)|S]).
+func(<,  [int(A), int(B)|S], [    bool(B < A)|S]).
+func(=,  [int(A), int(B)|S], [ bool(eq(B, A))|S]).
+func(>=, [int(A), int(B)|S], [   bool(B >= A)|S]).
+func(<=, [int(A), int(B)|S], [   bool(B =< A)|S]).
+func(<>, [int(A), int(B)|S], [bool(neq(B, A))|S]).
+% func(>,  [int(A), int(B)|S], [T|S]) :- B #> A #<==> R, r_truth(R, T).
+% func(<,  [int(A), int(B)|S], [T|S]) :- B #< A #<==> R, r_truth(R, T).
+% func(=,  [int(A), int(B)|S], [T|S]) :- B #= A #<==> R, r_truth(R, T).
+% func(>=, [int(A), int(B)|S], [T|S]) :- B #>= A #<==> R, r_truth(R, T).
+% func(<=, [int(A), int(B)|S], [T|S]) :- B #=< A #<==> R, r_truth(R, T).
+% func(<>, [int(A), int(B)|S], [T|S]) :- B #\= A #<==> R, r_truth(R, T).
+
+func(sqr) --> func(dup), func(mul).  % Pretty neat.
+
+r_truth(0, bool(false)).
+r_truth(1, bool(true)).
+
+
+/*
+
+ ██████╗ ██████╗ ███╗   ███╗██████╗ ██╗███╗   ██╗ █████╗ ████████╗ ██████╗ ██████╗ ███████╗
+██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║████╗  ██║██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗██╔════╝
+██║     ██║   ██║██╔████╔██║██████╔╝██║██╔██╗ ██║███████║   ██║   ██║   ██║██████╔╝███████╗
+██║     ██║   ██║██║╚██╔╝██║██╔══██╗██║██║╚██╗██║██╔══██║   ██║   ██║   ██║██╔══██╗╚════██║
+╚██████╗╚██████╔╝██║ ╚═╝ ██║██████╔╝██║██║ ╚████║██║  ██║   ██║   ╚██████╔╝██║  ██║███████║
+ ╚═════╝ ╚═════╝ ╚═╝     ╚═╝╚═════╝ ╚═╝╚═╝  ╚═══╝╚═╝  ╚═╝   ╚═╝    ╚═════╝ ╚═╝  ╚═╝╚══════╝
+
+*/
+
+combo(i,          [list(P)|S], S, Ei, Eo) :- append(P, Ei, Eo).
+combo(dip,     [list(P), X|S], S, Ei, Eo) :- append(P, [X|Ei], Eo).
+combo(dipd, [list(P), X, Y|S], S, Ei, Eo) :- append(P, [Y, X|Ei], Eo).
+
+combo(dupdip, [list(P), X|S], [X|S], Ei, Eo) :- append(P, [X|Ei], Eo).
+
+combo(branch, [list(T), list(_),  bool(true)|S], S, Ei, Eo) :- append(T, Ei, Eo).
+combo(branch, [list(_), list(F), bool(false)|S], S, Ei, Eo) :- append(F, Ei, Eo).
+
+combo(loop, [list(_), bool(false)|S], S, E,  E ).
+combo(loop, [list(B),  bool(true)|S], S, Ei, Eo) :- append(B, [list(B), symbol(loop)|Ei], Eo).
+
+combo(step, [list(_),    list([])|S],    S,  E,  E ).
+combo(step, [list(P), list([X|Z])|S], [X|S], Ei, Eo) :- append(P, [list(Z), list(P), symbol(step)|Ei], Eo).
+
+combo(times, [list(_), int(0)|S], S, E,  E ).
+combo(times, [list(P), int(1)|S], S, Ei, Eo) :- append(P, Ei, Eo).
+combo(times, [list(P), int(N)|S], S, Ei, Eo) :- N #>= 2, M #= N - 1, append(P, [int(M), list(P), symbol(times)|Ei], Eo).
+combo(times, [list(_), int(N)|S], S, _,  _ ) :- N #< 0, fail.
+
+combo(genrec, [R1, R0, Then, If|S],
+              [  Else, Then, If|S], E, [symbol(ifte)|E]) :-
+    append(R0, [list([If, Then, R0, R1, symbol(genrec)])|R1], Else).
+
+/*
+This is a crude but servicable implementation of the map combinator.
+
+Obviously it would be nice to take advantage of the implied parallelism.
+Instead the quoted program, stack, and terms in the input list are
+transformed to simple Joy expressions that run the quoted program on
+prepared copies of the stack that each have one of the input terms on
+top.  These expressions are collected in a list and the whole thing is
+evaluated (with infra) on an empty list, which becomes the output list.
+
+The chief advantage of doing it this way (as opposed to using Prolog's
+map) is that the whole state remains in the pending expression, so
+there's nothing stashed in Prolog's call stack.  This preserves the nice
+property that you can interrupt the Joy evaluation and save or transmit
+the stack+expression knowing that you have all the state.
+*/
+
+combo(map, [list(_),   list([])|S],               [list([])|S], E,                E ) :- !.
+combo(map, [list(P), list(List)|S], [list(Mapped), list([])|S], E, [symbol(infra)|E]) :-
+    prepare_mapping(list(P), S, List, Mapped).
+
+% Set up a program for each term in ListIn
+%
+%     [term S] [P] infrst
+%
+% prepare_mapping(P, S, ListIn, ListOut).
+
+prepare_mapping(Pl, S, In, Out) :- prepare_mapping(Pl, S, In, [], Out).
+
+prepare_mapping(    _,  _,     [],                                  Out,  Out) :- !.
+prepare_mapping(    Pl, S, [T|In],                                  Acc,  Out) :-
+    prepare_mapping(Pl, S,    In,  [list([T|S]), Pl, symbol(infrst)|Acc], Out).
+
+
+/*
+
+██████╗ ███████╗███████╗██╗███╗   ██╗██╗████████╗██╗ ██████╗ ███╗   ██╗███████╗
+██╔══██╗██╔════╝██╔════╝██║████╗  ██║██║╚══██╔══╝██║██╔═══██╗████╗  ██║██╔════╝
+██║  ██║█████╗  █████╗  ██║██╔██╗ ██║██║   ██║   ██║██║   ██║██╔██╗ ██║███████╗
+██║  ██║██╔══╝  ██╔══╝  ██║██║╚██╗██║██║   ██║   ██║██║   ██║██║╚██╗██║╚════██║
+██████╔╝███████╗██║     ██║██║ ╚████║██║   ██║   ██║╚██████╔╝██║ ╚████║███████║
+╚═════╝ ╚══════╝╚═╝     ╚═╝╚═╝  ╚═══╝╚═╝   ╚═╝   ╚═╝ ╚═════╝ ╚═╝  ╚═══╝╚══════╝
+
+*/
+
+joy_def(Codes) :-
+    text_to_expression(Codes, [symbol(Name)|Body]),
+    % writeln(Name),
+    assert_def(Name, Body).
+
+assert_defs(DefsFile) :-
+    read_file_to_codes(DefsFile, Codes, []),
+    lines(Codes, Lines),
+    maplist(joy_def, Lines).
+
+assert_def(Symbol, Body) :-
+    (  % Don't let this "shadow" functions or combinators.
+        \+ func(Symbol, _, _),
+        \+ combo(Symbol, _, _, _, _)
+    ) -> (  % Replace any existing defs of this name.
+        retractall(def(Symbol, _)),
+        assertz(def(Symbol, Body))
+    ) ; true.
+
+% Split on newline chars a list of codes into a list of lists of codes
+% one per line.  Helper function.
+lines([], []) :- !.
+lines(Codes, [Line|Lines]) :- append(Line, [0'\n|Rest], Codes), !, lines(Rest, Lines).
+lines(Codes, [Codes]).
+
+:- assert_defs("defs.txt").
+
+
+% A meta function that finds the names of all available functions.
+
+words(Words) :-
+    findall(Name, clause(func(Name, _, _), _), Funcs),
+    findall(Name, clause(combo(Name, _, _, _, _), _), Combos, Funcs),
+    findall(Name, clause(def(Name, _), _), Words0, Combos),
+    list_to_set(Words0, Words1),
+    sort(Words1, Words).
+
+
+/*
+
+ ██████╗ ██████╗ ███╗   ███╗██████╗ ██╗██╗     ███████╗██████╗
+██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║██║     ██╔════╝██╔══██╗
+██║     ██║   ██║██╔████╔██║██████╔╝██║██║     █████╗  ██████╔╝
+██║     ██║   ██║██║╚██╔╝██║██╔═══╝ ██║██║     ██╔══╝  ██╔══██╗
+╚██████╗╚██████╔╝██║ ╚═╝ ██║██║     ██║███████╗███████╗██║  ██║
+ ╚═════╝ ╚═════╝ ╚═╝     ╚═╝╚═╝     ╚═╝╚══════╝╚══════╝╚═╝  ╚═╝
+  _         ___      _   _
+ | |_ ___  | _ \_  _| |_| |_  ___ _ _
+ |  _/ _ \ |  _/ || |  _| ' \/ _ \ ' \
+  \__\___/ |_|  \_, |\__|_||_\___/_||_|
+                |__/
+
+
+We have a tabulator predicate.
+
+*/
+
+tabs(N) --> { N #> 0, M #= N - 1 },
+    tab, tabs(M).
+
+tabs(0) --> [].
+
+nl --> "\n".
+
+tab --> "    ".
+
+
+/*
+
+Convert Prolog terms to Python source.
+
+ */
+
+% stack_to_python(F) --> { writeln(F), fail }.
+
+stack_to_python(S) --> {atom(S), !, atom_codes(S, C)}, C.
+stack_to_python([]) --> "stack", !.
+stack_to_python([Term|Tail]) -->
+    "(", term_to_python(Term), ", ", stack_to_python(Tail), ")".
+
+
+% Unify unbound terms with fresh Python identifiers.
+pyvar(Prefix, Term, Codes) :-
+    ( var(Term) -> gensym(Prefix, Term) ; atom(Term) ),
+    atom_codes(Term, Codes).
+
+term_to_python(Term) -->
+    { pyvar(v, Term, Var) }, !, Var.
+
+term_to_python(bool(Term)) --> term_to_python(Term).
+
+term_to_python(int(Term)) -->
+    { ( integer(Term) ->
+        number_codes(Term, Int)
+      ;
+        pyvar(i, Term, Int)
+      )
+    },
+    Int.
+
+term_to_python(list(Term)) --> list_to_python(Term).
+
+term_to_python(Term) --> Term.
+
+
+list_to_python(Term) -->
+    { pyvar(s, Term, Var) }, !, Var.
+
+list_to_python([]) --> "()", !.
+
+list_to_python([Term|Tail]) -->
+    "(", term_to_python(Term), ", ", list_to_python(Tail), ")".
+
+
+
+/*
+
+Generate Python code.
+
+ */
+
+
+code_gen([Head|Tail]) --> Head, code_gen(Tail).
+code_gen([]) --> [].
+
+cg, Term --> [Term], cg.
+cg --> [].
+
+compile_fn(Name) --> gronk_fn(Name), cg, !.
+
+
+
+
+/*
+
+
+ ██████╗ ██████╗  ██████╗ ███╗   ██╗██╗  ██╗
+██╔════╝ ██╔══██╗██╔═══██╗████╗  ██║██║ ██╔╝
+██║  ███╗██████╔╝██║   ██║██╔██╗ ██║█████╔╝
+██║   ██║██╔══██╗██║   ██║██║╚██╗██║██╔═██╗
+╚██████╔╝██║  ██║╚██████╔╝██║ ╚████║██║  ██╗
+ ╚═════╝ ╚═╝  ╚═╝ ╚═════╝ ╚═╝  ╚═══╝╚═╝  ╚═╝
+
+With gronk we're juggling four things:
+
+    The incoming joy expression
+    The outgoing code tokens (for the code gen)
+    The incoming stack representation
+    and outgoing stack representation
+
+The basic formula is like so (the indent level is an implementation
+detail):
+
+gronk_fn_body(
+    [joy expression]
+    StackIn,
+    StackOut,
+    [code gen tokens]
+    ).
+
+(Let's leave out DCGs for now, eh?  Since I don't actually know how they
+work really yet, do I?  ;P )
+
+*/
+
+gronk_fn(Name, Expr, CodeGens)
+    :-
+    CodeGens = ["def ", Name,"(stack, expression, dictionary):", nl,
+                    tab, stack_to_python(StackIn), " = stack", nl|Cs],
+    CGTail = [tab, "return ", stack_to_python(StackOut), ", expression, dictionary", nl],
+    reset_gensym(s), reset_gensym(v), reset_gensym(i),
+    gronk_fn_list(Expr, StackIn, StackOut, CGTail, Cs, 1).
+
+
+gronk_fn_list(
+    [list(BodyFalse), list(BodyTrue), symbol(branch)|Js],
+    [bool(B)|StackIn],
+    StackOut,
+    CGTail,
+    CodeGens,
+    IndentLevel)
+    :-
+    !,
+    J #= IndentLevel + 1,
+    CodeGens = [
+        tabs(IndentLevel), "if ", term_to_python(B), ":", nl|Cs0],
+    True =  [tabs(J), stack_to_python(Stack), " = ", stack_to_python(StackT), nl,
+        tabs(IndentLevel), "else:", nl|Cs1],
+    False = [tabs(J), stack_to_python(Stack), " = ", stack_to_python(StackF), nl|Ck],
+    gronk_fn_list(BodyTrue, StackIn, StackT, True, Cs0, J),
+    gronk_fn_list(BodyFalse, StackIn, StackF, False, Cs1, J),
+    gronk_fn_list(Js, Stack, StackOut, CGTail, Ck, IndentLevel).
+
+gronk_fn_list(
+    [list(Body), symbol(loop)|Js],
+    [bool(B)|StackIn],
+    StackOut,
+    CGTail,
+    CodeGens,
+    IndentLevel)
+    :-
+    !,
+    J #= IndentLevel + 1,
+    CodeGens = [
+        tabs(IndentLevel), term_to_python(Tos), " = ", term_to_python(B), nl,
+        tabs(IndentLevel), "while ", term_to_python(Tos), ":", nl|Cs
+        ],
+    gronk_fn_list(Body, StackIn, [bool(Tos)|Stack], [tabs(J), stack_to_python(StackIn), " = ", stack_to_python(Stack), nl|Ck], Cs, J),
+    gronk_fn_list(Js, StackIn, StackOut, CGTail, Ck, IndentLevel).
+                    % ^^^^^^^  wha!? not Stack!?
+
+gronk_fn_list(
+    [list(Body), symbol(dip)|Js],
+    [Term|StackIn],
+    StackOut,
+    CGTail,
+    Cs,
+    IndentLevel)
+    :-
+    !,
+    gronk_fn_list(Body,      StackIn,    Stack,     Ck, Cs, IndentLevel),
+    gronk_fn_list(Js,   [Term|Stack], StackOut, CGTail, Ck, IndentLevel).
+
+gronk_fn_list(
+    [list(Body), symbol(step)|Js],
+    [list(B)|Stack0],
+    Stack,
+    CGTail,
+    CodeGens,
+    IndentLevel)
+    :-
+    !,
+    J #= IndentLevel + 1,
+    CodeGens = [
+        tabs(IndentLevel), stack_to_python(Stack1), " = ", stack_to_python(Stack0), nl,
+        tabs(IndentLevel), "while ", term_to_python(B), ":", nl,
+            tabs(J), "(", term_to_python(T), ", ", term_to_python(B), ") = ", term_to_python(B), nl|CG2
+        ],
+    CG1 = [tabs(J), stack_to_python(Stack1), " = ", stack_to_python(Stack2), nl|CG0],
+    gronk_fn_list(Body, [T|Stack1], Stack2, CG1, CG2, J),
+    gronk_fn_list(Js, Stack1, Stack, CGTail, CG0, IndentLevel).
+
+gronk_fn_list(
+    [symbol(abs)|Js],
+    [In|StackIn],
+    StackOut,
+    CGTail,
+    [tabs(IndentLevel), term_to_python(Out), " = abs(", term_to_python(In), ")", nl|Cs],
+    IndentLevel)
+    :-
+    !,  % green cut
+    gronk_fn_list(Js, [Out|StackIn], StackOut, CGTail, Cs, IndentLevel).
+
+gronk_fn_list(
+    [symbol(bool)|Js],
+    [In|StackIn],
+    StackOut,
+    CGTail,
+    [tabs(IndentLevel), term_to_python(Out), " = bool(", term_to_python(In), ")", nl|Cs],
+    IndentLevel)
+    :-
+    !,  % green cut
+    gronk_fn_list(Js, [bool(Out)|StackIn], StackOut, CGTail, Cs, IndentLevel).
+
+gronk_fn_list(
+    [symbol(stack)|Js],
+    StackIn,
+    StackOut,
+    CGTail,
+    [tabs(IndentLevel), stack_to_python(Stack), " = (", stack_to_python(StackIn), ", ", stack_to_python(StackIn), ")", nl|Cs],
+    IndentLevel)
+    :-
+    !,  % green cut
+    gronk_fn_list(Js, Stack, StackOut, CGTail, Cs, IndentLevel).
+
+gronk_fn_list(
+    [symbol(swaack)|Js],
+    [list(S)|StackIn],
+    StackOut,
+    CGTail,
+    % [tabs(IndentLevel), "pass", nl|Cs],
+    [tabs(IndentLevel), stack_to_python(Stack), " = (", stack_to_python(StackIn), ", ", stack_to_python(S), ")", nl|Cs],
+    IndentLevel)
+    :-
+    !,  % green cut
+    gronk_fn_list(Js, Stack, StackOut, CGTail, Cs, IndentLevel).
+
+gronk_fn_list(
+    [symbol(Sym)|Js],
+    [int(B), int(A)|StackIn],
+    StackOut,
+    CGTail,
+    [tabs(IndentLevel), term_to_python(int(C)), " = ", term_to_python(int(A)), Op, term_to_python(int(B)), nl|Cs],
+    IndentLevel)
+    :-
+    bin_math_op(Sym, Op), !,  % green cut
+    gronk_fn_list(Js, [int(C)|StackIn], StackOut, CGTail, Cs, IndentLevel).
+
+gronk_fn_list(
+    [symbol(Sym)|Js],
+    [int(B), int(A)|StackIn],
+    StackOut,
+    CGTail,
+    [tabs(IndentLevel), term_to_python(bool(C)), " = ", term_to_python(int(A)), Op, term_to_python(int(B)), nl|Cs],
+    IndentLevel)
+    :-
+    bin_bool_op(Sym, Op), !,  % green cut
+    gronk_fn_list(Js, [bool(C)|StackIn], StackOut, CGTail, Cs, IndentLevel).
+
+gronk_fn_list([symbol(Sym)|Js], S0, S, C0, C, IndentLevel) :-
+    yin(Sym),
+    func(Sym, S0, S1), !,  % green cut
+    gronk_fn_list(Js, S1, S, C0, C, IndentLevel).
+
+gronk_fn_list([symbol(Sym)|Js], S0, S, C0, C, IndentLevel) :-
+    yin(Sym),
+    def(Sym, Body), !,  % green cut
+    append(Body, Js, Expr),
+    gronk_fn_list(Expr, S0, S, C0, C, IndentLevel).
+
+gronk_fn_list([bool(true)|Js], S0, S, C0, C, IndentLevel) :- !,  % green cut
+    gronk_fn_list(Js, [bool("True")|S0], S, C0, C, IndentLevel).
+
+gronk_fn_list([bool(false)|Js], S0, S, C0, C, IndentLevel) :- !,  % green cut
+    gronk_fn_list(Js, [bool("False")|S0], S, C0, C, IndentLevel).
+
+gronk_fn_list([int(I)|Js], S0, S, C0, C, IndentLevel) :- !,  % green cut
+    gronk_fn_list(Js, [int(I)|S0], S, C0, C, IndentLevel).
+
+gronk_fn_list([list(L)|Js], S0, S, C0, C, IndentLevel) :- !,  % green cut
+    gronk_fn_list(Js, [list(L)|S0], S, C0, C, IndentLevel).
+
+gronk_fn_list([], Stack, Stack, Cs, Cs, _).
+
+
+bin_math_op(+, " + ").
+bin_math_op(-, " - ").
+bin_math_op(*, " * ").
+bin_math_op(div, " // ").
+bin_math_op( / , " // ").
+bin_math_op(mod, " % ").
+bin_math_op('%', " % ").
+
+bin_bool_op(>, " > ").
+bin_bool_op(<, " < ").
+bin_bool_op(=, " == ").
+bin_bool_op(>=, " >= ").
+bin_bool_op(<=, " <= ").
+bin_bool_op(<>, " != ").
+
+yin(bool).
+yin(cons).
+yin(dip).
+yin(dup).
+yin(dupd).
+yin(dupdd).
+yin(first).
+yin(gcd).
+yin(over).
+yin(pop).
+yin(product).
+yin(rest).
+yin(rolldown).
+yin(rollup).
+yin(shift).
+yin(step).
+yin(stackd).
+yin(sum).
+yin(swap).
+yin(tuck).
+yin(uncons).
+yin(unit).
+yin(Sym) :- def(Sym, Body), maplist(yins, Body).
+
+yins(int(_)).
+yins(bool(_)).
+yins(list(_)).
+
+yins(symbol(Sym)) :- yin(Sym).
+yins(symbol(Sym)) :- bin_math_op(Sym, _).
+yins(symbol(Sym)) :- bin_bool_op(Sym, _).
+
+
+/*
+concat
+flatten
+swaack
+clear
+bool+
+
+list ops (empty? list? ...)
+logic ops (and or ...)
+
+COMBINATORS
+
+ */
+
+
+gronk(Name, BodyText) :-
+    text_to_expression(BodyText, Expr),
+    gronk_fn(Name, Expr, Out),
+    code_gen(Out, A, []), !,
+    string_codes(S, A),
+    writeln(""),
+    writeln(S).
+
+
+
+
+
+
+do :-
+    gronk("abs", `abs`),
+    gronk("ccons", `ccons`),
+    gronk("cons", `cons`),
+    gronk("decr", `--`),
+    gronk("dup", `dup`),
+    gronk("dupd", `dupd`),
+    gronk("dupdd", `dupdd`),
+    gronk("first", `first`),
+    gronk("fourth", `fourth`),
+    gronk("incr", `++`),
+    gronk("non_negative", `!-`),
+    gronk("pop", `pop`),
+    gronk("popd", `popd`),
+    gronk("popop", `popop`),
+    gronk("popopd", `popopd`),
+    gronk("quoted", `quoted`),
+    gronk("reco", `reco`),
+    gronk("rest", `rest`),
+    gronk("rrest", `rrest`),
+    gronk("second", `second`),
+    gronk("shift", `shift`),
+    gronk("sqr", `sqr`),
+    gronk("stackd", `stackd`),  % Compiling func(stackd, ...) doesn't work.
+    gronk("swons", `swons`),
+    gronk("third", `third`),
+    gronk("truthy", `?`),
+    gronk("tuckl", `<{}`),
+    gronk("tuckld", `<<{}`),
+    gronk("uncons", `uncons`),
+    gronk("unit", `unit`),
+    gronk("unswons", `unswons`),
+    gronk("gcd", `gcd`),
+    gronk("sum", `sum`),
+    gronk("product", `product`),
+    writeln("").
diff --git a/implementations/Prolog/source/joy_defs.dot b/implementations/Prolog/source/joy_defs.dot
new file mode 100644
index 0000000..981b8d0
--- /dev/null
+++ b/implementations/Prolog/source/joy_defs.dot
@@ -0,0 +1,46 @@
+digraph joy_defs {
+    and -> unit;
+    and -> duo;
+    app2 -> ii;
+    app2 -> cons;
+    app2 -> infrst;
+    app2 -> dip;
+    app2 -> swap;
+    app2 -> grba;
+    b -> dip;
+    b -> i;
+    cleave -> popdd;
+    cleave -> fork;
+    clop -> popdd;
+    clop -> cleave;
+    duo -> cons;
+    duo -> unit;
+    fork -> app2;
+    fork -> i;
+    grba -> dip;
+    grba -> popd;
+    grba -> stack;
+    ii -> i;
+    ii -> dupdip;
+    ii -> dip;
+    infra -> dip;
+    infra -> i;
+    infra -> swaack;
+    infra -> swons;
+    infrst -> first;
+    infrst -> infra;
+    or -> duo;
+    or -> ii;
+    or -> unit;
+    popd -> dip;
+    popd -> pop;
+    popdd -> dipd;
+    popdd -> pop;
+    swons -> cons;
+    swons -> swap;
+    xor -> duo;
+    xor -> clop;
+    xor -> swap;
+    xor -> cons;
+    xor -> ii;
+}
diff --git a/implementations/Prolog/source/joy_defs.png b/implementations/Prolog/source/joy_defs.png
new file mode 100644
index 0000000000000000000000000000000000000000..97ebf9f0dcac60a972a83c2a80387b5180af5823
GIT binary patch
literal 176538
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 IZ?pA(0HpUtg8%>k literal 0 HcmV?d00001 diff --git a/implementations/Prolog/source/more-notes.txt b/implementations/Prolog/source/more-notes.txt new file mode 100644 index 0000000..824398a --- /dev/null +++ b/implementations/Prolog/source/more-notes.txt @@ -0,0 +1,114 @@ +?- gronk("fn", `stackd`). + +def fn(stack, expression, dictionary): + (v1, stack) = stack + return (v1, ((), stack)), expression, dictionary + +using the func/3 + +func(stackd, [A|B], [A, list(B)|B]). + + +However, compiling with + +gronk_fn_list([list(Body), symbol(dip)|Js], ... + +we get + +?- gronk("fn", `stackd`). + +def fn(stack, expression, dictionary): + (v1, stack) = stack + stack = (stack, stack) + return (v1, stack), expression, dictionary + + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + +What would "[dup] cons dip" compile to? + + + def fn(stack, expression, dictionary): + (v1, (v2, stack)) = stack + return (v2, (v1, (v1, stack))), expression, dictionary + +E.g.: + + ?- sjc(fn, `[dup] cons dip`). + func(fn, [B, A|C], [A, B, B|C]). + true . + +Hmm... + + + +/* +?- gronk("fn", `[+] step`). + + def fn(stack, expression, dictionary): + (s1, (i1, stack)) = stack + while s1: + (i2, s1) = s1 + i1 = i1 + i2 + return (i1, stack), expression, dictionary + + +So just the above works great, but initializing it with a zero leads to +BS: + +?- gronk("fn", `0 swap [+] step`). + + def fn(stack, expression, dictionary): + (s1, stack) = stack + while s1: + (i1, s1) = s1 + 0 = 0 + i1 + return (0, stack), expression, dictionary + + +We want something like this: + + def fn(stack, expression, dictionary): + (s1, stack) = stack + v1 = 0 + while s1: + (i1, s1) = s1 + v1 = v1 + i1 + return (v1, stack), expression, dictionary + + +Hmmm.... + + + + */ + + + +/* + +?- gronk("swaack", `swaack`). + +def swaack(stack, expression, dictionary): + (s1, stack) = stack + stack = (stack, s1) + return stack, expression, dictionary + +true. + +?- gronk("swaack", `[swaack] dip`). + +def swaack(stack, expression, dictionary): + (v1, (s1, stack)) = stack + stack = (stack, s1) + return (v1, stack), expression, dictionary + +true. + + +*/ + +['C:/Users/sforman/Desktop/src/PROLOG/Thun/source/thun.pl']. +['C:/Users/sforman/Desktop/src/PROLOG/Thun/source/joy2py.pl']. + +@command:editor.action.selectToBracket \ No newline at end of file diff --git a/implementations/Prolog/source/nerdsniped.txt b/implementations/Prolog/source/nerdsniped.txt new file mode 100644 index 0000000..99f4271 --- /dev/null +++ b/implementations/Prolog/source/nerdsniped.txt @@ -0,0 +1,62 @@ + + +https://c9x.me/notes/2019-01-15.html + + uint32_t mulinv(uint32_t a) { + uint32_t b = a; /* 1/a mod 2² */ + b *= 2 - a*b; /* 1/a mod 2⁴ */ + b *= 2 - a*b; /* 1/a mod 2⁸ */ + b *= 2 - a*b; /* 1/a mod 2¹⁶ */ + b *= 2 - a*b; /* 1/a mod 2³² */ + return b; + } + +In Joy: + + + b *= 2 - a*b + + b = b * (2 - a*b) + + + + a 2 a b * - b * + a 2 a*b - b * + a 2-(a*b) b * + a b*(2-(a*b)) + + a b over over + a b a b [* 2 swap -] dip * + a b a * 2 swap - b * + a b*a 2 swap - b * + a 2 b*a - b * + a 2-b*a b * + a (2-b*a)*b + + G == over over [* 2 swap -] dip * + mulinv == dup 5 [G] times popd + +Can compile G (mulinv must wait on times.) + + ?- gronk("fn", `over over [* 2 swap -] dip *`). + + def fn(stack, expression, dictionary): + (i1, (i2, stack)) = stack + i3 = i1 * i2 + i4 = 2 - i3 + i5 = i4 * i1 + return (i5, (i2, stack)), expression, dictionary + + + +Using Unary + + a b [F] dupdip * + a b F b * + a b * 2 swap - b * + + + G == [* 2 swap -] dupdip * + mulinv == dup 5 [[G] unary] times popd + +Bleah. \ No newline at end of file diff --git a/implementations/Prolog/source/notes.txt b/implementations/Prolog/source/notes.txt new file mode 100644 index 0000000..9c0dfcb --- /dev/null +++ b/implementations/Prolog/source/notes.txt @@ -0,0 +1,317 @@ +By using LoF to represent values all operations are effectively binary +digital circuits. So this is a way to model hardware by orchestrating it +with Joy code. For example, an 8-bit integer zero could be represented +as [[][][][][][][][]] and so on: + + [ [] [] [] [] [] [] [] [] ] 0 + [ [] [] [] [] [] [] [] [[]]] 1 + [ [] [] [] [] [] [] [[]] [] ] 2 + [ [] [] [] [] [] [] [[]][[]]] 3 + +Treating [] as zero and [[]] as one. + + +Sum = a xor b xor c +Carry = (b and a) or (c and (b xor a)) + +Sum = a ⊕ b ⊕ c +Carry = (b ∧ a) ∨ (c ∧ (b ⊕ a)) + +∧∨⊕ + +def full_bit_adder(a, b, c): + '''Based on the definitions from Wikipedia.''' + return ( + simplify(xor(xor(a, b), c)), + simplify(or_(and_(a, b), and_(c, xor(a, b)))), + ) + +c b a + [xor xor void] + [[and] [xor and] fork or void] + clop popdd + + +c b a [and] [xor and] clop +c (b and a) (c and (b xor a)) or +c ((b and a) or (c and (b xor a))) + + +fba == [xor xor void] [[and] [xor and] fork or void] clop popdd + + +So we have a full-bit adder (with carry in and out), it's a trinary +function with binary output. + + + carry b a fba + ------------------- + carry' (a+b) + + +Now we need a function that takes two 8-bit "numbers" and a carry bit and +returns a single 8-bit number with the carry bit out. + + + carry [.b.] [.a.] + + ------------------------- + carry' [a+b] + + +The first thing that comes to my mind as a "2step" combinator: + + + [b ...] [a ...] [F] 2step + --------------------------------- + b a F [,,,] [,,,] [F] 2step + +And so on; if lists aren't the same length...? + +Could just zip the tho lists and use step. Then merge zip and the step +form? + +I think you would still want to define zip in terms of 2step. + +zipF == <<{} [unswons] dip uncons [duo swap [cons] dip] dip + +How's that work: + + [b ...] [a ...] <<{} [unswons] dip uncons [duo swap [cons] dip] dip +[] [b ...] [a ...] [unswons] dip uncons [duo swap [cons] dip] dip +[] [b ...] unswons [a ...] uncons [duo swap [cons] dip] dip +[] [...] b [a ...] uncons [duo swap [cons] dip] dip +[] [...] b a [...] [duo swap [cons] dip] dip +[] [...] b a duo swap [cons] dip [...] +[] [...] [b a] swap [cons] dip [...] +[] [b a] [...] [cons] dip [...] + Opps! swons! +[] [b a] [...] [swons] dip [...] +[] [b a] swons [...] [...] +[[b a]] [...] [...] + +[[b a]] [...] [...] + +The other bug in this is that you wind up with the pairs in the new list +in reverse order from the original lists. Time to review recursion +combinators... + +Or, just collect the bits at the end? + + c8b == [] [[[[[[[[[]]]]]]]]] [cons] times + +To deal with the Carry bit let it ride at TOS then grab the bits under it +and swap to get the result byte above the carry bit. + + fin == [c8b] dip swap + +Or rewrite fba to have carry on tos? + + c [b ...] [a ...] [uncons] dip unswons rolldown + c [b ...] uncons [a ...] unswons rolldown + c b [...] [a ...] unswons rolldown + c b [...] [...] a rolldown + c b a [...] [...] + +Okay, then: + + c b a [...] [...] [fba swap] dipd + c b a fba swap [...] [...] + c' a+b swap [...] [...] + a+b c' [...] [...] + + +So (using ints as shorthand for Peano numbers): + + F == [uncons] dip unswons rolldown [fba swap] dipd + c8b == [] 8 [cons] times + + == 8 [F] times popop [c8b] dip swap + +Note the function that uncons's a pair from two lists compiles to a +primitive nicely: + + ?- sjc(uncons-pair, `[uncons] dip unswons rolldown`). + func(uncons-pair, [list([C|A]), list([D|B])|E], [list(A), list(B), C, D|E]). + + + uncons-pair == [uncons] dip unswons rolldown + F == uncons-pair [fba swap] dipd + c8b == [] 8 [cons] times + + == 8 [F] times popop [c8b] dip swap + +Also c8b: + + ?- sjc(foo, `[] 8 [cons] times`). + func(foo, [H, G, F, E, D, C, B, A|I], [list([A, B, C, D, E, F, G, H])|I]). + + + sjc(foo, `[[] 8 [cons] times] dip swap`). + + func(foo, [int(I), H, G, F, E, D, C, B, A|J], [list([A, B, C, D, E, F, G, H]), int(I)|J]). + +Hmm... (I'm using main thun, it hallucinates literals...) Easy enough +to fix manually... + + func(foo, [I, H, G, F, E, D, C, B, A|J], [list([A, B, C, D, E, F, G, H]), I|J]). + +But it would be nice to figure out exactly why it can't hallucinate the +most general case (first) and make it do that. + +- - - - + +THe other thing to do would be to build up the formulas with (atoms) vars +for the input signals and then use LoF simplification to precompute +formulas for each output bit (including carry) and the write one function +that directly builds the output byte and carry from the input byte and +carry by direct unification in Prolog and then run `cons [void] map +uncons` on it to reduce the formulas. + + +- - - - + + +Sum = a xor b xor c +Carry = (b and a) or (c and (b xor a)) + +Sum = a ⊕ b ⊕ c +Carry = (b ∧ a) ∨ (c ∧ (b ⊕ a)) + +∧∨⊕¬∥¿ + +c b a + [⊕ ⊕ ¿] + [[∧] [⊕ ∧] ∥ ∨ ¿] + ∥ppp + + +∥ = [i] app2 +∥p == ∥ popdd +∥pp == ∥p popdd +∥ppp == ∥pp popdd + + +cleave == fork popdd +clop == cleave popdd +clopp == clop popdd + + + + +[⊕ ⊕ ¿] [[∧] [⊕ ∧] ∥ ∨ ¿] ∥ppp + + +- - - - + +list\(([^)]+)\) + +or == [unit] ii unit cons +and == unit cons unit +not == unit + +or == [not] ii not cons +and == not cons not + +xor == [unit unit cons] [swap unit unit cons] cleave unit cons + + +------------------------------ + + +Messing about with binary Boolean semantics and the Joy programming +language, implementing a full-bit adder. + +https://en.wikipedia.org/wiki/Adder_(electronics)#Full_adder + +> A full adder adds binary numbers and accounts for values carried in as +well as out. A one-bit full-adder adds three one-bit numbers, often +written as A, B, and Cin; A and B are the operands, and Cin is a bit +carried in from the previous less-significant stage. + +As logical expression with operators: + + sum = a xor b xor c + carry = (b and a) or (c and (b xor a)) + +Replace the words with common symbols (APL envy?) + + sum = a ⊕ b ⊕ c + carry = (b ∧ a) ∨ (c ∧ (b ⊕ a)) + +As Python psuedo-code: + + def full_bit_adder(a, b, c): + return ( + simplify(xor(xor(a, b), c)), + simplify(or_(and_(a, b), and_(c, xor(a, b)))), + ) + +As Joy: + + [xor xor simplify] + [[and] [xor and] fork or simplify] + clop popdd + +With cute symbols: + + [⊕ ⊕ ¿] [[∧] [⊕ ∧] ∥ ∨ ¿] ∥ppp + +Factoring out the simplify function (represented by '¿'): + + [⊕ ⊕] [[∧] [⊕ ∧] ∥ ∨] ∥ppp [¿] ii + +One thing to note, the parentheses in the equational and Python forms are +encoding operator precedence while the square brackets in the Joy +expression encode the control-flow-independence of the sub-functions of +the main function, they *quote* the sub-subfunctions so they can be +arguments to the '∥' and '∥ppp' concurrency combinators. + +- - - - + +"Selfie" as an alternate target to Oberon. + +https://selfie.cs.uni-salzburg.at/ +https://news.ycombinator.com/item?id=22427189 +https://github.com/cksystemsteaching/selfie/blob/master/semantics.md + + +- - - - + +https://www.geoffreylitt.com/wildcard/salon2020/ + +Wildcard: Spreadsheet-Driven Customization of Web Applications +By Geoffrey Litt and Daniel Jackson + + + +https://edtr.io/ +https://news.ycombinator.com/item?id=22451568 + +https://handsontable.com/ +JavaScript data grid that looks and feels like a spreadsheet. + +https://www.tampermonkey.net/ +Tampermonkey is a userscript manager + + + +https://www.cs.kent.ac.uk/people/staff/dat/miranda/ +https://en.wikipedia.org/wiki/Miranda_(programming_language) +https://news.ycombinator.com/item?id=22447185 + + + +https://ncatlab.org/nlab/show/differentiation +file:///C:/Users/sforman/Desktop/FooNolder/1803.10228.pdf +Demystifying Differentiable Programming:Shift/Reset the Penultimate Backpropagator +https://news.ycombinator.com/item?id=22343285 + + +Domain Modelling made Functional +https://www.youtube.com/watch?v=Up7LcbGZFuo + + + + +https://langserver.org/ +https://news.ycombinator.com/item?id=22442133 + + diff --git a/implementations/Prolog/source/thun.pl b/implementations/Prolog/source/thun.pl new file mode 100644 index 0000000..462d4d3 --- /dev/null +++ b/implementations/Prolog/source/thun.pl @@ -0,0 +1,2817 @@ +/* + +████████╗██╗ ██╗██╗ ██╗███╗ ██╗ +╚══██╔══╝██║ ██║██║ ██║████╗ ██║ + ██║ ███████║██║ ██║██╔██╗ ██║ + ██║ ██╔══██║██║ ██║██║╚██╗██║ + ██║ ██║ ██║╚██████╔╝██║ ╚████║ + ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ + +A dialect of Joy. Version -10.0.0. + + Copyright © 2018, 2019, 2020 Simon Forman + + This file is part of Thun + + Thun is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Thun is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Thun. If not see . + +(Big fonts are from Figlet "ANSI Shadow" http://www.patorjk.com/software/taag/#p=display&f=ANSI%20Shadow&t=formatter and "Small".) + +Thun is an implementation of a dialect of the Joy executable notation. + +Table of Contents + Parser & Grammar + Semantics + Functions + Combinators + Definitions + Compiler + to Prolog + to Machine Code + Meta-Programming + Expand/Contract Definitions + Formatter + Partial Reducer + + */ + +:- use_module(library(clpfd)). +:- use_module(library(dcg/basics)). +:- use_module(library(gensym)). +:- dynamic func/3. +:- dynamic def/2. + + +/* +An entry point. +*/ + +joy(InputString, StackIn, StackOut) :- + text_to_expression(InputString, Expression), + !, + thun(Expression, StackIn, StackOut). + +/* + +██████╗ █████╗ ██████╗ ███████╗███████╗██████╗ ██╗ +██╔══██╗██╔══██╗██╔══██╗██╔════╝██╔════╝██╔══██╗ ██║ +██████╔╝███████║██████╔╝███████╗█████╗ ██████╔╝ ████████╗ +██╔═══╝ ██╔══██║██╔══██╗╚════██║██╔══╝ ██╔══██╗ ██╔═██╔═╝ +██║ ██║ ██║██║ ██║███████║███████╗██║ ██║ ██████║ +╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ ╚═════╝ + + ██████╗ ██████╗ █████╗ ███╗ ███╗███╗ ███╗ █████╗ ██████╗ +██╔════╝ ██╔══██╗██╔══██╗████╗ ████║████╗ ████║██╔══██╗██╔══██╗ +██║ ███╗██████╔╝███████║██╔████╔██║██╔████╔██║███████║██████╔╝ +██║ ██║██╔══██╗██╔══██║██║╚██╔╝██║██║╚██╔╝██║██╔══██║██╔══██╗ +╚██████╔╝██║ ██║██║ ██║██║ ╚═╝ ██║██║ ╚═╝ ██║██║ ██║██║ ██║ + ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ + +The grammar of Joy is very simple. A Joy expression is zero or more Joy +terms (separated by blanks, see below) and terms can be +integers, Booleans, quoted Joy expressions, or symbols (names of +functions.) + + joy ::= term* + + term ::= integer | bool | '[' joy ']' | symbol + + integer ::= [ '-' | '+' ] ('0'...'9')+ + bool ::= 'true' | 'false' + symbol ::= char+ + + char ::= + +There are a few wrinkles in the handling of blank space between terms +because we want to be able to omit it around brackets: + +Valid expressions: + + 1 2 3 + 1[2]3 + 1 [ 2 ] 3 + true + truedat (a symbol prefixed with the name of a boolean) + +Invalid: + + 12three (symbols can't start with numbers, and this shouldn't parse + as [12 three].) + +Symbols can be made of any non-blank characters except '['and ']' which +are fully reserved for list literals (aka "quotes"). 'true' and 'false' +would be valid symbols but they are reserved for Boolean literals. + +Integers are converted to Prolog integers, symbols and bools to Prolog +atoms, and list literals to Prolog lists. + +For now strings are neglected in favor of lists of numbers. (But there's +no support for parsing string notation and converting to lists of ints.) + +First lex the stream of codes into tokens separated by square brackets +or whitespace. We keep the brackets and throw away the blanks. +*/ + +joy_lex([tok(Token)|Ls]) --> chars(Token), !, joy_lex(Ls). +joy_lex([ lbracket|Ls]) --> "[", !, joy_lex(Ls). +joy_lex([ rbracket|Ls]) --> "]", !, joy_lex(Ls). + +joy_lex(Ls) --> [Space], {code_type(Space, space)}, !, joy_lex(Ls). + +joy_lex([]) --> []. + +% Then parse the tokens converting them to Prolog values and building up +% the list structures (if any.) + +joy_parse([J|Js]) --> joy_term(J), !, joy_parse(Js). +joy_parse([]) --> []. + +joy_term(list(J)) --> [lbracket], !, joy_parse(J), [rbracket]. +joy_term(Atomic) --> [tok(Codes)], {joy_token(Atomic, Codes)}. + +joy_token(int(I), Codes) :- number(I, Codes, []), !. % See dcg/basics. +joy_token(bool(true), `true`) :- !. +joy_token(bool(false), `false`) :- !. +joy_token(symbol(S), Codes) :- atom_codes(S, Codes). + + +text_to_expression(Text, Expression) :- + phrase(joy_lex(Tokens), Text), !, + phrase(joy_parse(Expression), Tokens). + +% Apologies for all the (green, I hope) cuts. The strength of the Joy +% syntax is that it's uninteresting. + +chars([Ch|Rest]) --> char(Ch), chars(Rest). +chars([Ch]) --> char(Ch). + +char(Ch) --> [Ch], {Ch \== 0'[, Ch \== 0'], code_type(Ch, graph)}. + + +/* Here is an example of Joy code: + + [ [[abs] ii <=] + [ + [<>] [pop !-] || + ] && + ] + [[ !-] [[++]] [[--]] ifte dip] + [[pop !-] [--] [++] ifte ] + ifte + +It probably seems unreadable but with a little familiarity it becomes +just as legible as any other notation. This function accepts two +integers on the stack and increments or decrements one of them such that +the new pair of numbers is the next coordinate pair in a square spiral +(like that used to construct an Ulam Spiral). It is adapted from the +code in the answer here: + +https://stackoverflow.com/questions/398299/looping-in-a-spiral/31864777#31864777 + +It can be used with the x combinator to make a kind of generator for +spiral square coordinates. + + + +███████╗███████╗███╗ ███╗ █████╗ ███╗ ██╗████████╗██╗ ██████╗███████╗ +██╔════╝██╔════╝████╗ ████║██╔══██╗████╗ ██║╚══██╔══╝██║██╔════╝██╔════╝ +███████╗█████╗ ██╔████╔██║███████║██╔██╗ ██║ ██║ ██║██║ ███████╗ +╚════██║██╔══╝ ██║╚██╔╝██║██╔══██║██║╚██╗██║ ██║ ██║██║ ╚════██║ +███████║███████╗██║ ╚═╝ ██║██║ ██║██║ ╚████║ ██║ ██║╚██████╗███████║ +╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝╚══════╝ + +The fundamental Joy relation involves an expression and two stacks. One +stack serves as input and the other as output. + + thun(Expression, InputStack, OutputStack) + +The null expression (denoted by an empty Prolog list) is effectively an +identity function and serves as the end-of-processing marker. As a +matter of efficiency (of Prolog) the thun/3 predicate picks off the first +term of the expression (if any) and passes it to thun/4 which can then +take advantage of Prolog indexing on the first term of a predicate. */ + +thun([], S, S). +thun([Term|E], Si, So) :- thun(Term, E, Si, So). + +/* The thun/4 predicate was originally written in terms of the thun/3 +predicate, which was very elegant, but prevented (I assume but have not +checked) tail-call recursion. In order to alleviate this, partial +reduction is used to generate the actual thun/4 rules, see below. + +Original thun/4 code: + +thun(int(I), E, Si, So) :- thun(E, [ int(I)|Si], So). +thun(bool(B), E, Si, So) :- thun(E, [bool(B)|Si], So). +thun(list(L), E, Si, So) :- thun(E, [list(L)|Si], So). +thun(symbol(Def), E, Si, So) :- def(Def, Body), append(Body, E, Eo), thun(Eo, Si, So). +thun(symbol(Func), E, Si, So) :- func(Func, Si, S), thun(E, S, So). +thun(symbol(Combo), E, Si, So) :- combo(Combo, Si, S, E, Eo), thun(Eo, S, So). + +Integers, Boolean values, and lists are put onto the stack, symbols are +dispatched to one of three kinds of processing: functions, combinators +and definitions (see "defs.txt".) */ + +thun(A, [], S, [A|S]) :- var(A), !. +thun(A, [T|E], S, So) :- var(A), !, thun(T, E, [A|S], So). + + +% Literals turn out okay. + +thun(int(A), [], B, [int(A)|B]). +thun(int(C), [A|B], D, E) :- thun(A, B, [int(C)|D], E). + +thun(bool(A), [], B, [bool(A)|B]). +thun(bool(C), [A|B], D, E) :- thun(A, B, [bool(C)|D], E). + +thun(list(A), [], B, [list(A)|B]). +thun(list(C), [A|B], D, E) :- thun(A, B, [list(C)|D], E). + +% Partial reduction works for func/3 cases. + +thun(symbol(A), [], B, C) :- func(A, B, C). +thun(symbol(A), [C|D], B, F) :- func(A, B, E), thun(C, D, E, F). + +% Combinators look ok too. + +% thun(symbol(A), D, B, C) :- combo(A, B, C, D, []). +% thun(symbol(A), C, B, G) :- combo(A, B, F, C, [D|E]), thun(D, E, F, G). + +% However, in this case, I think the original version will be more +% efficient. + +thun(symbol(Combo), E, Si, So) :- combo(Combo, Si, S, E, Eo), thun(Eo, S, So). + +% In the reduced rules Prolog will redo all the work of the combo/5 +% predicate on backtracking through the second rule. It will try +% combo/5, which usually won't end in Eo=[] so the first rule fails, then +% it will try combo/5 again in the second rule. In the original form +% after combo/5 has completed Prolog has computed Eo and can index on it +% for thun/3. +% +% Neither functions nor definitions can affect the expression so this +% consideration doesn't apply to those rules. The unification of the +% head clauses will distinguish the cases for them. + +% Definitions don't work though (See "Partial Reducer" section below.) +% I hand-wrote the def/3 cases here. + +thun(symbol(D), [], Si, So) :- def(D, [DH| E]), thun(DH, E, Si, So). +thun(symbol(D), [H|E0], Si, So) :- def(D, [DH|DE]), + append(DE, [H|E0], E), /* ................. */ thun(DH, E, Si, So). + +% Partial reduction has been the subject of a great deal of research and +% I'm sure there's a way to make definitions work, but it's beyond the +% scope of the project at the moment. It works well enough as-is that I'm +% happy to manually write out two rules by hand. + +% Some error handling. + +thun(symbol(Unknown), _, _, _) :- + \+ def(Unknown, _), + \+ func(Unknown, _, _), + \+ combo(Unknown, _, _, _, _), + write("Unknown: "), + writeln(Unknown), + fail. + +/* + +███████╗██╗ ██╗███╗ ██╗ ██████╗████████╗██╗ ██████╗ ███╗ ██╗███████╗ +██╔════╝██║ ██║████╗ ██║██╔════╝╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝ +█████╗ ██║ ██║██╔██╗ ██║██║ ██║ ██║██║ ██║██╔██╗ ██║███████╗ +██╔══╝ ██║ ██║██║╚██╗██║██║ ██║ ██║██║ ██║██║╚██╗██║╚════██║ +██║ ╚██████╔╝██║ ╚████║╚██████╗ ██║ ██║╚██████╔╝██║ ╚████║███████║ +╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ + +*/ + +func(words, S, [Words|S]) :- words(Words). + +func(swap, [A, B|S], [B, A|S]). +func(dup, [A|S], [A, A|S]). +func(pop, [_|S], S ). + +func(cons, [list(A), B |S], [list([B|A])|S]). +func(concat, [list(A), list(B)|S], [list(C)|S]) :- append(B, A, C). +func(flatten, [list(A)|S], [list(B)|S]) :- flatten(A, B). +func(swaack, [list(R)|S], [list(S)|R]). +func(stack, S , [list(S)|S]). +func(clear, _ , []). +func(first, [list([X|_])|S], [ X |S]). +func(rest, [list([_|X])|S], [list(X)|S]). +func(unit, [X|S], [list([X])|S]). + +func(rolldown, [A, B, C|S], [B, C, A|S]). +func(dupd, [A, B|S], [A, B, B|S]). +func(over, [A, B|S], [B, A, B|S]). +func(tuck, [A, B|S], [A, B, A|S]). +func(dupdd, [A, B, C|D], [A, B, C, C|D]). + +func(shift, [list([B|A]), list(C)|D], [list(A), list([B|C])|D]). + +func(rollup, Si, So) :- func(rolldown, So, Si). +func(uncons, Si, So) :- func(cons, So, Si). + +func(bool, [ int(0)|S], [bool(false)|S]). +func(bool, [ list([])|S], [bool(false)|S]). +func(bool, [bool(false)|S], [bool(false)|S]). + +func(bool, [ int(N)|S], [bool(true)|S]) :- N #\= 0. +func(bool, [list([_|_])|S], [bool(true)|S]). +func(bool, [ bool(true)|S], [bool(true)|S]). +% func(bool, [A|S], [bool(true)|S]) :- \+ func(bool, [A], [bool(false)]). + +func('empty?', [ list([])|S], [ bool(true)|S]). +func('empty?', [ list([_|_])|S], [bool(false)|S]). + +func('list?', [ list(_)|S], [ bool(true)|S]). +func('list?', [ bool(_)|S], [bool(false)|S]). +func('list?', [ int(_)|S], [bool(false)|S]). +func('list?', [symbol(_)|S], [bool(false)|S]). + +func('one-or-more?', [list([_|_])|S], [ bool(true)|S]). +func('one-or-more?', [ list([])|S], [bool(false)|S]). + +func(and, [bool(true), bool(true)|S], [ bool(true)|S]). +func(and, [bool(true), bool(false)|S], [bool(false)|S]). +func(and, [bool(false), bool(true)|S], [bool(false)|S]). +func(and, [bool(false), bool(false)|S], [bool(false)|S]). + +func(or, [bool(true), bool(true)|S], [ bool(true)|S]). +func(or, [bool(true), bool(false)|S], [ bool(true)|S]). +func(or, [bool(false), bool(true)|S], [ bool(true)|S]). +func(or, [bool(false), bool(false)|S], [bool(false)|S]). + +func( + , [int(A), int(B)|S], [int(A + B)|S]). +func( - , [int(A), int(B)|S], [int(B - A)|S]). +func( * , [int(A), int(B)|S], [int(A * B)|S]). +func( / , [int(A), int(B)|S], [int(B div A)|S]). +func('%', [int(A), int(B)|S], [int(B mod A)|S]). +% func( + , [int(A), int(B)|S], [int(C)|S]) :- C #= A + B. +% func( - , [int(A), int(B)|S], [int(C)|S]) :- C #= B - A. +% func( * , [int(A), int(B)|S], [int(C)|S]) :- C #= A * B. +% func( / , [int(A), int(B)|S], [int(C)|S]) :- C #= B div A. +% func('%', [int(A), int(B)|S], [int(C)|S]) :- C #= B mod A. + +func('/%', [int(A), int(B)|S], [int(B div A), int(B mod A)|S]). +func( pm , [int(A), int(B)|S], [int(A + B), int(B - A)|S]). +% func('/%', [int(A), int(B)|S], [int(C), int(D)|S]) :- C #= B div A, D #= B mod A. +% func( pm , [int(A), int(B)|S], [int(C), int(D)|S]) :- C #= A + B, D #= B - A. + +func(>, [int(A), int(B)|S], [ bool(B > A)|S]). +func(<, [int(A), int(B)|S], [ bool(B < A)|S]). +func(=, [int(A), int(B)|S], [ bool(eq(B, A))|S]). +func(>=, [int(A), int(B)|S], [ bool(B >= A)|S]). +func(<=, [int(A), int(B)|S], [ bool(B =< A)|S]). +func(<>, [int(A), int(B)|S], [bool(neq(B, A))|S]). +% func(>, [int(A), int(B)|S], [T|S]) :- B #> A #<==> R, r_truth(R, T). +% func(<, [int(A), int(B)|S], [T|S]) :- B #< A #<==> R, r_truth(R, T). +% func(=, [int(A), int(B)|S], [T|S]) :- B #= A #<==> R, r_truth(R, T). +% func(>=, [int(A), int(B)|S], [T|S]) :- B #>= A #<==> R, r_truth(R, T). +% func(<=, [int(A), int(B)|S], [T|S]) :- B #=< A #<==> R, r_truth(R, T). +% func(<>, [int(A), int(B)|S], [T|S]) :- B #\= A #<==> R, r_truth(R, T). + +func(sqr) --> func(dup), func(mul). % Pretty neat. + +r_truth(0, bool(false)). +r_truth(1, bool(true)). + + +/* + + ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗███╗ ██╗ █████╗ ████████╗ ██████╗ ██████╗ ███████╗ +██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║████╗ ██║██╔══██╗╚══██╔══╝██╔═══██╗██╔══██╗██╔════╝ +██║ ██║ ██║██╔████╔██║██████╔╝██║██╔██╗ ██║███████║ ██║ ██║ ██║██████╔╝███████╗ +██║ ██║ ██║██║╚██╔╝██║██╔══██╗██║██║╚██╗██║██╔══██║ ██║ ██║ ██║██╔══██╗╚════██║ +╚██████╗╚██████╔╝██║ ╚═╝ ██║██████╔╝██║██║ ╚████║██║ ██║ ██║ ╚██████╔╝██║ ██║███████║ + ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═════╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ + +*/ + +combo(i, [list(P)|S], S, Ei, Eo) :- append(P, Ei, Eo). +combo(dip, [list(P), X|S], S, Ei, Eo) :- append(P, [X|Ei], Eo). +combo(dipd, [list(P), X, Y|S], S, Ei, Eo) :- append(P, [Y, X|Ei], Eo). + +combo(dupdip, [list(P), X|S], [X|S], Ei, Eo) :- append(P, [X|Ei], Eo). + +combo(branch, [list(T), list(_), bool(true)|S], S, Ei, Eo) :- append(T, Ei, Eo). +combo(branch, [list(_), list(F), bool(false)|S], S, Ei, Eo) :- append(F, Ei, Eo). + +combo(loop, [list(_), bool(false)|S], S, E, E ). +combo(loop, [list(B), bool(true)|S], S, Ei, Eo) :- append(B, [list(B), symbol(loop)|Ei], Eo). + +combo(step, [list(_), list([])|S], S, E, E ). +combo(step, [list(P), list([X|Z])|S], [X|S], Ei, Eo) :- append(P, [list(Z), list(P), symbol(step)|Ei], Eo). + +combo(times, [list(_), int(0)|S], S, E, E ). +combo(times, [list(P), int(1)|S], S, Ei, Eo) :- append(P, Ei, Eo). +combo(times, [list(P), int(N)|S], S, Ei, Eo) :- N #>= 2, M #= N - 1, append(P, [int(M), list(P), symbol(times)|Ei], Eo). +combo(times, [list(_), int(N)|S], S, _, _ ) :- N #< 0, fail. + +combo(genrec, [R1, R0, Then, If|S], + [ Else, Then, If|S], E, [symbol(ifte)|E]) :- + append(R0, [list([If, Then, R0, R1, symbol(genrec)])|R1], Else). + +/* +This is a crude but servicable implementation of the map combinator. + +Obviously it would be nice to take advantage of the implied parallelism. +Instead the quoted program, stack, and terms in the input list are +transformed to simple Joy expressions that run the quoted program on +prepared copies of the stack that each have one of the input terms on +top. These expressions are collected in a list and the whole thing is +evaluated (with infra) on an empty list, which becomes the output list. + +The chief advantage of doing it this way (as opposed to using Prolog's +map) is that the whole state remains in the pending expression, so +there's nothing stashed in Prolog's call stack. This preserves the nice +property that you can interrupt the Joy evaluation and save or transmit +the stack+expression knowing that you have all the state. +*/ + +combo(map, [list(_), list([])|S], [list([])|S], E, E ) :- !. +combo(map, [list(P), list(List)|S], [list(Mapped), list([])|S], E, [symbol(infra)|E]) :- + prepare_mapping(list(P), S, List, Mapped). + +% Set up a program for each term in ListIn +% +% [term S] [P] infrst +% +% prepare_mapping(P, S, ListIn, ListOut). + +prepare_mapping(Pl, S, In, Out) :- prepare_mapping(Pl, S, In, [], Out). + +prepare_mapping( _, _, [], Out, Out) :- !. +prepare_mapping( Pl, S, [T|In], Acc, Out) :- + prepare_mapping(Pl, S, In, [list([T|S]), Pl, symbol(infrst)|Acc], Out). + + +/* + +██████╗ ███████╗███████╗██╗███╗ ██╗██╗████████╗██╗ ██████╗ ███╗ ██╗███████╗ +██╔══██╗██╔════╝██╔════╝██║████╗ ██║██║╚══██╔══╝██║██╔═══██╗████╗ ██║██╔════╝ +██║ ██║█████╗ █████╗ ██║██╔██╗ ██║██║ ██║ ██║██║ ██║██╔██╗ ██║███████╗ +██║ ██║██╔══╝ ██╔══╝ ██║██║╚██╗██║██║ ██║ ██║██║ ██║██║╚██╗██║╚════██║ +██████╔╝███████╗██║ ██║██║ ╚████║██║ ██║ ██║╚██████╔╝██║ ╚████║███████║ +╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚══════╝ + +*/ + +joy_def(Codes) :- + text_to_expression(Codes, [symbol(Name)|Body]), + % writeln(Name), + assert_def(Name, Body). + +assert_defs(DefsFile) :- + read_file_to_codes(DefsFile, Codes, []), + lines(Codes, Lines), + maplist(joy_def, Lines). + +assert_def(Symbol, Body) :- + ( % Don't let this "shadow" functions or combinators. + \+ func(Symbol, _, _), + \+ combo(Symbol, _, _, _, _) + ) -> ( % Replace any existing defs of this name. + retractall(def(Symbol, _)), + assertz(def(Symbol, Body)) + ) ; true. + +% Split on newline chars a list of codes into a list of lists of codes +% one per line. Helper function. +lines([], []) :- !. +lines(Codes, [Line|Lines]) :- append(Line, [0'\n|Rest], Codes), !, lines(Rest, Lines). +lines(Codes, [Codes]). + +:- assert_defs("defs.txt"). + + +symbols(E, S) :- symbols(E, [], S). + +symbols(symbol(S)) --> seen_sym(S), !. +symbols(symbol(S)), [S] --> []. +symbols( bool(_)) --> []. +symbols( int(_)) --> []. +symbols( list(L)) --> symbols(L). + +symbols([]) --> []. +symbols([T|Tail]) --> symbols(T), symbols(Tail). + +seen_sym(Term, List, List) :- member(Term, List). + +write_sym(Symbol) :- write('"'), write(Symbol), write('"'). + +/* + +Run with e.g.: + + $ swipl -g fooooo -g halt source/thun.pl > jd.dot + +*/ +fooooo :- + writeln("digraph joy_defs {"), + % writeln(" rankdir=LR;"), + forall( + def(Symbol, Body), + ( + symbols(list(Body), Deps), + forall( + member(Dep, Deps), + ( + write(" "), + write_sym(Symbol), + write(" -> "), + write_sym(Dep), + writeln(";") + ) + ) + ) + ), + writeln("}"). + + + +% A meta function that finds the names of all available functions. + +words(Words) :- + findall(Name, clause(func(Name, _, _), _), Funcs), + findall(Name, clause(combo(Name, _, _, _, _), _), Combos, Funcs), + findall(Name, clause(def(Name, _), _), Words0, Combos), + list_to_set(Words0, Words1), + sort(Words1, Words). + + +/* + + ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗██╗ ███████╗██████╗ +██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║██║ ██╔════╝██╔══██╗ +██║ ██║ ██║██╔████╔██║██████╔╝██║██║ █████╗ ██████╔╝ +██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██║██║ ██╔══╝ ██╔══██╗ +╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ██║███████╗███████╗██║ ██║ + ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ + _ ___ _ + | |_ ___ | _ \_ _ ___| |___ __ _ + | _/ _ \ | _/ '_/ _ \ | _ \/ _` | + \__\___/ |_| |_| \___/_|___/\__, | + |___/ + +This is an experimental compiler from Joy expressions to Prolog code. +As you will see it's also doing type inference and type checking. + +For many Joy expressions the existing code is enough to "compile" them to +Prolog code. E.g. the definition of 'third' is 'rest rest first' and +that's enough for the code to generate the "type" of the expression: + + ?- joy(`third`, Si, So). + Si = [list([_32906, _32942, _32958|_32960])|_32898], + So = [_32958|_32898] . + +Because 'third' is just manipulating lists (the stack is a list too) the +type signature is the whole of the (Prolog) implementation of the +function: + + ?- sjc(third, `third`). + func(third, [list([_, _, A|_])|B], [A|B]). + +So that's nice. + +Functions that involve just math require capturing the constraints +recorded by the CLP(FD) subsystem. SWI Prolog provide a predicate +call_residue_vars/2 to do just that. Together with copy_term/3 it's +possible to collect all the information needed to capture functions +made out of math and stack/list manipulation. (I do not understand the +details of how they work. Markus Triska said they would do the trick and +they did.) + +https://www.swi-prolog.org/pldoc/doc_for?object=call_residue_vars/2 + +https://www.swi-prolog.org/pldoc/doc_for?object=copy_term/3 + +I think this is sort of like "gradual" or "dependent" types. But the +formal theory there is beyond me. In any event, it captures the integer +constraints established by the expressions as well as the "types" of +inputs and outputs. + + ?- sjc(fn, `* + * -`). + func(fn, [int(H), int(I), int(F), int(D), int(C)|A], [int(B)|A]) :- + maplist(call, + + [ clpfd:(B+E#=C), + clpfd:(G*D#=E), + clpfd:(J+F#=G), + clpfd:(H*I#=J) + ]). + +For functions involving 'branch', compilation results in one rule for each +(reachable) path of the branch: + + ?- sjc(fn, `[+] [-] branch`). + + func(fn, [bool(true), int(C), int(D)|A], [int(B)|A]) :- + maplist(call, [clpfd:(B+C#=D)]). + + func(fn, [bool(false), int(B), int(C)|A], [int(D)|A]) :- + maplist(call, [clpfd:(B+C#=D)]). + +(Note that in the subtraction case (bool(true)) the CLP(FD) constraints +are coded as addition but the meaning is the same (subtraction) because of +how the logic variables are named: B + C #= D <==> B #= D - C.) + +?- sjc(fn, `[[+] [-] branch] [pop *] branch`). + + func(fn, [bool(true), _, int(B), int(C)|A], [int(D)|A]) :- + maplist(call, [clpfd:(B*C#=D)]). + + func(fn, [bool(false), bool(true), int(C), int(D)|A], [int(B)|A]) :- + maplist(call, [clpfd:(B+C#=D)]). + + func(fn, [bool(false), bool(false), int(B), int(C)|A], [int(D)|A]) :- + maplist(call, [clpfd:(B+C#=D)]). + +Three paths, three rules. Neat, eh? + +That leaves loop, genrec, and x combinators... + + +*/ + +joy_compile(Name, Expression) :- jcmpl(Name, Expression, Rule), asserta(Rule). + +show_joy_compile(Name, Expression) :- jcmpl(Name, Expression, Rule), portray_clause(Rule). + +jcmpl(Name, Expression, Rule) :- + call_residue_vars(thun(Expression, Si, So), Term), + copy_term(Term, Term, Gs), + Head =.. [func, Name, Si, So], + rule(Head, Gs, Rule). + +rule(Head, [], Head). +rule(Head, [A|B], Head :- maplist(call, [A|B])). + +sjc(Name, InputString) :- + text_to_expression(InputString, Expression), + show_joy_compile(Name, Expression). + +/* + +?- def(Name, _), compilable(Name). +Name = -- ; +Name = ? ; +Name = ++ ; +Name = '!-' ; +Name = abs ; +Name = ccons ; +Name = fourth ; +Name = neg ; +Name = not ; +Name = popop ; +Name = reco ; +Name = rrest ; +Name = second ; +Name = sqr ; +Name = swons ; +Name = third ; +Name = unswons ; +false. + + */ + +rules_of(Name, Expression, Rules) :- findall(Rule, jcmpl(Name, Expression, Rule), Rules). + +foo(Name-Body) :- + ( can_compile(Name) + -> call_with_depth_limit(rules_of(Name, Body, Rules), 100, _), + maplist(portray_clause, Rules), + nl + ; true % write(Name), writeln(" can't compile") + ). + +do :- + findall(Name-Body, def(Name, Body), Defs), + maplist(foo, Defs). + +can_compile(-). +can_compile(*). +can_compile(/). +can_compile(+). +can_compile(<). +can_compile(<=). +can_compile(<>). +can_compile(=). +can_compile(>). +can_compile(>=). +can_compile(bool). +can_compile(branch). +can_compile(cons). +can_compile(dup). +can_compile(first). +can_compile(pop). +can_compile(rest). +can_compile(rolldown). +can_compile(rollup). +can_compile(swap). +can_compile(uncons). +can_compile(unit). + +compilable(int(_)) :- !. +compilable(bool(_)) :- !. +compilable(Symbol) :- can_compile(Symbol), !. +compilable(Symbol) :- + def(Symbol, Body), + symbols(list(Body), Syms), + forall(member(Dep, Syms), compilable(Dep)). + + + + +/* + +Experiments with compilation. + +?- sjc(fn, `[+ dup bool] loop`). + +func(fn, [bool(false)|A], A). + +func(fn, [bool(true), int(B), int(C)|A], [int(0)|A]) :- + maplist(call, [clpfd:(B+C#=0)]). + +func(fn, [bool(true), int(D), int(E), int(B)|A], [int(0)|A]) :- + maplist(call, + [ clpfd:(B in inf.. -1\/1..sup), + clpfd:(C+B#=0), + clpfd:(C in inf.. -1\/1..sup), + clpfd:(D+E#=C) + ]). + +func(fn, [bool(true), int(F), int(G), int(D), int(B)|A], [int(0)|A]) :- + maplist(call, + [ clpfd:(B in inf.. -1\/1..sup), + clpfd:(C+B#=0), + clpfd:(C in inf.. -1\/1..sup), + clpfd:(E+D#=C), + clpfd:(E in inf.. -1\/1..sup), + clpfd:(F+G#=E) + ]). + + + +What if we unify a couple of the heads? Changing the variable names on +oneside so they are all unique, we have: + +func(fn, [bool(true), int(B), int(C)|A], [int(0)|A]) = func(fn, [bool(true), int(D), int(E), int(G)|F], [int(0)|F]). + + + +And: + +?- func(fn, [bool(true), int(B), int(C)|A], [int(0)|A]) = func(fn, [bool(true), int(D), int(E), int(G)|F], [int(0)|F]). +B = D, +C = E, +A = F, F = [int(G)|F]. + + + +Interesting... note the circular term for the rest of the stack. + + +func(fn, [bool(true), int(B), int(C)| A], [int(0)|A]) +func(fn, [bool(true), int(D), int(E), int(G)|F], [int(0)|F]). + +SO B=D and C=E, yeah, +and from the output stack we have the "rest" of the stack A=F +but from the input stack we have [int(G)|F]=F + +We already know that this function can consume two or more integers from +the stack under thr right conditions. So I /think/ this circular term +represents that fact. + + + +THe definition of this silly function if written by hand... + +The false case is easy enough: + + func(fn, [bool(false)|A], A). + +But the true case is a little tricky: + + true [+ dup bool] loop + ---------------------------------- + + dup bool [+ dup bool] loop + +And we want the result to actually be: + + + true fn + ------------------- + + dup bool fn + +That is, we want the compiled version to be defined in terms of itself (a +feature absent from the above mechanically-derived forms.) We can't put +the symbol of the fn onto the pending expression because we are making a +func, not a combinator, so we don't ahve the expression to work with. +Is that just a quirk of the compiler code above? It can only make funcs +because it's written that way, it's hard-coded. How would it know to +make a combinator rather than a func? + +In any event, by hand I might write a combinator like this: + + combo(fn, [bool(false)|S], S, E, E ). + combo(fn, [bool(true) |S], S, Ei, Eo) :- + append([symbol('+'), symbol(dup), symbol(bool), symbol(fn)], Ei, Eo). + +This works like the definition above, prepending code onto the pending +expression. Then you might try: + + sjc(fn_body, `+ dup bool`) + +Which, as it turns out, has only two solutions: + + ?- sjc(fn_body, `+ dup bool`). + + func(fn_body, [int(B), int(C)|A], [bool(false), int(0)|A]) :- B + C #= 0. + + func(fn_body, [int(C), int(D)|A], [bool(true), int(B)|A]) :- + maplist(call, + [ clpfd:(B in inf.. -1\/1..sup), + clpfd:(C+D#=B) + ]). + +Leading to an abbreviated version of the combinator: + + true fn + ------------------- w/ fn_body == + dup bool + fn_body fn + + combo(fn, [bool(false)|S], S, E, E ). + combo(fn, [bool(true) |S], S, Ei, Eo) :- + append([symbol(fn_body), symbol(fn)], Ei, Eo). + + + + + fn [fn_body] loop + fn_body + dup bool + + +I tried it and it works, in the sense that the above Prolog defintions +get the same solutions: + +?- sjc(fn, `fn`). +func(fn, [bool(false)|A], A). +true ; +func(fn, [bool(true), int(B), int(C)|A], [int(0)|A]) :- + maplist(call, [clpfd:(B+C#=0)]). +true ; +func(fn, [bool(true), int(D), int(E), int(B)|A], [int(0)|A]) :- + maplist(call, + + [ clpfd:(B in inf.. -1\/1..sup), + clpfd:(C+B#=0), + clpfd:(C in inf.. -1\/1..sup), + clpfd:(D+E#=C) + ]). +true ; +func(fn, [bool(true), int(F), int(G), int(D), int(B)|A], [int(0)|A]) :- + maplist(call, + + [ clpfd:(B in inf.. -1\/1..sup), + clpfd:(C+B#=0), + clpfd:(C in inf.. -1\/1..sup), + clpfd:(E+D#=C), + clpfd:(E in inf.. -1\/1..sup), + clpfd:(F+G#=E) + ]). + +So that's nice. + + + +This leads me to think that a viable strategy might be to: +1) Find the sub-functions that can compile to funcs and compile them. +2) For each combinator create a new combinator defintion that uses the + funcs defined above. + +It seems like it would be easy to go from this: + + combo(loop, [list(_), bool(false)|S], S, E, E ). + combo(loop, [list(B), bool(true)|S], S, Ei, Eo) :- + append(B, [list(B), symbol(loop)|Ei], Eo). + +To this: + + combo(fn, [bool(false)|S], S, E, E ). + combo(fn, [bool(true) |S], S, Ei, Eo) :- + append([symbol(fn_body), symbol(fn)], Ei, Eo). + +for some: + + fn == [fn_body] loop + + + +Incremental transformation? + + fn == [+ dup bool] loop + -------------------------------- + fn_body == + dup bool + fn == [fn_body] loop + + + fn == [fn_body] loop + +But we want to compile a combinator that works like this: + + ... false fn + ------------------ + ... + + ... true fn + -------------------- + ... fn_body fn + + + + + + ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗██╗ ███████╗██████╗ +██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║██║ ██╔════╝██╔══██╗ +██║ ██║ ██║██╔████╔██║██████╔╝██║██║ █████╗ ██████╔╝ +██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██║██║ ██╔══╝ ██╔══██╗ +╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ██║███████╗███████╗██║ ██║ + ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ + _ ___ _ _ + | |_ ___ | _ \_ _| |_| |_ ___ _ _ + | _/ _ \ | _/ || | _| ' \/ _ \ ' \ + \__\___/ |_| \_, |\__|_||_\___/_||_| + |__/ + + + +Compile to Python: + + def fn(stack, expression): + while stack[0]: + stack, expression = fn_body(stack[1], expression) + return stack[1], expression + +Well, that was easy. + + + + + + ██████╗ ███████╗███╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗ ███████╗███╗ ██╗ +██╔════╝ ██╔════╝████╗ ██║██╔════╝██╔═══██╗██╔══██╗██╔════╝ ██╔════╝████╗ ██║ +██║ ███╗█████╗ ██╔██╗ ██║██║ ██║ ██║██║ ██║█████╗ █████╗ ██╔██╗ ██║ +██║ ██║██╔══╝ ██║╚██╗██║██║ ██║ ██║██║ ██║██╔══╝ ██╔══╝ ██║╚██╗██║ +╚██████╔╝███████╗██║ ╚████║╚██████╗╚██████╔╝██████╔╝███████╗███████╗██║ ██║ ╚████║ + ╚═════╝ ╚══════╝╚═╝ ╚═══╝ ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝╚══════╝╚═╝ ╚═╝ ╚═══╝ + + + +*/ + +% gencode_ident(Prefix, Codes) :- +% gensym(Prefix, Atom), +% atom_codes(Atom, Codes). + +% compile_loop(F, Body) --> +% { gencode_ident(fn_loop_, F) +% , gencode_ident(fn_loop_body_, B) +% }, nl, +% gencode_loop(F, B), nl, +% gencode_fn(B, Body), nl. + +% gencode_fn(Name, Body) --> +% "def ", Name,"(stack, expression):", nl, +% gencode_list(Body), +% tab, "return stack, expression", nl. + +% gencode_loop(F, B) --> +% "def ", F, "(stack, expression):", nl, +% tab, "while stack[0]:", nl, +% tab, tab, "stack, expression = ", B, "(stack[1], expression)", nl, +% tab, "return stack[1], expression", nl. + +% gencode_list(List) --> +% tab, "pass", nl. + + + +% ??? + +% foo([list(Body), loop|Tail]) --> +% % We can't stop and generate loop and loop body functions inside the +% % current function, can we? I mean, if we get the indentation right +% % I think it would be syntactically correct Python code. +% compile_loop(Name, Body), % Schedule generation of the resulting functions... +% tab, "stack, expression = ", Name, "(stack, expression)", nl, +% foo(Tail). + +% foo([Symbol|Tail]) --> +% { symbol_is_primitive(Symbol) +% , atom_codes(Symbol, Name) +% }, +% tab, "stack, expression = ", Name, "(stack, expression)", nl, +% foo(Tail). + +% foo([]) --> []. + + +% symbol_is_primitive(sin). % What should be Python-built-in? +% symbol_is_primitive(cos). + + + +/* + + +So, what if we have a tabulator predicate. + +*/ + +tabs(N) --> { N #> 0, M #= N - 1 }, + tab, tabs(M). + +tabs(0) --> []. + +nl --> "\n". + +tab --> " ". + + +/* + +And we compile the loop inline: + + while stack[0]: + stack, expression = fn_body(stack[1], expression) + stack = stack[1] + +*/ + + +gencode_fn(Name, Body) --> + { reset_gensym(v) }, + "def ", Name,"(stack, expression, dictionary):", nl, + gencode_list_tail(Body, 1), + tab, "return stack, expression, dictionary", nl. + + +gencode_loop(Body, IndentLevel) --> + {J #= IndentLevel + 1}, + tabs(IndentLevel), "tos, stack = stack", nl, + tabs(IndentLevel), "while tos:", nl, + gencode_list(Body, J), + tabs(J), "tos, stack = stack", nl. + + +gencode_branch(BodyTrue, BodyFalse, IndentLevel) --> + {J #= IndentLevel + 1}, + tabs(IndentLevel), "tos, stack = stack", nl, + tabs(IndentLevel), "if tos:", nl, + gencode_list(BodyTrue, J), + tabs(IndentLevel), "else:", nl, + gencode_list(BodyFalse, J). + + +gencode_list([X|Xs], IndentLevel) --> + gencode_list_tail([X|Xs], IndentLevel). + +gencode_list([], IndentLevel) --> + tabs(IndentLevel), "pass", nl. + + +gencode_list_tail([bool(true)|Tail], IndentLevel) --> + tabs(IndentLevel), "stack = True, stack", nl, + gencode_list_tail(Tail, IndentLevel). + +gencode_list_tail([bool(false)|Tail], IndentLevel) --> + tabs(IndentLevel), "stack = False, stack", nl, + gencode_list_tail(Tail, IndentLevel). + +gencode_list_tail([int(I)|Tail], IndentLevel) --> + { integer(I) + , number_codes(I, Int) + }, + tabs(IndentLevel), "stack = ", Int, ", stack", nl, + gencode_list_tail(Tail, IndentLevel). + + +gencode_list_tail([list(Body), symbol(i)|Tail], IndentLevel) --> + { append(Body, Tail, Expr) }, + gencode_list_tail(Expr, IndentLevel). + +gencode_list_tail([list(Body), symbol(loop)|Tail], IndentLevel) --> + gencode_loop(Body, IndentLevel), + gencode_list_tail(Tail, IndentLevel). + +gencode_list_tail([list(BodyFalse), list(BodyTrue), symbol(branch)|Tail], IndentLevel) --> + gencode_branch(BodyTrue, BodyFalse, IndentLevel), + gencode_list_tail(Tail, IndentLevel). + +gencode_list_tail([symbol(+)|Tail], IndentLevel) --> !, + { Fin = [int(A), int(B)|S] + , Fout = [int(C)|S] + }, + tabs(IndentLevel), stack_to_python(Fin), " = stack", nl, + tabs(IndentLevel), term_to_python(C), " = ", term_to_python(A), " + ", term_to_python(B), nl, + tabs(IndentLevel), "stack = ", stack_to_python(Fout), nl, + gencode_list_tail(Tail, IndentLevel). + +gencode_list_tail([symbol(F), NotSym|Tail], IndentLevel) --> + { func(F, Fin, Fout), NotSym \= symbol(_) }, + tabs(IndentLevel), stack_to_python(Fin), " = stack", nl, + tabs(IndentLevel), "stack = ", stack_to_python(Fout), nl, + gencode_list_tail([NotSym|Tail], IndentLevel). + +% Combine functions. + +gencode_list_tail([symbol(F), symbol(G)|Tail], IndentLevel) --> + { func(F, Fin, Fout) + , func(G, Gin, Gout) + , Fout=Gin + }, + gencode_list_tail([func(Fin, Gout)|Tail], IndentLevel). + +gencode_list_tail([func(Fin, Fout), symbol(G)|Tail], IndentLevel) --> + { func(G, Gin, Gout) + , Fout=Gin + }, + gencode_list_tail([func(Fin, Gout)|Tail], IndentLevel). + +gencode_list_tail([func(Fin, Fout), NotSym|Tail], IndentLevel) --> + { nonvar(NotSym) + , NotSym \= symbol(_) + }, + gencode_list_tail([func(Fin, Fout)], IndentLevel), + gencode_list_tail([NotSym|Tail], IndentLevel). + +gencode_list_tail([symbol(F)], IndentLevel) --> + { func(F, Fin, Fout) }, + tabs(IndentLevel), stack_to_python(Fin), " = stack", nl, + tabs(IndentLevel), "stack = ", stack_to_python(Fout), nl. + +gencode_list_tail([func(Fin, Fout)], IndentLevel) --> + tabs(IndentLevel), stack_to_python(Fin), " = stack", nl, + tabs(IndentLevel), "stack = ", stack_to_python(Fout), nl. + + +gencode_list_tail([], _) --> []. + + +% lib_func(Name, Codes). + +lib_func(crap, "dup"). + + + + +/* + +[_39088|_39090] -> (a, stack) +[_39088,_39088,_39088|_39090] -> (a, (a, (a, stack))) + + + + +?- do(`dup dup`). + +(v1, ()) = stack +stack = (v1, (v1, (v1, ()))) + + +So far, so goof, er, good... + +Probably broken in horrible, obvious ways. + + +?- sjc(fn, `dup dup +`). +func(fn, [int(A)|B], [int(A+A), int(A)|B]). +true . + + +(v23, stack) = stack # [int(A)|B] +stack = ((v23 + v23), (v23, stack)) # [int(A+A), int(A)|B] + +Hmm...... + +HMM......... + + + + + + +███████╗████████╗ █████╗ ██████╗██╗ ██╗ ████████╗ ██████╗ ██████╗ ██╗ ██╗████████╗██╗ ██╗ ██████╗ ███╗ ██╗ +██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██╔╝ ╚══██╔══╝██╔═══██╗ ██╔══██╗╚██╗ ██╔╝╚══██╔══╝██║ ██║██╔═══██╗████╗ ██║ +███████╗ ██║ ███████║██║ █████╔╝ ██║ ██║ ██║ ██████╔╝ ╚████╔╝ ██║ ███████║██║ ██║██╔██╗ ██║ +╚════██║ ██║ ██╔══██║██║ ██╔═██╗ ██║ ██║ ██║ ██╔═══╝ ╚██╔╝ ██║ ██╔══██║██║ ██║██║╚██╗██║ +███████║ ██║ ██║ ██║╚██████╗██║ ██╗███████╗██║ ╚██████╔╝███████╗██║ ██║ ██║ ██║ ██║╚██████╔╝██║ ╚████║ +╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ + + + + */ + +% stack_to_python(F) --> { writeln(F), fail }. + +stack_to_python([]) --> "stack", !. +stack_to_python([Term|Tail]) --> + "(", term_to_python(Term), ", ", stack_to_python(Tail), ")". + + +% Unify unbound terms with fresh Python identifiers. +pyvar(Prefix, Term, Codes) :- + ( var(Term) -> gensym(Prefix, Term) ; atom(Term) ), + atom_codes(Term, Codes). + +term_to_python(Term) --> + { pyvar(v, Term, Var) }, !, Var. + +term_to_python(bool(Term)) --> term_to_python(Term). + +term_to_python(int(Term)) --> + { ( integer(Term) -> + number_codes(Term, Int) + ; + pyvar(i, Term, Int) + ) + }, + Int. + +term_to_python(list(Term)) --> list_to_python(Term). + +term_to_python(Term) --> Term. + + +list_to_python(Term) --> + { pyvar(s, Term, Var) }, !, Var. + +list_to_python([]) --> "()", !. + +list_to_python([Term|Tail]) --> + "(", term_to_python(Term), ", ", list_to_python(Tail), ")". + + +/* +term_to_python(bool(Term)) --> term_to_python(Term). + +term_to_python(int(A + B)) --> "(", term_to_python(A), " + ", term_to_python(B), ")". +term_to_python( A + B) --> "(", term_to_python(A), " + ", term_to_python(B), ")". +term_to_python(int(A - B)) --> "(", term_to_python(A), " - ", term_to_python(B), ")". +term_to_python( A - B) --> "(", term_to_python(A), " - ", term_to_python(B), ")". +term_to_python(int(A * B)) --> "(", term_to_python(A), " * ", term_to_python(B), ")". +term_to_python( A * B) --> "(", term_to_python(A), " * ", term_to_python(B), ")". +term_to_python(int(A div B)) --> "(", term_to_python(A), " // ", term_to_python(B), ")". +term_to_python( A div B) --> "(", term_to_python(A), " // ", term_to_python(B), ")". +term_to_python(int(A mod B)) --> "(", term_to_python(A), " % ", term_to_python(B), ")". +term_to_python( A mod B) --> "(", term_to_python(A), " % ", term_to_python(B), ")". + +% term_to_python(bool(true)) --> "True". +% term_to_python(bool(false)) --> "False". + +term_to_python(bool(A > B)) --> "(", term_to_python(A), " > ", term_to_python(B), ")". +term_to_python( A > B) --> "(", term_to_python(A), " > ", term_to_python(B), ")". +term_to_python(bool(A < B)) --> "(", term_to_python(A), " < ", term_to_python(B), ")". +term_to_python( A < B) --> "(", term_to_python(A), " < ", term_to_python(B), ")". +term_to_python(bool(A =< B)) --> "(", term_to_python(A), " <= ", term_to_python(B), ")". +term_to_python( A =< B) --> "(", term_to_python(A), " <= ", term_to_python(B), ")". +term_to_python(bool(A >= B)) --> "(", term_to_python(A), " >= ", term_to_python(B), ")". +term_to_python( A >= B) --> "(", term_to_python(A), " >= ", term_to_python(B), ")". +term_to_python(bool(eq(A, B))) --> "(", term_to_python(A), " == ", term_to_python(B), ")". +term_to_python( eq(A, B)) --> "(", term_to_python(A), " == ", term_to_python(B), ")". +term_to_python(bool(neq(A, B))) --> "(", term_to_python(A), " != ", term_to_python(B), ")". +term_to_python( neq(A, B)) --> "(", term_to_python(A), " != ", term_to_python(B), ")". + + */ +% stack_to_python([Term|Tail]) --> +% { Term = [_|_] }, +% "(", stack_to_python(Term), ", ", stack_to_python(Tail), ")". + + + +% gencode_list(_Body, IndentLevel) --> +% tabs(IndentLevel), "pass". + + +do(Input) :- + text_to_expression(Input, Expr), + phrase(gencode_list(Expr, 0), PythonCodes, []), !, + string_codes(PythonSource, PythonCodes), + writeln(""), + writeln(PythonSource). + +/* + +compile_function("gcd", `true [tuck % dup 0 >] loop pop`). + +*/ + +compile_function(Name, BodyText) :- + text_to_expression(BodyText, Expr), + phrase(gencode_fn(Name, Expr), PythonCodes, []), !, + string_codes(PythonSource, PythonCodes), + writeln(""), + writeln(PythonSource). + + +/* + +?- compile_function("gcd", `true [tuck % dup 0 >] loop pop`). + +def gcd(stack, expression, dictionary): + stack = True, stack + tos, stack = stack + while tos: + (v1, (v2, stack)) = stack + stack = ((v2 % v1), ((v2 % v1), (v1, stack))) + stack = 0, stack + (v3, (v4, stack)) = stack + stack = ((v4 > v3), stack) + tos, stack = stack + (v5, stack) = stack + stack = stack + return stack, expression, dictionary + +true. + + +So now we can compile functions consisting of basic integer math, binary +Boolean logic, and loops and branches. A function like: + + foo == [bar] cons i + +Would be problematical though. (FOr one thing, I need to write the code +to deal with list literals, and modify the handling of the i combinator.) + + +-------------------------------------------------------------- + + ?- do(`+`). + + (v3, (v4, stack)) = stack + stack = ((v3 + v4), stack) + + true. + +How to make it do like this instead? + + (v3, (v4, stack)) = stack + v5 = v3 + v4 + stack = ((v5), stack) + +More to the point: + + ?- do(`+ dup`). + + (v5, (v6, stack)) = stack + stack = ((v5 + v6), ((v5 + v6), stack)) + +should be: + + (v5, (v6, stack)) = stack + v7 = v5 + v6 + stack = (v7, (v7, stack)) + +to avoid duplication of work, eh? + + +?- compile_function("fn", `+ dup`). + +def fn(stack, expression, dictionary): + (v1, (v2, stack)) = stack + v3 = v1 + v2 + stack = (v3, stack) + (v4, stack) = stack + stack = (v4, (v4, stack)) + return stack, expression, dictionary + +true. + +Hmm, better, but we want the v3 and v4 vars to be unified in Prolog +before generating the Python code, to prevent redundant stack chatter. + + + + + ██████╗ ██████╗ ██████╗ ███╗ ██╗██╗ ██╗ +██╔════╝ ██╔══██╗██╔═══██╗████╗ ██║██║ ██╔╝ +██║ ███╗██████╔╝██║ ██║██╔██╗ ██║█████╔╝ +██║ ██║██╔══██╗██║ ██║██║╚██╗██║██╔═██╗ +╚██████╔╝██║ ██║╚██████╔╝██║ ╚████║██║ ██╗ + ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝╚═╝ ╚═╝ + + + + + +With gronk we're juggling four things: + + The incoming joy expression + The outgoing code tokens (for the code gen) + The incoming stack representation + and outgoing stack representation + +The basic formula is like so (the indent level is an implementation +detail): + +gronk_fn_body( + [joy expression] + StackIn, + StackOut, + [code gen tokens] + ). + +(Let's leave out DCGs for now, eh? Since I don't actually know how they +work really yet, do I? ;P ) + +*/ + + + +gronk_fn(Name, Expr, CodeGens) + :- + CodeGens = ["def ", Name,"(stack, expression, dictionary):", nl, + tab, stack_to_python(StackIn), " = stack", nl|Cs], + CGTail = [tab, "return ", stack_to_python(StackOut), ", expression, dictionary", nl], + reset_gensym(s), reset_gensym(v), reset_gensym(i), + gronk_fn_list(Expr, StackIn, StackOut, Cs, CGTail, 1). + + +gronk_fn_list( + [list(BodyFalse), list(BodyTrue), symbol(branch)|Js], + [bool(B)|StackIn], + StackOut, + CodeGens, + COut, + IndentLevel) + :- + !, + J #= IndentLevel + 1, + CodeGens = [ + tabs(IndentLevel), "if ", term_to_python(B), ":", nl|Cs0 + ], + True = [tabs(J), stack_to_python(Stack), " = ", stack_to_python(StackT), nl, + tabs(IndentLevel), "else:", nl|Cs1], + False = [tabs(J), stack_to_python(Stack), " = ", stack_to_python(StackF), nl|Ck], + gronk_fn_list(BodyTrue, StackIn, StackT, Cs0, True, J), + gronk_fn_list(BodyFalse, StackIn, StackF, Cs1, False, J), + gronk_fn_list(Js, Stack, StackOut, Ck, COut, IndentLevel). + +/* + +?- gronk("fn", `[swap] [] branch `). + +def fn(stack, expression, dictionary): + (v1, (v2, (v3, stack))) = stack + if v1: + stack = (v2, (v3, stack)) + else: + stack = (v3, (v2, stack)) + return stack, expression, dictionary + + + +?- gronk("fn", `[swap] [] branch pop`). + +def fn(stack, expression, dictionary): + (v1, (v2, (v3, stack))) = stack + if v1: + (v4, stack) = (v2, (v3, stack)) + else: + (v4, stack) = (v3, (v2, stack)) + return stack, expression, dictionary + + + +?- gronk("fn", `over over > [swap] [] branch pop`). + +def fn(stack, expression, dictionary): + (v1, (v2, stack)) = stack + v3 = v2 > v1 + if v3: + (v4, stack) = (v1, (v2, stack)) + else: + (v4, stack) = (v2, (v1, stack)) + return stack, expression, dictionary + + + +Here's a case where factoring the pop to after the branch results in +inefficient code. (Compare the function below to the versions above. It +doesn't create and then immediately discard a v4 variable.) + +?- gronk("fn", `[swap pop] [pop] branch`). + +def fn(stack, expression, dictionary): + (v1, (v2, (v3, stack))) = stack + if v1: + stack = (v3, stack) + else: + stack = (v2, stack) + return stack, expression, dictionary + + + */ + +gronk_fn_list( + [list(Body), symbol(loop)|Js], + [bool(B)|StackIn], + StackOut, + CodeGens, + COut, + IndentLevel) + :- + !, + J #= IndentLevel + 1, + CodeGens = [ + % tabs(IndentLevel), "stack = ", stack_to_python(StackIn), " # Repack-the-stack hack.", nl, + tabs(IndentLevel), term_to_python(Tos), " = ", term_to_python(B), nl, + tabs(IndentLevel), "while ", term_to_python(Tos), ":", nl|Cs + ], + gronk_fn_list(Body, StackIn, [bool(Tos)|Stack], Cs, [tabs(J), stack_to_python(StackIn), " = ", stack_to_python(Stack), nl|Ck], J), + gronk_fn_list(Js, StackIn, StackOut, Ck, COut, IndentLevel). + % ^^^^^^^ wha!? not Stack!? +/* + +gronk_fn_list([symbol(*)], [int(A),int(A)|B], StackOut, [tab,"return ",stack_to_python(StackOut),", expression, dictionary",nl], CGTail, 1) + + +def fn(stack, expression, dictionary): + tos = True + while tos: + (v1, (v2, stack)) = stack + v3 = v2 % v1 + tos = v3 > 0 + stack = (v3, (v1, stack)) + (v4, stack) = stack + return stack, expression, dictionary + + +Close, but broken. THe boundaries between blocks are too permeable. + +?- gronk("fn", `true [>] loop`). + +def fn(stack, expression, dictionary): + (v1, (v2, stack)) = stack + tos = True + while tos: + v3 = v1 > v2 + tos = v3 + return stack, expression, dictionary + + + + +gronk_fn_list( + [symbol(*)], + [int(A),int(A)|B], + StackOut, + [tab,"return ",stack_to_python(StackOut),", expression, dictionary",nl], + CGTail, + 1 + ). + + + + + + + +?- gronk("fn", `stack`). + +def fn(stack, expression, dictionary): + stack = stack + return ((), stack), expression, dictionary + +SHould be + +?- gronk("fn", `stack`). + +def fn(stack, expression, dictionary): + return (stack, stack), expression, dictionary + + + +Okay then... + +?- gronk("fn", `over over + stack dup`). + +def fn(stack, expression, dictionary): + (i1, (i2, stack)) = stack + v1 = i2 + i1 + (v2, stack) = ((v1, (i1, (i2, stack))), (v1, (i1, (i2, stack)))) + return (v2, (v2, stack)), expression, dictionary + + +*/ + +gronk_fn_list( + [symbol(stack)|Js], + StackIn, + StackOut, + [tabs(IndentLevel), stack_to_python(Stack), " = (", stack_to_python(StackIn), ", ", stack_to_python(StackIn), ")", nl|Cs], + CGTail, + IndentLevel) + :- + !, % green cut + gronk_fn_list(Js, Stack, StackOut, Cs, CGTail, IndentLevel). + +gronk_fn_list( + [symbol(Sym)|Js], + [int(B), int(A)|StackIn], + StackOut, + [tabs(IndentLevel), term_to_python(C), " = ", term_to_python(A), Op, term_to_python(B), nl|Cs], + CGTail, + IndentLevel) + :- + bin_math_op(Sym, Op), !, % green cut + gronk_fn_list(Js, [int(C)|StackIn], StackOut, Cs, CGTail, IndentLevel). + +gronk_fn_list( + [symbol(Sym)|Js], + [int(B), int(A)|StackIn], + StackOut, + [tabs(IndentLevel), term_to_python(C), " = ", term_to_python(A), Op, term_to_python(B), nl|Cs], + CGTail, + IndentLevel) + :- + bin_bool_op(Sym, Op), !, % green cut + gronk_fn_list(Js, [bool(C)|StackIn], StackOut, Cs, CGTail, IndentLevel). + +gronk_fn_list( + [symbol(Sym)|Js], + StackIn, + StackOut, + Cs, + CGTail, + IndentLevel) + :- + yin(Sym), + func(Sym, StackIn, Stack), !, % green cut + gronk_fn_list(Js, Stack, StackOut, Cs, CGTail, IndentLevel). + +gronk_fn_list( + [symbol(Sym)|Js], + StackIn, + StackOut, + Cs, + CGTail, + IndentLevel) + :- + yin(Sym), + def(Sym, Body), !, % green cut + append(Body, Js, Expr), + gronk_fn_list(Expr, StackIn, StackOut, Cs, CGTail, IndentLevel). + +gronk_fn_list( + [bool(true)|Js], + StackIn, + StackOut, + Cs, + CGTail, + IndentLevel) + :- + !, % green cut + gronk_fn_list(Js, [bool("True")|StackIn], StackOut, Cs, CGTail, IndentLevel). + +gronk_fn_list( + [bool(false)|Js], + StackIn, + StackOut, + Cs, + CGTail, + IndentLevel) + :- + !, % green cut + gronk_fn_list(Js, [bool("False")|StackIn], StackOut, Cs, CGTail, IndentLevel). + +gronk_fn_list( + [int(I)|Js], + StackIn, + StackOut, + Cs, + CGTail, + IndentLevel) + :- + !, % green cut + gronk_fn_list(Js, [int(I)|StackIn], StackOut, Cs, CGTail, IndentLevel). + +gronk_fn_list( + [list(L)|Js], + StackIn, + StackOut, + Cs, + CGTail, + IndentLevel) + :- + !, % green cut + gronk_fn_list(Js, [list(L)|StackIn], StackOut, Cs, CGTail, IndentLevel). + +gronk_fn_list([], Stack, Stack, Cs, Cs, _). + + +bin_math_op(+, " + "). +bin_math_op(-, " - "). +bin_math_op(*, " * "). +bin_math_op(div, " // "). +bin_math_op( / , " // "). +bin_math_op(mod, " % "). +bin_math_op('%', " % "). + +bin_bool_op(>, " > "). +bin_bool_op(<, " < "). +bin_bool_op(=, " == "). +bin_bool_op(>=, " >= "). +bin_bool_op(<=, " <= "). +bin_bool_op(<>, " != "). + +yin(dup). +yin(tuck). +yin(over). +yin(swap). +yin(pop). +yin(rolldown). +yin(rollup). +yin(dupd). +yin(cons). +yin(uncons). +yin(first). +yin(rest). +yin(unit). +yin(shift). +yin(Sym) :- def(Sym, Body), maplist(yins, Body). + +yins(symbol(Sym)) :- yin(Sym). + +/* +concat +flatten +swaack +clear +bool+ + +list ops (empty? list? ...) +logic ops (and or ...) + +COMBINATORS + + */ + + +gronk(Name, BodyText) :- + text_to_expression(BodyText, Expr), + gronk_fn(Name, Expr, Out), + code_gen(Out, A, []), !, + string_codes(S, A), + writeln(""), + writeln(S). + + +/* + + +gronk_fn_body([int(A), int(B)|S], StackOut, IndentLevel, [symbol(Sym)|D], E) :- + [symbol(Sym)|D]=[symbol(Sym)|F], + bin_math_op(Sym, Op), + G=F, + gronk_fn_body([int(C)|S], + StackOut, + IndentLevel, + G, + H), + E=[tabs(IndentLevel), term_to_python(C), " = ", term_to_python(A), Op, term_to_python(B), nl|H]. + +gronk_fn_body([int(A), int(B)|S], StackOut, IndentLevel, [symbol(Sym)|D], E) :- + [symbol(Sym)|D]=[symbol(Sym)|F], + bin_bool_op(Sym, Op), + G=F, + gronk_fn_body([bool(C)|S], + StackOut, + IndentLevel, + G, + H), + E=[tabs(IndentLevel), term_to_python(C), " = ", term_to_python(A), Op, term_to_python(B), nl|H]. + +gronk_fn_body(S, S, _, A, [tab, "return ", stack_to_python(S), ", expression, dictionary", nl|A]). + + +Yeah, that can't be right... I'm basically in "How did this ever work?" territory. + + + + + + + + +?- gronk("fn", `+ +`). + +def fn(stack, expression, dictionary): + (v1, (v2, (v3, stack))) = stack + v4 = v1 + v2 + v5 = v4 + v3 + return (v5, stack), expression, dictionary + + +?- gronk("fn", `+ * - div mod`). + +def fn(stack, expression, dictionary): + (v1, (v2, (v3, (v4, (v5, (v6, stack)))))) = stack + v7 = v1 + v2 + v8 = v7 * v3 + v9 = v8 - v4 + v10 = v9 // v5 + v11 = v10 % v6 + return (v11, stack), expression, dictionary + + + + + + + + +?- gronk_fn("name", [symbol(+)], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S). + +def name(stack, expression, dictionary): + (v1, (v2, stack)) = stack + stack = (v3, stack) + return stack, expression, dictionary + v3 = v1 + v2 + +Reversing the order reversed the output... I wish i knew what I was +doing... :) + +?- gronk_fn("name", [symbol(+)], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S). + +def name(stack, expression, dictionary): + (v1, (v2, stack)) = stack + v3 = v1 + v2 + stack = (v3, stack) + return stack, expression, dictionary + + +?- gronk_fn("name", [symbol(+), symbol(+)], Out), code_gen(Out, A, []), !, string_codes(S, A), writeln(""), writeln(S). + +def name(stack, expression, dictionary): + (v1, (v2, (v3, stack))) = stack + v4 = v1 + v2 + v5 = v4 + v3 + stack = (v5, stack) + return stack, expression, dictionary + +Whatever, it works now. + + */ + + + + + + + + +code_gen([Head|Tail]) --> Head, code_gen(Tail). +code_gen([]) --> []. + +cg, Term --> [Term], cg. +cg --> []. + +compile_fn(Name) --> gronk_fn(Name), cg, !. + + +/* + +?- gronk_fn("name", [], [], Out), code_gen(Out, In, []). +Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, "return stack, expression, dictionary", nl], +In = "def name(stack, expressio...nary +". + +?- listing(cg). +cg(A, D) :- + A=[C|B], + cg(B, E), + phrase(C, D, E). +cg(A, A). + +?- gronk_fn("name", [], [], Out), cg(Out,C). +Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, "return stack, expression, dictionary", nl], +C = "def name(stack, expressio...nary +" ; +Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, "return stack, expression, dictionary", nl], +C = [100, 101, 102, 32, 110, 97, 109, 101, 40|...] . + +?- phrase((gronk_fn("name", []), cg), [], Out). +Out = "def name(stack, expressio...nary +" ; +Out = [100, 101, 102, 32, 110, 97, 109, 101, 40|...] ; +Out = [100, 101, 102, 32, 110, 97, 109, 101, 40|...] ; +Out = [100, 101, 102, 32, 110, 97, 109, 101, 40|...] ; +Out = [100, 101, 102, 32, 110, 97, 109, 101, 40|...] ; +Out = [100, 101, 102, 32, 110, 97, 109, 101, "(stack, expression, dictionary):"|...] ; +Out = [100, 101, 102, 32, "name", "(stack, expression, dictionary):", nl, tab, "return stack, expression, dictionary"|...] . + +Bleah. + + + + + + + + + + + + + + +?- gronk_fn("name", [symbol(+)], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S). + +def name(stack, expression, dictionary): + (v1, (v2, stack)) = stack + stack = (v3, stack) + return stack, expression, dictionary + v3 = v1 + v2 + + +Almost, but not quite. The assignment is happening after the return call! + + + +=-=-=-=--=-=-=-=-==-=- + +?- gronk_fn("name", [], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S). + +def name(stack, expression, dictionary): + stack = stack + stack = stack + return stack, expression, dictionary + +Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, stack_to_python([]), " = stack", nl, tab|...], +A = "def name(stack, expressio...nary +", +S = "def name(stack, expression, dictionary):\n stack = stack\n stack = stack\n return stack, expression, dictionary\n" . + +?- gronk_fn("name", [symbol(+)], Out), writeln(Out). +[def ,name,(stack, expression, dictionary):,nl,tab,stack_to_python([int(_274090),int(_274100)|_274096]), = stack,nl,tab,stack = ,stack_to_python([int(_274110)|_274096]),nl,tab,return stack, expression, dictionary,nl,tabs(1),term_to_python(_274110), = ,term_to_python(_274090), + ,term_to_python(_274100),nl] +Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, stack_to_python([int(_274090), int(...)|...]), " = stack", nl, tab|...] . + +?- gronk_fn("name", [symbol(+)], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S). + +def name(stack, expression, dictionary): + (v1, (v2, stack)) = stack + stack = (v3, stack) + return stack, expression, dictionary + v3 = v1 + v2 + +Out = ["def ", "name", "(stack, expression, dictionary):", nl, tab, stack_to_python([int(v1), int(...)]), " = stack", nl, tab|...], +A = "def name(stack, expressio...+ v2 +", +S = "def name(stack, expression, dictionary):\n (v1, (v2, stack)) = stack\n stack = (v3, stack)\n return stack, expression, dictionary\n v3 = v1 + v2\n" . + + + + +=-=-=-=--=-=-=-=-==-=- + +There we go... + +?- gronk_fn("name", [symbol(+)], Out), code_gen(Out, A, []), string_codes(S, A), writeln(""), writeln(S). + +def name(stack, expression, dictionary): + (v1, (v2, stack)) = stack + v3 = v1 + v2 + stack = (v3, stack) + return stack, expression, dictionary + + + + + + + + + + + + + + + + + + + + + + +?- do(`dup dup +`). + +(v5, stack) = stack +stack = ((v5 + v5), (v5, stack)) + +true . + +That's better. + +?- do(`[* / - + dup] [dup + over *] branch * * `). + +tos, stack = stack +if tos: + (v16, (v17, stack)) = stack + stack = ((v17 * (v16 + v16)), (v17, stack)) +else: + (v18, (v19, (v20, (v21, (v22, stack))))) = stack + stack = (((v21 - (v20 // (v18 * v19))) + v22), (((v21 - (v20 // (v18 * v19))) + v22), stack)) +(v23, (v24, (v25, stack))) = stack +stack = (((v23 * v24) * v25), stack) + +true . + +That's beautiful. + + +Of course, if we carried through the expression for the stack... + + + tos, stack = stack + if tos: + (v16, (v17, stack)) = stack + (v23, (v24, (v25, stack))) = ((v17 * (v16 + v16)), (v17, stack)) + else: + (v18, (v19, (v20, (v21, (v22, stack))))) = stack + (v23, (v24, (v25, stack))) = (((v21 - (v20 // (v18 * v19))) + v22), (((v21 - (v20 // (v18 * v19))) + v22), stack)) + stack = (((v23 * v24) * v25), stack) + +we could assign the new variables directly from the previous stage, +saving the packing and unpacking of the "stack" tuple. + +"Something to think about." + + +With symbolic Booleans this works now (there were a lot of bugs but I +don't know what they were.) + +?- do(`<= [+] [-] branch`). + +(v1, (v2, stack)) = stack +stack = ((v2 <= v1), stack) +tos, stack = stack +if tos: + (v3, (v4, stack)) = stack + stack = ((v4 - v3), stack) +else: + (v5, (v6, stack)) = stack + stack = ((v5 + v6), stack) + +true. + + + +Now we can compile GCD: + +?- do(`true [tuck % dup 0 >] loop pop`). + +stack = True, stack +tos, stack = stack +while tos: + (v9, (v10, stack)) = stack + stack = ((v10 % v9), ((v10 % v9), (v9, stack))) + stack = 0, stack + (v11, (v12, stack)) = stack + stack = ((v12 > v11), stack) + tos, stack = stack +(v13, stack) = stack +stack = stack + +true. + + +It's not ideal, for example, it computes v10 % v9 twice. :( + +We would like, e.g.: + +tos = True +while tos: + (v9, (v10, stack)) = stack + vN = v10 % v9 + stack = ((vN), ((vN), (v9, stack))) + (v11, (v12, stack)) = 0, stack + stack = ((v12 > v11), stack) + tos, stack = stack +(v13, stack) = stack +stack = stack + + +tos = True +while tos: + (v9, (v10, stack)) = stack + vN = v10 % v9 + stack = ((vN), ((vN), (v9, stack))) + (v12, stack) = stack + stack = ((v12 > 0), stack) + tos, stack = stack +(v13, stack) = stack + + +tos = True +while tos: + (v9, (v10, stack)) = stack + vN = v10 % v9 + stack = ((vN), ((vN), (v9, stack))) + (v12, stack) = stack + tos = (v12 > 0) +(v13, stack) = stack + + + +tos = True +while tos: + (v9, (v10, stack)) = stack + vN = v10 % v9 + (v12, stack) = ((vN), ((vN), (v9, stack))) + tos = (v12 > 0) +(v13, stack) = stack + + + + +tos = True +while tos: + (v9, (v10, stack)) = stack + vN = v10 % v9 + stack = (vN, (v9, stack)) + tos = (vN > 0) +(v13, stack) = stack + +Anyhow... I could keep going but you get the idea. The simple +mechanical translation results in correct but inefficient code. +I'm not too worried about it, this is great progress nonetheless, but it +would be nice to tighten up that code gen. + +What's that "stack = stack" doing in there? + + + + + +do(`[[dup dup] [dup] branch dup [dup] loop dup] loop dup`). + +do(`[dup] [[dup dup dup] [dup dup] branch] branch`). + + +*/ + + +/* + + + + + + + + + + + + + + + + + +?- sjc(fn, `[] loop`). + +func(fn, [bool(false)|A], A). + +func(fn, [bool(true), bool(false)|A], A). + +func(fn, [bool(true), bool(true), bool(false)|A], A). + +func(fn, [bool(true), bool(true), bool(true), bool(false)|A], A). + +So... + + `[] loop` ::= true* false + +sorta... + + +The quine '[[dup cons] dup cons]' works fine: + +?- sjc(fn, `dup cons`). +func(fn, [list(A)|B], [list([list(A)|A])|B]). + +?- sjc(fn, `[dup cons] dup cons`). +func(fn, A, [list([list([symbol(dup), symbol(cons)]), symbol(dup), symbol(cons)])|A]). + +?- sjc(fn, `[dup cons] dup cons i`). +func(fn, A, [list([list([symbol(dup), symbol(cons)]), symbol(dup), symbol(cons)])|A]). + +?- sjc(fn, `[dup cons] dup cons i i i i`). +func(fn, A, [list([list([symbol(dup), symbol(cons)]), symbol(dup), symbol(cons)])|A]). + + +In the right context the system will "hallucinate" programs: + +?- sjc(fn, `x`). +func(fn, [list([])|A], [list([])|A]). + +func(fn, [list([int(A)])|B], [int(A), list([int(A)])|B]). + +func(fn, [list([bool(A)])|B], [bool(A), list([bool(A)])|B]). + +func(fn, [list([list(A)])|B], [list(A), list([list(A)])|B]). + +func(fn, [list([symbol(?)])|A], [bool(true), list([symbol(?)])|A]). + +func(fn, [list([symbol(app1)]), list([]), A|B], [A, A|B]). + +func(fn, [list([symbol(app1)]), list([int(A)]), B|C], [int(A), B|C]). + +func(fn, [list([symbol(app1)]), list([bool(A)]), B|C], [bool(A), B|C]). + +With iterative deepening this might be very interesting... + + +Infinite loops are infinite: + +?- sjc(fn, `[x] x`). +ERROR: Out of global-stack. + + +?- sjc(fn, `sum`). +func(fn, [list([])|A], [int(0)|A]). + +func(fn, [list([int(A)])|B], [int(A)|B]) :- + maplist(call, [clpfd:(A in inf..sup)]). + +func(fn, [list([int(C), int(B)])|A], [int(D)|A]) :- + maplist(call, [clpfd:(B+C#=D)]). + +func(fn, [list([int(E), int(D), int(B)])|A], [int(C)|A]) :- + maplist(call, + + [ clpfd:(B+F#=C), + clpfd:(D+E#=F) + ]). + +func(fn, [list([int(G), int(F), int(D), int(B)])|A], [int(C)|A]) :- + maplist(call, + + [ clpfd:(B+E#=C), + clpfd:(D+H#=E), + clpfd:(F+G#=H) + ]). + + +TODO: genrec, fix points. + + + + + ██████╗ ██████╗ ███╗ ███╗██████╗ ██╗██╗ ███████╗██████╗ +██╔════╝██╔═══██╗████╗ ████║██╔══██╗██║██║ ██╔════╝██╔══██╗ +██║ ██║ ██║██╔████╔██║██████╔╝██║██║ █████╗ ██████╔╝ +██║ ██║ ██║██║╚██╔╝██║██╔═══╝ ██║██║ ██╔══╝ ██╔══██╗ +╚██████╗╚██████╔╝██║ ╚═╝ ██║██║ ██║███████╗███████╗██║ ██║ + ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═╝ ╚═╝ + _ __ __ _ _ ___ _ + | |_ ___ | \/ |__ _ __| |_ (_)_ _ ___ / __|___ __| |___ + | _/ _ \ | |\/| / _` / _| ' \| | ' \/ -_) | (__/ _ \/ _` / -_) + \__\___/ |_| |_\__,_\__|_||_|_|_||_\___| \___\___/\__,_\___| + +Options for getting machine code out of Joy (in Prolog) code? + +1) Translate Joy to Factor and delegate to Factor's native code +generation. + +2) Use e.g. GNU Prolog to compile the Prolog code of Joy. + +3) Translate to: + + 3a) LLVM IR. + + 3b) Some subset of C. + + 3c) Python for Cython. + + 3d) WASM? Something else...? + +But those all rely on a big pile of OPC (Other Ppl's Code). WHich brings +me to... + +4) Oberon RISC CPU machine code. The one I really want to do. I have an +assembler for it, there are emulators and FPGA incarnations, and it's +small and clean. + + 4a) Prolog machine description of the RISC chip. + + 4b) How to actually compile Joy to asm? There is a wealth of + available information and research to draw on, but most of it is in + the context of conventional languages. Static Joy code presents few + problems but the dynamic nature of most Joy programs does, I think. + (I.e. a lot of Joy code starts by constructing some other Joy code + and running it. It remains to be seen how much of a challenge that + will be. In the limit, you need Prolog at runtime to JIT compile.) + + 4c) Self-hosting requires Prolog-in-Joy. + + + ___ ___ ___ ___ __ __ _ _ ___ _ +| _ |_ _/ __|/ __| | \/ |__ _ __| |_ (_)_ _ ___ / __|___ __| |___ +| /| |\__ | (__ | |\/| / _` / _| ' \| | ' \/ -_) | (__/ _ / _` / -_) +|_|_|___|___/\___| |_| |_\__,_\__|_||_|_|_||_\___| \___\___\__,_\___| + +This is an experimental compiler from Joy expressions to machine code. + +One interesting twist is that Joy doesn't mention variables, just the +operators, so they have to be inferred from the ops. + +So let's take e.g. '+'? + +It seems we want to maintain a mapping from stack locations to registers, +and maybe from locations in lists on the stack, and to memory locations as +well as registers? + +But consider 'pop', the register pointed to by stack_0 is put back in an +available register pool, but then all the stack_N mappings have to point +to stack_N+1 (i.e. stack_0 must now point to what stack_1 pointed to and +stack_1 must point to stack_2, and so on...) + +What if we keep a stack of register/RAM locations in the same order as +the Joy stack? + +Reference counting for registers? Can it be avoided? When you "free" a +register you can just check the stack to see if it's still in there and, +if not, release it back to the free pool. You can amortize that w/o +keeping a counter by keeping a linear list of registers alongside the +stack and pushing and popping registers from it as they are used/free'd +and then checking if a register is ready for reclaimation is just +member/3. Or you can just keep a reference count for each register... +Would it be useful to put CLP(FD) constraints on the ref counts? + +reggy(FreePool, References, ValueMap) + +*/ + +% encode_list(List, FP, FP, Addr) --> [], +% {addr(list(List))=Addr}. + +% get_reggy([], _, _) :- writeln('Out of Registers'), fail. +% get_reggy([Reg|FreePool], Reg, FreePool). + +% get_reg(Reg, reggy(FreePool0, References, V), reggy(FreePool, [Reg|References], V)) --> [], +% {get_reggy(FreePool0, Reg, FreePool)}. + +% free_reg(Reg, reggy(FreePool0, References0, V0), reggy(FreePool, References, V)) --> [], +% { select(Reg, References0, References), +% ( member(Reg, References) % If reg is still in use +% -> FreePool= FreePool0, V0=V % we can't free it yet +% ; FreePool=[Reg|FreePool0], % otherwise we put it back in the pool. +% del_assoc(Reg, V0, _, V) +% )}. + +% add_ref(Reg, reggy(FreePool, References, V), reggy(FreePool, [Reg|References], V)) --> []. + +% assoc_reg(Reg, Value, reggy(FreePool, References, V0), reggy(FreePool, References, V)) --> [], +% {put_assoc(Reg, V0, Value, V)}. + +% thun_compile(E, Si, So, FP) --> +% {empty_assoc(V), +% FP0=reggy([r0, r1, r2, r3, +% r4, r5, r6, r7, +% r8, r9, rA, rB, +% rC, rD, rE, rF], [], V)}, +% thun_compile(E, Si, So, FP0, FP). + +% thun_compile([], S, S, FP, FP) --> []. +% thun_compile([Term|Rest], Si, So, FP0, FP1) --> thun_compile(Term, Rest, Si, So, FP0, FP1). + +% thun_compile(int(I), E, Si, So, FP0, FP) --> +% [mov_imm(R, int(I))], +% get_reg(R, FP0, FP1), assoc_reg(R, int(I), FP1, FP2), +% thun_compile(E, [R|Si], So, FP2, FP). + +% thun_compile(bool(B), E, Si, So, FP0, FP) --> +% get_reg(R, FP0, FP1), assoc_reg(R, bool(B), FP1, FP2), +% thun_compile(E, [R|Si], So, FP2, FP). + +% thun_compile(list(L), E, Si, So, FP0, FP) --> +% encode_list(L, FP0, FP1, Addr), +% get_reg(R, FP1, FP2), +% [load_imm(R, Addr)], +% assoc_reg(R, Addr, FP2, FP3), +% thun_compile(E, [R|Si], So, FP3, FP). + +% thun_compile(symbol(Name), E, Si, So, FP0, FP) --> {def(Name, _)}, !, def_compile(Name, E, Si, So, FP0, FP). +% thun_compile(symbol(Name), E, Si, So, FP0, FP) --> {func(Name, _, _)}, !, func_compile(Name, E, Si, So, FP0, FP). +% thun_compile(symbol(Name), E, Si, So, FP0, FP) --> {combo(Name, _, _, _, _)}, combo_compile(Name, E, Si, So, FP0, FP). + +% % I'm going to assume that any defs that can be compiled to funcs already +% % have been. Defs that can't be pre-compiled shove their body expression +% % onto the pending expression (continuation) to be compiled "inline". + +% def_compile(Def, E, Si, So, FP0, FP) --> +% {def(Def, Body), +% append(Body, E, Eo)}, +% thun_compile(Eo, Si, So, FP0, FP). + + +% % swap (et. al.) doesn't change register refs nor introspect values +% % so we can delegate its effect to the semantic relation. +% non_alloc(swap). +% non_alloc(rollup). +% non_alloc(rolldown). + +% % Functions delegate to a per-function compilation relation. + +% func_compile(+, E, [A, B|S], So, FP0, FP) --> !, +% free_reg(A, FP0, FP1), +% free_reg(B, FP1, FP2), +% get_reg(R, FP2, FP3), +% assoc_reg(R, int(_), FP3, FP4), +% [add(R, A, B)], +% % Update value in the context? +% thun_compile(E, [R|S], So, FP4, FP). + +% func_compile(dup, E, [A|S], So, FP0, FP) --> !, +% add_ref(A, FP0, FP1), +% thun_compile(E, [A, A|S], So, FP1, FP). + +% func_compile(pop, E, [A|S], So, FP0, FP) --> !, +% free_reg(A, FP0, FP1), +% thun_compile(E, S, So, FP1, FP). + +% func_compile(cons, E, [List, Item|S], So, FP0, FP) --> !, +% % Assume list is already stored in RAM +% % and item ... +% % allocate a cons cell +% [alloc_cons(list(Item, List))], +% % https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-33.html#%_sec_5.3 +% % TODO whence the output list in So? +% thun_compile(E, S, So, FP0, FP). + +% func_compile(Func, E, Si, So, FP0, FP) --> { non_alloc(Func), !, +% func(Func, Si, S) }, +% thun_compile(E, S, So, FP0, FP). + +% func_compile(_Func, E, Si, So, FP0, FP) --> +% % look up function, compile it... +% {Si = S}, +% thun_compile(E, S, So, FP0, FP). + + +% combo_compile(_Combo, E, Si, So, FP0, FP) --> +% % look up combinator, compile it... +% {Si = S, E = Eo}, +% thun_compile(Eo, S, So, FP0, FP). + + +% compiler(InputString, MachineCode, StackIn, StackOut) :- +% phrase(joy_parse(Expression), InputString), !, +% phrase(thun_compile(Expression, StackIn, StackOut, _), MachineCode, []). + + +% show_compiler(InputString, StackIn, StackOut) :- +% phrase(joy_parse(Expression), InputString), !, +% phrase(thun_compile(Expression, StackIn, StackOut, reggy(_, _, V)), MachineCode, []), +% maplist(portray_clause, MachineCode), +% assoc_to_list(V, VP), +% portray_clause(VP). + + +/* + +?- compiler(`1 2 +`, MachineCode, StackIn, StackOut). +MachineCode = [mov_imm(_18272, int(1)), mov_imm(_18298, int(2))], +StackOut = [_18298, _18272|StackIn]. + + +- - - - + + +?- compiler(`1 2 +`, MachineCode, StackIn, StackOut). +MachineCode = [mov_imm(r1, int(1)), mov_imm(r2, int(2)), add(r1, r2, r1)], +StackOut = [r1|StackIn]. + +?- compiler(`1 2 +`, MachineCode, StackIn, StackOut). +MachineCode = [mov_imm(r1, int(1)), mov_imm(r2, int(2)), add(r1, r2, r1)], +StackOut = [r1|StackIn]. + +?- compiler(`1 2 + 3 +`, MachineCode, StackIn, StackOut). +MachineCode = [mov_imm(r1, int(1)), mov_imm(r2, int(2)), add(r1, r2, r1), mov_imm(r3, int(3)), add(r1, r3, r1)], +StackOut = [r1|StackIn]. + +?- compiler(`1 2 + +`, MachineCode, StackIn, StackOut). +MachineCode = [mov_imm(r1, int(1)), mov_imm(r2, int(2)), add(r1, r2, r1), add(_37848, r1, _37848)], +StackIn = StackOut, StackOut = [_37848|_37850]. + +?- compiler(`+ +`, MachineCode, StackIn, StackOut). +MachineCode = [add(_37270, _37264, _37270), add(_37688, _37270, _37688)], +StackIn = [_37264, _37270, _37688|_37690], +StackOut = [_37688|_37690]. + +?- compiler(`+ +`, MachineCode, [r1, r2, r3], StackOut). +MachineCode = [add(r2, r1, r2), add(r3, r2, r3)], +StackOut = [r3]. + +?- compiler(`+ +`, MachineCode, [r1, r2, r3, r4, r5, r6, r7], StackOut). +MachineCode = [add(r2, r1, r2), add(r3, r2, r3)], +StackOut = [r3, r4, r5, r6, r7]. + +- - - - - + + +?- compiler(`1 2 3 + +`, MachineCode, StackIn, StackOut). +MachineCode = [mov_imm(r0, int(1)), mov_imm(r1, int(2)), mov_imm(r2, int(3)), add(r1, r2, r1), add(r0, r1, r0)], +StackOut = [r0|StackIn]. + + +register free seems to work... + +?- compiler(`1 2 + 3 +`, MachineCode, StackIn, StackOut). +MachineCode = [mov_imm(r0, int(1)), mov_imm(r1, int(2)), add(r0, r1, r0), mov_imm(r1, int(3)), add(r0, r1, r0)], +StackOut = [r0|StackIn] ; +false. + +- - - - + +?- compiler(`1 2 dup + 3 +`, MachineCode, StackIn, StackOut). +MachineCode = [mov_imm(r0, int(1)), mov_imm(r1, int(2)), add(r1, r1, r1), mov_imm(r2, int(3)), add(r1, r2, r1)], +StackOut = [r1, r0|StackIn] . + +?- compiler(`dup +`, MachineCode, StackIn, StackOut). +MachineCode = [add(_37000, _37000, _37000)], +StackIn = StackOut, StackOut = [_37000|_37002]. + +?- compiler(`dup +`, MachineCode, [r0], StackOut). +MachineCode = [add(r0, r0, r0)], +StackOut = [r0]. + +?- compiler(`dup +`, MachineCode, [r0], [r0]). +MachineCode = [add(r0, r0, r0)]. + +- - - - + +?- compiler(`1 2 3 4 5 + + + 6 7 + 8 + +`, MachineCode, StackIn, StackOut), maplist(portray_clause, MachineCode). +mov_imm(r0, int(1)). +mov_imm(r1, int(2)). +mov_imm(r2, int(3)). +mov_imm(r3, int(4)). +mov_imm(r4, int(5)). +add(r3, r4, r3). +add(r2, r3, r2). +add(r1, r2, r1). +mov_imm(r2, int(6)). +mov_imm(r3, int(7)). +add(r2, r3, r2). +mov_imm(r3, int(8)). +add(r2, r3, r2). +add(r1, r2, r1). + + +Fun! + +- - - - + +Test that returning registers before asking for new ones +does reuse registers that are unused and preserve registers +that are still in use. + +?- show_compiler(`1 dup 2 + swap 3 +`, StackIn, StackOut). +mov_imm(r0, int(1)). +mov_imm(r1, int(2)). +add(r1, r1, r0). +mov_imm(r2, int(3)). +add(r0, r2, r0). +[r0-int(_), r1-int(_)]. +StackOut = [r0, r1|StackIn] . + + + + +███╗ ███╗███████╗████████╗ █████╗ ██████╗ ██████╗ ██████╗ ██████╗ ██████╗ █████╗ ███╗ ███╗███╗ ███╗██╗███╗ ██╗ ██████╗ +████╗ ████║██╔════╝╚══██╔══╝██╔══██╗ ██╔══██╗██╔══██╗██╔═══██╗██╔════╝ ██╔══██╗██╔══██╗████╗ ████║████╗ ████║██║████╗ ██║██╔════╝ +██╔████╔██║█████╗ ██║ ███████║█████╗██████╔╝██████╔╝██║ ██║██║ ███╗██████╔╝███████║██╔████╔██║██╔████╔██║██║██╔██╗ ██║██║ ███╗ +██║╚██╔╝██║██╔══╝ ██║ ██╔══██║╚════╝██╔═══╝ ██╔══██╗██║ ██║██║ ██║██╔══██╗██╔══██║██║╚██╔╝██║██║╚██╔╝██║██║██║╚██╗██║██║ ██║ +██║ ╚═╝ ██║███████╗ ██║ ██║ ██║ ██║ ██║ ██║╚██████╔╝╚██████╔╝██║ ██║██║ ██║██║ ╚═╝ ██║██║ ╚═╝ ██║██║██║ ╚████║╚██████╔╝ +╚═╝ ╚═╝╚══════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝ ╚═════╝ + + + + + + + +███████╗██╗ ██╗██████╗ █████╗ ███╗ ██╗██████╗ ██╗ ██████╗ ██████╗ ███╗ ██╗████████╗██████╗ █████╗ ██████╗████████╗ +██╔════╝╚██╗██╔╝██╔══██╗██╔══██╗████╗ ██║██╔══██╗ ██╔╝ ██╔════╝██╔═══██╗████╗ ██║╚══██╔══╝██╔══██╗██╔══██╗██╔════╝╚══██╔══╝ +█████╗ ╚███╔╝ ██████╔╝███████║██╔██╗ ██║██║ ██║ ██╔╝ ██║ ██║ ██║██╔██╗ ██║ ██║ ██████╔╝███████║██║ ██║ +██╔══╝ ██╔██╗ ██╔═══╝ ██╔══██║██║╚██╗██║██║ ██║ ██╔╝ ██║ ██║ ██║██║╚██╗██║ ██║ ██╔══██╗██╔══██║██║ ██║ +███████╗██╔╝ ██╗██║ ██║ ██║██║ ╚████║██████╔╝ ██╔╝ ╚██████╗╚██████╔╝██║ ╚████║ ██║ ██║ ██║██║ ██║╚██████╗ ██║ +╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ + +*/ + +% Simple DCGs to expand/contract definitions. + +expando, Body --> [symbol(Def)], {def(Def, Body)}. +contracto, [symbol(Def)] --> {def(Def, Body)}, Body. + +% Apply expando/contracto more than once, and descend into sub-lists. +% The K term is one of expando or contracto, and the J term is used +% on sub-lists, i.e. expando/grow and contracto/shrink. +% BTW, "rebo" is a meaningless name, don't break your brain +% trying to figure it out. + +rebo(K, J) --> K , rebo(K, J). +rebo(K, J), [list(E)] --> [list([H|T])], !, {call(J, [H|T], E)}, rebo(K, J). +rebo(K, J), [ A ] --> [ A ], !, rebo(K, J). +rebo(_, _) --> []. + +to_fixed_point(DCG, Ei, Eo) :- + phrase(DCG, Ei, E), % Apply DCG... + (Ei=E -> Eo=E ; to_fixed_point(DCG, E, Eo)). % ...until a fixed-point is reached. + +grow --> to_fixed_point(rebo(expando, grow )). +shrink --> to_fixed_point(rebo(contracto, shrink)). + +% ?- phrase(grow, [symbol(third)], Out). +% Out = [symbol(rest), symbol(rest), symbol(first)] ; +% Out = [symbol(rest), symbol(rest), symbol(first)] ; +% Out = [symbol(rest), symbol(second)] ; +% Out = [symbol(third)]. + +% ?- phrase(shrink, [symbol(rest), symbol(rest), symbol(first)], Out). +% Out = [symbol(rrest), symbol(first)] ; +% Out = [symbol(third)] ; +% Out = [symbol(rest), symbol(second)] ; +% Out = [symbol(rest), symbol(rest), symbol(first)]. + + +/* + +███████╗ ██████╗ ██████╗ ███╗ ███╗ █████╗ ████████╗████████╗███████╗██████╗ +██╔════╝██╔═══██╗██╔══██╗████╗ ████║██╔══██╗╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗ +█████╗ ██║ ██║██████╔╝██╔████╔██║███████║ ██║ ██║ █████╗ ██████╔╝ +██╔══╝ ██║ ██║██╔══██╗██║╚██╔╝██║██╔══██║ ██║ ██║ ██╔══╝ ██╔══██╗ +██║ ╚██████╔╝██║ ██║██║ ╚═╝ ██║██║ ██║ ██║ ██║ ███████╗██║ ██║ +╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ + + +?- phrase(joy_parse(E), `22 18 true [false] [1[2[3]]]`), !, format_joy_terms(E, A, []), string_codes(S, A). +E = [int(22), int(18), bool(true), list([bool(false)]), list([int(1), list([...|...])])], +A = [50, 50, 32, 49, 56, 32, 116, 114, 117|...], +S = "22 18 true [false] [1 [2 [3]]]". + +*/ + +format_joy_expression( int(I)) --> { number_codes(I, Codes) }, Codes. +format_joy_expression( bool(B)) --> { atom_codes(B, Codes) }, Codes. +format_joy_expression(symbol(S)) --> { atom_codes(S, Codes) }, Codes. +format_joy_expression( list(J)) --> "[", format_joy_terms(J), "]". + +format_joy_terms( []) --> []. +format_joy_terms( [T]) --> format_joy_expression(T), !. +format_joy_terms([T|Ts]) --> format_joy_expression(T), " ", format_joy_terms(Ts). + +joy_terms_to_string(Expr, String) :- + format_joy_terms(Expr, Codes, []), + string_codes(String, Codes). + + +/* + +██████╗ █████╗ ██████╗ ████████╗██╗ █████╗ ██╗ +██╔══██╗██╔══██╗██╔══██╗╚══██╔══╝██║██╔══██╗██║ +██████╔╝███████║██████╔╝ ██║ ██║███████║██║ +██╔═══╝ ██╔══██║██╔══██╗ ██║ ██║██╔══██║██║ +██║ ██║ ██║██║ ██║ ██║ ██║██║ ██║███████╗ +╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚══════╝ + +██████╗ ███████╗██████╗ ██╗ ██╗ ██████╗███████╗██████╗ +██╔══██╗██╔════╝██╔══██╗██║ ██║██╔════╝██╔════╝██╔══██╗ +██████╔╝█████╗ ██║ ██║██║ ██║██║ █████╗ ██████╔╝ +██╔══██╗██╔══╝ ██║ ██║██║ ██║██║ ██╔══╝ ██╔══██╗ +██║ ██║███████╗██████╔╝╚██████╔╝╚██████╗███████╗██║ ██║ +╚═╝ ╚═╝╚══════╝╚═════╝ ╚═════╝ ╚═════╝╚══════╝╚═╝ ╚═╝ + +Partial Reducer from "The Art of Prolog" by Sterling and Shapiro +Program 18.3, pg. 362 */ + +process(Program, ReducedProgram) :- + findall(PC1, (member(C1, Program), preduce(C1, PC1)), ReducedProgram). + +preduce( (A :- B), (Pa :- Pb) ) :- !, preduce(B, Pb), preduce(A, Pa). +preduce( true, true ) :- !. +preduce( (A, B), Residue ) :- !, preduce(A, Pa), preduce(B, Pb), combine(Pa, Pb, Residue). +% preduce( A, B ) :- should_fold(A, B), !. +preduce( A, Residue ) :- should_unfold(A), !, clause(A, B), preduce(B, Residue). +preduce( A, A ). + +% As {*,1} and {+,0} so we have {(,),true}. Whatsitsname? Monoid or something... +% {*,0} {+,Inf} {(,),fail}... + +combine(true, B, B) :- !. +combine(A, true, A) :- !. +combine(A, B, (A, B)). + +/* + +Partial reduction of thun/3 in the thun/4 relation gives a new +version of thun/4 that is tail-recursive. You generate the new +relation rules like so: + + ?- thunder(C), process(C, R), maplist(portray_clause, R). + +I just cut-n-paste from the SWI terminal and rearrange it. + +*/ + +should_unfold(thun(_, _, _)). +should_unfold(func(_, _, _)). +should_unfold(def(_, _)). + +thunder([ % Source code for thun/4. + (thun( int(I), E, Si, So) :- thun(E, [ int(I)|Si], So)), + (thun(bool(B), E, Si, So) :- thun(E, [bool(B)|Si], So)), + (thun(list(L), E, Si, So) :- thun(E, [list(L)|Si], So)), + (thun(symbol(Def), E, Si, So) :- def(Def, [Head|Body]), append(Body, E, Eo), thun(Head, Eo, Si, So)), + (thun(symbol(Func), E, Si, So) :- func(Func, Si, S), thun(E, S, So)), + (thun(symbol(Combo), E, Si, So) :- combo(Combo, Si, S, E, Eo), thun(Eo, S, So)) +]). + +partial_reduce_thun :- + thunder(C), + process(C, R), + setup_call_cleanup( + open("gen-defs+funcs.pl", write, Out), + maplist(portray_clause(Out), R), + close(Out) + ). + + +/* + +N.B.: in 'thun(symbol(Def)...' the last clause has changed from thun/3 to thun/4. +The earlier version doesn't transform into correct code: + + thun(symbol(B), D, A, A) :- def(B, C), append(C, D, []). + thun(symbol(A), C, F, G) :- def(A, B), append(B, C, [D|E]), thun(D, E, F, G). + +With the change to thun/4 it doesn't transform under reduction w/ thun/3. + +You can also unfold def/2 and func/3 (but you need to check for bugs!) + +Functions become clauses like these: + + thun(symbol(rolldown), [], [C, A, B|D], [A, B, C|D]). + thun(symbol(rolldown), [A|B], [E, C, D|F], G) :- thun(A, B, [C, D, E|F], G). + + thun(symbol(dupd), [], [A, B|C], [A, B, B|C]). + thun(symbol(dupd), [A|B], [C, D|E], F) :- thun(A, B, [C, D, D|E], F). + + thun(symbol(over), [], [B, A|C], [A, B, A|C]). + thun(symbol(over), [A|B], [D, C|E], F) :- thun(A, B, [C, D, C|E], F). + +Definitions become + + thun(symbol(of), A, D, E) :- + append([symbol(swap), symbol(at)], A, [B|C]), + thun(B, C, D, E). + + thun(symbol(pam), A, D, E) :- + append([list([symbol(i)]), symbol(map)], A, [B|C]), + thun(B, C, D, E). + + thun(symbol(popd), A, D, E) :- + append([list([symbol(pop)]), symbol(dip)], A, [B|C]), + thun(B, C, D, E). + +These are tail-recursive and allow for better indexing so I would expect +them to be more efficient than the originals. Ii would be even nicer to +get them looking like this: + + thun(symbol(of), A, D, E) :- thun(symbol(swap), [symbol(at)|A], D, E). + +And then if 'swap' was a definition you could push it out even further, +you could pre-expand definitions and functions (and maybe even some +combinators!) + +*/ diff --git a/implementations/Prolog/source/thun_compile.pl b/implementations/Prolog/source/thun_compile.pl new file mode 100644 index 0000000..fad1970 --- /dev/null +++ b/implementations/Prolog/source/thun_compile.pl @@ -0,0 +1,295 @@ +:- use_module(library(clpfd)). +:- [thun]. +/* + + Copyright © 2018, 2019, 2020 Simon Forman + + This file is part of Thun + + Thun is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Thun is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Thun. If not see . + + +The enviroment or context is a predicate reggy/4: + + reggy(FreePool, References, Values, Code) + +The FreePool is a list of atoms that each denote a free register; +References is a list of register atoms that keeps track of how many times +a register is used (it is in lieu of reference counting); Values is an +assoc list mapping register atoms to their current values; and lastly +Code is a list of machine code predicates emitted by the compiler. + + */ + +% just to hush the linter, which won't respect consult/1. +% def(Name, _). +% func(Name, _, _). +% combo(Name, _, _, _, _). +% joy_parse(_, _, _). + + +encode_list(List, addr(list(List))) --> []. + +% Retrieve the next free register. +get_reggy([], _, _) :- writeln('Out of Registers'), fail. +get_reggy([Reg|FreePool], Reg, FreePool). + +% free one reference and de-allocate if it was the last. +free_reg(Reg, Value, reggy(FreePool0, References0, V0, Code), + reggy(FreePool, References, V, Code)) :- + select(Reg, References0, References), + get_assoc(Reg, V0, Value), + ( member(Reg, References) % If reg is still in use + -> FreePool= FreePool0, V0=V % we can't free it yet + ; FreePool=[Reg|FreePool0], % otherwise we put it back in the pool. + del_assoc(Reg, V0, _, V) + ). + +add_ref(Reg, reggy(FreePool, References, V, Code), + reggy(FreePool, [Reg|References], V, Code)). + +assoc_reg(Reg, Value, reggy(FreePool0, References, V0, Code), + reggy(FreePool, [Reg|References], V, Code)) :- + get_reggy(FreePool0, Reg, FreePool), + put_assoc(Reg, V0, Value, V). + +fresh_env(reggy( % Create a fresh new env/context with... + [r0, r1, r2, r3, % Available registers + r4, r5, r6, r7, + r8, r9, rA, rB, + rC, rD, rE, rF], + [], % References. + V, % Register to value assoc list. + [] % List of (pseudo-)machine code. + )) :- + empty_assoc(V). + + +emit([]) --> []. +emit([A|Rest]) --> emit(A), emit(Rest). +emit(A) --> { A \= [], A \= [_|_] }, emit_code(A). + +emit_code(C, reggy(FreePool, References, V, [C|Code]), + reggy(FreePool, References, V, Code )). + + +/* Compiling + +THread through the env/context as DCG dif-lists + +*/ + +thun_compile(E, Si, So, Env) :- + fresh_env(Env0), + thun_compile(E, Si, So, Env0, Env). + +thun_compile([], S, S) --> []. +thun_compile([Term|Rest], Si, So) --> thun_compile(Term, Rest, Si, So). + +thun_compile(int(I), E, Si, So) --> + emit(mov_imm(R, int(I))), + assoc_reg(R, int(I)), + thun_compile(E, [R|Si], So). + +thun_compile(bool(B), E, Si, So) --> + assoc_reg(R, bool(B)), + thun_compile(E, [R|Si], So). + +thun_compile(list(L), E, Si, So) --> + encode_list(L, Addr), + assoc_reg(R, Addr), + emit(load_imm(R, Addr)), + thun_compile(E, [R|Si], So). + +thun_compile(symbol(Name), E, Si, So) --> + { def(Name, _) } -> def_compile(Name, E, Si, So) ; + { func(Name, _, _) } -> func_compile(Name, E, Si, So) ; + { combo(Name, _, _, _, _) } -> combo_compile(Name, E, Si, So). + + +% I'm going to assume that any defs that can be compiled to funcs already +% have been. Defs that can't be pre-compiled shove their body expression +% onto the pending expression (continuation) to be compiled "inline". + +def_compile(Def, E, Si, So) --> + { def(Def, Body), append(Body, E, Eo) }, + thun_compile(Eo, Si, So). + + +% swap (et. al.) doesn't change register refs nor introspect values +% so we can delegate its effect to the semantic relation. +non_alloc(swap). +non_alloc(rollup). +non_alloc(rolldown). + +% Functions delegate to a per-function compilation relation. + +func_compile(+, E, [A, B|S], So) --> !, + free_reg(A, int(N)), + free_reg(B, int(M)), + assoc_reg(R, int(K)), + emit(add(R, A, B)), + { K #= N + M }, + % Update value in the context? + thun_compile(E, [R|S], So). + +func_compile(dup, E, [A|S], So) --> !, + add_ref(A), + thun_compile(E, [A, A|S], So). + +func_compile(pop, E, [A|S], So) --> !, + free_reg(A, _), + thun_compile(E, S, So). + +func_compile(cons, E, [List, Item|S], So) --> !, + % Assume list is already stored in RAM + % and item ... + % allocate a cons cell + emit(alloc_cons(list(Item, List))), + % https://mitpress.mit.edu/sites/default/files/sicp/full-text/book/book-Z-H-33.html#%_sec_5.3 + thun_compile(E, S, So). + +func_compile(Func, E, Si, So) --> { non_alloc(Func), !, + func(Func, Si, S) }, + thun_compile(E, S, So). + +func_compile(_Func, E, Si, So) --> + % look up function, compile it... + {Si = S}, + thun_compile(E, S, So). + + +combo_compile(_Combo, E, Si, So) --> + % look up combinator, compile it... + {Si = S, E = Eo}, + thun_compile(Eo, S, So). + + +compiler(InputString, StackIn, StackOut, FreePool0, References0, Values0, MachineCode0, FreePool, References, Values, MachineCode) :- + phrase(joy_parse(Expression), InputString), !, + thun_compile(Expression, StackIn, StackOut, + reggy(FreePool0, References0, Values0, MachineCode0), + reggy(FreePool, References, Values, MachineCode ) + ). + + % phrase(thun_compile(Expression, StackIn, StackOut, _), MachineCode, []). + + + +compiler(InputString, StackIn, StackOut, FreePool, References, Values, MachineCode) :- + [r0, r1, r2, r3, % Available registers + r4, r5, r6, r7, + r8, r9, rA, rB, + rC, rD, rE, rF]=FreePool0, + empty_assoc(Values0), + compiler(InputString, StackIn, StackOut, + FreePool0, [], Values0, MachineCode, + FreePool, References, Values, []). + + +% compiler(`3 +`, [r0|StackIn], StackOut, [r1, r2, r3, r4, ], [r0], Values0, MachineCode0, FreePool, References, Values, MachineCode). + +/* + +compiler(`2`, StackIn, Stack1, FreePool0, References0, Values0, MachineCode0), +compiler(`3 +`, Stack1, StackOut, FreePool0, References0, Values0, MachineCode1, FreePool, References, Values, []). + +?- compiler(`2`, StackIn, Stack1, FreePool0, References0, Values0, MachineCode0), + compiler(`3 +`, Stack1, StackOut, FreePool0, References0, Values0, MachineCode1, FreePool, References, Values, []).| compiler(`3 +`, Stack1, StackOut, FreePool0, References0, Values0, MachineCode1, FreePool, References, Values, []). +Stack1 = StackOut, StackOut = [r0|StackIn], +FreePool0 = FreePool, FreePool = [r1, r2, r3, r4, r5, r6, r7, r8, r9|...], +References0 = References, References = [r0], +Values0 = t(r0, int(2), -, t, t), +MachineCode0 = [mov_imm(r0, int(2))], +MachineCode1 = [mov_imm(r1, int(3)), add(r0, r1, r0)], +Values = t(r0, int(_19548), -, t, t) . + + */ + + +% show_compiler(InputString, StackIn, StackOut) :- +% phrase(joy_parse(Expression), InputString), !, +% phrase(thun_compile(Expression, StackIn, StackOut, reggy(_, _, V)), MachineCode, []), +% maplist(portray_clause, MachineCode), +% assoc_to_list(V, VP), +% portray_clause(VP). + + + +/* + +So what happens when you compile just an integer literal? + +?- thun_compile([int(23)], Si, So, reggy(FreePool, References, Values, Code)). +So = [r0|Si], +FreePool = [r1, r2, r3, r4, r5, r6, r7, r8, r9|...], +References = [r0], +Values = t(r0, int(23), -, t, t), +Code = [mov_imm(r0, int(23))]. + +The int is put onto the next available register, which is returned on the stack. + + +?- compiler(`2 3 +`, MachineCode, StackIn, StackOut). +MachineCode = [mov_imm(r0, int(2)), mov_imm(r1, int(3)), add(r0, r1, r0)], +StackOut = [r0|StackIn] ; +false. + + + + + + + + + + + + + + +?- phrase(grow, [symbol('&&')], Out), writeln(Out). + + +[ + list([ + list([symbol(stack)]), + symbol(dip), + symbol(swap), + symbol(cons), + symbol(swaack), + list([symbol(i)]), + symbol(dip), + symbol(swaack), + symbol(first) + ]), + msymbol(cons), + list([ + list([symbol(stack)]), + symbol(dip), + symbol(swap), + symbol(cons), + symbol(swaack), + list([symbol(i)]), + symbol(dip), + symbol(swaack), + symbol(first), + list([bool(false)]) + ]), + symbol(dip), + symbol(branch) +] + + + */ \ No newline at end of file diff --git a/implementations/Prolog/test/test_thun.pl b/implementations/Prolog/test/test_thun.pl new file mode 100644 index 0000000..432725a --- /dev/null +++ b/implementations/Prolog/test/test_thun.pl @@ -0,0 +1,32 @@ +:- ["../source/thun.pl"]. +/* + +Tests + +Woefully inadequate, but it's a start. + +Run test/0. + + ?- tests. + YES! test_parser([],[[]]) + YES! test_parser([32],[[]]) + YES! test_parser([91,93],[[list([])]]) + YES! test_parser([50,51],[[int(23)]]) + YES! test_parser([50,91,51,93],[[int(2),list([int(3)])]]) + true. + +*/ + +tests :- forall(test_case(T), test(T)). + +test(Goal) :- (Goal -> write("YES! ") ; write("no! ")), writeln(Goal). + +test_parser(Source, Exprs) :- findall(Expr, joy_parse(Expr, Source, []), Exprs). + +test_case(test_parser(``, [[]])). +test_case(test_parser(` `, [[]])). +test_case(test_parser(`[]`, [[list([])]])). +test_case(test_parser(`23`, [[ int(23)]])). +test_case(test_parser(`2[3]`, [[int(2), list([int(3)])]])). + +% and so on...