Anda di halaman 1dari 36

Dynamo | A Language for Dynamic Logic Programming

Jan van Eijck


We describe a Haskell 4, 6] prototype implementation of dynamo, a simple language for dynamic logic programming.

Abstract

1 Dynamic Logic Programming


Dynamic logic programming was introduced in Van Eijck 2]. This paper describes a rst implementation of dynamo , a language for pure dynamic logic programming. The dynamo language implements the executable process interpretation of Dynamic Predicate Logic or DPL 3], augmented with constructs for bounded iteration and bounded choice, as described in 2]. The two main sources of inspiration for dynamo are DPL and Alma-0, a hybrid language for imperative programming mixed with logic programming developed by Apt c.s. 1]. The dynamo language demonstrates that dynamic interpretation of FOL can be used as guideline for dynamic logic programming. dynamo programs have a purely declarative dynamic semantics. There are no side e ects, and no control features: dynamo is pure dynamic logic. Because of this logical purity, weakest precondition reasoning for dynamo is completely straightforward.

2 Executable Interpretation of FOL


;

Let a domain M and a set of variables V be given. Let A := X V M X , let be the empty partial valuation (the only member of M ), and let be an object not in A. If a 2 M X , for X V , then a term t is a-closed if all variables in t are in X , an atom Pt1 tn is : a-closed if all ti are a-closed, and an identity t1 = t2 is a-closed if both of t1 ; t2 are. If a 2 M X we call X the domain of a. Use " for `unde ned' and # for `de ned'. Term interpretation in a (First Order) model M = (M; I ) wrt valuation a:

va
(ft1

:=

a(v) if v is a-closed

tn )a :=

"

I (f )ta 1

"

otherwise ta if t1 ; : : : ; tn a-closed n otherwise

: An identity t1 = t2 is an a-assignment if either t1 v, ta =", ta =#, or t2 v, ta =#, ta =". 1 2 1 2

Executable Interpretation of Negation Treatment of negation should be phrased in terms of the


so-called forward property: 1

If a?!b and a a , then there is a b


0

b with a ?!b .
0 0

Solution: Keep track of variables relevant to a particular path taken by the computation. Two distinguished registers:
dynamically free variables), l for the set of local variables that get declared and possibly assigned along a computation path (local variables are dynamically active variables).

g for the set of global variables that get assigned along a computation path (global variables are

A proper state is a triple consisting of a valuation a 2 A, a global store ga V , and a local store la V . The improper state is the item . We say that a transition (a; ga; la ) ?! (b; gb ; lb ) has the forward property if la gb dom (a), or equivalently, if la dom (a) and ga = gb . We say that b is safe for (a; ga ; la) if b = (b; gb ; lb) (i.e., b 6= ), la dom (a) and ga = gb . We say that B (PA P V P V ) f g is risky for (a; ga ; la) if B 6= ;, but no member of B is safe for (a; ga ; la). The transition rules for negation are the following: not (a; ga ; la) ?! ; and there are no b; gb; lb with (a; ga ; la ) ?! (b; gb ; lb) (a; ga ; la ) ?! (a; ga ; la)
:

there are ?! transitions for (a; ga ; la); but none of them safe for (a; ga ; la) (a; ga; la ) ?!
:

Propagation of
?!

Executable Interpretation of Atomic Tests


no ?! transitions from (a; ga ; la )
?

Pt1

tn a-closed and (ta ; : : : ; ta ) 2 I (P ) 1 n P t1 t (a; ga; la ) ?! (a; ga ; la)


n

Pt1 tn not a-closed t1 (a; ga; la ) P ?!t


n

Executable Interpretation of Identities

: t1 = t2 a-closed and ta = ta 1 2 : 1 =t a ; la ) t?! (a; g a ; la ) (a; g


a a

: t1 = t2 an a-assignment with t1 v; va ="; ta =#; v 2 la 2 : t1 =t (a; ga ; la ) ?! (a fv=tag; ga; la ) 2 : = t1 = t2 an a-assignment with t1 v; va ="; ta =#; v 2 la 2 : t1 =t (a; ga ; la) ?! (a fv=ta g; ga fvg; la) 2
a

: t1 = t2 an a-assignment with t2 v; va ="; ta =#; v 2 la 1 : t1 =t (a; ga ; la ) ?! (a fv=tag; ga; la ) 1


a

: = t1 = t2 an a-assignment with t2 v; va ="; ta =#; v 2 la 1 :t 1= (a; ga ; la) t?! (a fv=ta g; ga fvg; la) 1
a

: t1 = t2 not an a-assignment and not a-closed : 1 =t (a; ga ; la ) t?!


a

Executable Interpretation of Quanti cation


v 2 dom (a) = v (a; ga ; la ) ?! (a; ga ; la fvg)
9

(a; ga ; la)

v 2 dom (a) ?! (a ? fv=va g; ga; la fvg)


9

Executable Interpretation of Composition


1 (a; ga; la ) ?! 1; (a; ga; la ) ?!2 1 2 (a; ga ; la ) ?! (b; gb ; lb) (b; gb; lb ) ?! 1; (a; ga; la ) ?!2 1 2 (a; ga ; la) ?! (b; gb ; lb ) (b; gb ; lb )b ?! (c; gc; lc) 1; (a; ga ; la) ?!2 (c; gc ; lc)

Exec Interpretation of Union


(a; ga ; la )a ?! 1 (a; ga ; la ) ?!2
i i

i 2 f1; 2g

(a; ga ; la) ?! (b; gb ; lb) i 2 f1; 2g 1 (a; ga ; la ) ?!2 (b; gb; lb )

Executable Interpretation of Bounded Iteration We want to be able to execute a formula N

times, where the value of N possibly depends on previous computation. In other words: we want to be able to perform bounded iteration, with the bound not xed at compile time, but only at run time. Assuming we have a type for nonnegative integer expressions, and N is an expression of that type, we can write this as N . Assuming that n is a natural number, we write n for: execute n times. Note the di erence between n and N caused by the fact that n is a number and N a numerical expression. The executable interpretation of n and N is given by the following transition rules:

a ?! a

a ?! +1 a ?!
n n

a ?! b b ?! +1 a ?!
n n n

a ?! b b ?! c +1 a ?! c a ?!
N

N a ="
n

a ?! N a =#; max(0; N a) = n a ?!
n N N

a ?! b N a =#; max(0; N a) = n a ?! b Executable Interpretation of Bounded Choice Suppose we want to check whether program
succeeds for any i in a bounded range of possibilities, say M::N . Again, we don't want to x the values of M and N in advance, but let them be computed. For this, we add a new construct to the language, by introducing formulas of the form v M::N , with the following transition rules:

a ?!
i

v M::N

M a ="

a ?!

v M::N

N a ="

=j ; a v?! a ?!
i v M::N

v M::N

M a = m; N a = n; m j n

=j a v?!; b M a = m; N a = n; m j n a ?! b

3 Checks on the Executable Process De nition


The following facts about the executable interpretation of FOL are proved in 2]:
; 2 ); Comp is associative: a 1 ;( 2 ; 3 ) b i a ( 1?! 3 b. ?!

la dom (a) and ga = gb together guarantee the forward property for (a; ga ; la ) ?! (b; gb ; lb).
Executable interpretation is faithful to DPL in the following sense:

{ If (a; ga; la) ?! (b; gb; lb) then there are full extensions a ; b with b 2 ( )](a ). { If there are no -transitions from (a; ga; la), then for every full extension a of a it holds that ( )](a ) = ;.
0 0 0 0 0 0

4 Assignment Without Sting


In dynamic logic programming, destructive assignment is replaced by safe assignment. Write 9j ; j = i; 9i; i = t as:

i Jj t:
Destructive assignment statement x := x + 1 can be replaced by safe assignment statement x Jx x + 1. General procedure for removing the sting from assignment: 9x; x = t if x does not occur in t; (x := t) := x Jx t x =x] otherwise.
0

