Initial import.

This commit is contained in:
Kyle Isom 2018-01-16 09:46:44 -08:00
commit 36ed6df77f
29 changed files with 1035 additions and 0 deletions

12
README.rst Normal file
View File

@ -0,0 +1,12 @@
SANDBOX
=======
This is a repository of various repositories for learning or exploring
topics.
Projects
--------
+ ``lpn/``: `Learn Prolog Now <http://lpn.swi-prolog.org/>`_
+ ``uc/``: `Understanding Computation <http://computationbook.com/>`_

70
lpn/crossword.pl Normal file
View File

@ -0,0 +1,70 @@
%% Exercise 2.4: Here are six Italian words:
%%
%% - astante
%% - astoria
%% - baratto
%% - cobalto
%% - pistola
%% - statale
%%
%% They are to be arranged, crossword puzzle fashion, in the following grid:
%%
%% V1 V2 V3
%% . . .
%% H1 . 1 . 4 . 7 .
%% . . .
%% H2 . 2 . 5 . 8 .
%% . . .
%% H3 . 3 . 6 . 9 .
%% . . .
%%
%% The following knowledge base represents a lexicon containing these words:
word(astante, a,s,t,a,n,t,e).
word(astoria, a,s,t,o,r,i,a).
word(baratto, b,a,r,a,t,t,o).
word(cobalto, c,o,b,a,l,t,o).
word(pistola, p,i,s,t,o,l,a).
word(statale, s,t,a,t,a,l,e).
%% Write a predicate crossword/6 that tells us how to fill in the
%% grid. The first three arguments should be the vertical words from
%% left to right, and the last three arguments the horizontal words
%% from top to bottom.
crossword(V1, V2, V3, H1, H2, H3) :-
word(V1, _, _1, _, _2, _, _3, _),
word(V2, _, _4, _, _5, _, _6, _),
word(V3, _, _7, _, _8, _, _9, _),
word(H1, _, _1, _, _4, _, _7, _),
word(H2, _, _2, _, _5, _, _8, _),
word(H3, _, _3, _, _6, _, _9, _),
V1 \= V2, V1 \= V3, V1 \= H1, V1 \= H2, V1 \= H3,
V2 \= V3, V2 \= H1, V2 \= H2, V2 \= H3,
V3 \= H1, V3 \= H2, V3 \= H3,
H1 \= H2, H1 \= H3, H2 \= H3.
%% NB: execute the following query to find out the correct arrangement.
%% crossword(A, B, C, D, E, F).
%% $ swipl -q
%% 1 ?- [italiano].
%% true.
%%
%% 2 ?- crossword(A, B, C, D, E, F).
%% A = astante,
%% B = cobalto,
%% C = pistola,
%% D = astoria,
%% E = baratto,
%% F = statale ;
%% A = astoria,
%% B = baratto,
%% C = statale,
%% D = astante,
%% E = cobalto,
%% F = pistola ;
%% false.
%% Note that there are two solutions; these are mirrors of each other
%% with the vertical words substituted for the horizontal words.

8
lpn/eating.pl Normal file
View File

@ -0,0 +1,8 @@
is_digesting(X,Y) :- just_ate(X,Y).
is_digesting(X,Y) :-
just_ate(X,Z),
is_digesting(Z,Y).
just_ate(mosquito,blood(john)).
just_ate(frog,mosquito).
just_ate(stork,frog).

5
lpn/kb1.pl Normal file
View File

@ -0,0 +1,5 @@
woman(mia).
woman(jody).
woman(yolanda).
playsAirGuitar(jody).
party.

6
lpn/kb2.pl Normal file
View File

@ -0,0 +1,6 @@
happy(yolanda).
listens2Music(mia).
listens2Music(yolanda):- happy(yolanda).
playsAirGuitar(mia):- listens2Music(mia).
playsAirGuitar(yolanda):- listens2Music(yolanda).
listens2Music(bruce).

9
lpn/kb3.pl Normal file
View File

