gulcii 0.2.0.3 → 0.3
raw patch · 29 files changed
+1514/−423 lines, 29 filesdep ~filepathdep ~gtkbinary-added
Dependency ranges changed: filepath, gtk
Files
- README.md +104/−0
- doc/Makefile +12/−0
- doc/encoding.md +518/−0
- doc/encoding.tex +31/−0
- doc/visualisation.png binary
- extra/fudi2midi.pd +0/−59
- extra/gulcii.pd +29/−0
- extra/gulcii_m.pd +29/−0
- extra/gulcii_v.pd +78/−0
- gulcii.cabal +36/−47
- lib/church.gu +33/−0
- lib/edinburgh.gu +55/−0
- lib/event.gu +0/−6
- lib/mars.gu +0/−5
- lib/midi.gu +0/−7
- lib/prelude.gu +0/−2
- src/Bruijn.hs +11/−17
- src/Command.hs +2/−3
- src/Draw.hs +29/−31
- src/GC.hs +85/−0
- src/Graph.hs +10/−98
- src/Lambda.hs +1/−5
- src/Layout.hs +53/−23
- src/Main.hs +142/−109
- src/Meta.hs +11/−3
- src/Parse.hs +4/−3
- src/Reduce.hs +142/−0
- src/Setting.hs +97/−0
- src/Sugar.hs +2/−5
+ README.md view
@@ -0,0 +1,104 @@+GULCII+======++GULCII is an untyped lambda calculus interpreter supporting interactive+modification of a running program with graphical display of graph reduction.+++Syntax+------++Lambda calculus terms with some sugar coating:+ + term ::= variable -- free or bound+ | '\' var+ strategy term -- lambda abstraction+ | term+ -- application+ | '(' term ')'+ | integer+ | list+ variable ::= [a-z][A-Za-z0-9]*+ strategy = '.' | '!' | '?' -- lazy | strict | copy+ integer ::= [0-9]+ -- uses Scott-encoding+ list ::= '[' (term (, T)*)? ']' -- uses Scott-encoding++There are three variants of lambda abstraction: lazy, strict, and copy:++ \x . f x x -- x is evaluated lazily with sharing+ \x ! f x x -- x is evaluated strictly and shared+ \x ? f x x -- x is copied before any evaluation++There is additional syntax sugar for natural numbers and lists, using+<http://en.wikipedia.org/wiki/Mogensen%E2%80%93Scott_encoding#Scott_encoding>:++ [0,1,2,3]++There is a small standard library based loosely around the Haskell Prelude:++ :load prelude+ :browse++If you define a term using free variables, they can be modified while+the program is running, but sharing is lost. If you define a term as+a fixed point (perhaps with Y-combinator) then sharing works (to some+extent), but you can't modify the code while it is running any more.+++Meta Commands+-------------++To exit type:++ :quit++Entering a term evaluates it.++Terms can be bound to names, stored in a global dictionary:++ foo = bar++The global dictionary can be listed or wiped clean:++ :browse+ :clear++Installed files can be loaded:++ :load church++Machine-readable node statistics are output to stdout, as well as when+free variables are instantiated by looking up their definitions. These+are intended to be used for sonification, for example with Pure-data:++ pd extra/gulcii.pd &+ sleep 5+ gulcii | pdsend 8765++The sonification is controlled by two commands:++ :start+ :stop+++Settings+--------++There are some runtime adjustable settings:++ :get NewsOnTop+ :set NewsOnTop+ :unset NewsOnTop+ :toggle NewsOnTop++Where NewsOnTop is a setting. Settings include:++ TraceEvaluation+ CollectGarbage+ RealTimeDelay+ RealTimeAcceleration+ RetryIrreducible+ EmitStatistics+ EmitRebindings+ EchoToStdOut+ EchoToGUI+ SaveImages+ NewsOnTop
+ doc/Makefile view
@@ -0,0 +1,12 @@+all: encoding.pdf++clean:+ -rm -f encoding.aux encoding.log encoding.md.tex encoding.nav encoding.out encoding.pdf encoding.snm encoding.toc encoding.vrb++encoding.pdf: encoding.tex encoding.md.tex+ pdflatex encoding.tex+ pdflatex encoding.tex+ pdflatex encoding.tex++encoding.md.tex: encoding.md+ pandoc -t beamer --slide-level 2 encoding.md -o encoding.md.tex
+ doc/encoding.md view
@@ -0,0 +1,518 @@+# Lambda calculus encodings++## Notation++Backslash is used for lambda:++* easier to type+* familiar from Haskell++Neighbouring lambdas can be combined with one backslash:++ \a b s z . a s (b s z)++Corresponds to:++ λa. λb. λs. λz. a s (b s z)+++## Church and Scott encodings++* encode data as lambda terms+* continuation passing style+* Church folds vs Scott case analysis+++## Continuation passing style++* the datum is a black box that knows itself+* the datum is passed functions that it calls with its deconstruction+* the datum has one argument per constructor+* each continuation has one argument per constructor argument+++## Simple types++Church and Scott encoding coincide for simple types.+++## Bool++Haskell:++ data Bool+ = False+ | True++Church, Scott:++ true = \t f . t+ false = \t f . f++ and = \a b . a b a+ or = \a b . a a b+ not = \a . a false true++The datum "true" takes two arguments, and returns the first, which (by+convention) denotes the value True.+++## Pair++Haskell:++ data Pair a b+ = Pair a b++Church, Scott:++ pair = \a b p . p a b++ fst = \p . p (\a b . a)+ snd = \p . p (\a b . b)++The datum "pair x y" takes one argument, which is a function of two arguments,+and passes it the stored values of "x" and "y".+++## Maybe++Haskell:++ data Maybe a+ = Nothing+ | Just a++Church, Scott:++ nothing = \n j . n+ just = \a n j . j a++ maybe = \n j m . m n j+++## Either++Haskell:++ data Either a b+ = Left a+ | Right b++Church, Scott:++ left = \a l r . l a+ right = \b l r . r b++ either = \l r e . e l r+++## Recursive types++* Church and Scott encoding differ for recursive types.++* Church encoding uses folds.++ The deconstruction continuation threads throughout+ the structure.++* Scott encoding is similar to case analysis.++ The deconstruction continuation+ unwraps one layer of constructors only.+++## Natural numbers++Haskell:++ data Nat+ = Zero+ | Succ Nat+ +Church:++ zero = \s z . z+ succ = \n s z . s (n s z)+ +Scott:++ zero = \s z . z+ succ = \n s z . s n+++## Nat examples++Haskell:++ Zero, Succ Zero, Succ(Succ Zero), Succ(Succ(Succ Zero))++Church, applying the same "s" "n" times at once:++ \s z . z+ \s z . s z+ \s z . s (s z)+ \s z . s (s (s z))++Scott, applying different "s" "n" times separately:++ \s z . z+ \s z . s (\s z . z)+ \s z . s (\s z . s (\s z . z))+ \s z . s (\s z . s (\s z . s (\s z . z)))+++## Church Nat succ++ zero = \s z . z+ succ = \n s z . s (n s z)++ succ zero+ = {- definition of succ -}+ (\n s z . s (n s z)) zero+ = {- beta -}+ \s z . s (zero s z)+ = {- definition of zero -}+ \s z . s ((\s z . z) s z)+ = {- beta -}+ \s z . s ((\z . z) z)+ = {- beta -}+ \s z . s z+ = {- definition of one -}+ one+++## Scott Nat succ++ zero = \s z . z+ succ = \n s z . s n++ succ zero+ = {- definition of succ -}+ (\n s z . s n) zero+ = {- beta -}+ \s z . s zero+ = {- definition of zero -}+ \s z . s (\s z . z)+ = {- definition of one -}+ one+++## Nat arithmetic++Church:++ add = \m n . m succ n+ mul = \m n . m (add n) zero+ exp = \m n . n m++Scott, open terms with `letrec`:++ add = \m n . m (\p . succ (add p n)) n+ mul = \m n . m (\p . add n (mul p n)) zero+ exp = \m n . n (\p . mul m (exp m p)) one++Scott, closed terms with `fix`:++ add = fix (\add . \m n . m (\p . succ (add p n)) n)+ mul = fix (\mul . \m n . m (\p . add n (mul p n)) zero)+ exp = fix (\exp . \m n . n (\p . mul m (exp m p)) one)+++## Fixed point combinator++Semantics:++ fix f = f (fix f)++Implementation:++ \f . (\x . f (x x)) (\x . f (x x))++What it computes:++* The unique least fixed point under the definedness order.++* Allows recursive functions to be defined as closed terms.+++## Nat predecessor++Church (courtesy Wikipedia):++ pred = \n f x . n (\g h . h (g f)) (\u . x) (\v . v)++Scott:++ pred = \n . n (\p . p) zero+++## Nat conversion++Church arithmetic is more concise (and doesn't need `fix`).++Scott predecessor is comprehensible.++Mix and match?++ churchToScott = \n . n scottSucc scottZero++ scottToChurch = \n . n+ (\p . churchSucc (scottToChurch p))+ churchZero++ scottToChurch = fix (\scottToChurch . \n . n+ (\p . churchSucc (scottToChurch p))+ churchZero)+++## Nat subtract and equality++Church:++ sub = \m n . n pred m++Scott:++ sub = \m n . m+ (\p . n (\q . sub p q) m)+ zero+ equal = \m n . m+ (\p . n (\q . equal p q) false)+ (n (\q . false) true)++Unwrap a layer of constructor from each number and recurse.++There is a different `equal` for booleans:++ equalBool = \a b . a b (not b)+++## List++Haskell:++ data List a+ = Nil+ | Cons a (List a)++Church:++ nil = \c n . n+ cons = \x xs c n . c x (xs c n)++Scott:++ nil = \c n . n+ cons = \x xs c n . c x xs+++## List operations++Church, Scott:++ isnil = \l . l (\x xs . false) true+ head = \l . l (\x xs . x) error++Church (`tail` courtesy Wikipedia):++ length = \l . l (\x xs . succ xs) zero+ tail = \l c n . l+ (\x xs g . g x (xs c)) (\xs . n) (\x xs . xs)++Scott:++ length = \l . l (\x xs . succ (length xs)) zero+ tail = \l . l (\x xs . xs) nil+++## More Scott functions++ compose = \f g x . f (g x)++ fold = \f e l . l (\x xs . f x (fold f e xs)) e++ sum = fold add zero+ ands = fold and true+ ors = fold or false++ map = \f . fold (compose cons f) nil+ all = \f . compose ands (map f)+ any = \f . compose ors (map f)++ take = \n l. n(\p. l (\x xs. cons x (take p xs))nil)nil+ drop = \n l. n(\p. l (\x xs. drop p xs) nil) l++ iterate = \f x . cons x (iterate f (f x))+++# How to perform lambda calculus+++## How to perform lambda calculus++* single step graph reduction++* visualisation of current state++* sonification of changes in state++* open terms vs closed terms+++## Graph reduction++ data Term+ = Free String+ | Reference Integer+ | Bound -- de Bruijn index 0+ | Scope Term -- see Lambdascope paper+ | Lambda Strategy Term+ | Apply Term Term++ reduce+ :: Definitions -- Map String Term+ -> References -- Map Integer Term+ -> Term+ -> Maybe (References, Term)+++## Three kinds of Lambda++* strict (syntax inspired by Haskell's -XBangPatterns):++ (\v ! s) t++ t is fully reduced before substitution into s.++* copy:++ (\v ? s) t++ t is substituted for each occurrence of v in s.++* lazy:++ (\v . s) t++ a new `Reference` is created for t, and substituted into s.++ `reduce` reduces *inside* the `References` until it is irreducible, at which+ point the `Reference` is replaced with the `Term` it refers to.+++## Visualisation++{width=100%}\ ++## Sonification++* count number of nodes of each type++* statistics are forwarded to a Pure-data patch++* changes in each count control a harmonic (one for each type of node) in a+ simple phase modulation synth+++## Open terms++* free variables looked up on demand from environment++* allows definitions to be changed at runtime++* easier to write+++## Drawbacks of open terms++* no sharing++ subterms can be evaluated many times due to duplication++* exponential work (worst case)++* exponential space (worst case)+++## Fixed points++* closed terms with fixed point combinators++* allows evaluation to be shared++* sharing can be vital for efficiency+++# Future work+++## Better Evaluator++* current evaluator is still somewhat ad-hoc and doesn't preserve sharing++* previous evaluator even had correctness bugs++* switch to using Lambdascope (or similar) as a library?+++## Auto Fix++Automatically translating open terms to use fixed point combinators:++* recursive functions can use `fix`+* mutually recursive functions can use `fix` combined with tuples++ many = some `orElse` none+ some = one `andThen` many++ becomes:++ manysome = fix (\p -> pair+ (snd p `orElse` none)+ (one `andThen` fst p) )+ many = fst manysome+ some = snd manysome+++## Magic It++* refer to previously evaluated terms+* including the currently evaluating term+* without restarting evaluation++Haskell example (`ghci-8.0.1`):++ > 3+ 3+ > it + 5+ 8+ > it * 2+ 16+++## Further Performances / Project Ideas++* "An infinite deal of nothing", a variety of non-terminating loops each with+ their own intrinsic computational rhythm.++* Implement in untyped lambda calculus an interpreter for a known+ Turing-complete tape mutation based language and run some simple programs+ in it.++ Illustrates Turing-completeness of untyped lambda calculus, albeit slowly.+++# EOF++## EOF++Thanks!++Questions?++ https://mathr.co.uk+ mailto:claude@mathr.co.uk++ https://hackage.haskell.org/package/gulcii+ https://code.mathr.co.uk/gulcii
+ doc/encoding.tex view
@@ -0,0 +1,31 @@+\documentclass[aspectratio=43]{beamer}+\usepackage{lmodern}+\usepackage[utf8]{inputenc}+\usepackage[T1]{fontenc}+\DeclareUnicodeCharacter{03BB}{\(\lambda\)}+\AtBeginSection[]{\begin{frame}\frametitle{Outline}\tableofcontents[currentsection]\end{frame}}+\def\tightlist{}++\definecolor{mathr}{RGB}{128,64,64}+\setbeamercolor{structure}{fg=mathr}+\setbeamercolor{title}{fg=mathr}+\setbeamercolor{titlelike}{fg=mathr}+\beamertemplatenavigationsymbolsempty++\renewcommand{\familydefault}{\sfdefault}++\title[GULCII]{Graphical\\ Untyped Lambda Calculus\\ Interactive Interpreter}+\subtitle{(GULCII)}+\author{Claude Heiland-Allen}+\institute{\url{https://mathr.co.uk} \\ \url{mailto:claude@mathr.co.uk}}+\date[Ed 2017]{Edinburgh, 2017}+\subject{Computer Science}++\begin{document}++\begin{frame} \titlepage \end{frame}+\begin{frame} \frametitle{Outline} \tableofcontents \end{frame}++\input{encoding.md.tex}++\end{document}
+ doc/visualisation.png view
binary file changed (absent → 127718 bytes)
− extra/fudi2midi.pd
@@ -1,59 +0,0 @@-#N canvas 0 0 547 342 10;-#X obj 28 2 netreceive 8765;-#X obj 29 172 noteout;-#X msg 29 127 \$2 \$3 \$1;-#X msg 35 148 \$2 0 \$1;-#X obj 109 173 pgmout;-#X msg 109 150 \$2 \$1;-#X obj 176 173 ctlout;-#X msg 177 150 \$3 \$2 \$1;-#X msg 242 150 \$2 \$1;-#X obj 242 171 touchout;-#X msg 315 148 \$3 \$2 \$1;-#X obj 315 169 polytouchout;-#X msg 398 148 \$2 \$1;-#X obj 398 169 bendout;-#X obj 371 84 spigot;-#X obj 404 18 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1-1;-#X obj 158 1 print connection;-#X obj 371 105 print unhandled;-#X obj 416 44 spigot;-#X obj 447 16 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0-1;-#X obj 418 65 print debug;-#X obj 29 48 route noteon noteoff program control pressure touch bend-rebound;-#X floatatom 116 29 5 0 0 0 - - -;-#X obj 300 12 route rebound;-#X obj 296 36 print midi;-#X obj 297 -7 spigot;-#X obj 342 -5 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1-1;-#X connect 0 0 18 0;-#X connect 0 0 21 0;-#X connect 0 0 25 0;-#X connect 0 1 16 0;-#X connect 0 1 22 0;-#X connect 2 0 1 0;-#X connect 3 0 1 0;-#X connect 5 0 4 0;-#X connect 7 0 6 0;-#X connect 8 0 9 0;-#X connect 10 0 11 0;-#X connect 12 0 13 0;-#X connect 14 0 17 0;-#X connect 15 0 14 1;-#X connect 18 0 20 0;-#X connect 19 0 18 1;-#X connect 21 0 2 0;-#X connect 21 1 3 0;-#X connect 21 2 5 0;-#X connect 21 3 7 0;-#X connect 21 4 8 0;-#X connect 21 5 10 0;-#X connect 21 6 12 0;-#X connect 21 8 14 0;-#X connect 23 1 24 0;-#X connect 25 0 23 0;-#X connect 26 0 25 1;
+ extra/gulcii.pd view
@@ -0,0 +1,29 @@+#N canvas 3 58 335 369 10;+#X obj 18 10 netreceive 8765;+#X obj 21 101 loadbang;+#X obj 21 122 delay 1000;+#X msg 22 148 \; pd dsp 1;+#X obj 121 40 print;+#X obj 141 252 catch~ \$0-r;+#X obj 70 209 gulcii_v \$0 0;+#X obj 27 251 catch~ \$0-l;+#X obj 19 40 s \$0-control;+#X obj 168 126 list prepend set;+#X obj 168 148 list trim;+#X msg 168 170 rebound succ;+#X obj 59 65 route statistics;+#X obj 90 310 dac~;+#X obj 27 273 expr~ tanh($v1);+#X obj 142 275 expr~ tanh($v1);+#X connect 0 0 8 0;+#X connect 0 0 12 0;+#X connect 0 1 4 0;+#X connect 1 0 2 0;+#X connect 2 0 3 0;+#X connect 5 0 15 0;+#X connect 7 0 14 0;+#X connect 9 0 10 0;+#X connect 10 0 11 0;+#X connect 12 1 9 0;+#X connect 14 0 13 0;+#X connect 15 0 13 1;
+ extra/gulcii_m.pd view
@@ -0,0 +1,29 @@+#N canvas 3 58 450 300 10;+#X obj 21 22 receive~ \$1-p;+#X obj 20 218 throw~ \$1-l;+#X obj 97 218 throw~ \$1-r;+#X obj 21 134 expr~ sin($v1*6.283185307179586);+#X obj 97 157 expr~ sin($v1*6.283185307179586);+#X obj 147 19 inlet;+#X obj 147 40 float;+#X obj 147 61 vline~;+#X obj 21 43 *~ \$2;+#X obj 97 189 *~;+#X obj 20 192 *~;+#X obj 96 106 +~ 0.25;+#X obj 147 82 hip~ 1;+#X obj 147 103 lop~ 15;+#X connect 0 0 8 0;+#X connect 3 0 10 0;+#X connect 4 0 9 0;+#X connect 5 0 6 0;+#X connect 6 0 7 0;+#X connect 7 0 12 0;+#X connect 8 0 3 0;+#X connect 8 0 11 0;+#X connect 9 0 2 0;+#X connect 10 0 1 0;+#X connect 11 0 4 0;+#X connect 12 0 13 0;+#X connect 13 0 9 1;+#X connect 13 0 10 1;
+ extra/gulcii_v.pd view
@@ -0,0 +1,78 @@+#N canvas 3 63 472 453 10;+#X obj 46 214 vline~;+#X obj 37 392 *~;+#X obj 125 392 *~;+#X obj 182 246 send~ \$0-p;+#X obj 127 312 +~;+#X obj 143 267 catch~ \$0-l;+#X obj 223 267 catch~ \$0-r;+#X obj 209 312 +~;+#X obj 127 345 expr~ sin($v1*6.283185307179586);+#X obj 207 365 expr~ sin($v1*6.283185307179586);+#X obj 18 43 route start stop statistics;+#X obj 37 413 throw~ \$1-l;+#X obj 125 413 throw~ \$1-r;+#X obj 18 18 r \$1-control;+#X obj 358 107 gulcii_m \$0 6;+#X obj 358 128 gulcii_m \$0 5;+#X obj 358 149 gulcii_m \$0 4;+#X obj 358 170 gulcii_m \$0 3;+#X obj 358 191 gulcii_m \$0 2;+#X obj 358 212 gulcii_m \$0 1;+#X obj 143 288 *~ 0.125;+#X obj 224 288 *~ 0.125;+#X obj 133 109 +;+#X obj 163 109 +;+#X obj 193 109 +;+#X obj 223 109 +;+#X obj 253 109 +;+#X obj 132 136 vline~;+#X obj 132 179 lop~ 15;+#X obj 130 228 phasor~;+#X obj 132 158 hip~ 1;+#X obj 131 203 +~ 50;+#X msg 18 166 1 1000;+#X msg 80 168 0 1000;+#X obj 143 68 unpack f f f f f f;+#X connect 0 0 1 0;+#X connect 0 0 2 0;+#X connect 1 0 11 0;+#X connect 2 0 12 0;+#X connect 4 0 8 0;+#X connect 5 0 20 0;+#X connect 6 0 21 0;+#X connect 7 0 9 0;+#X connect 8 0 1 1;+#X connect 9 0 2 1;+#X connect 10 0 32 0;+#X connect 10 1 33 0;+#X connect 10 2 34 0;+#X connect 13 0 10 0;+#X connect 20 0 4 1;+#X connect 21 0 7 1;+#X connect 22 0 27 0;+#X connect 23 0 22 1;+#X connect 24 0 23 1;+#X connect 25 0 24 1;+#X connect 26 0 25 1;+#X connect 27 0 30 0;+#X connect 28 0 31 0;+#X connect 29 0 3 0;+#X connect 29 0 4 0;+#X connect 29 0 7 0;+#X connect 30 0 28 0;+#X connect 31 0 29 0;+#X connect 32 0 0 0;+#X connect 33 0 0 0;+#X connect 34 0 19 0;+#X connect 34 0 22 0;+#X connect 34 1 18 0;+#X connect 34 1 23 0;+#X connect 34 2 17 0;+#X connect 34 2 24 0;+#X connect 34 3 16 0;+#X connect 34 3 25 0;+#X connect 34 4 15 0;+#X connect 34 4 26 0;+#X connect 34 5 14 0;+#X connect 34 5 26 1;
gulcii.cabal view
@@ -1,84 +1,73 @@ Name: gulcii-Version: 0.2.0.3+Version: 0.3 Synopsis: graphical untyped lambda calculus interactive interpreter Description: GULCII is an untyped lambda calculus interpreter supporting interactive modification of a running program with graphical display of graph reduction. .- There are three variants of lambda abstraction: lazy, strict, and copy:- .- @> \x . f x x -- x is evaluated lazily with sharing@- @> \x ! f x x -- x is evaluated strictly and shared@- @> \x ? f x x -- x is copied before any evaluation@- .- There is additional syntax sugar for natural numbers and lists, using- <http://en.wikipedia.org/wiki/Mogensen%E2%80%93Scott_encoding#Scott_encoding>:- .- @> [0,1,2,3]@- .- There is a small standard library based loosely around the Haskell Prelude,- with additions geared towards MIDI generation for live-coding music, implemented- using a mechanism similar to 'Debug.Trace.trace':- .- @> \x y . { print : x } y@- .- Quick start:- .- > gulcii- > :load mars- > main- > :quit- .- If you have Pure-data and Timidity, try:- .- > timidity -iA -Oj &- > pd -alsamidi ~/.cabal/share/gulcii-0.2.0.3/extra/fudi2midi.pd &- > gulcii | pdsend 8765- .- See also: live-sequencer which has many more features (but no sharing during- evaluation).+ See README.md for the user manual. -Homepage: http://code.mathr.co.uk/gulcii+Homepage: https://code.mathr.co.uk/gulcii License: GPL-2 License-file: LICENSE Author: Claude Heiland-Allen Maintainer: claude@mathr.co.uk-Category: Sound, Music, GUI+Category: Compilers/Interpreters Build-type: Simple Cabal-version: >=1.6-Tested-With: GHC==7.6.3, GHC==7.8.2+Tested-With: GHC==8.2.1 -Data-files: extra/fudi2midi.pd,- lib/bits.gu,+extra-source-files: doc/Makefile+ doc/encoding.md+ doc/encoding.tex+ doc/visualisation.png+ README.md++Data-files: lib/bits.gu, lib/bool.gu,+ lib/church.gu, lib/either.gu,- lib/event.gu, lib/function.gu, lib/list.gu, lib/maybe.gu,- lib/midi.gu, lib/natural.gu, lib/pair.gu, lib/prelude.gu,- lib/mars.gu+ lib/edinburgh.gu,+ extra/gulcii.pd,+ extra/gulcii_m.pd,+ extra/gulcii_v.pd Executable gulcii HS-source-dirs: src Main-is: Main.hs Build-depends: base >= 3 && < 6, containers >= 0.3 && < 0.6,- filepath >= 1.1 && < 1.4,- gtk >= 0.11 && < 0.14,+ filepath >= 1.1 && < 1.5,+ gtk >= 0.11 && < 0.15, cairo >= 0.11 && < 0.14- Other-modules: Bruijn Command Draw Evaluation Graph Lambda Layout Meta Parse Sugar+ Other-modules: Bruijn+ Command+ Draw+ Evaluation+ GC+ Graph+ Lambda+ Layout+ Meta+ Parse+ Paths_gulcii+ Reduce+ Setting+ Sugar ghc-options: -Wall -threaded -rtsopts -- ghc-prof-options: -prof -auto-all -caf-all Source-Repository head Type: git- Location: http://code.mathr.co.uk/gulcii.git+ Location: https://code.mathr.co.uk/gulcii.git Source-Repository this Type: git- Tag: v0.2.0.3- Location: http://code.mathr.co.uk/gulcii.git+ Tag: v0.3+ Location: https://code.mathr.co.uk/gulcii.git
+ lib/church.gu view
@@ -0,0 +1,33 @@+id = \x . x+const = \x y . x+true = \t f . t+false = \t f . f+and = \a b . a b a+or = \a b . a a b+not = \a . a false true+zero = \s z . z+succ = \n s z . s (n s z)+iszero = \n . n (const false) true+add = \m n . m succ n+mul = \m n . m (add n) zero+exp = \m n . n m+pred = \n s z . n (\g h . h (g s)) (\u . z) (\u . u)+sub = \m n . n pred m+equal = \m n . and (iszero (sub m n)) (iszero (sub n m))+nil = \c n . n+cons = \x xs c n . c x (xs c n)+isnil = \l . l (\x xs . false) true+head = \l . l (\x xs . x) id+tail = \l c n . l (\x xs g . g x (xs c)) (\xs . n) (\x xs . xs)+map = \f l . l (\x xs . cons (f x) xs) nil+sum = \l . l (\x xs . add x xs) zero+length = \l . l (\x xs . succ xs) zero+pair = \a b p . p a b+fst = \p . p (\a b . a)+snd = \p . p (\a b . b)+nothing = \n j . n+just = \a n j . j a+maybe = \n j m . m n j+left = \a l r . l a+right = \b l r . r b+either = \l r e . e l r
+ lib/edinburgh.gu view
@@ -0,0 +1,55 @@+# boolean+false = \ t f . f+true = \ t f . t+and = \ a b . a b a+or = \ a b . a a b+not = \ a . a false true+# function+id = \ a . a+const = \ a b . a+compose = \ bc ab a . bc (ab a)+flip = \ abc b a . abc a b+fix = \ f . (\x . f (x x)) (\x . f (x x))+error = \e . error e+seq = \ a ! \b . b+# list+nil = \ c n . n+cons = \ x xs c n . c x xs+null = \ l . l (\x xs . false) true+head = \ l . l (\x xs . x) (error head)+tail = \ l . l (\x xs . xs) (error tail)+map = \ f l . l (\x xs . cons (f x) (map f xs)) nil+append = \ us vs . us (\x xs . cons x (append xs vs)) vs+filter = \ p l . l (\x xs . p x (cons x) id (filter p xs)) nil+index = \ l n . n (\p . l (\x xs . index xs p) (error index)) (l (\x xs . x) (error index))+reverse = \ l . l (\x xs . append (reverse xs) (cons x nil)) nil+foldr = \ f e l . l (\x xs . f x (foldr f e xs)) e+length = foldr (const succ) zero+ands = foldr and true+ors = foldr or false+concat = foldr append nil+all = \f . compose ands (map f)+any = \f . compose ors (map f)+concatMap = \f . compose concat (map f)+composes = foldr compose id+sum = foldr add zero+product = foldr mul (succ zero)+repeat = \x . cons x (repeat x)+cycle = compose concat repeat+take = \n l . n (\p . l (\x xs . cons x (take p xs)) nil) nil+drop = \n l . n (\p . l (\x xs . drop p xs) nil) l+transpose = \l . l (\xs xss . xs (\y ys . cons (cons y (concatMap (take 1) xss)) (transpose (cons ys (map (drop 1) xss)))) (transpose xss)) nil+iterate = \f x . cons x (iterate f (f x))+last = \l . l (\x xs . xs (\y ys . last xs) x) (error last)+replicate = \n x . take n (repeat x)+# natural+zero = \ s z . z+succ = \ n s z . s n+infinity = succ infinity+even = \n . n odd true+odd = \n . n even false+add = \ m n . m (\p . succ (add n p)) n+mul = \ m n . m (\p . add n (mul n p)) zero+exp = \ m n . n (\p . mul m (exp m p)) one+sub = \ m n . m (\p . n (sub p) m) zero+equal = \ m n . m (\mm . n (\nn . equal mm nn) false) (n (\nn . false) true)
− lib/event.gu
@@ -1,6 +0,0 @@-# event-wait = \n w e . w n-event = \x w e . e x-merge = \al bl . al (\a as . bl (\b bs . a (\aw . b (\bw . lessthan aw bw (cons a (merge as (cons (wait (sub bw aw)) bs))) (cons b (merge (cons (wait (sub aw bw)) as) bs))) (\e . cons b (merge al bs))) (\e . cons a (merge as bl))) al) bl-merges = foldr merge nil-play = \l . l (\x xs . x (\w . { wait : w } (play xs)) (\e . e (play xs))) (error play)
− lib/mars.gu
@@ -1,5 +0,0 @@-:load prelude-main = play (cons (event (program 0 48)) timpanis)-timpanis = append timpani timpanis-timpani = concatMap makeNote [20,20,20,60,60,30,30,60]-makeNote = \d ! note d 0 43 96
− lib/midi.gu
@@ -1,7 +0,0 @@-# midi-noteOn = \c n v ! \x . { noteon : [c, n, v] } x-noteOff = \c n v ! \x . { noteoff : [c, n, v] } x-program = \c p ! \x . { program : [c, p] } x-control = \c p v ! \x . { control : [c, p, v] } x-note = \d ! \c n v . [ event (noteOn c n v), wait d, event (noteOff c n v) ]-rest = \d ! [ wait d ]
lib/prelude.gu view
@@ -1,10 +1,8 @@ :load bits :load bool :load either-:load event :load function :load list :load maybe-:load midi :load natural :load pair
src/Bruijn.hs view
@@ -1,6 +1,6 @@ {- gulcii -- graphical untyped lambda calculus interpreter- Copyright (C) 2011, 2013 Claude Heiland-Allen+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -17,7 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -} -module Bruijn (Term(..), bruijn, freeVariablesIn, bind) where+module Bruijn (Term(..), bruijn, freeVariablesIn) where import Data.Maybe (listToMaybe) import Data.Set (Set, empty, singleton, union)@@ -27,10 +27,10 @@ data Term = Free String- | Bound Integer+ | Bound0+ | Scope Term | Lambda Strategy Term | Apply Term Term- | Trace String Term Term deriving (Read, Show, Eq, Ord) bruijn :: U.Term -> Term@@ -42,25 +42,19 @@ bruijn' m (U.Variable v) = case genericElemIndex v m of Nothing -> Free v- Just i -> Bound i-bruijn' m (U.Trace k s t) = Trace k (bruijn' m s) (bruijn' m t)--bind :: Strategy -> String -> Term -> Term-bind k v t = Lambda k (bind' 0 v t)+ Just i -> applyN i Scope Bound0 -bind' :: Integer -> String -> Term -> Term-bind' i v f@(Free u) = if u == v then Bound i else f-bind' _ _ b@(Bound _) = b-bind' i v (Apply s t) = Apply (bind' i v s) (bind' i v t)-bind' i v (Lambda _ t) = bind' (i + 1) v t-bind' i v (Trace k s t) = Trace k (bind' i v s) (bind' i v t)+applyN :: Integer -> (a -> a) -> a -> a+applyN n f+ | n == 0 = id+ | otherwise = f . applyN (n - 1) f freeVariablesIn :: Term -> Set String freeVariablesIn (Free v) = singleton v-freeVariablesIn (Bound _) = empty+freeVariablesIn Bound0 = empty+freeVariablesIn (Scope t) = freeVariablesIn t freeVariablesIn (Lambda _ t) = freeVariablesIn t freeVariablesIn (Apply s t) = freeVariablesIn s `union` freeVariablesIn t-freeVariablesIn (Trace _ s t) = freeVariablesIn s `union` freeVariablesIn t {- Generic list functions.
src/Command.hs view
@@ -1,6 +1,6 @@ {- gulcii -- graphical untyped lambda calculus interpreter- Copyright (C) 2011, 2013 Claude Heiland-Allen+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -25,11 +25,10 @@ import qualified Meta as M import Parse -data Command = Define String S.Term | Evaluate S.Term | Execute S.Term | Meta M.Meta+data Command = Define String S.Term | Evaluate S.Term | Meta M.Meta deriving (Read, Show, Eq, Ord) parse :: Parser String Command parse = (Define <$> name <* sym "=" <*> S.parse) <|> (Evaluate <$> S.parse)- <|> (Execute <$ sym "~" <*> S.parse) <|> (Meta <$ sym ":" <*> M.parse)
src/Draw.hs view
@@ -1,6 +1,6 @@ {- gulcii -- graphical untyped lambda calculus interpreter- Copyright (C) 2011, 2013 Claude Heiland-Allen+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -19,11 +19,10 @@ module Draw (draw) where -import Data.List (genericDrop) import qualified Data.Map.Strict as M import Data.Map.Strict (Map) -import Graphics.Rendering.Cairo+import Graphics.Rendering.Cairo hiding (x, y) import qualified Layout as L import Evaluation (Strategy(..))@@ -32,14 +31,14 @@ colour :: L.Term -> RGB colour (L.Free _ _) = (0, 0.5, 1)-colour (L.Bound _ _) = (0.5, 0, 1)+colour (L.Bound0 _) = (0.5, 0, 1)+colour (L.Scope _ _) = (1, 1, 0) colour (L.Lambda Strict _ _) = (1, 0, 0) colour (L.Lambda Lazy _ _) = (1, 0, 0.5) colour (L.Lambda Copy _ _) = (1, 0, 1) colour (L.Apply _ _ _) = (1, 0.5, 0) colour (L.RefInst _ _ _) = (0.5, 1, 0) colour (L.Reference _ _) = (0, 1, 0.5)-colour (L.Trace _ _ _ _) = (1, 1, 0) circle :: L.Coords -> RGB -> Render () circle (x, y) (r, g, b) = do@@ -94,54 +93,53 @@ e <- textExtents s moveTo (fromIntegral x - textExtentsWidth e / 2) (fromIntegral y) textPath s-drawNames (L.Bound _ _) = return ()+drawNames (L.Bound0 _) = return ()+drawNames (L.Scope t _) = drawNames t drawNames (L.Lambda _ t _) = drawNames t drawNames (L.Apply a b _) = drawNames a >> drawNames b drawNames (L.RefInst _ t _) = drawNames t drawNames (L.Reference _ _) = return ()-drawNames (L.Trace s a b (x,y)) = do- e <- textExtents s- moveTo (fromIntegral x - textExtentsWidth e / 2) (fromIntegral y)- textPath s- drawNames a- drawNames b drawLinks :: Map Integer L.Coords -> L.Term -> Render ()-drawLinks _ (L.Free _ _) = return ()-drawLinks _ (L.Bound _ _) = return ()-drawLinks ps (L.Lambda _ t xy) = let x'y' = L.coordinates t- in line xy x'y' >> drawLinks ps t-drawLinks ps (L.Apply a b xy) = let axay = L.coordinates a- bxby = L.coordinates b- in line xy axay >> line xy bxby >> drawLinks ps a >> drawLinks ps b-drawLinks ps (L.RefInst _ t xy) = let x'y' = L.coordinates t- in line xy x'y' >> drawLinks ps t-drawLinks ps (L.Reference p xy) = let Just x'y' = M.lookup p ps- in line xy x'y'-drawLinks ps (L.Trace _ a b xy) =+drawLinks _ (L.Free _ _) =+ return ()+drawLinks _ (L.Bound0 _) =+ return ()+drawLinks ps (L.Scope t xy) =+ let x'y' = L.coordinates t+ in line xy x'y' >> drawLinks ps t+drawLinks ps (L.Lambda _ t xy) =+ let x'y' = L.coordinates t+ in line xy x'y' >> drawLinks ps t+drawLinks ps (L.Apply a b xy) = let axay = L.coordinates a bxby = L.coordinates b in line xy axay >> line xy bxby >> drawLinks ps a >> drawLinks ps b+drawLinks ps (L.RefInst _ t xy) =+ let x'y' = L.coordinates t+ in line xy x'y' >> drawLinks ps t+drawLinks ps (L.Reference p xy) =+ let Just x'y' = M.lookup p ps+ in line xy x'y' drawVLinks :: [L.Coords] -> L.Term -> Render ()-drawVLinks ls (L.Bound n xy) =- case genericDrop n ls of- [] -> return ()- x'y':_ -> line xy x'y'+drawVLinks ls (L.Bound0 xy) = case ls of+ [] -> return () -- should be error?+ x'y':_ -> line xy x'y'+drawVLinks ls (L.Scope t _) = drawVLinks (drop 1 ls) t drawVLinks ls (L.Lambda _ t xy) = drawVLinks (xy : ls) t drawVLinks ls (L.Apply s t _) = drawVLinks ls s >> drawVLinks ls t drawVLinks ls (L.RefInst _ t _) = drawVLinks ls t-drawVLinks ls (L.Trace _ s t _) = drawVLinks ls s >> drawVLinks ls t drawVLinks _ _ = return () drawNodes :: L.Term -> Render () drawNodes n@(L.Free _ _) = drawNode n-drawNodes n@(L.Bound _ _) = drawNode n+drawNodes n@(L.Bound0 _) = drawNode n+drawNodes n@(L.Scope t _) = drawNode n >> drawNodes t drawNodes n@(L.Lambda _ t _) = drawNode n >> drawNodes t drawNodes n@(L.Apply a b _) = drawNode n >> drawNodes a >> drawNodes b drawNodes n@(L.RefInst _ t _) = drawNode n >> drawNodes t drawNodes n@(L.Reference _ _) = drawNode n-drawNodes n@(L.Trace _ a b _) = drawNode n >> drawNodes a >> drawNodes b drawNode :: L.Term -> Render () drawNode n = circle (L.coordinates n) (colour n)
+ src/GC.hs view
@@ -0,0 +1,85 @@+{-+ gulcii -- graphical untyped lambda calculus interpreter+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen++ This program 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 2 of the License, or+ (at your option) any later version.++ This program 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 this program; if not, write to the Free Software Foundation, Inc.,+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.+-}++module GC (gc) where++import Graph++import Data.Map as Map++gc+ :: References -> Term+ -> (References, Term)++gc refs term+ = let counts = refCount refs term Map.empty+ in ( Map.fromList+ [ (r, compact counts refs (refs Map.! r))+ | (r, n) <- Map.toList counts+ , n > 1 ]+ , compact counts refs term )+++type Count = Integer++refCount+ :: References -> Term+ -> Map Integer Count -> Map Integer Count++compact+ :: Map Integer Count -> References+ -> Term -> Term+++refCount refs (Scope a) counts+ = refCount refs a counts++refCount refs (Lambda _ a) counts+ = refCount refs a counts++refCount refs (Apply a b) counts+ = refCount refs a (refCount refs b counts)++refCount refs (Reference r) counts+ = case r `Map.lookup` counts of+ Just n ->+ Map.insert r (n + 1) counts+ Nothing ->+ refCount refs (refs Map.! r)+ (Map.insert r 1 counts)++refCount _ _ counts = counts+++compact counts refs (Scope term)+ = Scope (compact counts refs term)++compact counts refs (Lambda strat term)+ = Lambda strat (compact counts refs term)++compact counts refs (Apply a b)+ = Apply (compact counts refs a)+ (compact counts refs b)++compact counts refs term@(Reference r)+ = case r `Map.lookup` counts of+ Just 1 -> compact counts refs (refs Map.! r)+ _ -> term++compact _ _ term = term
src/Graph.hs view
@@ -1,6 +1,6 @@ {- gulcii -- graphical untyped lambda calculus interpreter- Copyright (C) 2011, 2013 Claude Heiland-Allen+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -17,9 +17,8 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -} -module Graph (Term(..), Definitions, References, Reduction(..), graph, reduce, pretty) where+module Graph (Term(..), Definitions, References, graph, pretty) where -import qualified Data.Map.Strict as M import Data.Map.Strict (Map) import qualified Bruijn as B@@ -27,23 +26,23 @@ data Term = Free !String- | Bound !Integer+ | Bound0+ | Scope !Term | Lambda !Strategy !Term | Apply !Term !Term | Reference !Integer- | Trace !String !Term !Term deriving (Read, Show, Eq, Ord) pretty :: Term -> String-pretty = unwords . pretty'+pretty = concat . pretty' pretty' :: Term -> [String] pretty' (Free s) = [s]-pretty' (Bound i) = [show i]+pretty' (Bound0) = ["0"]+pretty' (Scope t) = ["S"] ++ pretty' t pretty' (Reference i) = ['#':show i] pretty' (Lambda k t) = ["(", "\\", pretty'' k] ++ pretty' t ++ [")"]-pretty' (Apply s t) = ["("] ++ pretty' s ++ pretty' t ++ [")"]-pretty' (Trace k s t) = ["(", "{", k, ":"] ++ pretty' s ++ ["}"] ++ pretty' t ++ [")"]+pretty' (Apply s t) = ["("] ++ pretty' s ++ [" "] ++ pretty' t ++ [")"] pretty'' :: Strategy -> String pretty'' Strict = "!"@@ -53,96 +52,9 @@ type Definitions = Map String Term type References = Map Integer Term -next :: Map Integer a -> Integer-next refs = case M.maxViewWithKey refs of- Nothing -> 0- Just ((k,_),_) -> k + 1- graph :: B.Term -> Term graph (B.Free v) = Free v-graph (B.Bound i) = Bound i+graph (B.Bound0) = Bound0+graph (B.Scope t) = Scope (graph t) graph (B.Lambda k t) = Lambda k (graph t) graph (B.Apply s t) = Apply (graph s) (graph t)-graph (B.Trace k s t) = Trace k (graph s) (graph t)--{--bind :: String -> Term -> Term -> Term-bind v s t@(Free u) = if u == v then s else t-bind _ _ t@(Bound _) = t-bind v s (Lambda k t) = Lambda k (bind v s t)-bind v s (Apply a b) = Apply (bind v s a) (bind v s b)-bind _ _ t@(Reference _) = t--}--{--Reduce a graph one step, returning Nothing if it is irreducible.--}--data Reduction = Reduced Term References | Rebound String Term References | Traced String Term Term References- deriving (Read, Show, Eq, Ord)--mapR :: (Term -> Term) -> Reduction -> Reduction-mapR f (Reduced t refs) = Reduced (f t) refs-mapR f (Rebound s t refs) = Rebound s (f t) refs-mapR f (Traced k s t refs) = Traced k s (f t) refs--reduce, reduce' :: Definitions -> References -> Term -> Maybe Reduction-reduce _ refs (Trace k s t) = Just (Traced k s t refs)-reduce defs refs term = reduce' defs refs term---- free variables are replaced with their definition-reduce' defs refs (Free v) = case M.lookup v defs of- Nothing -> Nothing- Just t -> let r = next refs in Just (Rebound v (Reference r) (M.insert r t refs))---- bound variables are irreducible-reduce' _ _ (Bound _) = Nothing---- non top-level traces are irreducible?---reduce' _ _ (Trace _ _ _) = Nothing-reduce' _ refs (Trace k s t) = Just (Traced k s t refs)---- maybe reduce inside lambda-reduce' defs refs (Lambda k t) = mapR (Lambda k) `fmap` reduce' defs refs t--reduce' defs refs (Apply a b) = case a of- -- beta reduction- Lambda Strict a' -> case reduce defs refs b of- Just r -> Just (mapR (a `Apply`) r)- Nothing -> Just (uncurry Reduced (beta refs a' b))- Lambda Copy a' -> Just (Reduced (beta' 0 a' b) refs)- Lambda Lazy a' -> Just (uncurry Reduced (beta refs a' b))- Reference r -> case M.lookup r refs of- Just a' -> Just (Reduced (Apply a' b) refs)- _ -> Nothing- Free s -> case M.lookup s defs of- Just a' -> Just (Rebound s (Apply a' b) refs)- _ -> Nothing- t@(Apply _ _) -> case reduce defs refs t of- Just r -> Just (mapR (`Apply` b) r)- _ -> Nothing- _ -> Nothing -- Bound, Trace---- reduce references-reduce' defs refs s@(Reference r) = case M.lookup r refs of- Nothing -> Nothing- Just t -> case reduce' defs refs t of- Just (Reduced t' refs') -> Just (Reduced s (M.insert r t' refs'))- Just (Rebound v t' refs') -> Just (Rebound v s (M.insert r t' refs'))- _ -> Just (Reduced t refs)---- beta reduction-beta :: References -> Term -> Term -> (Term, References)-beta refs a b = (beta' 0 a t, refs')- where- r = next refs- refs' = M.insert r b refs- t = Reference r--beta' :: Integer -> Term -> Term -> Term-beta' i s@(Bound j) t = if i == j then t else s-beta' i (Lambda k s) t = Lambda k (beta' (i + 1) s t)-beta' i (Apply a b) t = Apply (beta' i a t) (beta' i b t)-beta' _ s@(Free _) _ = s-beta' _ s@(Reference _) _ = s-beta' i (Trace k a b) t = Trace k (beta' i a t) (beta' i b t)
src/Lambda.hs view
@@ -1,6 +1,6 @@ {- gulcii -- graphical untyped lambda calculus interpreter- Copyright (C) 2011, 2013 Claude Heiland-Allen+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -31,7 +31,6 @@ = Variable String | Lambda Strategy String Term | Apply Term Term- | Trace String Term Term deriving (Read, Show, Eq, Ord) {-@@ -45,7 +44,6 @@ pretty' (Variable v) = [v] pretty' (Lambda k v t) = ["(", "\\", v, pretty'' k] ++ pretty' t ++ [")"] pretty' (Apply s t) = ["("] ++ pretty' s ++ pretty' t ++ [")"]-pretty' (Trace k s t) = ["(", "{", k, ":"] ++ pretty' s ++ ["}"] ++ pretty' t ++ [")"] pretty'' :: Strategy -> String pretty'' Strict = "!"@@ -60,7 +58,6 @@ isFreeIn n (Variable v) = n == v isFreeIn n (Lambda _ v t) = if n == v then False else n `isFreeIn` t isFreeIn n (Apply t t') = n `isFreeIn` t || n `isFreeIn` t'-isFreeIn n (Trace _ t t') = n `isFreeIn` t || n `isFreeIn` t' {- Get all variable names defined or referenced by a term.@@ -70,7 +67,6 @@ variablesIn (Variable v) = [v] variablesIn (Lambda _ v t) = nub $ v : variablesIn t variablesIn (Apply t t') = nub $ variablesIn t ++ variablesIn t'-variablesIn (Trace _ t t') = nub $ variablesIn t ++ variablesIn t' {- Get all free variables referenced by a term.
src/Layout.hs view
@@ -1,6 +1,6 @@ {- gulcii -- graphical untyped lambda calculus interpreter- Copyright (C) 2011, 2013 Claude Heiland-Allen+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -17,7 +17,7 @@ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -} -module Layout (Term(..), Coords, Layout(..), layout, coordinates) where+module Layout (Term(..), Coords, Layout(..), layout, coordinates, Counts(..), counts) where import qualified Data.Map.Strict as M import Data.Map.Strict (Map)@@ -29,12 +29,12 @@ data Term = Free String Coords- | Bound Integer Coords+ | Bound0 Coords+ | Scope Term Coords | Lambda Strategy Term Coords | Apply Term Term Coords | RefInst Integer Term Coords | Reference Integer Coords- | Trace String Term Term Coords deriving (Read, Show, Eq, Ord) data Layout = Layout Term Integer Integer (Map Integer Coords)@@ -42,33 +42,63 @@ coordinates :: Term -> Coords coordinates (Free _ xy) = xy-coordinates (Bound _ xy) = xy+coordinates (Bound0 xy) = xy+coordinates (Scope _ xy) = xy coordinates (Lambda _ _ xy) = xy coordinates (Apply _ _ xy) = xy coordinates (RefInst _ _ xy) = xy coordinates (Reference _ xy) = xy-coordinates (Trace _ _ _ xy) = xy layout :: G.Term -> G.References -> Layout layout = layout' (0, 0) M.empty layout' :: Coords -> Map Integer Coords -> G.Term -> G.References -> Layout-layout' xy ps (G.Free v) _ = Layout (Free v xy) 1 1 ps-layout' xy ps (G.Bound v) _ = Layout (Bound v xy) 1 1 ps-layout' (x,y) ps (G.Lambda k t) g = let Layout lt w h ps' = layout' (x, y + 1) ps t g- (px, _) = coordinates lt- in Layout (Lambda k lt (px, y)) w (h + 1) ps'-layout' (x,y) ps (G.Apply a b) g = let Layout la aw ah psa = layout' (x, y + 1) ps a g- Layout lb bw bh psb = layout' (x + aw + 1, y + 1) psa b g- in Layout (Apply la lb (x + aw, y)) (1 + aw + bw) (1 + (ah `max` bh)) psb-layout' xy@(x,y) ps (G.Reference p) g = if p `M.member` ps- then Layout (Reference p xy) 1 1 ps- else case M.lookup p g of- Nothing -> error $ "layout': bad pointer: " ++ show p- Just t -> let Layout lt w h pst = layout' (x, y + 1) ps t g- (px, py) = coordinates lt- in Layout (RefInst p lt (px, y)) w (1 + h) (M.insert p (px, py) pst)-layout' (x,y) ps (G.Trace s a b) g =+layout' xy ps (G.Free v) _ =+ Layout (Free v xy) 1 1 ps+layout' xy ps (G.Bound0) _ =+ Layout (Bound0 xy) 1 1 ps+layout' (x, y) ps (G.Scope t) g =+ let Layout lt w h ps' = layout' (x, y + 1) ps t g+ (px, _) = coordinates lt+ in Layout (Scope lt (px, y)) w (h + 1) ps'+layout' (x,y) ps (G.Lambda k t) g =+ let Layout lt w h ps' = layout' (x, y + 1) ps t g+ (px, _) = coordinates lt+ in Layout (Lambda k lt (px, y)) w (h + 1) ps'+layout' (x,y) ps (G.Apply a b) g = let Layout la aw ah psa = layout' (x, y + 1) ps a g Layout lb bw bh psb = layout' (x + aw + 1, y + 1) psa b g- in Layout (Trace s la lb (x + aw, y)) (1 + aw + bw) (1 + (ah `max` bh)) psb+ in Layout (Apply la lb (x + aw, y)) (1 + aw + bw) (1 + (ah `max` bh)) psb+layout' xy@(x,y) ps (G.Reference p) g =+ if p `M.member` ps+ then Layout (Reference p xy) 1 1 ps+ else case M.lookup p g of+ Nothing -> error $ "layout': bad pointer: " ++ show p+ Just t ->+ let Layout lt w h pst = layout' (x, y + 1) ps t g+ (px, py) = coordinates lt+ in Layout (RefInst p lt (px, y)) w (1 + h) (M.insert p (px, py) pst)++data Counts = Counts+ { nFree, nBound0, nScope, nLambda, nApply, nRefInst, nReference :: !Int }++instance Monoid Counts where+ mempty = Counts 0 0 0 0 0 0 0+ mappend c d = Counts+ { nFree = nFree c + nFree d+ , nBound0 = nBound0 c + nBound0 d+ , nScope = nScope c + nScope d+ , nLambda = nLambda c + nLambda d+ , nApply = nApply c + nApply d+ , nRefInst = nRefInst c + nRefInst d+ , nReference = nReference c + nReference d+ }++counts :: Term -> Counts+counts (Free _ _) = mempty{ nFree = 1 }+counts (Bound0 _) = mempty{ nBound0 = 1 }+counts (Scope t _) = let c = counts t in c{ nScope = nScope c + 1 }+counts (Lambda _ t _) = let c = counts t in c{ nLambda = nLambda c + 1 }+counts (Apply a b _) = let c = counts a ; d = counts b in mappend c d+counts (RefInst _ t _) = let c = counts t in c{ nRefInst = nRefInst c + 1 }+counts (Reference _ _) = mempty{ nReference = 1 }
src/Main.hs view
@@ -1,6 +1,6 @@ {- gulcii -- graphical untyped lambda calculus interpreter- Copyright (C) 2011, 2013 Claude Heiland-Allen+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -19,30 +19,34 @@ module Main (main) where -import Control.Applicative ((<$>), (<*>))-import Control.Concurrent (forkIO, killThread, threadDelay, Chan, newChan, readChan, writeChan)-import Control.Monad (forever, when)+import Control.Applicative ((<$>))+import Control.Concurrent (forkIO, killThread, threadDelay, newChan, readChan, writeChan)+import Control.Monad (forever, when, unless, forM_) import qualified Data.Map.Strict as M-import Data.Map.Strict (Map)+import Data.Maybe (isJust) import Data.IORef (IORef, newIORef, readIORef, writeIORef, atomicModifyIORef) import System.IO (hSetBuffering, BufferMode(LineBuffering), stdout) import System.IO.Error (catchIOError) import System.FilePath ((</>), (<.>))-import Graphics.UI.Gtk hiding (Meta)-import Graphics.Rendering.Cairo+import Graphics.UI.Gtk hiding (Meta, Settings)+import Graphics.Rendering.Cairo hiding (width, height) import Paths_gulcii (getDataFileName) import qualified Command as C import qualified Meta as M+import Setting (Settings)+import qualified Setting import qualified Sugar as S import qualified Bruijn as B import qualified Graph as G+import qualified GC as GC+import qualified Reduce as R import qualified Layout as L import qualified Draw as D import qualified Parse as P -data Interpret = Fail | Skip | Define String G.Term | Pure G.Term | Run G.Term | Meta M.Meta+data Interpret = Fail | Skip | Define String G.Term | Pure G.Term | Meta M.Meta deriving (Read, Show, Eq, Ord) interpret :: String -> Interpret@@ -56,26 +60,32 @@ case S.desugar sterm of Just term -> Pure . G.graph . B.bruijn $ term _ -> Fail- Just ((C.Execute sterm, []):_) ->- case S.desugar sterm of- Just term -> Run . G.graph . B.bruijn $ term- _ -> Fail Just ((C.Meta m, []):_) -> Meta m Just [] -> Skip _ -> Fail main :: IO () main = do- _args <- initGUI+ args <- initGUI+ let (width, height, fullscreen) = case args of+ [w,h,"fullscreen"] -> (read w, read h, True)+ [w,h] -> (read w, read h, False)+ _ -> (1280, 720, False) envR <- newIORef M.empty lRef <- newIORef Nothing evalR <- newIORef Nothing+ settingsR <- newIORef Setting.defaults outC <- newChan- _ <- forkIO $ outputter outC let out = writeChan outC win <- windowNew _ <- onDestroy win mainQuit- windowSetDefaultSize win 1024 720+ windowSetDefaultSize win width height+ when fullscreen $ do+ windowSetGeometryHints win (Nothing `asTypeOf` Just win)+ (Just (width, height)) (Just (width, height)) Nothing Nothing Nothing+ set win [ windowDecorated := False ]+ windowSetKeepAbove win True+ windowMove win 0 0 vb <- vBoxNew False 0 hb <- hPanedNew tt <- textTagTableNew@@ -115,7 +125,7 @@ _ <- da `on` exposeEvent $ do dw <- eventWindow liftIO $ do- ml <- atomicModifyIORef lRef (\m -> (m, m))+ ml <- atomicModifyIORef lRef (\m -> (Nothing, m)) case ml of Nothing -> return () Just l -> do@@ -124,7 +134,7 @@ D.draw (fromIntegral ww) (fromIntegral hh) l return True en <- entryNew- entrySetWidthChars en 24+ entrySetWidthChars en 25 font <- fontDescriptionFromString "Monospaced 18" widgetModifyFont tv (Just font) widgetModifyFont en (Just font)@@ -133,8 +143,13 @@ containerAdd sw tv al <- alignmentNew 1 0 1 1 set al [ containerChild := da ]- boxPackStart vb en PackNatural 0- boxPackStart vb sw PackGrow 0+ if Setting.newsOnTop Setting.defaults+ then do+ boxPackStart vb en PackNatural 0+ boxPackStart vb sw PackGrow 0+ else do+ boxPackStart vb sw PackGrow 0+ boxPackStart vb en PackNatural 0 panedPack1 hb vb False True panedPack2 hb al True True set win [ containerChild := hb ]@@ -142,6 +157,7 @@ let scrollDown = do textViewScrollToMark tv mk 0 Nothing addText tag txt = do+ newsOnTop <- Setting.newsOnTop <$> readIORef settingsR start' <- textBufferGetIterAtOffset tb 0 end' <- textBufferGetIterAtOffset tb (-1) textBufferDelete tb start' end'@@ -149,9 +165,10 @@ start <- textBufferGetIterAtOffset tb 0 end <- textBufferGetIterAtOffset tb (-1) textBufferApplyTag tb tag start end- pos <- textBufferGetIterAtOffset tf (-1)+ pos <- textBufferGetIterAtOffset tf (if newsOnTop then 0 else -1) textBufferInsertRange tf pos start end- textBufferMoveMark tf mk pos+ pos' <- textBufferGetIterAtOffset tf (if newsOnTop then 0 else -1)+ textBufferMoveMark tf mk pos' _ <- en `onEntryActivate` do let exec echo txt = case interpret txt of@@ -172,21 +189,25 @@ case mtid of Nothing -> return () Just tid -> killThread tid- tid <- forkIO $ evaluator 10000 lRef out envR M.empty term goPure+ tid <- forkIO $ evaluator "gulcii-" 0 settingsR 500000 lRef out envR M.empty term goPure writeIORef evalR (Just tid)- Run term -> do+ Meta M.Start -> do when echo $ do- addText tagInputRun txt+ addText tagInputMeta txt entrySetText en ""- mtid <- readIORef evalR- case mtid of- Nothing -> return ()- Just tid -> killThread tid- tid <- forkIO $ evaluator 10000 lRef out envR M.empty term (goRun (postGUIAsync . addText tagOutput))- writeIORef evalR (Just tid)+ _ <- forkIO $ do+ out "start;"+ return ()+ Meta M.Stop -> do+ when echo $ do+ addText tagInputMeta txt+ entrySetText en ""+ _ <- forkIO $ do+ out "stop;"+ return () Meta M.Quit -> do _ <- forkIO $ do- out "quit ;"+ out "quit;" postGUISync mainQuit return () Meta M.Clear -> do@@ -212,10 +233,45 @@ mapM_ (exec False) (lines t) Left e -> addText tagError e+ Meta (M.Get s) -> do+ when echo $ do+ addText tagInputMeta txt+ entrySetText en ""+ addText tagOutputMeta . ((show s ++ " = ") ++) . show . Setting.get s =<< readIORef settingsR+ Meta (M.Set s) -> do+ when echo $ do+ addText tagInputMeta txt+ entrySetText en ""+ atomicModifyIORef settingsR $ (\ss -> (Setting.set s True ss, ()))+ addText tagOutputMeta . ((show s ++ " = ") ++) . show . Setting.get s =<< readIORef settingsR+ Meta (M.UnSet s) -> do+ when echo $ do+ addText tagInputMeta txt+ entrySetText en ""+ atomicModifyIORef settingsR $ (\ss -> (Setting.set s False ss, ()))+ addText tagOutputMeta . ((show s ++ " = ") ++) . show . Setting.get s =<< readIORef settingsR+ Meta (M.Toggle s) -> do+ when echo $ do+ addText tagInputMeta txt+ entrySetText en ""+ atomicModifyIORef settingsR $ (\ss -> (Setting.set s (not (Setting.get s ss)) ss, ()))+ addText tagOutputMeta . ((show s ++ " = ") ++) . show . Setting.get s =<< readIORef settingsR txt <- entryGetText en exec True txt scrollDown- _ <- flip timeoutAdd 100 $ widgetQueueDraw da >> return True++ _ <- forkIO $ do+ hSetBuffering stdout LineBuffering+ forever $ do+ settings <- readIORef settingsR+ s <- readChan outC+ when (Setting.echoToStdOut settings) $ putStrLn s+ when (Setting.echoToGUI settings) $ postGUIAsync (addText tagOutput s >> scrollDown)++ _ <- flip timeoutAdd 10 $ do+ ml <- atomicModifyIORef lRef (\m -> (m, m))+ when (isJust ml) $ widgetQueueDraw da+ return True widgetShowAll win mainGUI @@ -224,82 +280,59 @@ goPure :: Go goPure refs term = return (term, refs) -goRun :: (String -> IO ()) -> Go-goRun out refs term = do- out (G.pretty term)- return (term, refs)--gc :: G.References -> G.Term -> (G.Term, G.References)-gc refs term =- let keep = reachable refs term M.empty- (collapse, later) = M.partition (1 ==) keep- in (compact refs term, M.fromList [ (k, compact refs (refs M.! k)) | k <- M.keys collapse ] `M.union` M.fromList [ (k, refs M.! k) | k <- M.keys later ])--reachable :: G.References -> G.Term -> Map Integer Integer -> Map Integer Integer-reachable r (G.Lambda _ t) m = reachable r t m-reachable r (G.Apply s t) m = reachable r s (reachable r t m)-reachable r (G.Reference p) m = (if p `M.member` m then id else reachable r (r M.! p)) (M.insertWith (+) p 1 m)-reachable r (G.Trace _ s t) m = reachable r s (reachable r t m)-reachable _ _ m = m--compact :: G.References -> G.Term -> G.Term-compact r (G.Reference p) = r M.! p-compact _ t = t--evaluator :: Int -> IORef (Maybe L.Layout) -> (String -> IO ()) -> IORef G.Definitions -> G.References -> G.Term -> Go -> IO ()-evaluator tick layout out defsR refs term go = do+evaluator :: String -> Int -> IORef Settings -> Int -> IORef (Maybe L.Layout) -> (String -> IO ()) -> IORef G.Definitions -> G.References -> G.Term -> Go -> IO ()+evaluator pngPrefix frame settingsR tick layout out defsR refs term go = do defs <- readIORef defsR- let (term1, refs1) = gc refs term+ settings <- readIORef settingsR+ when (Setting.traceEvaluation settings) $ do+ out $ " " ++ G.pretty term+ unless (M.null refs) $ do+ out " where"+ forM_ (M.toList refs) $ \(r, t) -> do+ out $ " #" ++ show r ++ " = " ++ G.pretty t+ let (refs1, term1)+ | Setting.collectGarbage settings = GC.gc refs term+ | otherwise = (refs, term)+ collectedGarbage = term1 /= term || refs1 /= refs+ when (Setting.traceEvaluation settings && collectedGarbage) $ do+ out "$\\to$ {- collect garbage -}"+ out $ " " ++ G.pretty term1+ unless (M.null refs1) $ do+ out " where"+ forM_ (M.toList refs1) $ \(r, t) -> do+ out $ " #" ++ show r ++ " = " ++ G.pretty t (term0, refs0) <- go refs1 term1- case G.reduce defs refs0 term0 of- Nothing -> threadDelay tick >> evaluator tick layout out defsR refs0 term0 go- Just (G.Reduced term' refs') -> evaluator tick layout out defsR refs' term' go- Just (G.Rebound _var' term' refs') -> evaluator tick layout out defsR refs' term' go- Just (G.Traced k s term' refs') -> do- atomicModifyIORef layout $ \_ -> (Just $ L.layout term0 refs0, ())- case k of- "wait" -> case evalNatural (dereference refs0 s) of- Just n -> threadDelay (tick * fromInteger n)- _ -> return ()- "noteon" -> case evalList evalNatural (dereference refs0 s) of- Just msg@[_channel, _note, _velocity] ->- out $ "noteon " ++ unwords (map show msg) ++ " ;"- _ -> return ()- "noteoff" -> case evalList evalNatural (dereference refs0 s) of- Just msg@[_channel, _note, _velocity] ->- out $ "noteoff " ++ unwords (map show msg) ++ " ;"- _ -> return ()- "program" -> case evalList evalNatural (dereference refs0 s) of- Just msg@[_channel, _program] ->- out $ "program " ++ unwords (map show msg) ++ " ;"- _ -> return ()- "control" -> case evalList evalNatural (dereference refs0 s) of- Just msg@[_channel, _control, _value] ->- out $ "control " ++ unwords (map show msg) ++ " ;"- _ -> return ()- _ -> print (k, G.pretty (dereference refs0 s))- evaluator tick layout out defsR refs' term' go--dereference :: G.References -> G.Term -> G.Term-dereference r (G.Reference p) = dereference r (r M.! p)-dereference r (G.Lambda k t) = G.Lambda k (dereference r t)-dereference r (G.Apply a b) = G.Apply (dereference r a) (dereference r b)-dereference r (G.Trace k a b) = G.Trace k (dereference r a) (dereference r b)-dereference _ t = t--evalNatural :: G.Term -> Maybe Integer-evalNatural (G.Lambda _ (G.Lambda _ (G.Bound 0))) = Just 0-evalNatural (G.Lambda _ (G.Lambda _ (G.Apply (G.Bound 1) t))) = (1 +) `fmap` evalNatural t-evalNatural _ = Nothing--evalList :: (G.Term -> Maybe a) -> G.Term -> Maybe [a]-evalList _ (G.Lambda _ (G.Lambda _ (G.Bound 0))) = Just []-evalList f (G.Lambda _ (G.Lambda _ (G.Apply (G.Apply (G.Bound 1) s) t))) = (:) <$> f s <*> evalList f t-evalList _ _ = Nothing--outputter :: Chan String -> IO ()-outputter out = do- hSetBuffering stdout LineBuffering- forever $ do- s <- readChan out- putStrLn s+ (l, L.Counts c1 c2 c3 c4 c5 c6 c7) <- atomicModifyIORef layout $ \_ ->+ let l@(L.Layout lt _ _ _) = L.layout term0 refs0+ in (Just l, (l, L.counts lt))+ when (Setting.saveImages settings) $ do+ withImageSurface FormatRGB24 1920 1080 $ \surface -> do+ renderWith surface $ do+ let g = 23 / 255+ setSourceRGB g g g+ paint+ D.draw 1920 1080 l+ surfaceWriteToPNG surface (pngPrefix ++ show frame ++ ".png")+ when (Setting.emitStatistics settings) $ do+ out $ "statistics " ++ unwords (map show [c1,c2,c3,c4,c5,c6,c7]) ++ ";"+ when (Setting.realTimeDelay settings) $ do+ threadDelay tick+ let tick'+ | Setting.realTimeAcceleration settings = ceiling (fromIntegral tick * 0.97 + 0.03 * 42000 :: Double)+ | otherwise = tick+ case R.reduce defs refs0 term0 of+ Nothing -> do+ when (Setting.traceEvaluation settings) $ do+ out "$\\to$ {- in normal form -}"+ out " $\\qedsymbol$"+ when (Setting.retryIrreducible settings) $ do+ evaluator pngPrefix (frame + 1) settingsR tick layout out defsR refs0 term0 go+ Just (reason, (refs', term')) -> do+ when (Setting.traceEvaluation settings) $ do+ out $ "$\\to$ {- " ++ (case reason of+ R.Beta -> "beta reduce"+ R.RefInst -> "instantiate reference"+ R.Rebound var -> "definition of \"" ++ var ++ "\""+ R.Extrude -> "scope extrude"+ ) ++ " -}"+ evaluator pngPrefix (frame + 1) settingsR tick' layout out defsR refs' term' go
src/Meta.hs view
@@ -1,6 +1,6 @@ {- gulcii -- graphical untyped lambda calculus interpreter- Copyright (C) 2011, 2013 Claude Heiland-Allen+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -21,13 +21,21 @@ import Control.Applicative((<|>), (<$), (<*>)) +import Setting (Setting)+import qualified Setting import Parse -data Meta = Quit | Clear | Browse | Load String+data Meta = Start | Stop | Quit | Clear | Browse | Load String | Get Setting | Set Setting | UnSet Setting | Toggle Setting deriving (Read, Show, Eq, Ord) parse :: Parser String Meta-parse = (Quit <$ sym "quit")+parse = (Start <$ sym "start")+ <|> (Stop <$ sym "stop")+ <|> (Quit <$ sym "quit") <|> (Clear <$ sym "clear") <|> (Browse <$ sym "browse") <|> (Load <$ sym "load" <*> name)+ <|> (Get <$ sym "get" <*> Setting.parse)+ <|> (Set <$ sym "set" <*> Setting.parse)+ <|> (UnSet <$ sym "unset" <*> Setting.parse)+ <|> (Toggle <$ sym "toggle" <*> Setting.parse)
src/Parse.hs view
@@ -1,6 +1,6 @@ {- gulcii -- graphical untyped lambda calculus interpreter- Copyright (C) 2011, 2013 Claude Heiland-Allen+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -52,7 +52,7 @@ alphanum = alpha ++ digits symbols :: String-symbols = "\\.!?()[],=~:{}"+symbols = "\\.!?()[],=:" spaces :: String spaces = " "@@ -68,7 +68,8 @@ tokenize (c:cs) | c `elem` digits = let (t,ts) = span (`elem` digits) cs in ((c:t):) <$> tokenize ts- | c `elem` lowers = let (t,ts) = span (`elem` alphanum) cs+ | c `elem` uppers+ ||c `elem` lowers = let (t,ts) = span (`elem` alphanum) cs in ((c:t):) <$> tokenize ts | c `elem` symbols = ([c]:) <$> tokenize cs | c `elem` spaces = tokenize cs
+ src/Reduce.hs view
@@ -0,0 +1,142 @@+{-+ gulcii -- graphical untyped lambda calculus interpreter+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen++ This program 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 2 of the License, or+ (at your option) any later version.++ This program 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 this program; if not, write to the Free Software Foundation, Inc.,+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.+-}++module Reduce (Reduce(..), Reduction, reduce) where++import Prelude hiding (replicate)++import qualified Data.Map.Strict as Map++import Evaluation (Strategy(..))+import Graph++{-+Reduce a graph one step, returning Nothing if it is irreducible.+-}++data Reduce = Beta | RefInst | Rebound String | Extrude+ deriving (Read, Show, Eq, Ord)++type Reduction = (Reduce, (References, Term))++mapTerm :: (Term -> Term) -> Reduction -> Reduction+mapTerm f = fmap (fmap f)+++reduce :: Definitions -> References -> Term -> Maybe Reduction+reduce defs refs term =+ case reduce' False defs refs term of+ Nothing -> reduce' True defs refs term+ r -> r+++reduce' :: Bool -> Definitions -> References -> Term -> Maybe Reduction++reduce' _ defs refs (Free var)+ = (,) (Rebound var) `fmap` (,) refs `fmap` Map.lookup var defs++reduce' _ _ _ (Bound0)+ = Nothing++reduce' _ _ refs (Scope (Lambda strat term))+ = Just (Extrude, (refs, Lambda strat (lifting refs 1 term)))++reduce' f defs refs (Scope t@(Reference _))+ = case dereference refs t of+ l@(Lambda _ _) ->+ Just (RefInst, (refs, Scope l))+ _ -> mapTerm Scope `fmap` reduce' f defs refs t++reduce' _ _ refs (Scope (Apply a b))+ = Just (Extrude, (refs, Apply (Scope a) (Scope b)))++reduce' f defs refs (Scope term)+ = mapTerm Scope `fmap` reduce' f defs refs term++reduce' f defs refs (Lambda strat term)+ = mapTerm (Lambda strat) `fmap` reduce' f defs refs term++reduce' f defs refs term@(Reference ref)+ = case Map.lookup ref refs of+ Just refTerm ->+ case reduce' f defs refs refTerm of+ Just (reason, (refs', term')) ->+ Just (reason, (Map.insert ref term' refs', term))+ Nothing ->+ Just (RefInst, (refs, refTerm))+ Nothing -> Nothing -- error "reference not found"++reduce' _ _ refs (Apply (Lambda Copy a) b)+ = Just (Beta, (refs, beta refs a b))++reduce' f defs refs (Apply l@(Lambda Strict a) b)+ = case reduce' f defs refs b of+ Just (reason, (refs', b')) -> Just (reason, (refs', Apply l b'))+ Nothing -> Just (Beta, (refs, beta refs a b))++reduce' _ _ refs (Apply (Lambda Lazy a) b)+ = let r = next refs+ refs' = Map.insert r b refs+ in Just (Beta, ( refs'+ , beta refs' a (Reference r) ))++reduce' f defs refs (Apply a b)+ = case (a, dereference refs a) of+ (Reference _, l@(Lambda _ _)) -> Just (RefInst, (refs, Apply l b))+ _ ->+ case reduce' f defs refs a of+ Just (reason, (refs', a')) ->+ Just (reason, (refs', Apply a' b))+ Nothing+ | f -> mapTerm (Apply a) `fmap` reduce' f defs refs b+ | otherwise -> Nothing+++beta :: References -> Term -> Term -> Term+beta refs l v = substitute refs l v 0++substitute :: References -> Term -> Term -> Integer -> Term+substitute _ Bound0 s 0 = s+substitute _ Bound0 _ _ = Bound0+substitute _ (Scope t) _ 0 = t+substitute refs (Scope t) s i = Scope (substitute refs t s (i - 1))+substitute refs (Lambda k t) s i = Lambda k (substitute refs t s (i + 1))+substitute refs (Apply a b) s i = Apply (substitute refs a s i) (substitute refs b s i)+substitute _ t@(Free _) _ _ = t+substitute refs (Reference r) s i = substitute refs (refs Map.! r) s i++lifting :: References -> Integer -> Term -> Term+lifting _ 0 t = Scope t+lifting _ _ s@(Free _) = s+lifting _ _ Bound0 = Bound0+lifting refs i (Scope t) = Scope (lifting refs (i - 1) t)+lifting refs i (Lambda k t) = Lambda k (lifting refs (i + 1) t)+lifting refs i (Apply a b) = Apply (lifting refs i a) (lifting refs i b)+lifting refs i (Reference r) = lifting refs i (refs Map.! r)+++dereference :: References -> Term -> Term+dereference refs (Reference r) = dereference refs (refs Map.! r)+dereference _ t = t+++next :: References -> Integer+next refs = case Map.maxViewWithKey refs of+ Nothing -> 0+ Just ((k,_),_) -> k + 1
+ src/Setting.hs view
@@ -0,0 +1,97 @@+{-+ gulcii -- graphical untyped lambda calculus interpreter+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen++ This program 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 2 of the License, or+ (at your option) any later version.++ This program 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 this program; if not, write to the Free Software Foundation, Inc.,+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.+-}++module Setting (Setting(..), parse, Settings(..), get, set, defaults) where++import Control.Applicative ((<$))+import Data.Foldable (asum)++import Parse++data Setting+ = TraceEvaluation+ | CollectGarbage+ | RealTimeDelay+ | RealTimeAcceleration+ | RetryIrreducible+ | EmitStatistics+ | EmitRebindings+ | EchoToStdOut+ | EchoToGUI+ | SaveImages+ | NewsOnTop+ deriving (Eq, Ord, Enum, Bounded, Read, Show)++parse :: Parser String Setting+parse = asum [ s <$ sym (show s) | s <- [minBound .. maxBound :: Setting] ]++data Settings = Settings+ { traceEvaluation :: Bool+ , collectGarbage :: Bool+ , realTimeDelay :: Bool+ , realTimeAcceleration :: Bool+ , retryIrreducible :: Bool+ , emitStatistics :: Bool+ , emitRebindings :: Bool+ , echoToStdOut :: Bool+ , echoToGUI :: Bool+ , saveImages :: Bool+ , newsOnTop :: Bool+ }++get :: Setting -> Settings -> Bool+get TraceEvaluation = traceEvaluation+get CollectGarbage = collectGarbage+get RealTimeDelay = realTimeDelay+get RealTimeAcceleration = realTimeAcceleration+get RetryIrreducible = retryIrreducible+get EmitStatistics = emitStatistics+get EmitRebindings = emitRebindings+get EchoToStdOut = echoToStdOut+get EchoToGUI = echoToGUI+get SaveImages = saveImages+get NewsOnTop = newsOnTop++set :: Setting -> Bool -> Settings -> Settings+set TraceEvaluation b s = s{ traceEvaluation = b }+set CollectGarbage b s = s{ collectGarbage = b }+set RealTimeDelay b s = s{ realTimeDelay = b }+set RealTimeAcceleration b s = s{ realTimeAcceleration = b }+set RetryIrreducible b s = s{ retryIrreducible = b }+set EmitStatistics b s = s{ emitStatistics = b }+set EmitRebindings b s = s{ emitRebindings = b }+set EchoToStdOut b s = s{ echoToStdOut = b }+set EchoToGUI b s = s{ echoToGUI = b }+set SaveImages b s = s{ saveImages = b }+set NewsOnTop b s = s{ newsOnTop = b }++defaults :: Settings+defaults = Settings+ { traceEvaluation = False+ , collectGarbage = True+ , realTimeDelay = True+ , realTimeAcceleration = True+ , retryIrreducible = True+ , emitStatistics = True+ , emitRebindings = True+ , echoToStdOut = True+ , echoToGUI = False+ , saveImages = False+ , newsOnTop = True+ }
src/Sugar.hs view
@@ -1,6 +1,6 @@ {- gulcii -- graphical untyped lambda calculus interpreter- Copyright (C) 2011, 2013 Claude Heiland-Allen+ Copyright (C) 2011, 2013, 2017 Claude Heiland-Allen This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by@@ -31,7 +31,7 @@ module Sugar(Term(..), parse, desugar) where -import Control.Applicative(Applicative, Alternative, (<|>), (<$>), (<$), (<*>), (<*), some, liftA2)+import Control.Applicative((<|>), (<$>), (<$), (<*>), (<*), some, liftA2) import Data.List ((\\)) import Parse@@ -45,7 +45,6 @@ | Group Term | Natural Integer | List [Term]- | Trace String Term Term deriving (Read, Show, Eq, Ord) parse :: Parser String Term@@ -57,7 +56,6 @@ <|> (Variable <$> name) <|> (Natural <$> integer) <|> (List <$ sym "[" <*> manySep (sym ",") parse <* sym "]")- <|> (Trace <$ sym "{" <*> name <* sym ":" <*> parse <* sym "}" <*> parse) strategy :: Parser String Strategy strategy = (Strict <$ sym "!") <|> (Lazy <$ sym ".") <|> (Copy <$ sym "?")@@ -118,7 +116,6 @@ let (c:n:_) = (variables \\ U.freeVariablesIn t') \\ U.freeVariablesIn ts' in Just $ lam c (lam n (U.Apply (U.Apply (U.Variable c) t') ts')) _ -> Nothing-desugar (Trace k s t) = U.Trace k <$> desugar s <*> desugar t lam :: String -> U.Term -> U.Term lam = U.Lambda Lazy