In dynamo , x Jy t is written as x

>> y = t.

5 Extended DPL Syntax Versus Dynamo Syntax


A nal extension of DPL that we need is indexed variables. If a set Var of variables is given, we can form new variables by means of appending indices. Assuming for a moment that we are computing on the natural numbers, we get the following language of extended DPL. v ::= var j v N ] _ N ::= 0 j 1 j j v j (N1 + N2 ) j (N1 ?N2 ) j (N1 N2 ) j fN1 Nn Sv : N2 j 9v j : j ( 1 ; 2 ) j ( 1 2 ) j N j ::= ? j PN1 Nn j N1 = M::N Figure (1) introduces the dynamo syntax by means of a translation to the extended DPL language. The translation xes the intended meaning of every dynamo construct.

6 The Dynamo Statements One by One


Some Statement Example:
some x, y, z

Figure 1: Translation from dynamo to Extended DPL. (begin S1 ; : : : ; Sn end) (skip) (fail) (t1 = t2 ) (some v) (some v1 ; : : : ; vn ) (v1 >> v2 = t) (find v in N::M ] with S ) (do N times S ) (if B then S1 else S2 ) (either S1 orelse S2 ) (test B ) (donot S ) (t1 = t2 ) (Pt1 tn ) (not B ) (B1 (B1
and or

:= := := := := := := := := := := := :=

S1 ; : : : ; S n

:? ?

t1 = t 2 9v 9v1 ; : : : ; 9vn 9v2 ; v1 = v2 ; 9v1 ; v1 = t v M::N S (S )N (::B ; S1 ) (:B ; S2 ) S1 S2 ::B :S

:= t1 = t2 := Pt1 tn := :B := B1 ; B2 := B1 B2

B2 ) B2 )

Skip Statement
skip

Fail Statement
fail

Identity Statement Example:


x = 2

Composite Statement Example:


begin x = 0; y = 1; z = 3 end

Put Statement Example:


x >> x0 = x0 + 1

This example statement increments the value of x while using x0 as an auxiliary variable. The example statement is equivalent to the following composite statement:
begin some x0; x0 = x; some x; x = x0 + 1 end

Test Statement Example:


test x = 2

This behaves di erent from the identity statement x=2 for input states that have no value for x. For such inputs, the identity statement x=2 will assign 2 to x, but the test statement test x = 2 will raise an exception, because the test cannot be evaluated for the current input.

If Statement Example:
if x = 2 then skip else fail

Here the condition x = 2 is read as a test. In case there is no input value for x, the example statement will raise an exception.

Either Statement Example:


either x = 2 orelse x = 3

Note that any if statement can be paraphrased in terms of an either statement and a test statement. The if statement example above is equivalent to:
either test x = 2 orelse fail

Do Statement Example:
do 100 times begin x >> x0 = x0 + 1; y x] = 0 end

If x does have a value n for the current input, this initializes an array y n + 1]; : : : ; y n + 100]. If x does not have a value for the current input, the example raises an exception.

Find Statement Example:


find i in 1..100] with i = j

Not Statement Example:


donot find i in 1..100] with i = j

7 Some Simple Dynamo Programs and Their Outputs


Here is a dynamo program that computes values for x and y:
begin some x; x = 0; x = y; some x; x = 1 end

In its output, a distinction is made between the global variable y and the local variable x. Also note that the output indicates that the solution set is complete.
"y":0 e.g. "x":1 There are no further solutions

Next, consider the following program:


begin either x = 2 orelse x = 3; either y = x + 1 orelse 2 = y; 2 * x = 3 * y end

Its output:
"x":3 "y":2 There are no further solutions

When we change the order of the rst and the second statement the execution mechanism encounters the statement y = x + 1 in a situation where neither x nor y is known. This raises an exception which is propagated. 8

begin either y = x + 1 orelse 2 = y; either x = 2 orelse x = 3; 2 * x = 3 * y end