@ -0,0 +1,9 @@
happy(vincent).
listens2Music(butch).
playsAirGuitar(vincent):-
listens2Music(vincent),
happy(vincent).
playsAirGuitar(butch):-
happy(butch).
playsAirGuitar(butch):-
listens2Music(butch).

8
lpn/kb4.pl Normal file
View File

@ -0,0 +1,8 @@
woman(mia).
woman(jody).
woman(yolanda).
loves(vincent,mia).
loves(marsellus,mia).
loves(pumpkin,honey_bunny).
loves(honey_bunny,pumpkin).

11
lpn/kb5.pl Normal file
View File

@ -0,0 +1,11 @@
loves(vincent,mia).
loves(marsellus,mia).
loves(pumpkin,honey_bunny).
loves(honey_bunny,pumpkin).
/**
* original definition:
* jealous(X,Y):- loves(X,Z), loves(Y,Z).
*/
jealous(X,Y):- loves(X,Z), loves(Y,Z), X \= Y.

13
lpn/lexicon.pl Normal file
View File

@ -0,0 +1,13 @@
word(determiner,a).
word(determiner,every).
word(noun,criminal).
word(noun,'big kahuna burger').
word(verb,eats).
word(verb,likes).
sentence(Word1,Word2,Word3,Word4,Word5):-
word(determiner,Word1),
word(noun,Word2),
word(verb,Word3),
word(determiner,Word4),
word(noun,Word5).

2
lpn/lines.pl Normal file
View File

@ -0,0 +1,2 @@
vertical(line(point(X,Y),point(X,Z))).
horizontal(line(point(X,Y),point(Z,Y))).

9
lpn/proof22.pl Normal file
View File

@ -0,0 +1,9 @@
f(a).
f(b).
g(a).
g(b).
h(b).
k(X) :- f(X), g(X), h(X).

3
lpn/test.pl Normal file
View File

@ -0,0 +1,3 @@
listensToMusic(X) :- happy(X).
playsAirGuitar(X) :- listensToMusic(X).
happy(a).

2
uc/README.txt Normal file
View File

@ -0,0 +1,2 @@
This is the superproject where all my attempts at working through the
book "Understanding Computation" are.

1
uc/cl/README.txt Normal file
View File

@ -0,0 +1 @@
Lisp code supporting "Universal Computation".

114
uc/cl/meaning.lisp Normal file
View File