The output indicates that the value `unknown' was encountered:


"y":2 "x":3 There may be further solutions

8 Example: Stable Marriages


For a slightly more involved example, here is a dynamo program for marrying o 4 women to 4 men in a random fashion. The program has a name, followed by a list containing the schemes of the display variables. A display list (a,b,c) indicates that the single variables a, b and c are to be displayed. A display list (a,b ],c ] ]) indicates that the single variable a, the indexed array b and the doubly indexed array c are to be displayed. In the following example, (WifeOf ]) indicates that the computed values of the array WifeOf should be displayed.
program marriage (WifeOf ]);

begin n = 4; m = 0; some w; w = 0; do n times { marry off the begin w >> w0 = w0 + 1; find m in 1 .. n] with begin b m] = 1; w = WifeOf m]; some b m]; b m] = 0; end end end

women one by one }

{ m is still a bachelor } { propose woman w to man m } { m is no bachelor anymore }

The output of this program is given in Figure (2). If we have full information about the preferences of the men and women we can compute the matches that are stable. When is your marriage stable? An equivalent but possibly more disturbing version of this question runs: when is your marriage in danger? If your marriage is in danger it can be (basically) for two reasons 7]: Either you yourself prefer someone else to your partner, and that other person either is single or prefers you to his/her own partner, or ... your partner prefers someone else to you, and that other person either is single or prefers your partner to his/her own partner. 9

Figure 2: dynamo output for the marriage program.


"WifeOf 4]":1 "WifeOf 1]":2 "WifeOf "WifeOf 4]":1 "WifeOf 1]":2 "WifeOf "WifeOf 4]":1 "WifeOf 2]":2 "WifeOf "WifeOf 4]":1 "WifeOf 2]":2 "WifeOf "WifeOf 4]":1 "WifeOf 3]":2 "WifeOf "WifeOf 4]":1 "WifeOf 3]":2 "WifeOf "WifeOf 3]":1 "WifeOf 1]":2 "WifeOf "WifeOf 3]":1 "WifeOf 1]":2 "WifeOf "WifeOf 3]":1 "WifeOf 2]":2 "WifeOf "WifeOf 3]":1 "WifeOf 2]":2 "WifeOf "WifeOf 3]":1 "WifeOf 4]":2 "WifeOf "WifeOf 3]":1 "WifeOf 4]":2 "WifeOf "WifeOf 2]":1 "WifeOf 1]":2 "WifeOf "WifeOf 2]":1 "WifeOf 1]":2 "WifeOf "WifeOf 2]":1 "WifeOf 3]":2 "WifeOf "WifeOf 2]":1 "WifeOf 3]":2 "WifeOf "WifeOf 2]":1 "WifeOf 4]":2 "WifeOf "WifeOf 2]":1 "WifeOf 4]":2 "WifeOf "WifeOf 1]":1 "WifeOf 2]":2 "WifeOf "WifeOf 1]":1 "WifeOf 2]":2 "WifeOf "WifeOf 1]":1 "WifeOf 3]":2 "WifeOf "WifeOf 1]":1 "WifeOf 3]":2 "WifeOf "WifeOf 1]":1 "WifeOf 4]":2 "WifeOf "WifeOf 1]":1 "WifeOf 4]":2 "WifeOf There are no further solutions 3]":3 2]":3 3]":3 1]":3 2]":3 1]":3 4]":3 2]":3 4]":3 1]":3 2]":3 1]":3 4]":3 3]":3 4]":3 1]":3 3]":3 1]":3 4]":3 3]":3 4]":3 2]":3 3]":3 2]":3 "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf "WifeOf 2]":4 3]":4 1]":4 3]":4 1]":4 2]":4 2]":4 4]":4 1]":4 4]":4 1]":4 2]":4 3]":4 4]":4 1]":4 4]":4 1]":4 3]":4 3]":4 4]":4 2]":4 4]":4 2]":4 3]":4

10

A relational situation is stable if the above two dangers are avoided for everyone. How do we recognize or create stable situations? By means of a dynamo program. The program has to represent the preferences of the men and women involved. We can do that by means of two arrays m and f . f 4] 2] = 1 signi es that woman 4 puts man 2 top of her list.1
program StableMatching (WifeOf ]); begin { first we list the women's ranking of the men f f f f 1] 2] 3] 4] 1] 1] 1] 1] = = = = 4; 1; 1; 2; f f f f 1] 2] 3] 4] 2] 2] 2] 2] = = = = 2; 3; 2; 1; f f f f 1] 2] 3] 4] 3] 3] 3] 3] = = = = 3; 4; 3; 4; f f f f 1] 2] 3] 4] 4] 4] 4] 4] = = = = } 1; 2; 4; 3; } 3; 4; 4; 4;

{ second m m m m 1] 2] 3] 4] 1] 1] 1] 1] = = = =

we list the men's ranking of the women 2; 2; 2; 3; m m m m 1] 2] 3] 4] 2] 2] 2] 2] = = = = 1; 1; 1; 1; m m m m 1] 2] 3] 4] 3] 3] 3] 3] = = = = 4; 3; 3; 2; m m m m 1] 2] 3] 4] 4] 4] 4] 4] = = = =

1 Anne Kaldeway informs me that there is some world knowledge involved in devising an e cient stable marriage algorithm. One should always take the women's preferences as one's point of departure. Why? Because it is a fact of life | always according to Kaldeway | that men tend to have their preferences shaped by super cial sentiments, with virtually identical wish lists as a result.

n = 4; m = 0; do n times { record that all men are bachelors } begin m >> m0 = m0 + 1; b m] = 1 end; some w; w = 0; do n times { try to marry off the women one by one } begin w >> w0 = w0 + 1; find m in 1 .. n] with begin some i; f w] m] = i; { m is w's i-th preference } b m] = 1; { m is still a bachelor } w = WifeOf m]; { propose woman w to man m } some b m]; b m] = 0; { m is no bachelor anymore } donot find rival in 1 .. n] with begin test f w] rival] < i;{ w prevers rival to m } either b rival] = 1 { rival is still unmarried } orelse begin b rival] = 0; { or .. rival is married } test m rival] w] < m rival] WifeOf rival]]; { rival prefers w to his own Wife } end

11

end end end end

The output of program StableMatching is in Figure (3). Figure 3: dynamo output for the stable marriage program
"WifeOf 4]":1 "WifeOf 1]":2 "WifeOf 2]":3 "WifeOf 3]":4 There are no further solutions

9 Example: N Queens
The challenge: position N queens on a chess-board of size N N in such manner that none of them is under attack from any of the others 7]. Represent a placement of the queens on the board as a function f from columns to rows. Then f (4) = 5 expresses that the queen of column 4 is in row 5. If f (k) = r, then: 1. No queen from i 2 f1; : : : ; k ? 1g, should be on row r. 2. No queen from i 2 f1; : : : ; k ? 1g should be on the % diagonal through r (i.e., on position r +(k ? i)). 3. No queen from i 2 f1; : : : ; k ? 1g should be on the & diagonal through r (i.e., on position r ? (k ? i)). Here is a dynamo program for the eight queens problem.
program Nqueens (f ]); begin n = 8; some k; k = 0; do n times begin k >> k0 = k0 + 1; find r in 1 .. n] with begin r = f k]; donot find i in 1 .. k-1] with either f i] = r orelse either f i] = r + (k - i) orelse f i] = r - (k - i) end end end

The program generates the 92 solutions and displays them (see Figure 4). 12

Figure 4: dynamo output for the Eight Queens problem.


"f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 1]":8 1]":8 1]":8 1]":8 1]":7 1]":7 1]":7 1]":7 1]":7 1]":7 1]":7 1]":7 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":6 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 1]":5 "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 2]":2 2]":2 2]":3 2]":4 2]":1 2]":2 2]":2 2]":3 2]":3 2]":4 2]":4 2]":5 2]":1 2]":2 2]":2 2]":3 2]":3 2]":3 2]":3 2]":3 2]":3 2]":3 2]":3 2]":4 2]":4 2]":4 2]":4 2]":8 2]":1 2]":1 2]":1 2]":2 2]":2 2]":2 2]":2 2]":3 2]":3 2]":3 2]":7 2]":7 2]":7 2]":7 2]":7 2]":7 2]":8 2]":8 "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 3]":5 3]":4 3]":1 3]":1 3]":3 3]":6 3]":4 3]":8 3]":1 3]":2 3]":2 3]":3 3]":5 3]":7 3]":7 3]":7 3]":7 3]":7 3]":5 3]":5 3]":1 3]":1 3]":1 3]":7 3]":7 3]":2 3]":1 3]":2 3]":8 3]":8 3]":4 3]":8 3]":6 3]":4 3]":4 3]":8 3]":1 3]":1 3]":4 3]":2 3]":2 3]":2 3]":1 3]":1 3]":4 3]":4 "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 4]":3 4]":1 4]":6 4]":3 4]":8 4]":3 4]":1 4]":2 4]":6 4]":5 4]":8 4]":1 4]":2 4]":1 4]":1 4]":2 4]":2 4]":4 4]":7 4]":8 4]":7 4]":8 4]":8 4]":1 4]":1 4]":8 4]":5 4]":4 4]":4 4]":6 4]":6 4]":1 4]":1 4]":6 4]":7 4]":4 4]":6 4]":7 4]":1 4]":4 4]":6 4]":6 4]":3 4]":4 4]":1 4]":1 "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 5]":1 5]":7 5]":2 5]":6 5]":6 5]":1 5]":8 5]":5 5]":8 5]":8 5]":6 5]":6 5]":8 5]":4 5]":3 5]":8 5]":4 5]":1 5]":1 5]":1 5]":5 5]":5 5]":4 5]":8 5]":3 5]":5 5]":8 5]":1 5]":2 5]":3 5]":8 5]":4 5]":7 5]":8 5]":3 5]":7 5]":8 5]":2 5]":3 5]":8 5]":3 5]":3 5]":8 5]":2 5]":7 5]":3 "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 6]":7 6]":5 6]":5 6]":2 6]":4 6]":4 6]":5 6]":1 6]":5 6]":1 6]":1 6]":8 6]":3 6]":8 6]":5 6]":5 6]":8 6]":8 6]":4 6]":4 6]":8 6]":2 6]":2 6]":2 6]":5 6]":7 6]":2 6]":7 6]":7 6]":7 6]":2 6]":7 6]":4 6]":3 6]":8 6]":1 6]":2 6]":8 6]":8 6]":1 6]":1 6]":1 6]":6 6]":8 6]":2 6]":6 "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 7]":4 7]":3 7]":7 7]":7 7]":2 7]":8 7]":3 7]":6 7]":2 7]":3 7]":3 7]":2 7]":7 7]":5 7]":8 7]":1 7]":1 7]":2 7]":2 7]":2 7]":2 7]":4 7]":7 7]":5 7]":2 7]":1 7]":7 7]":5 7]":3 7]":2 7]":7 7]":3 7]":8 7]":1 7]":6 7]":6 7]":4 7]":6 7]":6 7]":3 7]":8 7]":4 7]":4 7]":6 7]":6 7]":2 "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 8]":6 8]":6 8]":4 8]":5 8]":5 8]":5 8]":6 8]":4 8]":4 8]":6 8]":5 8]":4 8]":4 8]":3 8]":4 8]":4 8]":5 8]":5 8]":8 8]":7 8]":4 8]":7 8]":5 8]":3 8]":8 8]":3 8]":3 8]":3 8]":6 8]":4 8]":3 8]":6 8]":3 8]":7 8]":1 8]":2 8]":7 8]":4 8]":2 8]":6 8]":4 8]":8 8]":2 8]":3 8]":3 8]":7 "f 1]":4 "f 2]":1 "f "f 1]":4 "f 2]":1 "f "f 1]":4 "f 2]":2 "f "f 1]":4 "f 2]":2 "f "f 1]":4 "f 2]":2 "f "f 1]":4 "f 2]":2 "f "f 1]":4 "f 2]":2 "f "f 1]":4 "f 2]":2 "f "f 1]":4 "f 2]":6 "f "f 1]":4 "f 2]":6 "f "f 1]":4 "f 2]":6 "f "f 1]":4 "f 2]":7 "f "f 1]":4 "f 2]":7 "f "f 1]":4 "f 2]":7 "f "f 1]":4 "f 2]":7 "f "f 1]":4 "f 2]":8 "f "f 1]":4 "f 2]":8 "f "f 1]":4 "f 2]":8 "f "f 1]":3 "f 2]":1 "f "f 1]":3 "f 2]":5 "f "f 1]":3 "f 2]":5 "f "f 1]":3 "f 2]":5 "f "f 1]":3 "f 2]":5 "f "f 1]":3 "f 2]":6 "f "f 1]":3 "f 2]":6 "f "f 1]":3 "f 2]":6 "f "f 1]":3 "f 2]":6 "f "f 1]":3 "f 2]":6 "f "f 1]":3 "f 2]":6 "f "f 1]":3 "f 2]":6 "f "f 1]":3 "f 2]":6 "f "f 1]":3 "f 2]":7 "f "f 1]":3 "f 2]":7 "f "f 1]":3 "f 2]":8 "f "f 1]":2 "f 2]":4 "f "f 1]":2 "f 2]":5 "f "f 1]":2 "f 2]":5 "f "f 1]":2 "f 2]":6 "f "f 1]":2 "f 2]":6 "f "f 1]":2 "f 2]":7 "f "f 1]":2 "f 2]":7 "f "f 1]":2 "f 2]":8 "f "f 1]":1 "f 2]":5 "f "f 1]":1 "f 2]":6 "f "f 1]":1 "f 2]":7 "f "f 1]":1 "f 2]":7 "f There are no further 3]":5 "f 4]":8 3]":5 "f 4]":8 3]":8 "f 4]":5 3]":8 "f 4]":6 3]":7 "f 4]":3 3]":7 "f 4]":3 3]":7 "f 4]":5 3]":5 "f 4]":8 3]":8 "f 4]":2 3]":8 "f 4]":3 3]":1 "f 4]":5 3]":5 "f 4]":2 3]":5 "f 4]":3 3]":3 "f 4]":8 3]":1 "f 4]":8 3]":5 "f 4]":3 3]":1 "f 4]":3 3]":1 "f 4]":5 3]":7 "f 4]":5 3]":8 "f 4]":4 3]":7 "f 4]":1 3]":2 "f 4]":8 3]":2 "f 4]":8 3]":8 "f 4]":1 3]":8 "f 4]":1 3]":8 "f 4]":2 3]":4 "f 4]":1 3]":4 "f 4]":2 3]":2 "f 4]":5 3]":2 "f 4]":7 3]":2 "f 4]":7 3]":2 "f 4]":8 3]":2 "f 4]":8 3]":4 "f 4]":7 3]":6 "f 4]":8 3]":7 "f 4]":1 3]":7 "f 4]":4 3]":8 "f 4]":3 3]":1 "f 4]":7 3]":5 "f 4]":8 3]":3 "f 4]":6 3]":6 "f 4]":1 3]":8 "f 4]":6 3]":8 "f 4]":3 3]":5 "f 4]":8 3]":4 "f 4]":6 solutions "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 5]":6 5]":2 5]":7 5]":1 5]":6 5]":6 5]":1 5]":6 5]":7 5]":1 5]":2 5]":6 5]":1 5]":2 5]":5 5]":1 5]":6 5]":7 5]":8 5]":1 5]":4 5]":6 5]":1 5]":5 5]":4 5]":4 5]":8 5]":8 5]":8 5]":5 5]":1 5]":6 5]":5 5]":1 5]":3 5]":3 5]":1 5]":1 5]":4 5]":1 5]":8 5]":3 5]":3 5]":7 5]":2 5]":8 "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 6]":3 6]":7 6]":1 6]":3 6]":8 6]":8 6]":8 6]":1 6]":1 6]":7 6]":8 6]":1 6]":6 6]":5 6]":2 6]":7 6]":2 6]":2 6]":2 6]":7 6]":2 6]":4 6]":7 6]":7 6]":7 6]":1 6]":5 6]":5 6]":1 6]":1 6]":4 6]":4 6]":1 6]":6 6]":1 6]":8 6]":8 6]":4 6]":8 6]":4 6]":5 6]":5 6]":7 6]":4 6]":4 6]":2 "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 7]":7 7]":3 7]":3 7]":5 7]":5 7]":1 7]":6 7]":3 7]":3 7]":5 7]":3 7]":3 7]":8 7]":1 7]":6 7]":2 7]":7 7]":6 7]":4 7]":2 7]":8 7]":7 7]":4 7]":2 7]":5 7]":7 7]":7 7]":7 7]":7 7]":8 7]":8 7]":1 7]":4 7]":2 7]":7 7]":6 7]":6 7]":7 7]":3 7]":6 7]":1 7]":7 7]":2 7]":2 7]":6 7]":5 "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f "f 8]":2 8]":6 8]":6 8]":7 8]":1 8]":5 8]":3 8]":7 8]":5 8]":2 8]":7 8]":8 8]":2 8]":6 8]":3 8]":6 8]":5 8]":3 8]":6 8]":6 8]":6 8]":1 8]":6 8]":4 8]":2 8]":5 8]":2 8]":1 8]":4 8]":4 8]":5 8]":5 8]":6 8]":5 8]":5 8]":4 8]":3 8]":5 8]":5 8]":3 8]":4 8]":4 8]":4 8]":5 8]":3 8]":3

13

10 The Implementation
We now turn to the description of the Haskell implementation of dynamo . The following gives the A (almost) complete code. In fact, the paper you are now reading is the L TEX output of the literate code for dynamo . The only exception is the parsing code, which is generated by a Haskell parser generator (see below). The Haskell implementation of dynamo consists of three modules: the main module dynamo, and the modules L (for lexical scanning) and D (for parsing). Everything below that appears in a frame and is typeset in typescript font is part of the actual Haskell code.

11 Import Scanning and Parsing Modules


The module Dynamo uses the modules L and D.

module Dynamo where import L import D

The modules L and D contain the lexicalizer and the parser for dynamo programs.

12 Lexical Scanning
The lexical analysis maps a string (hopefully constituting a valid dynamo program) to a list of tokens. For example, the procedure lexer maps the dynamo program
begin either x = 3 orelse y = 4; x = 4 + y end

to the following list of tokens:


TokenBegin, TokenEither, TokenName "x", TokenEq, TokenInt 3, TokenOrelse, TokenName "y", TokenEq, TokenInt 4, TokenSC, TokenName "x", TokenEq, TokenInt 4, TokenPlus, TokenName "y", TokenEnd]

To achieve this, the lexicalizer scans the input and puts in appropriate tags. the result of lexical analysis.

Token

is the datatype for

14

module L where data Token = TokenBegin | TokenFail | TokenFind | TokenEither | TokenThen | TokenSome | TokenName String | TokenNeg | TokenEq | TokenPlus | TokenDiv | TokenSC | TokenDot deriving Show

| | | | | | | | | | | | |

TokenEnd TokenDo TokenIn TokenOrelse TokenElse TokenDonot TokenTrue TokenAnd TokenMore TokenMinus TokenOB TokenOSB TokenComma

| | | | | | | | | | | | |

TokenSkip TokenIterations TokenWith TokenIf TokenTest TokenInt Int TokenFalse TokenOr TokenLess TokenTimes TokenCB TokenCSB TokenProgram

lexer

is the main procedure for lexical tagging:

lexer :: String -> Token] lexer ] = ] lexer (c:cs) | isSpace c = lexer cs | isAlpha c = lexName (c:cs) | isDigit c = lexNum (c:cs) lexer ('=':cs) = TokenEq : lexer cs lexer ('+':cs) = TokenPlus : lexer cs lexer ('-':cs) = TokenMinus : lexer cs lexer ('*':cs) = TokenTimes : lexer cs lexer ('/':cs) = TokenDiv : lexer cs lexer ('(':cs) = TokenOB : lexer cs lexer (')':cs) = TokenCB : lexer cs lexer ('>':cs) = TokenMore : lexer cs lexer ('<':cs) = TokenLess : lexer cs lexer (';':cs) = TokenSC: lexer cs lexer (' ':cs) = TokenOSB: lexer cs lexer (']':cs) = TokenCSB: lexer cs lexer ('.':cs) = TokenDot: lexer cs lexer (',':cs) = TokenComma: lexer cs lexer (c:cs) | c == (chr 123) = lexComment cs 0

15

(chr 123)

is a representation of an opening curly bracket. Comments in dynamo programs start with an opening curly bracket { and end with the next matching occurrence of a closing curly bracket }. lexComment is called when an opening comment bracket is read; it puts the comment level to 0 and skips everything until another curly bracket is read. If the next curly bracket is an opening bracket, the comment level is incremented. If it is a closing bracket and we are at comment level 0 the comment ends. If it is a closing bracket at comment level greater than 0 the comment level is decremented. This allows comments to be nested. E.g., the programming fragment
do n times { record that all men are bachelors } begin m >> m0 = m0 + 1; b m] = 1 end;

can be commented out as follows:


{ do n times { record that all men are bachelors } begin m >> m0 = m0 + 1; b m] = 1 end; }

This is a very handy feature when debugging dynamo programs. The representations for opening and closing curly brackets that we use are (chr 123) and (chr 125). Haskell allows to refer to `f' and `g' directly, by means of { and }. We prefer (chr 123) and (chr 125) because unlike the brackets these A representations do not interfere with the L TEX processing of the source le.2
lexComment (c:cs) n | c == (chr 125) = case n <= 0 of True -> lexer cs False -> lexComment cs (n-1) | c == (chr 123) = lexComment cs (n+1) | otherwise = lexComment cs n

lexNum

recognizes integers and puts a marker TokenInt in front of them.

lexNum cs = TokenInt (read num) : lexer rest where (num,rest) = span isDigit cs

lexName

sorts out the identi ers from the reserved words of the language. An identi er (a name of a program or variable) is marked by putting a TokenName in front of it. Identi ers start with an alphabetic character, but may contain digits as well.
2 Recall that what you are reading now is the literate documentation of the dynamo implementation, as it was typeset A by L TEX.

16

lexName cs = case span isAlphaDigit cs of ("program",rest) -> TokenProgram : lexer rest ("begin",rest) -> TokenBegin : lexer rest ("end",rest) -> TokenEnd : lexer rest ("skip",rest) -> TokenSkip : lexer rest ("fail",rest) -> TokenFail : lexer rest ("do",rest) -> TokenDo : lexer rest ("times",rest) -> TokenIterations : lexer rest ("find", rest) -> TokenFind : lexer rest ("in", rest) -> TokenIn : lexer rest ("with", rest) -> TokenWith : lexer rest ("either",rest) -> TokenEither : lexer rest ("orelse",rest) -> TokenOrelse : lexer rest ("if",rest) -> TokenIf : lexer rest ("then",rest) -> TokenThen : lexer rest ("else",rest) -> TokenElse : lexer rest ("test",rest) -> TokenTest : lexer rest ("some",rest) -> TokenSome : lexer rest ("donot", rest) -> TokenDonot : lexer rest ("true",rest) -> TokenTrue : lexer rest ("false",rest) -> TokenFalse : lexer rest ("not",rest) -> TokenNeg : lexer rest ("and",rest) -> TokenAnd: lexer rest ("or",rest) -> TokenOr : lexer rest (name,rest) -> TokenName name : lexer rest where isAlphaDigit c = isAlpha c || isDigit c