@ -0,0 +1,114 @@
;;; # The Meaning of Programs
;;; What is computation? what a computer does.
;;; computation environment:
;;;
;;; * machine
;;; * language
;;; * program
;;;
;;; Fundamentally, programming is about ideas: a program is a snapshot of
;;; an idea, a codification of the mental structures of the programmer(s)
;;; involved.
;;;
;;; ## The Meaning of "Meaning"
;;;
;;; *semantics* is the connection between words and meanings: while "dog"
;;; is an arrangment of shapes on a page, an actual dog is a very separate
;;; things. Semantics relates concrete signifiers and abstract meanings,
;;; and attempts to study the fundamental nature of the abstract meanings.
;;;
;;; *Formal semantics* is the attempt at formalising the meanings of
;;; programmings, and using this formalisation to reason about
;;; languages. In order to specify a programming language, we need to
;;; define both its *syntax* (the representation) and its *semantics* (the
;;; meaning).
;;;
;;; Most languages lack a formal specification and opt to use a canonical
;;; reference implementation. An alternative is to write a prose
;;; specification, which is the approach of C++. A third approach is to
;;; mathematically specify the language such that automated mathematical
;;; analysis can be done.
;;;
;;; ### Syntax
;;;
;;; The language's syntax is what differentiates valid examples of code like
(defun y (x) (+ x 1))
;;; from nonsense like `$%EHI`. In general, a parser reads a string
;;; (like "(defun y (x) (+ x 1)") and turns it into an *abstract
;;; syntax tree*. Syntax is ultimately only concerned with the surface
;;; appearance of the program
;;; ### Operational Semantics
;;; A practical means of thinking about the meaning of a program is
;;; *what it does*. *operational semantics* defines rules for how
;;; programs run on some machine (often an *abstract machine*).
;;; ### Small-Step Semantics
;;;
;;; Let's imagine an abstract machine that evaluates by directly
;;; operating on the syntax, reducing it iteratively to bring about the
;;; final result.
;;;
;;; For example, to evaluate (1 * 2) + (3 * 4):
;;; 0. Compute the left-hand multiplication (1 * 2 -> 2), simplifying
;;; the expression to 2 + (3 * 4).
;;; 0. Compute the right-hand multiplication (3 * 14 -> 12),
;;; simplifying the expression to 2 + 12.
;;; 0. Carry out the addition, resulting in 14.
;;; We determine that 14 is the result because it cannot be simplified
;;; any further. It is a special type of algebraic expression (a
;;; *value*) with its own meaning.
;;; We can write down formal rules like these describing how to proceed
;;; with each small reduction step; these rules are written in a
;;; *metalanguage* (which is often mathematical notation).
;;; Let's explore the semantics of a toy language called SIMPLE. There
;;; is a formal mathematical language to this, but we'll use a
;;; programming language to make it more clear.
(defclass ast-node ()
()
(:documentation "The superclass of all AST nodes."))
(defclass ast-number (ast-node)
((value :initarg :value :accessor nvalue))
(:documentation "A discrete, irreducible value."))
(defclass ast-add (ast-node)
((left :initarg :left :accessor ast-left)
(right :initarg :right :accessor ast-right))
(:documentation "AST node that adds its arguments together."))
(defclass ast-mult (ast-node)
((left :initarg :left :accessor ast-left)
(right :initarg :right :accessor ast-right))
(:documentation "AST node that multiplies its arguments together."))
(defun make-node (&key (type :number) (values (list 0 0)))
(cond
((equal type :number) (make-instance 'ast-number :value (car values)))
((equal type :add) (make-instance 'ast-add :left (car values) :right (cadr values)))
((equal type :mult) (make-instance 'ast-mult :left (car values) :right (cadr values)))))
(make-node :type :add
:values (list (make-node :type :mult
:values (list (make-node :values (list 1))
(make-node :values (list 2))))
(make-node :type :mult
:values (list (make-node :values (list 3))
(make-node :values (list 4))))))
;;; We can use these to build an AST by hand:
;;; print Add(Multiply (Number 2) Multiply (Number 3))

5
uc/cl/package.lisp Normal file
View File

@ -0,0 +1,5 @@
;;;; package.lisp
(defpackage #:uc
(:use #:cl))

11
uc/cl/uc.asd Normal file
View File

@ -0,0 +1,11 @@
;;;; uc.asd
(asdf:defsystem #:uc
:serial t
:description "Describe uc here"
:author "Your Name <your.name@example.com>"
:license "Specify license here"
:components ((:file "package")
(:file "uc")
(:file "meaning")))

6
uc/cl/uc.lisp Normal file
View File

@ -0,0 +1,6 @@
;;;; uc.lisp
(in-package #:uc)
;;; "uc" goes here. Hacks and glory await!

2
uc/hs-simple/README.txt Normal file
View File

@ -0,0 +1,2 @@
## understanding computation

104
uc/hs-simple/meaning.hs Normal file
View File

@ -0,0 +1,104 @@
-- # The Meaning of Programs
-- What is computation? what a computer does.
-- computation environment:
--
-- * machine
-- * language
-- * program
--
-- Fundamentally, programming is about ideas: a program is a snapshot of
-- an idea, a codification of the mental structures of the programmer(s)
-- involved.
--
-- ## The Meaning of "Meaning"
--
-- *semantics* is the connection between words and meanings: while "dog"
-- is an arrangment of shapes on a page, an actual dog is a very separate
-- things. Semantics relates concrete signifiers and abstract meanings,
-- and attempts to study the fundamental nature of the abstract meanings.
--
-- *Formal semantics* is the attempt at formalising the meanings of
-- programmings, and using this formalisation to reason about
-- languages. In order to specify a programming language, we need to
-- define both its *syntax* (the representation) and its *semantics* (the
-- meaning).
--
-- Most languages lack a formal specification and opt to use a canonical
-- reference implementation. An alternative is to write a prose
-- specification, which is the approach of C++. A third approach is to
-- mathematically specify the language such that automated mathematical
-- analysis can be done.
--
-- ### Syntax
--
-- The language's syntax is what differentiates valid examples of code like
y x = x + 1
-- from nonsense like `$%EHI`. In general, a parser reads a string
-- (like "y x = x + 1") and turns it into an /abstract syntax
-- tree/. Syntax is ultimately only concerned with the surface
-- appearance of the program
-- ### Operational Semantics
-- A practical means of thinking about the meaning of a program is
-- *what it does*. *operational semantics* defines rules for how
-- programs run on some machine (often an *abstract machine*).
-- ### Small-Step Semantics
--
-- Let's imagine an abstract machine that evaluates by directly
-- operating on the syntax, reducing it iteratively to bring about the
-- final result.
--
-- For example, to evaluate (1 * 2) + (3 * 4):
-- 0. Compute the left-hand multiplication (1 * 2 -> 2), simplifying
-- the expression to 2 + (3 * 4).
-- 0. Compute the right-hand multiplication (3 * 14 -> 12),
-- simplifying the expression to 2 + 12.
-- 0. Carry out the addition, resulting in 14.
-- We determine that 14 is the result because it cannot be simplified
-- any further. It is a special type of algebraic expression (a
-- *value*) with its own meaning.
-- We can write down formal rules like these describing how to proceed
-- with each small reduction step; these rules are written in a
-- *metalanguage* (which is often mathematical notation).
-- Let's explore the semantics of a toy language called SIMPLE. There
-- is a formal mathematical language to this, but we'll use a
-- programming language to make it more clear.
class AST a where
reduce :: a -> Number
reducible :: a -> Bool
data Number = Number { value :: Integer } deriving (Show)
instance AST Number where
reduce (Number value) = (Number value)
reducible (Number _) = False
data Add = Add { leftAdd :: Number
, rightAdd :: Number
} deriving (Show)
instance AST Add where
reduce (Add x y) = Number (value x + value y)
reducible (Add _ _) = True
data Multiply = Multiply { leftMult :: Number
,rightMult :: Number
} deriving (Show)
-- We can use these to build an AST by hand:
-- print Add(Multiply (Number 2) Multiply (Number 3))

47
uc/hs-simple/meaning.md Normal file
View File

@ -0,0 +1,47 @@
-- # The Meaning of Programs
-- What is computation? what a computer does.
-- computation environment:
-- * machine
-- * language
-- * program
--
-- Fundamentally, programming is about ideas: a program is a snapshot of
-- an idea, a codification of the mental structures of the programmer(s)
-- involved.
--
-- ## The Meaning of "Meaning"
--
-- /semantics/ is the connection between words and meanings: while "dog"
-- is an arrangment of shapes on a page, an actual dog is a very separate
-- things. Semantics relates concrete signifiers and abstract meanings,
-- and attempts to study the fundamental nature of the abstract meanings.
--
-- *Formal semantics* is the attempt at formalising the meanings of
-- programmings, and using this formalisation to reason about
-- languages. In order to specify a programming language, we need to
-- define both its *syntax* (the representation) and its *semantics* (the
-- meaning).
--
-- Most languages lack a formal specification and opt to use a canonical
-- reference implementation. An alternative is to write a prose
-- specification, which is the approach of C++. A third approach is to
-- mathematically specify the language such that automated mathematical
-- analysis can be done.
--
-- ### Syntax
--
-- The language's syntax is what differentiates valid examples of code like
y x = x + 1
-- from nonsense like `$%EHI`. In general, a parser reads a string
-- (like "y x = x + 1") and turns it into an /abstract syntax
-- tree/. Syntax is ultimately only concerned with the surface
-- appearance of the program
-- ### Operational Semantics
-- A practical means of thinking about the meaning of a program is
-- *what it does*.

View File

@ -0,0 +1,46 @@
What is computation? what a computer does.
computation environment:
* machine
* language
* program
Fundamentally, programming is about ideas: a program is a snapshot of
an idea, a codification of the mental structures of the programmer(s)
involved.
== The Meaning of "Meaning"
/semantics/ is the connection between words and meanings: while "dog"
is an arrangment of shapes on a page, an actual dog is a very separate
things. Semantics relates concrete signifiers and abstract meanings,
and attempts to study the fundamental nature of the abstract meanings.
_Formal semantics_ is the attempt at formalising the meanings of
programmings, and using this formalisation to reason about
languages. In order to specify a programming language, we need to
define both its /syntax/ (the representation) and its /semantics/ (the
meaning).
Most languages lack a formal specification and opt to use a canonical
reference implementation. An alternative is to write a prose
specification, which is the approach of C++. A third approach is to
mathematically specify the language such that automated mathematical
analysis can be done.
=== Syntax
The language's syntax is what differentiates valid examples of code like
> y x = x + 1
from nonsense like `$%EHI`. In general, a parser reads a string (like
"y x = x + 1") and turns it into an /abstract syntax tree/. Syntax is ultimately only concerned with the surface appearance of the program
=== Operational Semantics
A practical means of thinking about the meaning of a program is /what it does/.

5
uc/hs/ChangeLog.md Normal file
View File

@ -0,0 +1,5 @@
# Revision history for uc
## 0.1.0.0 -- YYYY-mm-dd
* First version. Released on an unsuspecting world.

20
uc/hs/LICENSE Normal file
View File

@ -0,0 +1,20 @@
Copyright (c) 2016 Kyle Isom
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

2
uc/hs/Setup.hs Normal file
View File

@ -0,0 +1,2 @@
import Distribution.Simple
main = defaultMain

Binary file not shown.

After

Width:  |  Height:  |  Size: 526 KiB

228
uc/hs/src/Meaning.lhs Normal file
View File

@ -0,0 +1,228 @@
Chapter 2: The Meaning of Programs
==================================
> module Meaning where
Programming is about ideas, not about the programs themselves;
they're manifestations (sometimes even physical) of someone's ideas;
there must be some *raison d'être* for program to be written. That
reason can be to scratch an itch, but some motivation needs to drive
the program.
Linguistics has a field called *semantics*, which "is the study of
the connection between words and their meanings;" computer science
has *formal semantics*, which deals with specifying these connections
mathemtically (more or less). Specifying a programming language
requires both a syntax (*how does the program look*?) and semantics
(*what does the program mean*?). Funny enough, many (most?) PL's
don't have some canonical formal specification, relying on
"specification by implementation."
Syntax
------
Remember: syntax = how does the program look?
Programming is currently done largely as text files, which are
really just long strings of characters where newlines give the
appearance of some structure. We fead this string into a parser of
some sort, which knows the language's syntax rules. The parser takes
the string and converts into an *abstract syntax tree*, which is
the computer's representation of the program in memory. **N.B.**:
parsers are covered later.
Syntax provides a mechanism for the meaning of a program to be
expressed, but it doesn't have any deeper meaning. The connection
is entirely arbitrary, though there are common patterns that have
arisen (c.f. the C-style of langauges).
Operational Semantics
---------------------
"The most practical way to think about the meaning of a program is
*what it does*." That is, what happens when it runs? I really liked
how Tom phrased it: "How do different constructs in the programming
language behave at run time, and what effect do they have when
they're plugged together to make larger programs?"
*Operational semantics* defines rules for how programs execute,
typically on an *abstract machine*, allowing for more precise
specifications of a language's behaviour.
Small-Step Semantics
--------------------
How do we go about designing an abstract machine? A first step is
to think about how a program that works by reduction on the syntax.
For example, evaluating (1 × 2) + (3 × 4):
1. First, the leftmost expression is reduced: × is applied to 1
and 2. This leaves us with 2 + (3 × 4).
2. The rightmost expression is reduced: × is applied to 3
and 4. This leaves us with 2 + 12.
3. Finally, + is applied to 2 and 12, yielding 14.
4. 14 can't be reduced any further, so this is taken as the result
of the expression. This is a *value*, which has a standalone
meaning.
The rules for these reductions need to be codified in their own
*metalanguage*. To explore the semantics of a very simple programming
language called SIMPLE.
[ insert meaning_simple_semantics.png here ]
These are *inference rules* defining a *reduction relation* on
SIMPLE ASTs. "Practically speaking, it's a bunch of weird symbols
that don't say anything intelligible about the meaning of computer
programs." Time to dive into an implementation!
Tom is careful to note that this isn't an attempt at specification
by implementation; it's making the description more approachable
for readers without a mathematical background.
Expressions
-----------
We can start by defining some types for the different elements ofthe AST.
> data Element = Number Int |
> Boolean Bool |
> Add Element Element |
> Multiply Element Element |
> LessThan Element Element |
> Equals Element Element |
> GreaterThan Element Element
For convenience, we'll override show. In the book, SIMPLE ASTs are
displayed inside «».
> -- strElement returns the string representation of an AST element.
> strElement :: Element -> String
> strElement (Number n) = show n
> strElement (Boolean b) = show b
> strElement (Add a b) = (strElement a) ++ " + " ++ (strElement b)
> strElement (Multiply a b) = (strElement a) ++ " × " ++ (strElement b)
> -- showElement implements show for Element.
> showElement :: Element -> String
> showElement e = "«" ++ (strElement e) ++ "»"
> instance Show Element where show = showElement
These elements can be strung together to hand-build an AST:
> anExpression = Add (Multiply (Number 1) (Number 2))
> (Multiply (Number 3) (Number 4))
This *should* display as
```
Ok, modules loaded: Meaning.
*Meaning>
*Meaning> anExpression
«1 × 2 + 3 ×
```
Note that this doesn't take into account operator precedence, so
`anExpression` is the same as `otherExpression` defined below:
> otherExpression = Multiply (Number 1)
> (Multiply (Add (Number 2) (Number 3))
> (Number 4))
With proper precendence, this should be `1 * (2 + 3) * 4`, but note
```
*Meaning> anExpression
«1 × 2 + 3 ×
*Meaning> otherExpression
«1 × 2 + 3 ×
```
This is a problem, but it's not relevant to the discussion of the
semantics of SIMPLE.
Now we need to implement a reducer. First, let's determine if an
expression *can* be reduced:
> -- reducible returns true if the expression is reducible.
> reducible :: Element -> Bool
> reducible (Number _) = False
> reducible _ = True
> -- reduce reduces the expression.
> reduce :: Element -> Element
> reduce n@(Number _) = n
> reduce (Add (Number a) (Number b)) = Number (a + b)
> reduce (Add a b) = if (reducible a)
> then (Add (reduce a) b)
> else (Add a (reduce b))
> reduce (Multiply (Number a) (Number b)) = Number (a * b)
> reduce (Multiply a b) = if (reducible a)
> then (Multiply (reduce a) b)
> else (Multiply a (reduce b))
Using `reduce` right now means repeatedly running `reduce` over
functions:
```
*Meaning> reduce anExpression
«2 + 3 ×
*Meaning> reduce (reduce anExpression)
«2 + 12»
*Meaning> reduce (reduce (reduce anExpression))
«14»
```
Note that `reduce` returns different expressions for `anExpression`
and `otherExpression`, which demonstrates that the string display
issue is tangential to our semantics:
```
*Meaning> reduce anExpression
«2 + 3 ×
*Meaning> reduce otherExpression
«1 × 5 ×
```
Let's abstract this into a machine; note that this won't work quite
like the Ruby examples in the book, as we can't really keep state
in Haskell.
> -- Machine abstracts the SIMPLE VM.
> data Machine = Machine Element
> instance Show Machine where
> show (Machine e) = "EXP ← " ++ (show e)
We can define two functions on it: `step` and `run`:
> -- step runs a single reduction.
> step :: Machine -> Machine
> step (Machine e) = Machine (reduce e)
> -- run keeps calling step until a non-reducible expression is
> -- found.
> run :: Machine -> IO Machine
> run m@(Machine e) = do
> putStrLn (show m)
> if (reducible e)
> then run (step m)
> else return (Machine e)
Let's try this with `anExpression`:
> anMachine = Machine anExpression
```
*Meaning> step anMachine
EXP ← «2 + 3 ×
*Meaning> run anMachine
EXP ← «1 × 2 + 3 ×
EXP ← «2 + 3 ×
EXP ← «2 + 12»
EXP ← «14»
EXP ← «14»
```

67
uc/hs/uc.cabal Normal file
View File

@ -0,0 +1,67 @@
-- Initial uc.cabal generated by cabal init. For further documentation,
-- see http://haskell.org/cabal/users-guide/
-- The name of the package.
name: uc
-- The package version. See the Haskell package versioning policy (PVP)
-- for standards guiding when and how versions should be incremented.
-- https://wiki.haskell.org/Package_versioning_policy
-- PVP summary: +-+------- breaking API changes
-- | | +----- non-breaking API additions
-- | | | +--- code changes with no API change
version: 0.1.0.0
-- A short (one-line) description of the package.
synopsis: Literate Haskell explorations from the book 'Understanding Computation'.
-- A longer description of the package.
-- description:
-- The license under which the package is released.
license: MIT
-- The file containing the license text.
license-file: LICENSE
-- The package author(s).
author: Kyle Isom
-- An email address to which users can send suggestions, bug reports, and
-- patches.
maintainer: kyle@imap.cc
-- A copyright notice.
-- copyright:
category: Meta
build-type: Simple
-- Extra files to be distributed with the package, such as examples or a
-- README.
extra-source-files: ChangeLog.md
-- Constraint on the version of Cabal needed to build this package.
cabal-version: >=1.10
library
-- Modules exported by the library.
-- exposed-modules:
-- Modules included in this library but not exported.
-- other-modules:
-- LANGUAGE extensions used by modules in this package.
-- other-extensions:
-- Other library packages from which modules are imported.
build-depends: base >=4.9 && <4.10
-- Directories containing source files.
hs-source-dirs: src
-- Base language which the package is written in.
default-language: Haskell2010

219
uc/py/simple.py Normal file
View File

@ -0,0 +1,219 @@
# # The Meaning of Programs
# Let's define a simple *AST*, with three basic members. An AST node can
# be reducible, like the + in (2 + 2), or not-reducible, like the
# number 2. We'll also reduce from left to right.
class Number:
def __init__(self, value):
self.value = int(value)
def __repr__(self):
return "#" + str(self.value)
def is_reducible(self):
return False
class Add:
def __init__(self, l, r):
self.left = l
self.right = r
def __repr__(self):
return repr(self.left) + " + " + repr(self.right)
def is_reducible(self):
return True
def reduce(self, env):
if self.left.is_reducible():
return Add(self.left.reduce(env), self.right)
elif self.right.is_reducible():
return Add(self.left, self.right.reduce(env))
else:
return Number(self.left.value + self.right.value)
class Mult:
def __init__(self, l, r):
self.left = l
self.right = r
def __repr__(self):
return repr(self.left) + " * " + repr(self.right)
def is_reducible(self):
return True
def reduce(self, env):
if self.left.is_reducible():
return Mult(self.left.reduce(env), self.right)
elif self.right.is_reducible():
return Mult(self.left, self.right.reduce(env))
else:
return Number(self.left.value * self.right.value)
ast1 = Add(Mult(Number(1), Number(2)),
Mult(Number(3), Number(4)))
# It would be nice to have a machine that could run these, so we don't
# have to run them by hand.
class Machine:
def __init__(self, expr, env):
self.expr = expr
self.env = env
self.iters = 0
def step(self):
self.iters += 1
self.expr = self.expr.reduce(self.env)
def run(self):
while self.expr.is_reducible():
print self
self.step()
print "execution complete in ", self.iters, " steps."
return self.expr
def __repr__(self):
return "(" + str(self.iters) + ") " + repr(self.expr) + "\n" + repr(self.env)
class Boolean:
def __init__(self, value):
self.value = value
def __repr__(self):
return str(self.value).lower()
def is_reducible(self):
return False
class LessThan:
def __init__(self, l, r):
self.left = l
self.right = r
def __repr__(self):
return repr(self.left) + " < " + repr(self.right)
def is_reducible(self):
return True
def reduce(self, env):
if self.left.is_reducible():
return LessThan(self.left.reduce(env), self.right)
elif self.right.is_reducible():
return LessThan(self.left, self.right.reduce(env))
else:
return Boolean(self.left.value < self.right.value)
ast2 = LessThan(Number(5), Add(Number(2), Number(2)))
ast2b = LessThan(Add(Number(2), Number(2)), Number(5))
class Variable:
def __init__(self, name):
self.name = name
def __repr__(self):
return "$" + self.name
def is_reducible(self):
return True
def reduce(self, env):
return env[self.name]
ast3 = Add(Variable('x'), Variable('y'))
env3 = {'x': Number(3), 'y': Number(4)}
m3 = lambda : Machine(ast3, env3)
class Nop:
def __init__(self):
pass
def __repr__(self):
return "@NOP"
def eq(self, st):
return st.instanceof(Nop)
def is_reducible(self):
return False
class Assign:
def __init__(self, name, expr):
self.name = name
self.expr = expr
def __repr__(self):
return self.name + " = " + repr(self.expr)
def is_reducible(self):
return True
def reduce(self, env):
if self.expr.is_reducible():
return Assign(self.name, self.expr.reduce(env)), env.copy()
else:
env[self.name] = self.expr
return Nop(), env.copy()
stmt4 = Assign("x", Add(Variable("x"), Number(1)))
env4 = {"x": Number(2)}
class Machine2:
def __init__(self, stmt, env):
self.stmt = stmt
self.env = env
self.iters = 0
def __repr__(self):
return "(" + str(self.iters) + ") " + repr(self.stmt) + "\n" + repr(self.env)
def step(self):
self.iters += 1
self.stmt, self.env = self.stmt.reduce(self.env)
print self
def run(self):
while self.stmt.is_reducible():
self.step()
print "execution complete in ", self.iters, " steps."
print self
stmt5 = Assign("x", Add(Variable("x"), Number(1)))
env5 = {"x": Number(2)}
m5 = lambda : Machine2(stmt5, env5)
class If:
def __init__(self, cond, cons, alt):
self.cond = cond
self.cons = cons
self.alt = alt
def __repr__(self):
s = "if |" + repr(self.cond) + "| then |" + repr(self.cons)
return s + "| else |" + repr(self.alt) + "|"
def is_reducible(self):
return True
def reduce(self, env):
if self.cond.is_reducible():
return If(self.cond.reduce(env), self.cons, self.alt), env.copy()
elif self.cond.value:
return self.cons, env.copy()
else:
return self.alt, env.copy()
stmt6 = If(Variable("x"), Assign("y", Number(1)), Assign("y", Number(2)))
env6 = {"x": Boolean(True)}
m6 = lambda : Machine2(stmt6, env6)
class Sequence:
def __init__(self, first, second):
self.first = first
self.second = second
def __repr__(self):
return repr(self.first) + ";" + repr(self.second)
def is_reducible(self):
return True
def reduce(self, env):
if isinstance(self.first, Nop):
return self.second, env.copy()
else:
first, env = self.first.reduce(env)
return Sequence(first, self.second), env.copy()
stmt7 = Sequence(Assign("x", Add(Number(1), Number(1))),
Assign("y", Add(Variable("x"), Number(3))))
env7 = {}
m7 = lambda : Machine2(stmt7, env7)
class While:
def __init__(self, cond, body):
self.cond = cond
self.body = body
def __repr__(self):
return "while |" + repr(self.cond) + "|:\n\t" + repr(self.body)
def is_reducible(self):
return True
def reduce(self, env):
return If(self.cond, Sequence(self.body, self), Nop()), env.copy()
stmt8 = While(LessThan(Variable("x"), Number(5)),
Assign("x", Mult(Variable("x"), Number(3))))
env8 = {"x": Number(1)}
m8 = lambda : Machine2(stmt8, env8)