That's all there is to lexical analysis.

13 DataType Declarations for Syntax


The parser has to know about a number of datatypes in order to construct the appropriate parse trees. The datatype declaration for variables distinguishes between single variables (x1), indexed variables (x 5]), and doubly indexed variables (x 5] 4]):

data Var = Svar String | Ivar String Exp | Divar String Exp Exp deriving Show

17

Exp

for numerical expressions (example:

x + (y * z)):

data Exp = Plus Exp Term | Minus Exp Term | Term Term deriving Show

Term

for numerical terms (example:

y * z):

data Term = Times Term Factor | Div Term Factor | Factor Factor deriving Show

Factor

for numerical factors (example: y):

data Factor = Int Int | Var Var | Brack Exp deriving Show

Bexp

for Boolean expressions (example:

x >= 0):

18

data Bexp = Bool Bool | Eq Exp Exp | Gr Exp Exp | Geq Exp Exp | Less Exp Exp | Leq Exp Exp | Neg Bexp | Conj Bexp Bexp | Disj Bexp Bexp deriving Show

Rng

for ranges (example:

1 .. 100]):

type Rng = (Exp,Exp)

Stmt

for program statements:

data Stmt = Some Var | Skip | Fail | Iseq Exp Exp | Put Var Var Exp | Test Bexp | If Bexp Stmt Stmt | Either Stmt Stmt | Do Exp Stmt | Find Var Rng Stmt | Find1 Var Int Int Stmt | Not Stmt | Comp Stmts deriving Show

Stmts

for lists of program statements.

19

type Stmts =

Stmt]

14 Parsing
The BNF de nition of dynamo program statements is given by the following piece of code. We give part of the input for the parser module, which is generated from the code below (typeset in typescript, between horizontal lines) by the Haskell Happy parser generator 5]. A program is either a single statement, or it starts with the reserved word program, followed by a program name, a list of display variable schemes, and a statement. The display variable schemes indicate whether a display variable is a single variable a, a variable with a single index a ], or a variable with a double index a ] ]. Indicating display variables by schemes is necessary because in dynamo nothing prevents the use of the same name a for these three purposes.
Pgm : Stmt | program Name ';' Stmt | program Name Vdecl ';' Stmt { (False, ],$1) { (False, ],$4) { (True,$3,$5) { { $2 { $3 : $1 { $1] { ] { ($1,0) { ($1,1) { ($1,2) } } } } } } } } } } }

Name : name Vdecl : '(' Vschemes ')' Vschemes : Vschemes ',' Vscheme | Vscheme | {- empty -} Vscheme : name | name ' ' ']' | name ' ' ']' ' ' ']'

The form of a dynamo statement is given by the following:


Stmt : | | | | | | | | some Var { Some $2 } some Vars { Comp (Some x) | x <- (reverse $2) ] } skip { Skip } fail { Fail } Exp '=' Exp { Iseq $1 $3 } Var '>' '>' Var '=' Exp { Put $1 $4 $6 } test Bexp { Test $2 } if Bexp then Stmt else Stmt { If $2 $4 $6 } either Stmt orelse Stmt { Either $2 $4 }

20

| | | | |

do Exp times Stmt find Var in Rng with Stmt donot Stmt begin Stmts end {- empty -}

{ { { { {

Do $2 $4 } Find $2 $4 $6 } Not $2 } Comp (reverse $2)} Skip }

The statements in a list are generated by the parser in the form of a parse stack, so the list has to be reversed to get its meaning right. The same holds for the variables is a variable list.
Stmts : Stmts ';' Stmt | Stmt Vars : Vars ',' Var | Var ',' Var { $3 : $1 { $1] { $3 : $1 { $3,$1] } } } }

Variables can be strings, strings with single indices, or strings with double indices. Note the di erence between variables and the variable schemes above.
Var : name | name ' ' Exp ']' | name ' ' Exp ']' ' ' Exp ']' { Svar $1 { Ivar $1 $3 { Divar $1 $3 $6 } } }

Ranges for bounded search are also indicated by square brackets:

Rng

: ' ' Exp '.' '.' Exp ']'

{ ($2,$5)

The capitalized keywords on the righthand side are the datatypes that are used for the construction of the parse trees. For example, the dynamo program
begin either x = 3 orelse y = 4; x = 4 + y end

gives rise to the following parse tree:


Comp Either (Iseq (Term (Factor (Var (Svar "x"))))

21

(Term (Factor (Int 3)))) (Iseq (Term (Factor (Var (Svar "y")))) (Term (Factor (Int 4)))), Iseq (Term (Factor (Var (Svar "x")))) (Plus (Term (Factor (Int 4))) (Factor (Var (Svar "y"))))]

The main parse procedure is called parse.

15 Datatype Declarations for Semantics


The following type declarations are needed for the semantics. All variables are represented as strings. If a variable has an index, it is incorporated in the string. The advantage of this is that it will usually not be necessary to allocate space for arrays. Instead, we just represent those values of the array that get actually computed by the program. A valuation is a list of pairs consisting of variables with their values. A store is a list of variables.
type Variable = String type Valuation = (Variable,Int)] type Store = Variable]

Vscheme

is the type of a variable scheme: the integer indicates the number of indexes. Pgm is the type of a program: the boolean in the rst component indicates whether a display constraint was stated, the list of variable schemes in the second component speci es the display constraint, and the statement in the third component gives the actual program. The indication of a display constraint allows us to distinguish between program Test; n = 1 and program Test (); n = 1. The rst program will display the value of n, the second will merely state that that the test evaluates to true.

type Vscheme = (Variable,Int) type Pgm = (Bool, Vscheme],Stmt)

A proper state is a triple consisting of a valuation, a global store and a local store. Two proper states are equal when they are equal componentwise. A state is either just a proper state, or improper.

22

data ProperState = Ps Valuation Store Store instance Eq ProperState where Ps a g l == Ps b g' l' = a == b && g == g' && l == l' type State = Maybe ProperState

The Maybe datatype is a convenient Haskell provision for handling exceptions. A datatype Maybe a for type a, has as possible values Nothing and Just a. The value Nothing signi es that an exception has arisen, the value Just a indicates that a proper answer has been computed. We will use Nothing for the improper state .

16 Values for Arithmetic and Boolean Expressions


Exception handling also plays a role in the computation process of answers for operators that may get inproper inputs. The following functions lift operators of types a -> b and a -> a -> b to a type that can handle inproper input by raising an exception.

maybe_op1 :: (a -> b) -> Maybe a -> Maybe b maybe_op1 op Nothing = Nothing maybe_op1 op (Just x) = Just (op x) maybe_op2 maybe_op2 maybe_op2 maybe_op2 :: op op op (a -> a -> b) -> Maybe a -> Maybe a -> Maybe b Nothing arg = Nothing arg Nothing = Nothing (Just x) (Just y) = Just (op x y)

Computing the values of arithmetic expressions, with exception handling:

23

a_val_exp :: Exp -> Valuation -> Maybe Int a_val_exp(Plus a_1 a_2) s = maybe_op2 (+) (a_val_exp a_1 s) (a_val_term a_2 s) a_val_exp(Minus a_1 a_2) s = maybe_op2 (-) (a_val_exp a_1 s) (a_val_term a_2 s) a_val_exp(Term a) s = a_val_term a s a_val_term :: Term -> Valuation -> Maybe Int a_val_term(Times a_1 a_2) s = maybe_op2 (*) (a_val_term a_1 s) (a_val_factor a_2 s) a_val_term(Div a_1 a_2) s = maybe_op2 div (a_val_term a_1 s) (a_val_factor a_2 s) a_val_term(Factor a) s = a_val_factor a s

In order to handle indexed variables, we convert the index to a string, by means of the following:
intStr :: Int -> String intStr i = intStrStr i ] intStrStr :: Int -> String -> String intStrStr i s | i == last = (intToDigit last):s | otherwise = intStrStr (quot i 10) (intToDigit(last):s) where last = rem i 10

This allows us to use the index as an appendix to the variable name, and do the variable lookup:

a_val_factor :: Factor -> Valuation -> Maybe Int a_val_factor(Int n) s = Just n a_val_factor(Var (Svar x)) s = lookup x s a_val_factor(Var (Ivar x expr)) s = lookup (x ++ (' ':(intStr i)) ++ "]") s where Just i = a_val_exp expr s a_val_factor(Var (Divar x expr1 expr2)) s = lookup (x ++ ' ':(intStr i) ++ ']':' ':(intStr j) ++ "]") s where Just i = a_val_exp expr1 s Just j = a_val_exp expr2 s a_val_factor(Brack expr) s = a_val_exp expr s

Computing the values of Boolean expressions, with exception handling: 24

b_val_exp :: Bexp -> Valuation -> Maybe Bool b_val_exp(Bool t) s = Just t b_val_exp(Eq e_1 e_2) s = maybe_op2 (==) (a_val_exp e_1 s) (a_val_exp e_2 s) b_val_exp(Gr e_1 e_2) s = maybe_op2 (>) (a_val_exp e_1 s) (a_val_exp e_2 s) b_val_exp(Geq e_1 e_2) s = maybe_op2 (>=) (a_val_exp e_1 s) (a_val_exp e_2 s) b_val_exp(Less e_1 e_2) s = maybe_op2 (<) (a_val_exp e_1 s) (a_val_exp e_2 s) b_val_exp(Leq e_1 e_2) s = maybe_op2 (<=) (a_val_exp e_1 s) (a_val_exp e_2 s) b_val_exp(Neg e) s = maybe_op1 not (b_val_exp e s) b_val_exp(Conj e_1 e_2) s = maybe_op2 (&&) (b_val_exp e_1 s) (b_val_exp e_2 s) b_val_exp(Disj e_1 e_2) s = maybe_op2 (||) (b_val_exp e_1 s) (b_val_exp e_2 s)

17 Handling Valuations and Stores


Resetting a variable in a valuation:

reset :: Variable -> Valuation -> Valuation reset v ] = ] reset v ((x,d):xds) | v == x = reset v xds | otherwise = ((x,d):reset v xds)

Taking the union of two lists. Needed further on to add elements to valuations, to stores, and to state lists. Note that no duplicates are introduced.

union :: Eq a => a] -> a] -> a] union s ] = s union s (e:ee) | elem e s = union s ee | otherwise = e:(union s ee)

Check if Exp consists of a single variable, an indexed variable or a doubly indexed variable, with the index or indices de ned for the current valuation, and if so, return it: 25

getvar :: Exp -> Valuation -> Variable] getvar (Term (Factor (Var (Svar v)))) s = v] getvar (Term (Factor (Var (Ivar v exp)))) s = make_ivar v s (a_val_exp exp s) getvar (Term (Factor (Var (Divar v exp1 exp2)))) s = make_divar v s (a_val_exp exp1 s) (a_val_exp exp2 s) getvar _ _ = ] getvarstring :: Var -> Valuation -> Variable] getvarstring (Svar x) s = x] getvarstring (Ivar x exp) s = make_ivar x s (a_val_exp exp s) getvarstring (Divar x exp1 exp2) s = make_divar x s (a_val_exp exp1 s) (a_val_exp exp2 s) getvarstring _ _ = ] make_ivar :: Variable -> Valuation -> Maybe Int -> make_ivar v val i = maybe ] f i where f j = v ++ (' ':(intStr j)) ++ "]"] Variable]

make_divar :: Variable -> Valuation -> Maybe Int -> Maybe Int -> make_divar v val i = maybe ( -> ]) f i where f i j = maybe ] g j where g j = v ++ ' ':(intStr i) ++ ']':' ':(intStr j) ++ "]"]

Variable]

processId

processes the two expressions constituting an identity, in the context of a valuation, in order to see whether the identity is an assignment or a test.

processId :: Exp -> Exp -> Valuation -> ( Variable],Maybe Int, Variable],Maybe Int) processId exp1 exp2 s = (getvar exp1 s, a_val_exp exp1 s, getvar exp2 s, a_val_exp exp2 s)

Check whether a set of outputs is safe for a given input triple (a; ga; la ). A set of outputs is safe if it contains at least one triple (b; gb ; lb ) that guarantees the forward property for (a; ga ; la) ! (b; gb ; lb). The condition for this is: la gb dom (a). Since the two stacks can only grow, and the global store grows only when a value gets assigned to a variable not in the local input store and not in the domain of the input valuation, we can replace this by two simpler conditions: gb = ga and la dom (a). Needed for a proper treatment of negation.

26

isSafe :: ProperState -> State] -> Bool isSafe _ ] = False isSafe agl states | not (localSafe agl) = False | otherwise = globalSafe agl states localSafe :: ProperState -> Bool localSafe (Ps a g l) = containedIn l

x | (x,d) <- a ]

globalSafe :: ProperState -> State] -> Bool globalSafe pstate ] = False globalSafe pstate (Nothing:ss) = globalSafe pstate ss globalSafe (Ps a g l) (Just (Ps b g' l'):ss) = g == g' || (isSafe (Ps a g l) ss) containedIn :: Eq a => a] -> a] -> Bool containedIn ] bs = True containedIn (a:as) bs = (elem a bs) && (containedIn as bs)

18 Evaluating Statements
run

runs a statement on a state, and outputs a list of states. Note that the input state may be improper. The procedure run checks whether the input is proper. If not, the output list only contains the improper state, otherwise the procedure dorun is invoked:

run :: Stmt -> State -> State] run stmt = maybe Nothing] (dorun stmt)

runs runs a sequence of statements for a list of input states, running the sequence for every member of the list of inputs and taking the union of the results.

runs :: Stmt] -> State] -> State] runs ] sts = sts runs (stmt:stmts) ] = ] runs (stmt:stmts) (st:sts) = runs stmts (union (run stmt st) (runs

stmt] sts))

doruncomp

runs a list of statements for a proper input state, and outputs a list of states. 27

doruncomp :: Stmt] -> ProperState -> State] doruncomp ] pstate = Just pstate] doruncomp stmt] pstate = dorun stmt pstate doruncomp (stmt:stmts) pstate = runs stmts (dorun stmt pstate)

doSome

is used in the evaluation of Some statements and in the evaluation of Find1 statements.

doSome :: Var -> ProperState -> ProperState doSome v (Ps a g l) = (Ps (reset x a) g (union l where x] = getvarstring v a

x]))

dorun

is the main procedure for executing single statements in the context of a proper input valuation. It takes a statement and a proper state, and returns a state list.

dorun :: Stmt -> ProperState ->

State]

A Some v statement resets the variable v. We isolate the procedure doSome, as we need it later on in the de nition of the evaluation of Find1 statements.
dorun (Some v) pstate = Just (doSome v pstate)]

The Skip statement returns the input. The Fail statement returns an empty state set.

dorun Skip pstate = dorun Fail pstate =

Just pstate] ]

The evaluation of the Iseq statement relies on the function processId.

28

dorun (Iseq e1 e2) (Ps val g l) = case (processId e1 e2 val) of ( v],Nothing,_,Just m) | elem v l -> Just (Ps ((v,m):val) g l)] | otherwise -> Just (Ps ((v,m):val) (union g v]) l)] (_,Just m, v],Nothing) | elem v l -> Just (Ps ((v,m):val) g l)] | otherwise -> Just (Ps ((v,m):val) (union g v]) l)] (_,Just m,_,Just n ) | m == n -> Just (Ps val g l)] | otherwise -> ] (_,_,_,_) -> Nothing]

The Put v x exp statement merely abbreviates the sequential composition of Some x, an identity statement, Some v, and a second identity statement:
dorun (Put v x exp) pstate = doruncomp Some x,id1,Some v,id2] pstate where id1 = (Iseq (Term (Factor (Var v))) (Term (Factor (Var x)))) id2 = (Iseq (Term (Factor (Var v))) exp)

The Test statement checks the value of a Boolean expression. If the expression cannot be evaluated for the current input an exception is raised. Note the di erence between the program statements test x = 3 and x = 3. The rst raises an exception if x is not in the domain of the input, the second assigns 3 to x in that case.
dorun (Test b) (Ps val g l) | b_val_exp b val == Nothing = Nothing] | b_val_exp b val == Just True = Just (Ps val g l)] | b_val_exp b val == Just False = ]

The If statement raises an exception when its Boolean expression cannot be evaluated. Otherwise it picks the branch indicated by the evaluation of the Boolean.
dorun (If b s1 s2) (Ps val | b_val_exp b val == | b_val_exp b val == | b_val_exp b val == g l) Nothing = Nothing] Just True = dorun s1 (Ps val g l) Just False = dorun s2 (Ps val g l)

29

Evaluating the Either statement is taking the union of the results of the component statements.

dorun (Either s1 s2) (Ps val g l) = union (dorun s1 (Ps val g l)) (dorun s2 (Ps val g l))

The Do statement for bounded iteration evaluates the arithmetical expression for the number of iterations. If this fails, an exception is raised. If it succeeds, the embedded statement is executed the appropriate number of times, or skipped if the control number is not positive.

dorun (Do exp s) (Ps val g l) | a_val_exp exp val == Nothing = Nothing] | otherwise = (doruncomp (iter n s) (Ps val g l)) where Just n = (a_val_exp exp val) iter m s | m <= 0 = ] | otherwise = s:(iter (m-1) s)

The Find statement tries to evaluate the range expressions, and evaluates a Find1 statement when it nds proper values, and raises an exception otherwise.

dorun | | |

(Find var (exp1,exp2) s) (Ps val g l) a_val_exp exp1 val == Nothing = Nothing] a_val_exp exp2 val == Nothing = Nothing] otherwise = dorun (Find1 var m n s) (Ps val g l) where Just m = (a_val_exp exp1 val) Just n = (a_val_exp exp2 val)

The Find1 statement executes a bounded search.

30

dorun | | |

(Find1 var m n s) pstate n < m = ] m == n = try otherwise = union try next where try = (doruncomp id,s] new) new = (doSome var pstate) id = (Iseq (Term (Factor (Var var))) (Term (Factor (Int m)))) next = dorun (Find1 var (m+1) n s) pstate

The Not statement evaluates the embedded statement, and checks the result for safety.
dorun (Not stmt) pstate = case (dorun stmt pstate) of ] -> Just pstate] x | isSafe pstate x -> ] | otherwise -> Nothing]

Finally, a Comp statement is evaluated by invoking doruncomp.


dorun (Comp stmts) pstate = doruncomp stmts pstate

19 Starting the Dynamo Computation Process


A computation with a dynamo program gets as input from the parser a pair (displayC,vschemes,stmt), where displayC indicates whether a display constraint was speci ed or not, vschemes gives the display constraint, and stmt gives the program statement in the abstract syntactic form produced by the parser. The actual dynamo computation is called by process, and starts with an empty valuation and empty stores.
compute :: Pgm -> (Bool, Vscheme], State]) compute (displayC,vschemes,stmt) = (displayC,vschemes, (process stmt)) process :: Stmt -> State] process stmt = dorun stmt (Ps

])

31

20 Displaying the Results


The goal is to display all instantiations of the dynamically free variables of a dynamo program, and for each of those a sample instantiation of the dynamically active variables of the program. The dynamically active variables are the variables that are introduced by means of a wide scope some statement. Thus, for any given output valuation, the global variables have universal force, the local variables existential force, and we have to clearly indicate the distinction when we display the results. The procedure splitValues splits the variable-value pairs in the valuation of a proper state into existentally quanti ed ones and the universally quanti ed ones, according to whether or not the variable occurs in the local variable store. Note that splitValues e ectively reverses a valuation before display. This comes in handy, for the valuation is computed as a stack, with the most recently computed value on top. The net e ect of rst building a stack and then reversing it is that the valuations are displayed in the order in which they were computed.

splitValues :: ProperState -> (Valuation,Valuation) splitValues pstate = splitValues1 pstate ] ] splitValues1 :: ProperState -> Valuation -> Valuation -> (Valuation,Valuation) splitValues1 (Ps ] _ _) uVal eVal = (uVal,eVal) splitValues1 (Ps ((v,d):rest) g l) uVal eVal | elem v l = splitValues1 (Ps rest g l) uVal ((v,d):eVal) | otherwise = splitValues1 (Ps rest g l) ((v,d):uVal) eVal

In case there are no solutions, we say so. In case the solution has an empty set of global variables, the program was just a check, so we can return `True' as outcome. The output should distinguish between the values that are computed solutions (the universal values) and the values that are mere sample values (the existential values). The list of sample values is preceded by \e.g.".

32

showsV :: ProperState -> String -> String showsV pstate string = case (uVals,eVals) of ( ], ]) -> "True" otherwise -> showsuVal uVals (showseVal eVals string) where (uVals,eVals) = splitValues pstate showsuVal :: Valuation -> String -> String showsuVal ] string = "True" ++ string showsuVal a string = showsValues a string showseVal :: Valuation -> String -> String showseVal ] string = string showseVal a string = " e.g. " ++ (showsValues a string) showsValues :: Valuation -> String -> String showsValues ] string = ' ':string showsValues ((v,d):rest) string = shows v (':':(shows d (' ':(showsValues rest string))))

It is important to inform the user about possible incompleteness of the computed solution set. This is done by means of `There may be solutions, but none were found' if there are only improper outcomes, or `There may be further solutions' if both proper and improper outcomes are generated. showStates displays the solution set, and gives informative feedback on possible incompleteness of the computed answer. Note that (chr 10) is a representation for the \newline" character. It is possible to refer to A \newline" in Haskell directly, by means of '\n', but we refrain from doing so as this confuses the L TEX processor.

33

showStates :: State] -> String showStates ] = "False" showStates Nothing] = "There may be solutions, but none were found" showStates (Nothing:states) = showMaybeStates states showStates (Just pstate:states) = showsV pstate (showMoreStates states) showMaybeStates :: State] -> String showMaybeStates ] = "There may be solutions, but none were found" showMaybeStates Nothing] = "There may be solutions, but none were found" showMaybeStates (Nothing:states) = showMaybeStates states showMaybeStates (Just pstate:states) = showsV pstate (showMaybeMoreStates states) showMoreStates :: State] -> String showMoreStates ] = (chr 10):"There are no further solutions" showMoreStates Nothing] = (chr 10):"There may be further solutions" showMoreStates (Nothing:states) = showMaybeMoreStates states showMoreStates (Just pstate:states) = (chr 10):(showsV pstate (showMoreStates states)) showMaybeMoreStates :: State] -> String showMaybeMoreStates ] = (chr 10):"There may be further solutions" showMaybeMoreStates Nothing] = (chr 10):"There may be further solutions" showMaybeMoreStates (Nothing:states) = showMaybeMoreStates states showMaybeMoreStates (Just pstate:states) = (chr 10):(showsV pstate (showMaybeMoreStates states))

21 Pruning the State Space


If a display list of variable schemes is given at the beginning of the program, we select those variables from the valuation that satisfy a scheme from the list. For this, we rst need to decompose a variable into its variable scheme: its identi er pre x, plus a number indicating the count of indices.

decompVar :: Variable -> Vscheme decompVar varstring = case break (x, ]) (x,(y:ys)) | ' ' `elem` ys | otherwise

(== ' ') varstring of -> (x,0) -> (x,2) -> (x,1)

varForm

checks whether a variable satis es a display list of schemes. 34

varForm :: Variable -> Vscheme] -> Bool varForm variable ] = False varForm variable ((s,i):rest) = ((s,i) == (decompVar variable)) || varForm variable rest

In any case, we don't want to see all the values of the local variables. If a local variable is required for display, one example instantiation is enough. If a display constraint was speci ed, we prune away all variables from the valuations that are not speci ed by the display requirements.

pruneStates :: (Bool, Vscheme], State]) -> State] pruneStates (displayC,schemes,states) | displayC == False = pruneStates1 states ] ] | otherwise = pruneStates2 schemes states

pruneStates1 :: State] -> Valuation] -> State] -> State] pruneStates1 ] vals states = states pruneStates1 (Nothing:states) vals newstates = (Nothing:(pruneStates1 states vals newstates)) pruneStates1 (Just (Ps a g l):states) vals newstates | val `elem` vals = (pruneStates1 states vals newstates) | otherwise = (pruneStates1 states (val:vals) (Just (Ps val g l):newstates)) where val = (x,d) | (x,d) <- a, x `elem` g] pruneStates2 :: Vscheme] -> State] -> Valuation] -> State] -> State] pruneStates2 schemes ] vals states = states pruneStates2 schemes (Nothing:states) vals newstates = (Nothing:(pruneStates2 schemes states vals newstates)) pruneStates2 schemes (Just (Ps a g l):states) vals newstates | val `elem` vals = (pruneStates2 schemes states vals newstates) | otherwise = (pruneStates2 schemes states (val:vals) (Just (Ps val g l):newstates)) where val = (x,d) | (x,d) <- a, x `elem` g, varForm x schemes]

22 Wrapping it all up
Ask for a program, lexicalize and parse its contents, compute the output valuations, and prune and display the results:

35

main = do putStr "Input program: " ifile <- getLine readFile ("/home/jve/dynamo/" ++ ifile) >>= (putStr . showStates . pruneStates . compute . parse . lexer) mainw = do putStr "Input program: " ifile <- getLine s <- readFile ("/home/jve/dynamo/" ++ ifile) writeFile "dynamo.output" ((showStates . pruneStates . compute . parse . lexer) s) putStr "result written to file"

References
1] Krzysztof Apt, Jacob Brunekreef, Vincent Parrington, and Andrea Schaerf. Alma-0: An imperative language that supports declarative programming. Technical Report PNA-R9713, CWI, 1997. 2] Jan van Eijck. Programming with dynamic predicate logic. Report, CWI/ILLC, November 1998. Available from www.cwi.nl/~jve. 3] J. Groenendijk and M. Stokhof. Dynamic predicate logic. Linguistics and Philosophy, 14:39{100, 1991. 4] P. Hudak, J. Fasel, and J. Peterson. A gentle introduction to Haskell. Technical report, Yale University, 1996. Available from the Haskell homepage: http://www.haskell.org. 5] S. Marlow. Happy user guide. Description of the Haskell Parser Generator, Version 1.4, December 1997. 6] J. Peterson and K. Hammond (eds). Report on the programming language Haskell,Version 1.4. Available from the Haskell homepage: http://www.haskell.org, 1997. 7] N. Wirth. Algorithms + Datastructures = Programs. Prentice Hall, 1978.

36

Anda mungkin juga menyukai