bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created...

220
An Implementation of Bach A Work In Perpetual Progress Version 2007 Kee Siong Ng, Joshua Cole

Transcript of bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created...

Page 1: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

An Implementation of Bach

A Work In Perpetual ProgressVersion 2007

Kee Siong Ng, Joshua Cole

Page 2: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

Contents

1 Types and Terms 1

1.1 Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.1.1 Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101.1.2 Type Checking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

1.2 Terms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211.2.1 Term Representation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211.2.2 Memory Management . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311.2.3 Sharing of Nodes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341.2.4 Free and Bound Variables . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351.2.5 Variable Renaming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391.2.6 Term Substitution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411.2.7 Theorem Prover Helper Functions . . . . . . . . . . . . . . . . . . . . . . . 44

2 Equational Reasoning 50

2.1 Term Rewriting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502.1.1 Internal Rewrite Routines . . . . . . . . . . . . . . . . . . . . . . . . . . . . 502.1.2 Computing and Reducing Candidate Redexes . . . . . . . . . . . . . . . . . 702.1.3 Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86

2.2 Interaction with the Theorem Prover . . . . . . . . . . . . . . . . . . . . . . . . . . 972.2.1 Rank k Computations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 972.2.2 Free Variable Instantiation . . . . . . . . . . . . . . . . . . . . . . . . . . . 99

3 Theorem Proving in Bach 106

3.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063.2 Labels and Worlds . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1063.3 Accessibility Relation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1143.4 Tableau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1153.5 Tableaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120

3.5.1 The Tableaux Expansion Algorithm . . . . . . . . . . . . . . . . . . . . . . 1233.5.2 Classical Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1303.5.3 Structural Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1413.5.4 Propagation Rules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142

3.6 A Constraints Solver . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144

4 The Parser 151

4.1 Parsing using Lex and Yacc . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1524.1.1 Scanner . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1524.1.2 Parser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1564.1.3 Bach Main Program . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176

5 Global Data Structures 178

I

Page 3: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

CONTENTS II

6 System Modules 196

6.1 The Booleans Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1966.2 The Numbers Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2016.3 The List Module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 203

7 A Listing of the Code Chunks 210

Page 4: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

Chapter 1

Types and Terms

1.1 Types

Comment 1.1.1. Types are defined inductively in the logic, thus lending itself nicely to the useof composite pattern [GHJV95, p.163] for its implementation.

We differentiate between atomic and composite types. Atomic types are obtained from typeconstructors with arity 0. Examples of these include int, float, nat, char, string, etc. (Note thatstring is a nullary type constructor in this case. Strings in general can also be constructed fromList char.) They are the base types, and occupy the leaf nodes of a composite type structure.Everything else are composite types. Examples of composite types include types obtained fromtype constructors of non-zero arity like List α, Btree α, Graph α β, etc; function types like set α(this is equivalent to α → Ω) and multiset α (α → nat); and product types obtained from thetuple-forming operator.

The following is an outline of the data types module. We first give the abstract classes, followedby the actual data types.

1 〈types.h 1〉≡#ifndef _DATATYPE_H_

#define _DATATYPE_H_

#include <set>#include <vector>#include <string>#include <assert.h>#include <iostream>using namespace std;#define dcast dynamic_cast

extern const string underscore, alpha, Parameter, Tuple, Arrow,gBool, gInt, gFloat, gChar, gString;

〈type::function declarations 5e〉〈type::type 2b〉〈type::composite types 3b〉〈type::parameters 4c〉〈type::tuples 6b〉〈type::algebraic types 9a〉〈type::abstractions 7a〉〈type::synonyms 6a〉

1

Page 5: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 2

#endif

2a 〈types.cc 2a〉≡#include "types.h"

#define uint unsigned int

〈type::functions 3a〉〈type::composite types::implementation 3c〉〈type::parameters::implementation 4d〉〈type::tuples::implementation 6c〉〈type::algebraic types::implementation 9c〉〈type::abstractions::implementation 7b〉

Comment 1.1.2. The top-level type structure contains as members those variables and functionsthat are common to all types. Every type obviously has a name.

The functions setAlpha and addAlpha are used to configure subtypes; they are defined onlyfor composite types like tuples and list. (See Comment 1.1.4 for details.)

2b 〈type::type 2b〉≡ (1) 2c ⊲

class type public:

int count;type() count = 0; type(string n) : tag(n) count = 0; virtual ∼type() virtual void setAlpha(type ∗ x, unsigned int y) virtual void addAlpha(type ∗ x) virtual type ∗ getAlpha(unsigned int x) return NULL; virtual int alphaCount() return 0; virtual bool isComposite() return false; virtual bool isTuple() return false; virtual bool isAbstract() return false; virtual bool isParameter() return false; virtual bool isSynonym() return false; virtual bool isUdefined() return false; virtual string getName() return tag; virtual string & getTag() return tag; virtual type ∗ clone() count++; return this; virtual void deccount() count−−; virtual void getParameters(set<string> & ret) virtual void renameParameters() virtual void renameParameter(string name)

protected:string tag;

;Uses getParameters 4a, renameParameter 4b, and renameParameters 4b.

Comment 1.1.3. We use reference counting for the memory management of the base types. Thevariable count keeps track of the number of references to a type. Deallocation of a type structureis done using the function delete-type defined as follows.

2c 〈type::type 2b〉+≡ (1) ⊳ 2b

void delete_type(type ∗ x);Defines:

delete_type, used in chunks 3c, 6a, 11–13, 18a, 20d, 82b, 178, 186, 190a, and 193.

Page 6: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 3

3a 〈type::functions 3a〉≡ (2a) 5f ⊲

void delete_type(type ∗ x) // if (x->isComposite() || x->isParameter()) assert(x->count == 0);if (x→count ≡ 0) delete x; else x→deccount();

Defines:

delete_type, used in chunks 3c, 6a, 11–13, 18a, 20d, 82b, 178, 186, 190a, and 193.

Comment 1.1.4. The following is the class declaration for composite types. The member alphastores the sub-types in the composite structure. It serves different purposes for different kinds ofcomposite types.

3b 〈type::composite types 3b〉≡ (1)

class type_composite : public type protected:

vector<type ∗> alpha;public:

virtual ∼type_composite();bool isComposite() return true; virtual void deccount();virtual void setAlpha(type ∗ x, unsigned int y);virtual void addAlpha(type ∗ x) alpha.push_back(x); virtual type ∗ getAlpha(unsigned int x);virtual int alphaCount() return alpha.size(); virtual string getName();virtual type ∗ clone() assert(false); virtual void getParameters(set<string> & x);virtual void renameParameters();virtual void renameParameter(string name);

;

Uses getParameters 4a, renameParameter 4b, and renameParameters 4b.

3c 〈type::composite types::implementation 3c〉≡ (2a) 3d ⊲

type_composite::∼type_composite() for (unsigned int i=0; i6=alpha.size(); i++) delete_type(alpha[i]);

Uses delete_type 2c 3a.

3d 〈type::composite types::implementation 3c〉+≡ (2a) ⊳ 3c 3e ⊲

void type_composite::deccount() count−−;for (unsigned int i=0; i6=alpha.size(); i++) alpha[i]→deccount();

3e 〈type::composite types::implementation 3c〉+≡ (2a) ⊳ 3d 4a ⊲

void type_composite::setAlpha(type ∗ x, unsigned int y) assert(y < alpha.size()); alpha[y] = x;

type ∗ type_composite::getAlpha(unsigned int x) assert(x < alpha.size()); return alpha[x];

string type_composite::getName() assert(false); return "";

Page 7: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 4

Comment 1.1.5. The following functions are used during unification and type-checking. Thefirst one collects in a set all the parameters in a type. This is used in the unification algorithm. Thesecond and third functions are used to rename parameters during instantiation and type checking.

4a 〈type::composite types::implementation 3c〉+≡ (2a) ⊳ 3e 4b ⊲

void type_composite::getParameters(set<string> & ret) for (unsigned int i=0; i6=alpha.size(); i++)

alpha[i]→getParameters(ret);

Defines:getParameters, used in chunks 2–5 and 11d.

4b 〈type::composite types::implementation 3c〉+≡ (2a) ⊳ 4a

void type_composite::renameParameters() set<string> ps;getParameters(ps);set<string>::iterator p = ps.begin();while (p 6= ps.end()) renameParameter(∗p); inc_counter(); p++;

void type_composite::renameParameter(string name)

for (unsigned int i=0; i6=alpha.size(); i++)alpha[i]→renameParameter(name);

Defines:renameParameter, used in chunks 2–5.renameParameters, used in chunks 2–5 and 16a.

Uses getParameters 4a and inc_counter 5f.

Comment 1.1.6. Parameters are type variables.

4c 〈type::parameters 4c〉≡ (1)

class type_parameter : public type public: type_parameter();

type_parameter(string x) tag = Parameter; vname = x; type ∗ clone() return new type_parameter(vname); bool isParameter() return true; string getName() return tag + underscore + vname; void getParameters(set<string> & ret);void renameParameters();void renameParameter(string name);

private:string vname;

;

extern string newParameterName();

Uses getParameters 4a, renameParameter 4b, and renameParameters 4b.

Comment 1.1.7. When we create a new type parameter, a distinct name of the form alpha_i

where i is a number will be assigned to the parameter.

4d 〈type::parameters::implementation 4d〉≡ (2a) 5a ⊲

#include "global.h"

static int parameterCount = 0;type_parameter::type_parameter()

tag = Parameter; vname = newParameterName();

Page 8: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 5

Comment 1.1.8. New parameter names are created using this next function. The variableparameterCount is used here as the index for new parameter names. This function can be replacedwith newVar in terms.nw.

5a 〈type::parameters::implementation 4d〉+≡ (2a) ⊳ 4d 5b ⊲

string newParameterName() string vname = alpha + numtostring(parameterCount++);return vname;

5b 〈type::parameters::implementation 4d〉+≡ (2a) ⊳ 5a 5c ⊲

void type_parameter::getParameters(set<string> & ret) string temp = tag + underscore + vname;ret.insert(temp);

Uses getParameters 4a and insert 23a.

5c 〈type::parameters::implementation 4d〉+≡ (2a) ⊳ 5b 5d ⊲

void type_parameter::renameParameters() string temp = tag+underscore+vname; renameParameter(temp); inc_counter();

Uses inc_counter 5f, renameParameter 4b, and renameParameters 4b.

Comment 1.1.9. If a parameter has been indexed, we will first remove its index and then attacha new one. The function rfind returns npos if an underscore cannot be found in vname. (Searchproceeds from the end of vname.)

5d 〈type::parameters::implementation 4d〉+≡ (2a) ⊳ 5c

void type_parameter::renameParameter(string name) string tname = tag + underscore + vname;if (tname 6= name) return;char temp[10]; sprintf(temp, "_%d", get_counter_value());

uint i = vname.rfind(underscore);if (i 6= string::npos) vname.erase(i, vname.size()-i);

string temp2(temp); vname = vname + temp2;

Uses get_counter_value 5f and renameParameter 4b.

Comment 1.1.10. Some times, parameters need to be renamed to avoid name capture. We usea global counter for this purpose.

5e 〈type::function declarations 5e〉≡ (1)

void inc_counter();int get_counter_value();

Uses get_counter_value 5f and inc_counter 5f.

5f 〈type::functions 3a〉+≡ (2a) ⊳ 3a

static int counter = 0;void inc_counter() counter++; int get_counter_value() return counter;

Defines:get_counter_value, used in chunk 5.inc_counter, used in chunks 4 and 5.

Page 9: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 6

Comment 1.1.11. Users can define type synonyms of the form t1 = t2, where t1 is an identifierand t2 the actual type. These are handled using the following class. The identifier t1 is stored intname; the actual type t2 is stored in actual.

6a 〈type::synonyms 6a〉≡ (1)

class type_synonym : public type public:

type_synonym(string name, type ∗ ac) tag = name; tname = name; actual = ac;

∼type_synonym() delete_type(actual); type ∗ clone()

// assert(actual); count++; actual->count++; return this; assert(actual);return new type_synonym(tname,actual→clone());

void deccount() assert(false); bool isSynonym() return true; type ∗ getActual() return actual; string getName() return actual→getName();

private:type ∗ actual;string tname;

;Uses delete_type 2c 3a.

Comment 1.1.12. We support the following base types: boolean, integer, float point numberand string. Natural number is not supported because we can always use integer in its place.

Comment 1.1.13. The following is used to create product types.

6b 〈type::tuples 6b〉≡ (1)

class type_tuple : public type_composite public:

type_tuple() tag = Tuple; type ∗ clone();bool isTuple() return true; string getName();

;

6c 〈type::tuples::implementation 6c〉≡ (2a) 6d ⊲

type ∗ type_tuple::clone() type_tuple ∗ ret = new type_tuple;for (int i=0; i6=alphaCount(); i++)

ret→addAlpha(alpha[i]→clone());return ret;

6d 〈type::tuples::implementation 6c〉+≡ (2a) ⊳ 6c

string type_tuple::getName() string ret = "( ";for (unsigned int i=0; i6=alpha.size()-1; i++)

ret = ret + alpha[i]→getName() + " * ";ret = ret + alpha[alpha.size()-1]→getName() + ")";return ret;

Page 10: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 7

Comment 1.1.14. This is used for the construction of function types. It is worth mentioningthat sets and multisets have function types.

Function types of particular interest here are those for transformations. The variable rank

is used to record the rank of transformations. This value can be calculated using compRank.The functions getSource and getTarget returns the source and target of a transformation. Thefunction getArg returns the n-th argument.

7a 〈type::abstractions 7a〉≡ (1)

class type_abstraction : public type_composite public:

int rank;type_abstraction() tag = Arrow; rank = -5; type_abstraction(type ∗ source, type ∗ target)

tag = Arrow; rank = -5;addAlpha(source); addAlpha(target);

bool isAbstract() return true; type ∗ clone();type ∗ getArg(int n);type ∗ getSource();type ∗ getTarget();string getName();int compRank();

;

Defines:getArg, never used.getSource, never used.getTarget, never used.

Uses compRank 8d.

7b 〈type::abstractions::implementation 7b〉≡ (2a) 7c ⊲

type ∗ type_abstraction::clone() type_abstraction ∗ ret =

new type_abstraction(alpha[0]→clone(), alpha[1]→clone());ret→rank = rank;return ret;

7c 〈type::abstractions::implementation 7b〉+≡ (2a) ⊳ 7b 8a ⊲

string type_abstraction::getName() string ret;if (alpha[0]→isComposite())

ret = "(" + alpha[0]→getName() + ") -> ";else ret = alpha[0]→getName() + " -> ";if (alpha[1]→isComposite())

ret = ret + "(" + alpha[1]→getName() + ")";else ret = ret + alpha[1]→getName();return ret;

Page 11: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 8

8a 〈type::abstractions::implementation 7b〉+≡ (2a) ⊳ 7c 8b ⊲

type ∗ type_abstraction::getArg(int n) assert(n < rank);type ∗ p = this;int temp = 0;while (temp 6= n) p = p→getAlpha(1); temp++; return p→getAlpha(0);

Defines:

getArg, never used.

8b 〈type::abstractions::implementation 7b〉+≡ (2a) ⊳ 8a 8c ⊲

type ∗ type_abstraction::getSource() assert(rank 6= -5);type ∗ p = this;for (int i=0; i6=rank; i++) p = p→getAlpha(1);assert(p→getAlpha(0)); return p→getAlpha(0);

Defines:

getSource, never used.

8c 〈type::abstractions::implementation 7b〉+≡ (2a) ⊳ 8b 8d ⊲

type ∗ type_abstraction::getTarget() assert(rank 6= -5);type ∗ p = this;for (int i=0; i6=rank; i++) p = p→getAlpha(1);return p→getAlpha(1);

Defines:

getTarget, never used.

Comment 1.1.15. This function computes the rank of a transformation. We inspect the spineof the type and count the number of predicate types appearing in it.

8d 〈type::abstractions::implementation 7b〉+≡ (2a) ⊳ 8c

int type_abstraction::compRank() if (alpha[1]→isAbstract() ∧ alpha[0]→isAbstract() ∧

alpha[0]→getAlpha(1)→getTag() ≡ gBool) type_abstraction ∗ t = dcast<type_abstraction ∗>(alpha[1]);return 1 + t→compRank();

return 0;

Defines:compRank, used in chunk 7a.

Page 12: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 9

Comment 1.1.16. Algebraic types are supported using the following classes. The class type_udefinedsupports nullary type constructors; the class type_alg supports non-nullary type constructors.Perhaps it makes sense to combine the two in one type.

9a 〈type::algebraic types 9a〉≡ (1) 9b ⊲

class type_udefined : public type const vector<string> values;

public:type_udefined(string & tname, const vector<string> &vals)

: type(tname), values(vals) type_udefined(string & tname) : type(tname) bool isUdefined() return true; // type * clone() count++; return this; const vector<string> & getValues() return values;

;

9b 〈type::algebraic types 9a〉+≡ (1) ⊳ 9a

class type_alg : public type_composite public:

type_alg(string tid) tag = tid; type_alg(string tid, vector<type ∗> x)

tag = tid;for (unsigned int i=0; i6=x.size(); i++)

addAlpha(x[i]→clone());type_alg(string tid, type_tuple ∗ x)

tag = tid;for (int i=0; i6=x→alphaCount(); i++)

addAlpha(x→getAlpha(i)→clone());type ∗ clone() return new type_alg(tag, alpha); string getName();

;

9c 〈type::algebraic types::implementation 9c〉≡ (2a)

string type_alg::getName() string ret = "(" + tag;for (uint i=0; i6=alpha.size()-1; i++)

ret = ret + " " + alpha[i]→getName();ret = ret + " " + alpha[alpha.size()-1]→getName() + ")";return ret;

Page 13: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 10

1.1.1 Unification

Comment 1.1.17. We now discuss type unification. The type unification algorithm given hereis adapted from the one given in [Pey87, Chap.5].

10a 〈unification.h 10a〉≡#ifndef _UNIFICATION_H_

#define _UNIFICATION_H_

#include "terms.h"

#include "types.h"

#include <vector>#include <utility>struct term_type term ∗ first; type ∗ second; ;extern bool unify(vector<pair<string,type ∗> > &eqns,type ∗tvn,type ∗t);extern type ∗ apply_subst(vector<pair<string, type ∗> > & eqns, type ∗ x);extern type ∗ wellTyped(term ∗ t);extern pair<type ∗, vector<term_type> > mywellTyped(term ∗ t);extern type ∗ get_type_from_syn(type ∗ in);

#endif

Defines:term_type, used in chunks 15b, 16b, 21b, 97, 178, 181, and 186a.

Uses apply_subst 11a, get_type_from_syn 12b, and unify 13.

10b 〈unification.cc 10b〉≡#include <iostream>#include <utility>#include <vector>#include <string>#include "types.h"

#include "unification.h"

using namespace std;

bool unify_verbose = false; // set this to see the unification process〈unification body 10c〉〈type checking 21a〉

Comment 1.1.18. The function getBinding returns the binding for parameter x in a typesubstitution θ.

10c 〈unification body 10c〉≡ (10b) 11a ⊲

type ∗ getBinding(vector<pair<string, type ∗> > & eqns, type ∗ x) assert(x→isParameter());string vname = x→getName();for (unsigned int i=0; i6=eqns.size(); i++)

if (eqns[i].first ≡ vname) return eqns[i].second;return x;

Defines:getBinding, used in chunks 11a and 13.

Page 14: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 11

Comment 1.1.19. Given a type substitution θ and a type t with parameters, apply_subst

computes tθ.

11a 〈unification body 10c〉+≡ (10b) ⊳ 10c 11b ⊲

type ∗ apply_subst(vector<pair<string, type ∗> > & eqns, type ∗ t) if (t→isParameter())

return getBinding(eqns, t)→clone();type ∗ ret = t→clone();for (int i=0; i6=ret→alphaCount(); i++)

type ∗ temp = apply_subst(eqns, ret→getAlpha(i));delete_type(ret→getAlpha(i));ret→setAlpha(temp, i);

return ret;

Defines:apply_subst, used in chunks 10a, 12a, 13, and 18a.

Uses delete_type 2c 3a and getBinding 10c.

Comment 1.1.20. This function extends a substitution θ with an additional equation x = t.If t is x, then the extension succeeds trivially. Otherwise, unless x appears in t, the extensionsucceeds.

11b 〈unification body 10c〉+≡ (10b) ⊳ 11a 12b ⊲

bool extend(vector<pair<string, type ∗> > & eqns, type ∗ x, type ∗ t) assert(x→isParameter());〈delete eqns of the form x = x 11c〉〈if x appears in t, return false 11d〉〈apply (x,t) to each eqn in eqns, extend eqns and return true 12a〉

Defines:extend, used in chunk 13.

11c 〈delete eqns of the form x = x 11c〉≡ (11b)

if (t→isParameter())if (x→getName() ≡ t→getName()) return true;

11d 〈if x appears in t, return false 11d〉≡ (11b)

// case of t not a parameterset<string> parameters;t→getParameters(parameters);// set<string>::iterator p = parameters.begin();// cout << "parameters : ";// while (p != parameters.end()) cout << *p << " "; p++; if (parameters.find(x→getName()) 6= parameters.end())

return false;

Uses getParameters 4a.

Page 15: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 12

12a 〈apply (x,t) to each eqn in eqns, extend eqns and return true 12a〉≡ (11b)

for (unsigned int i=0; i6=eqns.size(); i++) type ∗ temp = eqns[i].second;eqns[i].second = apply_subst(eqns, temp);delete_type(temp);

pair<string, type ∗> eqn(x→getName(), t→clone());eqns.push_back(eqn);return true;

Uses apply_subst 11a and delete_type 2c 3a.

Comment 1.1.21. This function extracts the actual type of a synonym. We may need to gothrough several redirections to get to the actual type.

12b 〈unification body 10c〉+≡ (10b) ⊳ 11b 13 ⊲

type ∗ get_type_from_syn(type ∗ in) type ∗ ret = in;while (ret→isSynonym())

ret = dcast<type_synonym ∗>(ret)→getActual();return ret;

Defines:get_type_from_syn, used in chunks 10a, 13, and 18a.

Page 16: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 13

Comment 1.1.22. This function returns whether two types tvn and t are unifiable. If one of thetwo, say tvn, is a parameter, we will try extending eqns with the equation (tvn = t). Otherwise,we compare the tags and try to recursively unify the subtypes if the tags match.

13 〈unification body 10c〉+≡ (10b) ⊳ 12b

bool unify(vector<pair<string,type ∗> > &eqns, type ∗ tvn, type ∗ t) 〈unify::verbose 1 14b〉if (tvn→isSynonym()) tvn = get_type_from_syn(tvn);if (t→isSynonym()) t = get_type_from_syn(t);〈unify::verbose 2 14c〉

bool ret = false;if (tvn→isParameter())

type ∗ phitvn = getBinding(eqns, tvn)→clone();

type ∗ phit = apply_subst(eqns, t);// if phitvn == tvnif (phitvn→isParameter())

if (tvn→getName() ≡ phitvn→getName()) ret = extend(eqns, tvn, phit);delete_type(phit); delete_type(phitvn);if (unify_verbose) cerr ≪ ret ≪ endl;return ret;

else

ret = unify(eqns, phitvn, phit);delete_type(phit); delete_type(phitvn);if (unify_verbose) cerr ≪ ret ≪ endl;return ret;

// switch placeif (tvn→isParameter() ≡ false ∧ t→isParameter())

return unify(eqns, t, tvn);

〈unify::case of both non-parameters 14a〉return true;

Defines:unify, used in chunks 10a, 14a, 18a, 19a, and 82b.

Uses apply_subst 11a, delete_type 2c 3a, extend 11b, get_type_from_syn 12b, and getBinding 10c.

Page 17: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 14

14a 〈unify::case of both non-parameters 14a〉≡ (13)

if (tvn→isParameter() ≡ false ∧ t→isParameter() ≡ false) if (tvn→getTag() 6= t→getTag()) return false;if (tvn→getTag() ≡ Tuple ∧ t→getTag() ≡ Tuple)

if (tvn→alphaCount() 6= t→alphaCount()) if (unify_verbose) cerr ≪ false ≪ endl;return false;

// unify each componentif (tvn→alphaCount() 6= t→alphaCount())

cerr ≪ "Error in unification. Argument counts don’t match.\n";cerr ≪ "tvn = " ≪ tvn→getName() ≪ endl;cerr ≪ " t = " ≪ t→getName() ≪ endl;assert(false);

for (int i=0; i6=tvn→alphaCount(); i++)

bool r = unify(eqns,tvn→getAlpha(i),t→getAlpha(i));if (r ≡ false) return false;

Uses unify 13.

Comment 1.1.23. We print out some information to help debugging.

14b 〈unify::verbose 1 14b〉≡ (13 14c)

if (unify_verbose)cerr ≪ "Unifying " ≪ tvn→getName() ≪ " and " ≪ t→getName() ≪endl;

14c 〈unify::verbose 2 14c〉≡ (13)

if (unify_verbose) cerr ≪ "After transformation:\n";〈unify::verbose 1 14b〉

Page 18: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 15

1.1.2 Type Checking

Comment 1.1.24. The type-checking procedure implements the following algorithm. For moredetails on type checking and type inference, see, for example, [Mit96, Chap. 11].

WT (C ) = α where α is the declared signature of C

WT (x ) =

α if WT (x ) = α has been established before;

a otherwise; here, a is a fresh parameter.

WT ((t1 , . . . , tn)) = WT (t1 )× · · · ×WT (tn)

WT (λx .t) =

α→ β if WT (t) = β and x is free with relative type α in t.

a→ β where a is a parameter otherwise.

WT ((s t)) = βθ if WT (s) = α→ β, WT (t) = γ, and α and γ are unifiable using θ.

The input term is not well-typed if any one of the WT calls on its subterms fails.

15a 〈type checking actual 15a〉≡ (21a)

type ∗ wellTyped2(term ∗ t, vector<var_name> bvars, int scope) type ∗ ret = NULL;〈wellTyped2::case of t a constant 16a〉〈wellTyped2::case of t a variable 17a〉〈wellTyped2::case of t an application 18a〉〈wellTyped2::case of t an abstraction 19b〉〈wellTyped2::case of t a modal term 19c〉〈wellTyped2::case of t a tuple 20a〉return ret;

Defines:wellTyped2, used in chunks 18–21.

Comment 1.1.25. We first look at some data structures. The vector term_types is used to storethe inferred type for each subterm of the input term. The structure var_name is used to handlevariables; see Comment 1.1.28 for more details.

15b 〈type checking variables 15b〉≡ (21a)

vector<term_type> term_types;struct var_name int vname; string pname; ;

Uses term_type 10a.

Page 19: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 16

Comment 1.1.26. If the input term t is a constant, we find its signature α from the globalconstants repository (the function get_signature will halt with an error if t is unknown), renameall the parameters in α to obtain α′ and then return α′. We need to rename parameters becausesome of the parameters in α may have been introduced (and constrained) up to this point in thetype checking process. To illustrate, consider the following type declarations.

top : a → Ω

ind : a → Ω

The term (top ind) is clearly well-typed. But the type checking procedure will fail if we do notfirst rename, say, the first parameter a because the unification procedure will fail when attemptingto equate a and a→ Ω.

16a 〈wellTyped2::case of t a constant 16a〉≡ (15a)

if (t→isF() ∨ t→isD()) if (t→isint) ret = new type(gInt);else if (t→isfloat) ret = new type(gFloat);else if (t→isChar()) ret = new type(gChar);else if (t→isString()) ret = new type(gString);else

ret = get_signature(t→cname);if (ret) ret = ret→clone(); ret→renameParameters(); else return NULL;

〈wellTyped2::save n return 16b〉

Uses get_signature 189d, isChar 25a, isD 22f, isF 22f, isString 25a, and renameParameters 4b.

Comment 1.1.27. Each subterm is stored in term_types the moment its type is inferred. Theseentries may be updated later on when parameters get instantiated further. See Comment 1.1.29.

16b 〈wellTyped2::save n return 16b〉≡ (16–20)

term_type res; res.first = t; res.second = ret;term_types.push_back(res);// setSelector(STDOUT); ioprint("\t"); t->print(); ioprint(" : ");// ioprintln(ret->getName()); setSelector(SILENT);return ret;

Uses ioprint, ioprintln, setSelector, and term_type 10a.

Page 20: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 17

Comment 1.1.28. To determine the type of a variable x, we need to know two things:

1. Is it a bound or a free variable?2. Has it occurred before?

If x is a bound variable that has occurred previously, we just recycle the previously computedtype. Else if x is a bound variable that has not occurred previously, we use the parameter namethat has been assigned earlier to create a new parameter. (See Comment 1.1.32.) Otherwise, if xis free, we check (in term_types) to see whether a type for x has been inferred earlier. If so, wereturn the inferred type. Otherwise, we create a new parameter with a new parameter name.

17a 〈wellTyped2::case of t a variable 17a〉≡ (15a)

if (t→isVar()) uint start = 0;for (int i=(int)bvars.size()-1; i6=-1; i−−)

if (t→cname ≡ bvars[i].vname) start = scope;〈variable case::lookup previous occurrence 17b〉ret = new type_parameter(bvars[i].pname);〈wellTyped2::save n return 16b〉

〈variable case::lookup previous occurrence 17b〉ret = new type_parameter();〈wellTyped2::save n return 16b〉

if (t→tag ≡ SV)

for (uint j=0; j6=term_types.size(); j++)if (term_types[j].first→tag ≡ SV)

if (t→cname ≡ term_types[j].first→cname) ret = term_types[j].second→clone();〈wellTyped2::save n return 16b〉

ret = new type_parameter();〈wellTyped2::save n return 16b〉

Uses isVar 22f.

17b 〈variable case::lookup previous occurrence 17b〉≡ (17a)

for (uint j=start; j6=term_types.size(); j++)if (term_types[j].first→isVar())

if (t→cname ≡ term_types[j].first→cname) ret = term_types[j].second→clone();〈wellTyped2::save n return 16b〉

Uses isVar 22f.

Page 21: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 18

Comment 1.1.29. If the input term is an application of the form (s t), we first infer the typesof s and t separately. Assuming the type of s has the form α → β, we then attempt to unify αwith γ, the type of t. If there exists a θ that unifies the two, we can then return βθ as the typefor (s t). We also update entries in term_types with θ to reflect new knowledge. The variablevlength keeps track of the part of term_types we can safely change.

18a 〈wellTyped2::case of t an application 18a〉≡ (15a)

if (t→isApp()) unsigned int vlength = term_types.size();type ∗ t1 = wellTyped2(t→lc(), bvars, scope);if (t1→isSynonym()) t1 = get_type_from_syn(t1);〈wellTyped2::application::t1 should have right form 18b〉type ∗ t2 = wellTyped2(t→rc(), bvars, scope);if (¬t2) printErrorMsg(t→rc()); return NULL;

vector<pair<string, type ∗> > slns;bool result = unify(slns, t1→getAlpha(0), t2);if (¬result) 〈wellTyped2::application::error reporting2 19a〉 ret = apply_subst(slns, t1→getAlpha(1));

for (uint i=vlength; i6=term_types.size(); i++) type ∗ temp = term_types[i].second;term_types[i].second = apply_subst(slns, temp);delete_type(temp);

for (uint j=0; j6=slns.size(); j++) delete_type(slns[j].second);slns.clear();〈wellTyped2::save n return 16b〉

Uses apply_subst 11a, delete_type 2c 3a, get_type_from_syn 12b, isApp 22f, lc 23a, printErrorMsg 20b, rc 23a,

unify 13, and wellTyped2 15a.

Comment 1.1.30. The type t1 should be a function type. If this is not the case but t1 is aparameter, we can rescue the situation by making t1 a type of the form a→ b, where both a andb are parameters. (This is equivalent to saying that s has type c, and that c = a → b.) If t1 isnot a parameter and not a function type, we have a typing error.

18b 〈wellTyped2::application::t1 should have right form 18b〉≡ (18a)

if (¬t1) printErrorMsg(t→lc()); return NULL;

if (¬t1→isAbstract() ∧ t1→isParameter()) type ∗ temp = t1;t1 = new type_abstraction(temp, new type_parameter());term_types[term_types.size()-1].second = t1;

if (¬t1→isAbstract())

int osel = getSelector();setSelector(STDERR); ioprint("*** Error: ");t→lc()→print(); ioprint(" : "); ioprintln(t1→getName());ioprintln(" does not have function type.");setSelector(osel);return NULL;

Uses getSelector, ioprint, ioprintln, lc 23a, printErrorMsg 20b, and setSelector.

Page 22: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 19

Comment 1.1.31. Given s : α→ β and t : γ, the term (s t) is not well typed if we cannot unifyα and γ.

19a 〈wellTyped2::application::error reporting2 19a〉≡ (18a)

int osel = getSelector();setSelector(STDERR); t→print(); ioprintln(" is not well typed.");ioprint(t1→getAlpha(0)→getName()); ioprint(" and ");ioprint(t2→getName()); ioprintln(" are not unifiable\n");slns.clear();unify_verbose = true;unify(slns, t1→getAlpha(0), t2);setSelector(osel); unify_verbose = false;return NULL;

Uses getSelector, ioprint, ioprintln, setSelector, and unify 13.

Comment 1.1.32. Given a lambda term λx.t, the variable x is given a new parameter name(stored in bvars), and every occurrence of x in t will use the same parameter name afterwards.

The type checking procedure is simple. We first check the type of t. Then we find the relativetype of x in t (recorded in term_types). If t does not contain x, then we just use the initiallyassigned parameter name to create a new parameter. If x has type α and t has type β, we returnα→ β.

19b 〈wellTyped2::case of t an abstraction 19b〉≡ (15a)

if (t→isAbs()) uint vlength = term_types.size();

var_name tmp; tmp.vname = t→fields[0]→cname;tmp.pname = newParameterName();bvars.push_back(tmp);

type ∗ t2 = wellTyped2(t→fields[1], bvars, vlength);if (¬t2) printErrorMsg(t); return NULL;

type ∗ vt = NULL;for (uint i=vlength; i6=term_types.size(); i++)

if (term_types[i].first→isVar(t→fields[0]→cname)) vt = term_types[i].second→clone(); break;

if (vt ≡ NULL) vt = new type_parameter(tmp.pname);

ret = new type_abstraction(vt, t2→clone());〈wellTyped2::save n return 16b〉

Uses isAbs 22f, isVar 22f, printErrorMsg 20b, and wellTyped2 15a.

Comment 1.1.33. We now look at modal terms. Given it, if we can infer t has type α, thenwe can infer it has type α.

19c 〈wellTyped2::case of t a modal term 19c〉≡ (15a)

if (t→isModal()) type ∗ ret = wellTyped2(t→fields[0], bvars, scope);if (¬ret) printErrorMsg(t); return NULL; ret = ret→clone();〈wellTyped2::save n return 16b〉

Uses isModal 22f, printErrorMsg 20b, and wellTyped2 15a.

Page 23: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.1. TYPES 20

Comment 1.1.34. The case for tuples is easy. We just infer the types of each component andthen put them together.

20a 〈wellTyped2::case of t a tuple 20a〉≡ (15a)

if (t→isProd()) ret = new type_tuple;for (unsigned int i=0; i6=t→fields.size(); i++)

type ∗ ti = wellTyped2(t→fields[i], bvars, scope);if (¬ti) printErrorMsg(t); return NULL; ret→addAlpha(ti→clone());

〈wellTyped2::save n return 16b〉

Uses isProd 22f, printErrorMsg 20b, and wellTyped2 15a.

20b 〈type checking subsidiary functions 20b〉≡ (21a) 20c ⊲

void printErrorMsg(term ∗ t) int osel = getSelector();setSelector(STDERR); t→print();ioprintln(" is not well typed."); setSelector(osel);

Defines:printErrorMsg, used in chunks 18–21.

Uses getSelector, ioprintln, and setSelector.

Comment 1.1.35. This is a function written for debugging purposes. It prints out the contentsof term_types.

20c 〈type checking subsidiary functions 20b〉+≡ (21a) ⊳ 20b 20d ⊲

void print_term_types() int osel = getSelector(); setSelector(STDOUT);ioprintln(" *** ");for (uint i=0; i6=term_types.size(); i++)

term_types[i].first→print();ioprint(" : "); ioprintln(term_types[i].second→getName());

setSelector(osel);

Uses getSelector, ioprint, ioprintln, and setSelector.

Comment 1.1.36. We need to free up the memory occupied by the intermediate types inferredfor the subterms.

20d 〈type checking subsidiary functions 20b〉+≡ (21a) ⊳ 20c

void cleanup_term_types() // print_term_types();for (uint i=0; i6=term_types.size(); i++)

delete_type(term_types[i].second);term_types.clear();

Defines:cleanup_term_types, used in chunk 21a.

Uses delete_type 2c 3a.

Page 24: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 21

Comment 1.1.37. The function wellTyped is a wrapper around the actual type-checking pro-cedure wellTyped2.

21a 〈type checking 21a〉≡ (10b) 21b ⊲

#include <string>#include <vector>#include "global.h"

#include "terms.h"

〈type checking variables 15b〉〈type checking subsidiary functions 20b〉〈type checking actual 15a〉

type ∗ wellTyped(term ∗ t) vector<var_name> bvars;type ∗ ret = wellTyped2(t, bvars, 0);if (¬ret) printErrorMsg(t); return NULL; ret = ret→clone();cleanup_term_types();return ret;

Uses cleanup_term_types 20d, printErrorMsg 20b, and wellTyped2 15a.

Comment 1.1.38. The following is a version of wellTyped that returns both the type of theterm being checked and the type of each subterm computed. The latter is needed for checkingtypeof side conditions on statements.

21b 〈type checking 21a〉+≡ (10b) ⊳ 21a

pair<type ∗, vector<term_type> > mywellTyped(term ∗ t) pair<type ∗, vector<term_type> > res;vector<var_name> bvars;type ∗ ret = wellTyped2(t, bvars, 0);if (¬ret) printErrorMsg(t); res.first = NULL; return res; ret = ret→clone();res.first = ret; res.second = term_types;term_types.clear();return res;

Uses printErrorMsg 20b, term_type 10a, and wellTyped2 15a.

1.2 Terms

1.2.1 Term Representation

Comment 1.2.1. We use a standard approach to represent terms. A term is a graph of nodes,where each node is a term-schema as defined. One possible optimization is to distinguish betweenboxed and unboxed fields [Pey87, pg. 190]. For a discussion on term representations, see [Pey87,Chap. 10].

Comment 1.2.2. A term schema can be any one of the following: a syntactical variable (SV), avariable (V), a function symbol (F), a data constructor (D), an application (APP), an abstraction(ABS), a product (PROD) or a modal term (MOD). This information is recorded in tag.

21c 〈term-schema::type defs 21c〉≡ (103) 41 ⊲

enum kind SV, V, F, D, APP, ABS, PROD, MODAL ;

Page 25: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 22

22a 〈term-schema parts 22a〉≡ (103) 22b ⊲

kind tag;

Comment 1.2.3. Syntactic variables, variables, functions and data constructors have names.For efficiency considerations, we use integers to represent names. (See Comment 5.0.38 for themappings.) Modal terms have indices.

22b 〈term-schema parts 22a〉+≡ (103) ⊳ 22a 22g ⊲

int cname;int modality;

22c 〈term-schema initializations 22c〉≡ (23b) 23d ⊲

cname = -5;modality = -5;

22d 〈term-schema clone parts 22d〉≡ (32b) 30e ⊲

ret→cname = cname;if (tag ≡ MODAL) ret→modality = modality;

22e 〈term-schema replace parts 22e〉≡ (33b) 23e ⊲

cname = t→cname;if (t→tag ≡ MODAL) modality = t→modality;

Comment 1.2.4. Terms with names are called atomic terms. Terms that does not have namesare called composite terms.

22f 〈term-schema::function declarations 22f〉≡ (103) 23a ⊲

bool isF() return (tag ≡ F); bool isF(int code) return (tag ≡ F ∧ cname ≡ code); bool isApp() return (tag ≡ APP); bool isD() return (tag ≡ D); bool isD(int code) return (tag ≡ D ∧ cname ≡ code); bool isVar() return (tag ≡ V); bool isVar(int v) return (tag ≡ V ∧ cname ≡ v); bool isAbs() return (tag ≡ ABS); bool isProd() return (tag ≡ PROD); bool isModal() return (tag ≡ MODAL);

Defines:isAbs, used in chunks 19b, 51c, 58, and 97.isApp, used in chunks 18a, 24, 25a, 28, 45, 47b, 48c, 62, 64b, 67b, 69, 76b, 77, 83a, 97, 129b, 133a, 134b,

and 139a.isD, used in chunks 16a, 25a, 28, 45c, 47b, 51d, 53b, 54, 56, 57, 64c, 67c, 71b, 77, 97, 111e, 140c, 145a, 148,

and 149.isF, used in chunks 16a, 24e, 27, 45, 47b, 48c, 51b, 57, 62, 64, 67, 69, 77, 97, 129b, 133a, 134b, 139a, and 149c.isModal, used in chunks 19c, 45b, 69, 97, 139b, 141b, and 142.isProd, used in chunks 20a, 52a, and 97.isVar, used in chunks 17, 19b, 51, 60b, 66b, 97, 139b, 147a, and 150b.

Comment 1.2.5. The parameters tag and kind does not have default values. They are initializedin the constructor code with pass-in values.

Comment 1.2.6. Application, abstraction and product terms have subterms. These are capturedin the vector fields.

22g 〈term-schema parts 22a〉+≡ (103) ⊳ 22b 23c ⊲

vector<term ∗> fields;

Page 26: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 23

23a 〈term-schema::function declarations 22f〉+≡ (103) ⊳ 22f 25d ⊲

term ∗ lc() /∗ assert(tag == APP); ∗/ return fields[0]; term ∗ rc() /∗ assert(tag == APP); ∗/ return fields[1]; void insert(term ∗ t) fields.push_back(t);

Defines:insert, used in chunks 5b, 25, 39b, 44b, 47–49, 63, 69, 80–82, 99a, 109b, 110c, 114, 115b, 120a, 131a, 133b,

135a, 141b, 146b, and 192c.lc, used in chunks 18, 24, 25, 27, 28, 45, 47, 48, 50, 51d, 54, 56–62, 64–69, 72, 74–77, 83a, 85, 94, 97, 99a, 101b,

131–39, 146, 149, and 150.rc, used in chunks 18a, 25a, 27, 28, 45, 47, 48, 50, 51d, 54, 56–60, 62–69, 72, 74, 75a, 77, 83a, 85, 94, 97, 99a,

101b, 128–41, 143, 146, 149, and 150.

23b 〈term-schema::constructors 23b〉≡ (103)

term(kind k) tag = k; 〈term-schema initializations 22c〉 term(kind k, const int code)

tag = k; 〈term-schema initializations 22c〉cname = code; // important this comes after initializations

Comment 1.2.7. Certain basic data constructors like numbers can best be dealt with in theiroriginal machine representations. (Otherwise, a lot of conversions from and to strings are needed.)The variable num replaces the cname field for numbers.

Cloning of isfloat, isint, numi and numf is done in the clone() procedure. We do not haveto worry about them here.

23c 〈term-schema parts 22a〉+≡ (103) ⊳ 22g 23f ⊲

bool isfloat, isint;long long int numi;double numf;

23d 〈term-schema initializations 22c〉+≡ (23b) ⊳ 22c 23g ⊲

isfloat = false; isint = false;

23e 〈term-schema replace parts 22e〉+≡ (33b) ⊳ 22e 23h ⊲

if (t→tag ≡ D) isfloat = t→isfloat; isint = t→isint;numi = t→numi; numf = t→numf;

Comment 1.2.8. Sometimes we want to prevent a certain subterm from being modified. This isdone by setting a freeze flag.

23f 〈term-schema parts 22a〉+≡ (103) ⊳ 23c 24a ⊲

bool freeze;

Defines:freeze, used in chunks 23, 38c, 42, 59, and 60b.

23g 〈term-schema initializations 22c〉+≡ (23b) ⊳ 23d 24b ⊲

freeze = false;

Uses freeze 23f.

23h 〈term-schema replace parts 22e〉+≡ (33b) ⊳ 23e 24c ⊲

freeze = t→freeze;

Uses freeze 23f.

Page 27: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 24

Comment 1.2.9. A term of the form (t1 (t2 · · · (tn−1 tn) · · · )) can be visualized to take on theshape of a spine. (Draw it!) The (leftmost) term t1 is called the tip of the spine. At differentplaces throughout a computation, we need to access the leftmost term in a nested applicationnode, and the following two functions provide this service. The input x to the second functionwill get assigned the value n− 1. We currently perform a (linear) traversal down the spine. It ispossible to make this go faster if necessary.

We cache the results in spinetip and spinelength.

24a 〈term-schema parts 22a〉+≡ (103) ⊳ 23f 26c ⊲

term ∗ spinetip;int spinelength;int spine_time;

24b 〈term-schema initializations 22c〉+≡ (23b) ⊳ 23g 26d ⊲

spinetip = NULL; spinelength = -1; spine_time = -5;

Comment 1.2.10. All these values become obsolete on replacing.

24c 〈term-schema replace parts 22e〉+≡ (33b) ⊳ 23h 30f ⊲

spinetip = NULL; spinelength = -1; spine_time = -5;

24d 〈term-schema::function definitions 24d〉≡ (105a) 24e ⊲

term ∗ term::spineTip() if (spinetip ∧ spinelength > -1 ∧ spine_time ≡ltime) return spinetip;spine_time = ltime;if (tag 6= APP) spinetip = this; spinelength = 1; return spinetip; spinelength = 2; spinetip = fields[0];while (spinetip→isApp())

spinetip = spinetip→fields[0]; spinelength++; return spinetip;

term ∗ term::spineTip(int & numarg)

if (tag 6= APP) numarg = 0; return this; numarg = 1; term ∗ p = fields[0];while (p→isApp()) p = p→fields[0]; numarg++; return p;

Defines:

spineTip, used in chunks 53, 71b, 77, 78c, 103, 129b, and 136b.Uses isApp 22f.

Comment 1.2.11. The following function checks whether the current term has the general form((f t1) t2), where f is given as input. If spinetip has already been computed, we can do thingsslightly faster.

24e 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 24d 25a ⊲

bool term::isFunc2Args() if (spinetip ∧ spinelength ≡ 3 ∧ spinetip→isF()) return true;return (isApp() ∧ lc()→isApp() ∧ lc()→lc()→isF());

bool term::isFunc2Args(int f)

if (spinetip ∧ spinelength ≡ 3 ∧ spinetip→isF(f)) return true;return (isApp() ∧ lc()→isApp() ∧ lc()→lc()→isF(f));

Defines:

isFunc2Args, used in chunks 27, 47b, 60, 65–68, 77, 85, 103, 131, 132a, 135–37, 139a, 146, and 148–50.Uses isApp 22f, isF 22f, and lc 23a.

Page 28: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 25

Comment 1.2.12. This function checks whether a term is a string.

25a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 24e 25b ⊲

bool term::isAString() return (isApp() ∧ lc()→isApp() ∧ lc()→lc()→isD(iHash)

∧ lc()→rc()→isChar());bool term::isChar()

if (isfloat ∨ isint) return false;return (tag ≡ D ∧ cname ≥ 2000 & cname < 3000);

bool term::isString()

if (isfloat ∨ isint) return false;return (tag ≡ D ∧ strings.find(cname) 6= strings.end());

Defines:

isAString, used in chunks 28a, 51d, 70a, and 103.isChar, used in chunks 16a, 28a, and 103.isString, used in chunks 16a and 103.

Uses iHash 183, isApp 22f, isD 22f, lc 23a, and rc 23a.

Comment 1.2.13. Constants that are rigid have the same meaning in each possible world. Aterm is rigid if every constant in it is rigid.

25b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 25a 26a ⊲

bool term::isRigid() if (tag ≡ V ∨ tag ≡ D) return true;if (tag ≡ F) return is_rigid_constant(cname);if (tag ≡ ABS) return fields[1]→isRigid();if (tag ≡ MODAL) return fields[0]→isRigid();assert(tag ≡ PROD ∨ tag ≡ APP);for (uint i=0; i6=fields.size(); i++)

if (¬fields[i]→isRigid()) return false;return true;

Comment 1.2.14. The following function creates a new term having the form ((f t1) t2) wheref (given) is a function symbol of arity two. The arguments t1 and t2 needs to be initialized bythe calling function.

25c 〈terms.cc::local functions 25c〉≡ (105a) 31b ⊲

term ∗ newT2Args(kind k, int f) term ∗ ret = new_term(APP);ret→insert(new_term(APP)); ret→lc()→insert(new_term(k, f));return ret;

Defines:

newT2Args, used in chunks 48, 52a, 53a, 63, 76b, 103, 137c, 138a, 147, and 148b.Uses insert 23a, lc 23a, and new_term 31b.

Comment 1.2.15. The following function initializes the two arguments of a term created usingnewT2Args.

25d 〈term-schema::function declarations 22f〉+≡ (103) ⊳ 23a 34c ⊲

void initT2Args(term ∗ t1, term ∗ t2) lc()→insert(t1); insert(t2); Defines:

initT2Args, used in chunks 48, 52a, 53a, 63, 76b, 137c, 138a, 147, and 148b.Uses insert 23a and lc 23a.

Page 29: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 26

Comment 1.2.16. The following function checks whether two terms are equal to each other.This is currently only used in debugging code.

26a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 25b 27 ⊲

bool term::equal(term ∗ t) if (tag 6= t→tag) return false;if (cname 6= t→cname) return false;if (modality 6= t→modality) return false;〈term schema::equal::numbers 26b〉uint size1 = fields.size();uint size2 = t→fields.size();if (size1 6= size2) return false;for (uint i=0; i6=size1; i++)

if (fields[i]→equal(t→fields[i]) ≡ false)return false;

return true;

Defines:equal, used in chunks 45c, 46a, 75a, 79a, 84a, 93a, 103, 112a, 135–37, 146a, and 148a.

Comment 1.2.17. We treat numbers in a slightly peculiar way. We will equate an integer anda floating-point number (even though the types do not agree) if they are the same number. Wedo this because the internal arithmetic of Escher can add, subtract, multiply and divide integersand floating-point numbers to produce another floating-point number. See Comment 2.1.11.

26b 〈term schema::equal::numbers 26b〉≡ (26a)

if (isint ∧ t→isint ∧ numi 6= t→numi) return false;if (isint ∧ t→isfloat ∧ (double)numi 6= t→numf) return false;if (isfloat ∧ t→isint ∧ numf 6= (double)t→numi) return false;if (isfloat ∧ t→isfloat ∧ numf 6= t→numf) return false;

Comment 1.2.18. This is used for marking and printing redexes.

26c 〈term-schema parts 22a〉+≡ (103) ⊳ 24a 30c ⊲

bool redex;

26d 〈term-schema initializations 22c〉+≡ (23b) ⊳ 24b 30d ⊲

redex = false;

Page 30: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 27

Comment 1.2.19. The variable redex does not really play a part during cloning and reusing.

Comment 1.2.20. A term is printed in the way it is represented. The redex (if one exists) ismarked out using square brackets. Shared nodes are also marked with their reference count.

27 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 26a 29a ⊲

extern const string pve;void term::print()

if (getSelector() ≡ SILENT) return;〈term schema::print strings 28a〉〈term schema::print lists 28b〉if (redex) ioprint(" [[[ ");〈term schema::print if-then-else 28c〉// if (refcount > 1) ioprint("_s_");if (cname ≥ 5000) ioprint(pve); ioprint(cname - 5000); else if (cname > 0) ioprint(getString(cname));else if (isfloat) ioprint(numf);else if (isint) ioprint(numi);else if (isFunc2Args())

ioprint("("); lc()→lc()→print(); ioprint(" ");lc()→rc()→print(); ioprint(" "); rc()→print(); ioprint(")");

else if (tag ≡ APP ∧ (lc()→isF(iSigma) ∨ lc()→isF(iPi))) if (lc()→isF(iSigma)) ioprint("\\exists ");else ioprint("\\forall ");rc()→fields[0]→print(); ioprint(".");rc()→fields[1]→print();

else if (tag ≡ APP) ioprint("("); fields[0]→print(); ioprint(" ");fields[1]→print(); ioprint(")");

else if (tag ≡ ABS) ioprint("\\"); fields[0]→print();ioprint("."); fields[1]→print();

else if (tag ≡ PROD) int size = fields.size();if (size ≡ 0) ioprint("()"); return; ioprint("(");for (int i=0; i6=size-1; i++)

fields[i]→print(); ioprint(","); fields[size-1]→print(); ioprint(")");

else if (tag ≡ MODAL) ioprint("["); ioprint(modality); ioprint("] ");fields[0]→print();

else 〈print error handling 28d〉 if (redex) ioprint(" ]]] ");

Uses getSelector, ioprint, iPi 183, isF 22f, isFunc2Args 24e, iSigma 183, lc 23a, and rc 23a.

Page 31: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 28

Comment 1.2.21. (Composite) strings are represented as lists of characters. Printing them aslists is not good for the eyes. What we do here is to collect the characters together and print astring as a string.

28a 〈term schema::print strings 28a〉≡ (27)

if (isAString()) string temp = ""; temp += getString(lc()→rc()→cname)[1];term ∗ arg2 = rc();while (¬arg2→isD(iEmptyList))

assert(arg2→lc()→rc()→isChar());temp += getString(arg2→lc()→rc()→cname)[1];arg2 = arg2→rc();

ioprint("\""); ioprint(temp); ioprint("\""); return;

Uses iEmptyList 183, ioprint, isAString 25a, isChar 25a, isD 22f, lc 23a, and rc 23a.

Comment 1.2.22. We print a list in the syntactic sugar form.

28b 〈term schema::print lists 28b〉≡ (27)

if (isApp() ∧ lc()→isApp() ∧ lc()→lc()→isD(iHash)) ioprint("["); lc()→rc()→print();term ∗ arg2 = rc();while (arg2→isD(iEmptyList) ≡ false)

ioprint(", ");if (arg2→isApp() ∧ arg2→lc()→isApp() ∧

arg2→lc()→lc()→isD(iHash)) arg2→lc()→rc()→print(); arg2 = arg2→rc();

else arg2→print(); break; ioprint("]");return;

Uses iEmptyList 183, iHash 183, ioprint, isApp 22f, isD 22f, lc 23a, and rc 23a.

Comment 1.2.23. We print if-then-else statements in a more human-readable form here.

28c 〈term schema::print if-then-else 28c〉≡ (27)

if (isApp() ∧ lc()→cname ≡ iIte) ioprint("if "); rc()→fields[0]→print();ioprint(" then "); rc()→fields[1]→print();/∗ ioprint("\n\t"); ∗/ ioprint(" else "); rc()→fields[2]→print();return;

Uses iIte 183, ioprint, isApp 22f, lc 23a, and rc 23a.

28d 〈print error handling 28d〉≡ (27 29a)

cerr ≪ "Printing untagged term.\ttag = " ≪ tag ≪ endl;assert(false);

Comment 1.2.24. In vertical printing, we print the current term vertically (with some inden-tation). Miscellaneous information about the individual subterms are also printed. This is aconvenient way to look at sharing and other information associated with each node.

Page 32: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 29

29a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 27 32b ⊲

void term::printVertical(uint level) if (getSelector() ≡ SILENT) return;〈print white spaces 29b〉if (cname ≥ 5000) ioprint(pve); ioprint(cname-5000); else if (cname > 0)

ioprint(getString(cname)); 〈print extra information 29c〉 else if (isfloat) ioprint(numf); 〈print extra information 29c〉 else if (isint) ioprint(numi); 〈print extra information 29c〉 else if (tag ≡ APP)

ioprint("("); 〈print extra information 29c〉fields[0]→printVertical(level+1);fields[1]→printVertical(level+1);〈print white spaces 29b〉 ioprint(")\n");

else if (tag ≡ ABS) ioprint("\\"); fields[0]→print(); ioprint(".");〈print extra information 29c〉fields[1]→printVertical(level+1);

else if (tag ≡ PROD) int size = fields.size();if (size ≡ 0)

ioprint("()"); 〈print extra information 29c〉 return; ioprint("("); 〈print extra information 29c〉for (int i=0; i6=size-1; i++)

fields[i]→printVertical(level+1); ioprint(",\n"); fields[size-1]→printVertical(level+1);〈print white spaces 29b〉 ioprint(")\n");

else if (tag ≡ MODAL) assert(false);

else 〈print error handling 28d〉

Defines:printVertical, used in chunk 103.

Uses getSelector and ioprint.

29b 〈print white spaces 29b〉≡ (29a)

for (uint i=0; i6=level; i++) ioprint(" ");

Uses ioprint.

29c 〈print extra information 29c〉≡ (29a)

ioprint("\t\t");if (refcount > 1) ioprint("shared"); ioprint(refcount); ioprintln();

Uses ioprint, ioprintln, and shared 34d.

1.2.1.1 Constraints for Syntactic Variables

Comment 1.2.25. We have a (limited) syntax for specifying constraints on what sort of terms asyntactical variable can range over. (See the grammar for Escher.) Four types of constraints aresupported at present. The constraint CVAR forces a syntactical variable to range over variablesonly; CCONST forces a syntactical variable to range over constants only. The constraint CEQUALdictates that the value of one syntactical variable must be equal to the value of one other; Theconstraint CNOTEQUAL dictates that the value of one syntactical variable must not be equal

Page 33: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 30

to the value of one other. For details on how these constraints are implemented, see Comment2.1.100.

30a 〈term-schema::definitions 30a〉≡ (103)

#define CVAR 1

#define CCONST 2

#define CEQUAL 3

#define CNOTEQUAL 4

30b 〈term-schema::supporting types 30b〉≡ (103)

struct condition int tag; int svname; ;

30c 〈term-schema parts 22a〉+≡ (103) ⊳ 26c 34a ⊲

condition ∗ cond; // only applies to SV

30d 〈term-schema initializations 22c〉+≡ (23b) ⊳ 26d 34b ⊲

cond = NULL;

30e 〈term-schema clone parts 22d〉+≡ (32b) ⊳ 22d 36d ⊲

if (cond) assert(tag ≡ SV);ret→cond = new condition;ret→cond→svname = cond→svname; ret→cond→tag = cond→tag;

30f 〈term-schema replace parts 22e〉+≡ (33b) ⊳ 24c 35d ⊲

if (cond) delete cond;cond = t→cond;

Page 34: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 31

1.2.2 Memory Management

Comment 1.2.26. We look at some memory management issues in this section. A naive schemerelying on new and delete is in use at the moment. It is not clear to the author whether a separateheap-allocating scheme would make the system go a whole lot faster.

Comment 1.2.27. We put wrappers around new and delete to collect some statistics. Theprocedure mem_report shows the total number of terms allocated and subsequently freed. This isused to check whether there is a memory leak.

31a 〈term-schema::memory management 31a〉≡ (103)

extern term ∗ new_term(kind k);extern term ∗ new_term(kind k, int code);extern term ∗ new_term_int(int x);extern term ∗ new_term_int(long long int x);extern term ∗ new_term_float(float x);extern void mem_report();

Uses mem_report 32a, new_term 31b, new_term_float 31b, and new_term_int 31b.

31b 〈terms.cc::local functions 25c〉+≡ (105a) ⊳ 25c 32a ⊲

#ifdef DEBUG_MEM

static long int allocated = 0;static long int freed = 0;#endif

// #define DEBUG_MEMterm ∗ new_term(kind k)

〈memory debugging code 31c〉return new term(k);

term ∗ new_term(kind k, int code) 〈memory debugging code 31c〉return new term(k, code);

term ∗ new_term_int(int x)

〈memory debugging code 31c〉term ∗ ret = new term(D);ret→isint = true; ret→numi = x; return ret;

term ∗ new_term_int(long long int x)

〈memory debugging code 31c〉term ∗ ret = new term(D);ret→isint = true; ret→numi = x; return ret;

term ∗ new_term_float(float x)

〈memory debugging code 31c〉term ∗ ret = new term(D);ret→isfloat = true; ret→numf = x; return ret;

Defines:

new_term, used in chunks 25c, 31a, 32b, 47, 48, 51–53, 56, 63, 65–67, 69, 80–82, 85, 99a, 131a, 133–35, 140, 141,and 147–49.

new_term_float, used in chunks 31a, 32b, 54, 55, and 57.new_term_int, used in chunks 31a, 32b, 54, and 55.

31c 〈memory debugging code 31c〉≡ (31b)

#ifdef DEBUG_MEM

allocated++;#endif

Page 35: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 32

32a 〈terms.cc::local functions 25c〉+≡ (105a) ⊳ 31b 73b ⊲

void delete_term(term ∗ x) #ifdef DEBUG_MEM

freed++;#endif

delete x;void mem_report() #ifdef DEBUG_MEM

cout ≪ "\n\nReport from Memory Manager:\n";cout ≪ "\tAllocated " ≪ allocated ≪ endl;cout ≪ "\tFreed " ≪ freed ≪ endl;cout ≪ "\tUnaccounted " ≪ allocated - freed ≪ endl ≪ endl;

#endif

// >>

Defines:delete_term, used in chunk 32c.mem_report, used in chunk 31a.

Comment 1.2.28. Cloning of a term with shared nodes will result in an identical term withoutshared nodes.

32b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 29a 32c ⊲

term ∗ term::clone() term ∗ ret;if (isfloat) ret = new_term_float(numf);else if (isint) ret = new_term_int(numi);else if (tag ≥ SV ∧ tag ≤ D) ret = new_term(tag, cname);else ret = new_term(tag);〈term-schema clone parts 22d〉

int size = fields.size();for (int i=0; i6=size; i++) ret→fields.push_back(fields[i]→clone());return ret;

Uses new_term 31b, new_term_float 31b, and new_term_int 31b.

Comment 1.2.29. We explicitly free memory instead of relying on destructors. The functionfreememory must take node sharing into account. A term is in use while its reference count is stillnon-zero.

32c 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 32b 33b ⊲

void term::freememory() refcount−−;〈freememory error checking 33a〉if (refcount 6= 0) return;int size = fields.size();for (int i=0; i6=size; i++) if (fields[i]) fields[i]→freememory();if (cond) delete cond;delete_term(this);

Uses delete_term 32a.

Page 36: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 33

33a 〈freememory error checking 33a〉≡ (32c)

if (refcount < 0) setSelector(STDERR); print(); ioprintln();ioprint("refcount = "); ioprintln(refcount);

assert(refcount ≥ 0);

Uses ioprint, ioprintln, and setSelector.

Comment 1.2.30. This function overwrites the root of the current term with the input termt. We need to do this if the current node is shared (see §1.2.3) or when the current term is theroot term (with no parent). The procedure is simple. The information on the root of t is copied,and all the child nodes of t are reused. We first reuse the child nodes of t because we could bereplacing the current term with its children, in which case t can get deleted before we can reuseits child nodes if we are not careful.

33b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 32c 35a ⊲

void term::replace(term ∗ t) tag = t→tag;〈term-schema replace parts 22e〉int tsize = t→fields.size();for (int i=0; i6=tsize; i++) t→fields[i]→reuse();

int size = fields.size();for (int i=0; i6=size; i++) if (fields[i]) fields[i]→freememory();fields.resize(tsize);copy(t→fields.begin(), t→fields.end(), fields.begin());

Defines:replace, used in chunks 42a, 44–48, 51a, 67d, 75b, 77, 85, 99a, and 103.

Uses reuse 34c.

Page 37: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 34

1.2.3 Sharing of Nodes

Comment 1.2.31. We use reference counting to implement sharing of nodes.

34a 〈term-schema parts 22a〉+≡ (103) ⊳ 30c 35b ⊲

int refcount;

34b 〈term-schema initializations 22c〉+≡ (23b) ⊳ 30d 35c ⊲

refcount = 1;

Comment 1.2.32. A cloned object of a shared term would have refcount 1. Also, after replacing,the term retains its original refcount value.

34c 〈term-schema::function declarations 22f〉+≡ (103) ⊳ 25d 34d ⊲

term ∗ reuse() refcount++; return this;

Defines:reuse, used in chunks 33b, 43a, 52a, 53a, 58, 59, 63–65, 67–69, and 87–89.

34d 〈term-schema::function declarations 22f〉+≡ (103) ⊳ 34c 34e ⊲

bool shared() return (refcount > 1); Defines:

shared, used in chunks 29c and 43a.

Comment 1.2.33. A few notes on sharing. One of the biggest advantages of sharing is thatcommon subexpressions need only be evaluated once. Sharing of nodes can, however, interferewith a few basic operations in Escher.

Firstly, I believe the operation of checking for free-variable capture, a test we need to do frequentlyduring pattern matching (see §2.1.3) and term substitution (see §1.2.6), cannot be done efficientlyif a variable that occurs both free and bound in a term is shared.

Second, sharing of nodes is not always safe. Some statements in the booleans module, espe-cially the ones that support logic programming (see for example Comment 2.1.14), can potentiallychange shared nodes in destructive ways. The extensive use of such sharing-unfriendly statementsin Escher is the primary reason I gave up on sharing.

In the absence of sharing, the computational saving that can be obtained from common subex-pression evaluation can be achieved using (intelligent) caching.

Having said all that, sharing does have at least one important use in our interpreter; see Comment2.1.82.

Comment 1.2.34. The following function, which is no longer in use, provides a way to unshareshared nodes using the side effect of the cloning operation (see Comment 1.2.28). Time complexity:the entire term needs to be traversed; nodes that are not traversed by this function will be traversedby clone.

34e 〈term-schema::function declarations 22f〉+≡ (103) ⊳ 34d 37b ⊲

void unshare(term ∗ parent, uint id);

Comment 1.2.35. We should assert(parent) because a shared node, by definition, have atleast two parents.

Page 38: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 35

35a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 33b 36a ⊲

void term::unshare(term ∗ parent, uint id) if (refcount > 1)

assert(parent); term ∗ temp = clone();parent→fields[id]→freememory(); parent→fields[id] = temp;return;

int size = fields.size();for (int i=0; i6=size; i++) fields[i]→unshare(this, i);

1.2.4 Free and Bound Variables

Comment 1.2.36. One must be careful when dealing with free and bound variables. This issomething that is not difficult to get right, but incredibly easy to get wrong! So please pay someattention.

Definition 1.2.37. An occurrence of a variable x in a term is bound if it occurs within a subtermof the form λx.t.

Definition 1.2.38. An occurrence of a variable in a term is free if it is not a bound occurrence.

Fact 1.2.39. A variable is free in a term iff it has a free occurrence.

Comment 1.2.40. The following function returns all the free variables inside a term. It isassumed that we have called labelVariables on the term to initialize all the labels and bindinglabels.

Comment 1.2.41. Computed free variables are cached in the vector myfreevars. The flagfreevars_computed tells us whether myfreevars has been initialized. A vector instead of a setis used to store the free variables. This means free variables with multiple occurrences will berecorded multiple times.

35b 〈term-schema parts 22a〉+≡ (103) ⊳ 34a 36b ⊲

bool freevars_computed; int time_computed;vector<int> myfreevars;

35c 〈term-schema initializations 22c〉+≡ (23b) ⊳ 34b 36c ⊲

freevars_computed = false;time_computed = -5;

Comment 1.2.42. These values become obsolete on replacing.

35d 〈term-schema replace parts 22e〉+≡ (33b) ⊳ 30f 37a ⊲

freevars_computed = false;time_computed = -5;

Page 39: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 36

36a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 35a 37c ⊲

vector<int> & term::getFreeVars() if (freevars_computed ∧ time_computed ≡ ltime) return myfreevars;myfreevars.clear();freevars_computed = true; time_computed = ltime;if (tag ≡ D ∨ tag ≡ F) return myfreevars;if (tag ≡ V) myfreevars.push_back(cname); return myfreevars; if (tag ≡ ABS)

fields[1]→getFreeVars();myfreevars = fields[1]→myfreevars;for (uint i=0; i6=myfreevars.size(); i++)

if (myfreevars[i] ≡ fields[0]→cname)myfreevars[i] = -5;

return myfreevars;int size = fields.size();for (int i=0; i6=size; i++)

fields[i]→getFreeVars();copy(fields[i]→myfreevars.begin(), fields[i]→myfreevars.end(),

back_insert_iterator<vector<int> >(myfreevars));return myfreevars;

Defines:getFreeVars, used in chunks 38a, 39a, 97, 103, and 137a.

Comment 1.2.43. For terms that stay unchanged throughout the whole computation (e.g. pro-gram statements), freeness checking of variables can be done (slightly) more efficiently by flaggingeach bound variable in the term directly up front. This is achieved using the following functionlabelStaticBoundVars().

Comment 1.2.44. We first look at the free parameter. To ensure safe use, the free parameter isonly valid if the validfree parameter is true. (The function labelStaticBoundVars is responsiblefor setting this latter parameter. Its value will get set to false during cloning and replacing.)

36b 〈term-schema parts 22a〉+≡ (103) ⊳ 35b 70c ⊲

bool free;bool validfree;

36c 〈term-schema initializations 22c〉+≡ (23b) ⊳ 35c 70d ⊲

validfree = false;

Comment 1.2.45. If the whole term t on which labelBoundVars is called is to be cloned, thenthe existing value of the free parameter would remain correct. However, if only a subterm t1 oft is to be cloned, then some variables that are bound in t can become free in t1. Variables thatare free in t would remain free in t1 though. However, if t (respectively t1) is then subsequentlysubstituted into another term (using the mechanism of syntactical variables), then free variablesin t (respectively t1) can become bound. For all these reasons, we will not attempt to recyclevalues of free parameters during cloning and replacing.

36d 〈term-schema clone parts 22d〉+≡ (32b) ⊳ 30e 70e ⊲

ret→validfree = false;

Page 40: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 37

Comment 1.2.46. Ditto for replacing. Free variables can become bound after replacing whilebound variables remain bound after replacing. The trouble here is that we do not really want totraverse the input graph to label the variables. At present, we only use the free parameters insidethe head of a program statement during pattern matching. We will just mark in the replace codethat proper handling of the free parameter is not yet implemented.

37a 〈term-schema replace parts 22e〉+≡ (33b) ⊳ 35d 71a ⊲

validfree = false;

37b 〈term-schema::function declarations 22f〉+≡ (103) ⊳ 34e 70b ⊲

bool isFree() assert(tag ≡ V ∧ validfree); return free;

Defines:isFree, used in chunks 43b, 88, 89, 93c, 116, 118a, and 120b.

Comment 1.2.47. A straightforward tree traversal is used to label the bound variables. Boundvariables inside a lambda term are marked before the free variables. Hence the way labelling isdone inside the (tag == V) case.

37c 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 36a 37d ⊲

void term::unmarkValidfree() validfree = false;for (uint i=0; i6=fields.size(); i++) fields[i]→unmarkValidfree();

void term::labelStaticBoundVars()

if (tag ≡ F ∨ tag ≡ D ∨ tag ≡ SV) return;if (tag ≡ V) if (¬validfree) validfree = true; free = true;

return; if (tag ≡ ABS)

fields[0]→validfree = true; fields[0]→free = false;fields[1]→labelBound(fields[0]→cname);fields[1]→labelStaticBoundVars();return;

int size = fields.size();for (int i=0; i6=size; i++) fields[i]→labelStaticBoundVars();

Defines:labelStaticBoundVars, used in chunks 42a, 44a, 76b, 103, and 139a.

Uses labelBound 37d.

37d 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 37c 38a ⊲

void term::labelBound(int x) if (tag ≡ F ∨ tag ≡ D ∨ tag ≡ SV) return;if (tag ≡ V) if (cname ≡ x) validfree = true; free = false;

return; if (tag ≡ ABS) fields[1]→labelBound(x); return; int size = fields.size();for (int i=0; i6=size; i++) fields[i]→labelBound(x);

Defines:labelBound, used in chunks 37c and 103.

Page 41: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 38

Comment 1.2.48. The functions isFree and isFreeInside discussed above allows one to checkwhether a subterm s of a term t occurs free inside t. Some times we want to check whether avariable x has a free occurrence inside another term. The following functions allow us to do that.Some occurrences of the input variable could be bound. We return upon seeing the first freeoccurrence the input variable.

There are two versions of this function. The first, occursFree, uses getFreeVars to compute allthe free variables in a term and then check whether var is inside this set. If occursFree is calledrepeatedly by the same term, this caching of computed free variables is beneficial. The second,occursFreeNaive, performs a simple traversal of the term to check whether var occurs free.

38a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 37d 38b ⊲

bool term::occursFree(int var) getFreeVars();int size = myfreevars.size();for (int i=0; i6=size; i++) if (myfreevars[i] ≡ var) return true;return false;

Defines:

occursFree, used in chunks 60b, 62, 66b, 100b, and 103.Uses getFreeVars 36a.

38b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 38a 38c ⊲

bool term::occursFreeNaive(int var) vector<int> boundv; return occursFreeNaive(var, boundv);

Defines:

occursFreeNaive, used in chunks 38c, 60b, and 103.

38c 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 38b 39a ⊲

bool term::occursFreeNaive(int var, vector<int> boundv) if (tag ≡ F ∨ tag ≡ D) return false;if (tag ≡ V)

if (freeze) return false;if (cname ≡ var ∧ inVector(cname,boundv)≡ false) return true;return false;

if (tag ≡ ABS) boundv.push_back(fields[0]→cname);return fields[1]→occursFreeNaive(var, boundv);

int size = fields.size();for (int i=0; i6=size; i++)

if (fields[i]→occursFreeNaive(var, boundv)) return true;return false;

Uses freeze 23f and occursFreeNaive 38b.

Comment 1.2.49. This function checks whether any free variable inside the calling term iscaptured by at least one of the bounded variables. The index of the captured variable is recordedin captd. We store pointers to binding abstraction terms instead of strings for two reasons. First,we sometimes need to change the name of a binding variable when a free variable is captured. Thishappens, for example, during term substitution. Having a pointer to the abstraction term allowsus to jump straight to the offending term. Second, in terms of memory usage, storing pointers toterms is cheaper. If we want to use a set instead of a vector to store the binding variables (maybe

Page 42: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 39

for efficiency reasons), it is easy to put a wrapper around term_schema * and define a pointer pto be less than q iff p->fields[0]->name < q->fields[0]->name.

39a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 38c 39b ⊲

bool term::captured(vector<term ∗> & bvars, int & captd) if (bvars.empty()) return false;getFreeVars();int fsize = myfreevars.size(); int bsize = bvars.size();for (int i=0; i6=fsize; i++)

for (int j=0; j6=bsize; j++)if (myfreevars[i] ≡ bvars[j]→fields[0]→cname)

captd = j; return true; return false;

Defines:captured, used in chunks 43c, 94a, 95d, and 103.

Uses getFreeVars 36a.

Comment 1.2.50. For small terms, the use of vector for both myfreevars and bvars is probablyokay. For larger terms, the use of set (red-black trees) could be much better.

Comment 1.2.51. The following function collects in a multiset all the bound variables in a term.

39b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 39a 39c ⊲

void term::collectLambdaVars(multiset<int> & ret) if (tag ≥ SV ∧ tag ≤ D) return;if (tag ≡ ABS)

ret.insert(fields[0]→cname);fields[1]→collectLambdaVars(ret); return;

for (uint i=0; i6=fields.size(); i++)

fields[i]→collectLambdaVars(ret);

Uses insert 23a.

1.2.5 Variable Renaming

Comment 1.2.52. Different forms of variable renaming are required in performing computations.We discuss these operations in this section.

Comment 1.2.53. This function renames all occurrences of a variable var1 inside the currentterm to var2. Note that both free and bound occurrences are renamed. This is okay since thefunction is only called (sensibly) as a subroutine by the other variable-renaming functions in thissection.

39c 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 39b 40 ⊲

void term::rename(int var1, int var2) if (tag ≡ SV ∨ tag ≡ F ∨ tag ≡ D) return;if (tag ≡ V) if (cname ≡ var1) cname = var2; return; int size = fields.size();for (int i=0; i6=size; i++) fields[i]→rename(var1, var2);

Defines:

rename, used in chunks 40 and 103.

Page 43: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 40

Comment 1.2.54. This function renames one particular lambda variable in a term. This is usedin term substitutions in the case when a free variable capture occurs.

40 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 39c 42a ⊲

void term::renameLambdaVar(int var1, int var2) freevars_computed = false;if (tag ≡ SV ∨ tag ≡ V ∨ tag ≡ F ∨ tag ≡ D) return;if (tag ≡ ABS)

if (fields[0]→cname ≡ var1) fields[0]→cname = var2;fields[1]→rename(var1, var2);

// if lambda variables are distinct, this is not neededfields[1]→renameLambdaVar(var1, var2);return;

int size = fields.size();for (int i=0; i6=size; i++)

fields[i]→renameLambdaVar(var1, var2);

Defines:renameLambdaVar, used in chunks 43c and 103.

Uses rename 39c.

Page 44: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 41

1.2.6 Term Substitution

Definition 1.2.55. [Llo03, pg. 55] A term substitution is a finite set of the form x1/t1, . . . , xn/tnwhere each xi is a variable, each ti is a term distinct from xi, and x1, . . . , xn are distinct.

Comment 1.2.56. Each pair xi/ti is represented as a structure as follows.

41 〈term-schema::type defs 21c〉+≡ (103) ⊳ 21c

struct substitution int first;term ∗ second;substitution() second = NULL; substitution(int v, term ∗ t) first = v; second = t;

;Defines:

substitution, used in chunks 42, 44a, 46b, 58, 59, 64a, 67d, 68b, 74, 75a, 91–93, 99–103, 129c, 133a, 134b, 144,147, 149, and 150.

Definition 1.2.57. [Llo03, pg. 56] Let t be a term and θ = x1/t1, . . . , xn/tn a term substitution.The instance tθ of t by θ is the well-formed expression defined as follows.

1. If t is a variable xi for some i ∈ 1, . . . , n, then xiθ = ti.If t is a variable y distinct from all the xi, then yθ = y.

2. If t is a constant C, then Cθ = C.

3. If t is an abstraction λxi.s, for some i ∈ 1, . . . , n, then

(λxi.s)θ = λxi.(sx1/t1, . . . , xi−1/ti−1, xi+1/ti+1, . . . , xn/tn).

If t is an abstraction λy.s, where y is distinct from all the xi,

(a) if for some i ∈ 1, . . . , n, y is free in ti and xi is free in s, then

(λy.s)θ = λz.(sy/zθ)

where z is a new variable.

(b) else (λy.s)θ = λy.(sθ);

4. If t is an application (u v), then (u v)θ = (uθ vθ).

5. If t is a tuple (t1, . . . , tn), then (t1, . . . , tn)θ = (t1θ, . . . , tnθ).

Comment 1.2.58. Term substitutions are performed by the function subst. There are twoversions of it, one deals with singleton sets, the other with non-singleton sets. In both cases, realwork is done by the function subst2.

Comment 1.2.59. A single traversal of the tree achieves the desired parallel-instantiation-of-variables effect.

Comment 1.2.60. Given t and θ, the function subst will handle the special case where t is avariable (and thus free) or a syntactical variable. All other cases are handled by subst2. Beforecalling subst2, we call labelStaticBoundVars to label the variables. The free values computedare safe for use here because they are read only once by subst2 and changes introduced by subst2

are all localized on the spots where free variables live in the term.

Comment 1.2.61. Pointers to terms in subs are all pointers to subterms in an existing structurethat will be deleted after the term substitution. For that reason, these pointers can be safelyreused once, but not more than that.

For the special case where the current term is a variable or a syntactical variable, we need to makethe term replacement in place using replace.

Page 45: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 42

42a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 40 42b ⊲

void term::subst(vector<substitution> & subs) if (tag ≡ V ∨ tag ≡ SV)

if (freeze) return;int size = subs.size();for (int i=0; i6=size; i++)

if (cname ≡ subs[i].first) this→replace(subs[i].second); return;

return;labelStaticBoundVars();vector<term ∗> bindingAbss;subst2(subs, bindingAbss, NULL);unmarkValidfree();

Defines:subst, used in chunks 44a, 46b, 58, 59, 65c, 67d, 68b, 74–76, 84a, 103, 133a, and 134b.

Uses freeze 23f, labelStaticBoundVars 37c, replace 33b, subst2 42b, and substitution 41.

Comment 1.2.62. All the complications in Definition 1.2.57 are in the abstraction case. Oper-ationally, checking all those conditions every time we encounter an abstraction is expensive. Wecan perform these checks only when strictly necessary by delaying them until before we apply asubstitution, that is, until we see a free variable in t that matches one of the variables in θ.

42b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 42a 44a ⊲

void term::subst2(vector<substitution> & subs, vector<term ∗> bindingAbss,term ∗∗ pointer)

if (tag ≡ SV) if (freeze) return; 〈subst2::case of SV 42c〉 if (tag ≡ V) if (freeze) return; 〈subst2::case of V 43b〉 if (tag ≡ F ∨ tag ≡ D) return;if (tag ≡ ABS)

if (fields[0]→tag ≡ SV)fields[0]→subst2(subs, bindingAbss, &fields[0]);

bindingAbss.push_back(this);fields[1]→subst2(subs, bindingAbss, &fields[1]);return;

int size = fields.size();for (int i=0; i6=size; i++)

fields[i]→subst2(subs, bindingAbss, &fields[i]);

Defines:subst2, used in chunks 42a, 44a, and 103.

Uses freeze 23f and substitution 41.

Comment 1.2.63. Term substitution is not formally defined for syntactical variables. It shouldbehave like a free variable (see Comment 1.2.65), except that we do not have to worry about freevariable capture.

42c 〈subst2::case of SV 42c〉≡ (42b)

int size = subs.size();for (int i=0; i6=size; i++)

if (cname ≡ subs[i].first) 〈subst2::replace by ti 43a〉 return; return;

Page 46: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 43

Comment 1.2.64. See the first part of Comment 1.2.61 for why we do what we do here. Theparent pointer must exist because the case where it does not exist is handled by subst.

43a 〈subst2::replace by ti 43a〉≡ (42c 43b)

assert(pointer);this→freememory();if (subs[i].second→shared()) ∗pointer = subs[i].second→clone();else ∗pointer = subs[i].second→reuse();

Uses reuse 34c and shared 34d.

Comment 1.2.65. We now look at the tag == V case. If the current term is a bound variablein t, then the first part of Definition 1.2.57 (3) applies and nothing changes. If the current termis a free variable in t and does not occur in θ, the second part of Definition 1.2.57 (1) applies andagain nothing happens. If the current term is a free variable in t that matches an xi in θ, then thefirst part of Definition 1.2.57 (1) applies and we substitute the current term with ti. Before wedo that, however, we check whether any free variable in ti is captured by any λ abstraction thatencloses the current term. If yes, part (a) of Definition 1.2.57 (3) applies and we must rename theoffending λ variable before replacing the current term with ti. Otherwise, part (b) of Definition1.2.57 (3) applies and we can just go ahead and replace the current term with ti.

43b 〈subst2::case of V 43b〉≡ (42b)

if (isFree() ≡ false) return;int size = subs.size();for (int i=0; i6=size; i++)

if (cname 6= subs[i].first) continue;〈subst2::free variable captured 43c〉〈subst2::replace by ti 43a〉return;

return;

Uses isFree 37b.

43c 〈subst2::free variable captured 43c〉≡ (43b)

int k;while (subs[i].second→captured(bindingAbss,k))

bindingAbss[k]→renameLambdaVar(bindingAbss[k]→fields[0]→cname,newPVar());Uses captured 39a, newPVar 185b, and renameLambdaVar 40.

Comment 1.2.66. The use of captured (hence the use of cached computed free variables) herewarrants some caution. If subs[i].second does not remain unchanged throughout the termsubstitution process, errors can creep in. We now argue that subs[i].second stays unchangedthroughout.

Term substitution is only used in two places in Escher. The first place is in the constructionof body instances after successful pattern matching on the head of a statement. (See Comment2.1.62.) The use of subst has no problem here because all the terms in θ are in the matched redex,whereas we only do surgery on the (cloned) body of a statement.

The other place term substitutions take place is in some of the internal simplification routinesdescribed in §2.1.1. Such uses only ever involve a single pair x/t. In all routines except betareduction, t will remain unchanged because of the requirement that x does not occur free in t. Inbeta reduction (see Comment 2.1.14), it is easy to see that t remains unchanged since substitutionis a once off operation. That is, even if x occurs free in t, it will never be substituted. (Otherwise,we will have an infinite recursion.)

Page 47: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 44

Comment 1.2.67. The following is the version of subst that handles singleton term substitutions.We make a vector out of the single pair and use subst2 to do the job.

44a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 42b 44b ⊲

void term::subst(substitution & sub) if (tag ≡ V ∨ tag ≡ SV)

if (cname ≡ sub.first) this→replace(sub.second); return; labelStaticBoundVars();vector<term ∗> bindingAbs;vector<substitution> subs; subs.push_back(sub);subst2(subs, bindingAbs, NULL);unmarkValidfree();

Uses labelStaticBoundVars 37c, replace 33b, subst 42a, subst2 42b, and substitution 41.

Comment 1.2.68. A correct implementation of substitution should get the following right. Giventhe statement

(func z) = \x.\y.\x.(&& z (|| x y)).

and the query

: (func (f x y)),

Escher should produce the following

\pve0.\pve1.\pve0.(&& (f x y) (|| pve0 pve1)).

Notice that two free variables got captured along the way.

1.2.7 Theorem Prover Helper Functions

Comment 1.2.69. We now look at some functions that check whether a given term satisfy someproperties. These functions are needed by the theorem prover.

Comment 1.2.70. A free variable is a variable with a cname that is larger or equal to 100000.

44b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 44a 45a ⊲

bool isUVar(term ∗ t) return (t→tag ≡ V ∧ t→cname ≥ 100000); bool isUVar(int cn) return (cn ≥ 100000);

bool term::containsFreeVariable() if (tag ≡ V) return isUVar(cname);for (uint i=0; i6=fields.size(); i++)

if (fields[i]→containsFreeVariable()) return true;return false;

void term::collectFreeVariables(set<int> & fvars) if (tag ≡ V ∧ isUVar(cname)) fvars.insert(cname);for (uint i=0; i6=fields.size(); i++)

fields[i]→collectFreeVariables(fvars);return;

Defines:

collectFreeVariables, used in chunks 103 and 134b.containsFreeVariable, used in chunks 103 and 128c.isUVar, used in chunks 100b, 103, 137a, 138a, and 146.

Uses insert 23a.

Page 48: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 45

Comment 1.2.71. This next function checks whether the current term has the form ¬φ for someφ.

45a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 44b 45b ⊲

bool term::isNegation() return (isApp() ∧ lc()→isF(iNot)); Defines:

isNegation, used in chunks 45, 103, 128b, 130a, 131a, 133, 136b, 137b, 139–41, and 143.Uses iNot 183, isApp 22f, isF 22f, and lc 23a.

Comment 1.2.72. This function checks whether the current term has the form ¬2i¬φ (≡ 3φ)for some φ.

45b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 45a 45c ⊲

bool term::isDiamond() return (isNegation() ∧ rc()→isModal() ∧

rc()→fields[0]→isNegation());

Defines:isDiamond, used in chunks 103 and 143.

Uses isModal 22f, isNegation 45a, and rc 23a.

Comment 1.2.73. The next function checks whether the input term t2 is the negation (at thesyntactic level only) of the calling term t1. We have to worry about symmetries here.

45c 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 45b 45d ⊲

bool term::isNegationOf(term ∗ t2) term ∗ t1 = this;if ((t1→isD(iTrue) ∧ t2→isD(iFalse)) ∨

(t1→isD(iFalse) ∧ t2→isD(iTrue)))return true;

if (t2→isApp() ∧ t2→lc()→isF(iNot) ∧ t1→equal(t2→rc()))return true;

if (t1→isApp() ∧ t1→lc()→isF(iNot) ∧ t2→equal(t1→rc()))return true;

return false;

Defines:isNegationOf, used in chunks 103 and 141a.

Uses equal 26a, iFalse 183, iNot 183, isApp 22f, isD 22f, isF 22f, iTrue 183, lc 23a, and rc 23a.

Comment 1.2.74. This function strips off double negations from a term.

45d 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 45c 46a ⊲

void term::stripNegations() if (isNegation() ∧ rc()→isNegation())

term ∗ term = rc()→rc();rc()→fields[1] = NULL;this→replace(term);

Defines:stripNegations, used in chunks 103 and 130c.

Uses isNegation 45a, rc 23a, and replace 33b.

Page 49: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 46

Comment 1.2.75. Sometimes, we need to replace a subterm s in t by another term r. Thefollowing function performs this operation. Note that free-variable capture can occur as a result;no attempt is made to check for this condition.

46a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 45d 46b ⊲

bool term::termReplace(term ∗ s, term ∗ r, term ∗ parent, int id) if (equal(s))

if (parent ≡ NULL) this→replace(r→clone());else parent→fields[id]→freememory();

parent→fields[id] = r→clone(); return true;

bool ret = false;int size = fields.size();for (int i=0; i6=size; i++)

ret = (ret ∨ fields[i]→termReplace(s, r, this, i));return ret;

Defines:

termReplace, used in chunks 103, 136a, 148c, and 149a.Uses equal 26a and replace 33b.

Comment 1.2.76. The function matchReplace takes a term s having the form i1 · · ·ijx, a

term r having the form j1 · · ·jkx, and replaces every subterm t of the calling term such that

t = sθ for some θ with the term rθ. We find θ using the redex_match function used for patternmatching. This is perhaps a lazy way of doing things....

46b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 46a 47a ⊲

extern bool redex_match(term ∗ head, term ∗ body, vector<substitution> & theta);

bool term::matchReplace(term ∗ s, term ∗ r, term ∗ parent, int id) vector<substitution> theta;if (redex_match(s, this, theta))

term ∗ r2 = r→clone();r2→subst(theta);if (parent ≡ NULL) this→replace(r2);else parent→fields[id]→freememory();

parent→fields[id] = r2; return true;

bool ret = false;int size = fields.size();for (int i=0; i6=size; i++)

ret = (ret ∨ fields[i]→matchReplace(s, r, this, i));return ret;

Defines:matchReplace, used in chunks 103 and 139c.

Uses redex_match 91a 91b 92a, replace 33b, subst 42a, and substitution 41.

Page 50: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 47

Comment 1.2.77. We normalise a term to contain only some minimal set of system-definedconstants. This is done in two steps. In the first step, we perform the basic transformations. Thisprocess generates many double negations. We remove these in the second step.

47a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 46b 47b ⊲

term ∗ term::normalise() return (this→normalise1())→normalise2();

Defines:normalise, used in chunks 47b, 80, 99a, 103, and 140a.

Uses normalise1 47b and normalise2 48c.

Comment 1.2.78. This next function transforms the calling term into normal form. A term isin normal form if it contains only the following system-defined function symbols: False, not , ‖‖,3(≡ not2not) and ∃.

47b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 47a 48c ⊲

term ∗ term::normalise1() for (uint i=0; i6=fields.size(); i++)

fields[i] = fields[i]→normalise();term ∗ ret;if (isD(iTrue))

ret = new_term(APP); ret→insert(new_term(F, iNot));ret→insert(new_term(D, iFalse));this→replace(ret); return this;

if (isFunc2Args(iImplies))

lc()→lc()→cname = iOr;ret = new_term(APP); ret→insert(new_term(F, iNot));ret→insert(lc()→rc());lc()→fields[1] = ret;return this;

if (isFunc2Args(iAnd)) 〈normalise1::and 48a〉 if (isFunc2Args(iIff)) 〈normalise1::iff 48b〉 if (isApp() ∧ lc()→isF(iPi))

lc()→cname = iSigma;ret = new_term(APP); ret→insert(new_term(F, iNot));ret→insert(rc()→fields[1]);rc()→fields[1] = ret;ret = new_term(APP); ret→insert(new_term(F, iNot));ret→insert(this);return ret;

return this;

Defines:normalise1, used in chunks 47a, 48b, and 103.

Uses iAnd 183, iFalse 183, iIff 183, iImplies 183, iNot 183, insert 23a, iOr 183, iPi 183, isApp 22f,isD 22f, isF 22f, isFunc2Args 24e, iSigma 183, iTrue 183, lc 23a, new_term 31b, normalise 47a, rc 23a,and replace 33b.

Page 51: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 48

Comment 1.2.79. We turn a formula of the form (&& p q) into another formula (|| (not p)

(not q)).

48a 〈normalise1::and 48a〉≡ (47b)

ret = new_term(APP); ret→insert(new_term(F, iNot));term ∗ arg2 = newT2Args(F, iOr);term ∗ arg21 = new_term(APP);arg21→insert(new_term(F, iNot)); arg21→insert(lc()→rc()→clone());term ∗ arg22 = new_term(APP);arg22→insert(new_term(F, iNot)); arg22→insert(rc()→clone());arg2→initT2Args(arg21, arg22);ret→insert(arg2);this→replace(ret); return this;

Uses initT2Args 25d, iNot 183, insert 23a, iOr 183, lc 23a, new_term 31b, newT2Args 25c, rc 23a,and replace 33b.

Comment 1.2.80. We change a formula of the form (iff p q) into a formula of the form(&& (|| (not p) q) (|| (not q) p)) and then normalise again to turn the conjunction into adisjunction.

48b 〈normalise1::iff 48b〉≡ (47b)

ret = newT2Args(F, iAnd);term ∗ arg1 = newT2Args(F, iOr);term ∗ arg1a = new_term(APP);arg1a→insert(new_term(F, iNot)); arg1a→insert(lc()→rc()→clone());arg1→initT2Args(arg1a, rc()→clone());term ∗ arg2 = newT2Args(F, iOr);term ∗ arg2a = new_term(APP);arg2a→insert(new_term(F, iNot)); arg2a→insert(rc()→clone());arg2→initT2Args(arg2a, lc()→rc()→clone());ret→initT2Args(arg1, arg2);ret = ret→normalise1();this→replace(ret); return this;

Uses iAnd 183, initT2Args 25d, iNot 183, insert 23a, iOr 183, lc 23a, new_term 31b, newT2Args 25c,normalise1 47b, rc 23a, and replace 33b.

48c 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 47b 49 ⊲

term ∗ term::normalise2() if (isApp() ∧ rc()→isApp() ∧ lc()→isF(iNot) ∧

rc()→lc()→isF(iNot)) term ∗ ret = rc()→rc();rc()→fields[1] = NULL;freememory();ret = ret→normalise2();return ret;

for (uint i=0; i6=fields.size(); i++)

fields[i] = fields[i]→normalise2();return this;

Defines:normalise2, used in chunks 47a and 103.

Uses iNot 183, isApp 22f, isF 22f, lc 23a, and rc 23a.

Page 52: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

1.2. TERMS 49

Comment 1.2.81. The next function allows us to collect together all the function symbols in aterm.

49 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 48c 50 ⊲

void term::collectFunctionNames(set<int> & x) if (tag ≡ F) x.insert(cname); return; for (uint i=0; i6=fields.size(); i++)

fields[i]→collectFunctionNames(x);

Uses insert 23a.

Page 53: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

Chapter 2

Equational Reasoning

2.1 Term Rewriting

2.1.1 Internal Rewrite Routines

Comment 2.1.1. To capture precisely and completely statement schemas in the booleans module,some of which have complicated side conditions on syntactical variables, we implement them asalgorithms. These algorithms form the internal rewrite module of Escher, and they are calledbefore any other program statements.

Comment 2.1.2. This next function implements the following equality statements:

= : a→ a→ Ω

(C x1 . . . xn = C y1 . . . yn) = (x1 = y1) ∧ · · · ∧ (xn = yn)

% where C is a data constructor of arity n.

(C x1 . . . xn = D y1 . . . ym) = ⊥

% where C and D are data constructors of arity n and m respectively, and C 6= D.

(() = ()) = ⊤

((x1, . . . , xn) = (y1, . . . , yn)) = (x1 = y1) ∧ · · · ∧ (xn = yn)

% where n = 2, 3, . . . .

50 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 49 54 ⊲

bool term::simplifyEquality(term ∗ parent, uint id) bool changed = false;term ∗ ret = this;term ∗ t1 = lc()→rc(), ∗ t2 = rc();〈simplifyEquality::local variables 52d〉

〈simplifyEquality::identical variables and function symbols 51b〉〈simplifyEquality::irrelevant cases 51c〉〈simplifyEquality::case of strings 51d〉〈simplifyEquality::case of products 52a〉〈simplifyEquality::case of applications 53a〉

simplifyEquality_cleanup:if (changed) 〈simplify update pointers 51a〉 return changed;

Defines:

50

Page 54: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 51

simplifyEquality, used in chunks 52c, 77, and 103.Uses lc 23a and rc 23a.

Comment 2.1.3. The pointer ret points to the current term under consideration. If changedis true by the end of the operation, then an equality embedded in the current term would havebeen simplified. Otherwise, it stays the same as before the function is called. Assuming the termhas been changed, we have two cases to consider. If the current term is the root term (parent ==

NULL), then we overwrite the current term with ret. Otherwise, we simply redirect the pointerparent->fields[id] to ret. Note that this code chunk is used in the other simplification routinesas well.

51a 〈simplify update pointers 51a〉≡ (50 54 56–58 62 64a 67–69)

assert(ret);if (parent ≡ NULL)

this→replace(ret); ret→freememory(); else parent→fields[id]→freememory(); parent→fields[id] = ret;

Uses replace 33b.

51b 〈simplifyEquality::identical variables and function symbols 51b〉≡ (50)

if ((t1→isVar() ∧ t2→isVar()) ∨ (t1→isF() ∧ t2→isF()))if (t1→cname ≡ t2→cname)

changed = true; ret = new_term(D, iTrue);goto simplifyEquality_cleanup;

Uses isF 22f, isVar 22f, iTrue 183, and new_term 31b.

Comment 2.1.4. This simplification does not apply when one of the terms is a variable. We alsodo not handle equality of abstractions. That is done using statements in the booleans module.

51c 〈simplifyEquality::irrelevant cases 51c〉≡ (50)

if (t1→isVar() ∨ t2→isVar()) return false;if (t1→isAbs()) return false;

Uses isAbs 22f and isVar 22f.

Comment 2.1.5. We have a special case for strings. Strings are represented internally as lists ofcharacters. Using the default rule to check the equality of two lists involves making many smallersteps. The procedure here reduces comparison of strings to a single-step operation. Surprisingly,this is actually not a great deal faster than the default multi-step procedure.

51d 〈simplifyEquality::case of strings 51d〉≡ (50)

if (t1→isAString() ∧ t2→isAString()) changed = true;term ∗ p1 = t1, ∗ p2 = t2;while (true)

if (p1→isD(iEmptyList) ∧ p2→isD(iEmptyList)) break;if (p1→tag 6= p2→tag ∨

p1→lc()→rc()→cname6=p2→lc()→rc()→cname) ret=new_term(D,iFalse); goto simplifyEquality_cleanup;

p1 = p1→rc();p2 = p2→rc();

ret = new_term(D, iTrue);goto simplifyEquality_cleanup;

Uses iEmptyList 183, iFalse 183, isAString 25a, isD 22f, iTrue 183, lc 23a, new_term 31b, and rc 23a.

Page 55: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 52

Comment 2.1.6. We need to check that both t1 and t2 are products before proceeding becauseone of them can be a (nullary) function symbol that stands for another product. However, oncewe have done that, we only have to check the dimension of t1 because the type checker wouldhave made sure that t2 has the same dimension. Given (x1, . . . , xn) = (y1, . . . , yn), we create aterm of the form ((· · · ((x1 ∧ y1) ∧ (x2 ∧ y2)) · · · ) ∧ (xn ∧ yn)).

52a 〈simplifyEquality::case of products 52a〉≡ (50)

if (t1→isProd() ∧ t2→isProd()) changed = true; uint t1_args = t1→fields.size();

〈simplifyEquality::case of products::empty tuples 52b〉〈simplifyEquality::case of products::error handling 52c〉

term ∗ eq1 = newT2Args(F, iEqual);eq1→initT2Args(t1→fields[0]→reuse(), t2→fields[0]→reuse());term ∗ eq2 = newT2Args(F, iEqual);eq2→initT2Args(t1→fields[1]→reuse(), t2→fields[1]→reuse());

ret = newT2Args(F, iAnd); ret→initT2Args(eq1, eq2);for (uint i=0; i6=t1_args-2; i++)

term ∗ eqi = newT2Args(F, iEqual);eqi→initT2Args(t1→fields[i+2]→reuse(),

t2→fields[i+2]→reuse());term ∗ temp = newT2Args(F, iAnd);temp→initT2Args(ret, eqi);ret = temp;

goto simplifyEquality_cleanup;

Uses iAnd 183, iEqual 183, initT2Args 25d, isProd 22f, newT2Args 25c, and reuse 34c.

Comment 2.1.7. The boolean module as it stands in [Llo03] does not handle the expression() = (). We will cater for that case here, which should of course evaluate to ⊤.

52b 〈simplifyEquality::case of products::empty tuples 52b〉≡ (52a)

if (t1_args ≡ 0) ret = new_term(D, iTrue); goto simplifyEquality_cleanup;

Uses iTrue 183 and new_term 31b.

Comment 2.1.8. Besides the empty tuple, we handle all finite-length tuples of dimension at leasttwo. It does not make a great deal of sense to have a tuple of dimension one.

52c 〈simplifyEquality::case of products::error handling 52c〉≡ (52a)

if (t1_args 6= t2→fields.size() ∨ t1_args < 2) setSelector(STDERR); ioprint("Error in simplifyEquality:products\n");t1→print(); ioprintln(); t2→print(); ioprintln();

assert(t1_args ≡ t2→fields.size() ∧ t1_args ≥ 2);

Uses ioprint, ioprintln, setSelector, and simplifyEquality 50.

52d 〈simplifyEquality::local variables 52d〉≡ (50)

int t1_arity = 0, t2_arity = 0;

Page 56: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 53

53a 〈simplifyEquality::case of applications 53a〉≡ (50)

〈simplifyEquality::check whether we have data constructors 53b〉changed = true;

if (t1_arity ≡ 0 ∧ t2_arity ≡ 0) if (t1→spineTip()→isfloat ∧ t2→spineTip()→isfloat)

if (t1→spineTip()→numf ≡ t2→spineTip()→numf)ret = new_term(D, iTrue);

else ret = new_term(D, iFalse);goto simplifyEquality_cleanup;

else if (t1→spineTip()→isint ∧ t2→spineTip()→isint) if (t1→spineTip()→numi ≡ t2→spineTip()→numi)

ret = new_term(D, iTrue);else ret = new_term(D, iFalse);goto simplifyEquality_cleanup;

if (t1→spineTip()→cname ≡ t2→spineTip()→cname)

ret= new_term(D,iTrue);else ret = new_term(D, iFalse);goto simplifyEquality_cleanup;

if (t1_arity 6= t2_arity ∨ t1→spineTip()→cname 6= t2→spineTip()→cname)

ret = new_term(D, iFalse); goto simplifyEquality_cleanup;

ret = newT2Args(F, iEqual);ret→initT2Args(t1→fields[1]→reuse(), t2→fields[1]→reuse());t1_arity−−;while (t1_arity 6= 0)

t1 = t1→fields[0]; t2 = t2→fields[0];term ∗ temp = newT2Args(F, iEqual);temp→initT2Args(t1→fields[1]→reuse(), t2→fields[1]→reuse());term ∗ temp2 = newT2Args(F, iAnd); temp2→initT2Args(temp, ret);ret = temp2;t1_arity−−;

Uses iAnd 183, iEqual 183, iFalse 183, initT2Args 25d, iTrue 183, new_term 31b, newT2Args 25c, reuse 34c,

and spineTip 24d.

Comment 2.1.9. We need to check whether the leftmost symbol of both t1 and t2 is a dataconstructor. If we go pass this point, t1 and t2 have the right form for comparison.

53b 〈simplifyEquality::check whether we have data constructors 53b〉≡ (53a)

if (¬t1→spineTip(t1_arity)→isD()) return false;if (¬t2→spineTip(t2_arity)→isD()) return false;

Uses isD 22f and spineTip 24d.

Page 57: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 54

Comment 2.1.10. This function implements the different arithmetic operations. We currentlysupport the following functions on numbers. More can be added if necessary.

54 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 50 56 ⊲

bool term::simplifyArithmetic(term ∗ parent, uint id) if (¬(rc()→isD() ∧ lc()→rc()→isD())) return false;

int op = fields[0]→lc()→cname;if (¬(op ≥ iAdd ∧ op ≤ iAtan2)) return false;

term ∗ t1 = lc()→rc(), ∗ t2 = rc();if (t1→isD(iInfinity) ∨ t2→isD(iInfinity)) return false;

term ∗ ret = NULL;〈simplifyArithmetic::add, subtract, multiply and divide 55〉else if (op ≡ iMax)

if (t1→isfloat ∧ t2→isfloat) if (t1→numf ≥ t2→numf) ret =new_term_float(t1→numf);else ret = new_term_float(t2→numf) ;

else if (t1→isint ∧ t2→isint) if (t1→numi ≥ t2→numi) ret = new_term_int(t1→numi);else ret = new_term_int(t2→numi) ;

else return false; else if (op ≡ iMin)

if (t1→isfloat ∧ t2→isfloat) if (t1→numf ≤ t2→numf) ret =new_term_float(t1→numf);else ret = new_term_float(t2→numf) ;

else if (t1→isint ∧ t2→isint) if (t1→numi ≤ t2→numi) ret = new_term_int(t1→numi);else ret = new_term_int(t2→numi) ;

else return false; else if (op ≡ iMod)

assert(t1→isint ∧ t2-isint);ret = new_term_int(int(t1→numi % t2→numi));

else if (op ≡ iAtan2) assert(t1→isfloat ∧ t2→isfloat);ret = new_term_float(atan2(t1→numf, t2→numf));

〈simplify update pointers 51a〉return true;

Defines:simplifyArithmetic, used in chunks 77 and 103.

Uses iAdd 183, iAtan2 183, iInfinity 183, iMax 183, iMin 183, iMod 183, isD 22f, lc 23a, new_term_float 31b,new_term_int 31b, and rc 23a.

Page 58: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 55

Comment 2.1.11. We overload the basic addition, subtraction, multiplication and division op-erations to act on numbers, be they integers or floating-point numbers. The definitions are fairlystandard, when one of the arguments is a floating-point number, the result is a floating-pointnumber. When both arguments are integers, the result is an integer, except when we are dividingtwo integers, in which case the result can be a floating-point number.

55 〈simplifyArithmetic::add, subtract, multiply and divide 55〉≡ (54)

if (op ≡ iAdd) if (t1→isfloat ∧ t2→isfloat)

ret = new_term_float(t1→numf + t2→numf);else if (t1→isfloat ∧ t2→isint)

ret = new_term_float(t1→numf + t2→numi);else if (t1→isint ∧ t2→isfloat)

ret = new_term_float(t1→numi + t2→numf);else if (t1→isint ∧ t2→isint)

ret = new_term_int(t1→numi + t2→numi);else return false;

else if (op ≡ iSub) if (t1→isfloat ∧ t2→isfloat)

ret = new_term_float(t1→numf - t2→numf);else if (t1→isfloat ∧ t2→isint)

ret = new_term_float(t1→numf - t2→numi);else if (t1→isint ∧ t2→isfloat)

ret = new_term_float(t1→numi - t2→numf);else if (t1→isint ∧ t2→isint)

ret = new_term_int(t1→numi - t2→numi);else return false;

else if (op ≡ iMul) if (t1→isfloat ∧ t2→isfloat)

ret = new_term_float(t1→numf ∗ t2→numf);else if (t1→isfloat ∧ t2→isint)

ret = new_term_float(t1→numf ∗ t2→numi);else if (t1→isint ∧ t2→isfloat)

ret = new_term_float(t1→numi ∗ t2→numf);else if (t1→isint ∧ t2→isint)

ret = new_term_int(t1→numi ∗ t2→numi);else return false;

else if (op ≡ iDiv) if (t1→isfloat ∧ t2→isfloat)

ret = new_term_float(t1→numf ÷ t2→numf);else if (t1→isfloat ∧ t2→isint)

ret = new_term_float(t1→numf ÷ t2→numi);else if (t1→isint ∧ t2→isfloat)

ret = new_term_float(t1→numi ÷ t2→numf);else if (t1→isint ∧ t2→isint)

double res = (double)t1→numi ÷ (double)t2→numi;if (res ≡ floor(res)) ret = new_term_int(t1→numi ÷ t2→numi);else ret = new_term_float(res);

else return false;

Uses iAdd 183, iDiv 183, iMul 183, iSub 183, new_term_float 31b, and new_term_int 31b.

Comment 2.1.12. This function implements the different inequalities. It has the same overallstructure as simplifyArithmetic.

Page 59: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 56

56 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 54 57 ⊲

bool term::simplifyInequalities(term ∗ parent, uint id) if (¬(rc()→isD() ∧ lc()→rc()→isD())) return false;int rel = lc()→lc()→cname;

if (¬(rel ≥ iLT ∧ rel ≤iGTE)) return false;

term ∗ t1 = lc()→rc() ;term ∗ t2 = rc() ;

if (t1→isD(iInfinity) ∨ t2→isD(iInfinity)) return false;

term ∗ ret = NULL;if (rel ≡ iLT)

if (t1→isint ∧ t2→isint) if (t1→numi < t2→numi) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isfloat ∧ t2→isfloat) if (t1→numf < t2→numf) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isint ∧ t2→isfloat) if (t1→numi < t2→numf) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isfloat ∧ t2→isint) if (t1→numf < t2→numi) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else return false; else if (rel ≡ iLTE)

if (t1→isint ∧ t2→isint) if (t1→numi ≤ t2→numi) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isfloat ∧ t2→isfloat) if (t1→numf ≤ t2→numf) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isint ∧ t2→isfloat) if (t1→numi ≤ t2→numf) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isfloat ∧ t2→isint) if (t1→numf ≤ t2→numi) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else return false; else if (rel ≡ iGT)

if (t1→isint ∧ t2→isint) if (t1→numi > t2→numi) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isfloat ∧ t2→isfloat) if (t1→numf > t2→numf) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isint ∧ t2→isfloat) if (t1→numi > t2→numf) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isfloat ∧ t2→isint) if (t1→numf > t2→numi) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

Page 60: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 57

else return false; else if (rel ≡ iGTE)

if (t1→isint ∧ t2→isint) if (t1→numi ≥ t2→numi) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isfloat ∧ t2→isfloat) if (t1→numf ≥ t2→numf) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isint ∧ t2→isfloat) if (t1→numi ≥ t2→numf) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else if (t1→isfloat ∧ t2→isint) if (t1→numf ≥ t2→numi) ret = new_term(D,iTrue);else ret = new_term(D,iFalse);

else return false;〈simplify update pointers 51a〉return true;

Defines:simplifyInequalities, used in chunks 77 and 103.

Uses iFalse 183, iGT 183, iGTE 183, iInfinity 183, iLT 183, iLTE 183, isD 22f, iTrue 183, lc 23a, new_term 31b,and rc 23a.

Comment 2.1.13. We use the C math library to support common math operations like sin, cos,etc.

57 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 56 58 ⊲

bool term::simplifyMath(term ∗ parent, uint id) if (¬(lc()→isF() ∧ rc()→isD())) return false;int op = lc()→cname;if (¬(op ≥ iSin ∧ op ≤ iExp)) return false;

term ∗ ret = NULL;if (op ≡ iSin)

assert(rc()→isfloat);ret = new_term_float(sin(rc()→numf));

else if (op ≡ iCos) assert(rc()→isfloat);ret = new_term_float(cos(rc()→numf));

else if (op ≡ iSqrt) assert(rc()→isfloat);ret = new_term_float(sqrt(rc()→numf));

else if (op ≡ iExp) assert(rc()→isfloat);ret = new_term_float(exp(rc()→numf));

〈simplify update pointers 51a〉return true;

Defines:simplifyMath, used in chunks 77 and 103.

Uses iCos 183, iExp 183, isD 22f, isF 22f, iSin 183, iSqrt 183, lc 23a, new_term_float 31b, and rc 23a.

Page 61: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 58

Comment 2.1.14. The β-reduction rule (λx.u t) = ux/t in the booleans module is not reallya valid program statement. (The leftmost symbol on the LHS of the equation is not a functionsymbol.) It should therefore be thought of as a part of the internal simplification routine of Escher.This rule is also the first among a few we will encounter where sharing of nodes in the currentterm is not safe because of the appearance of term substitutions on the RHS of the equation. (SeeComments 2.1.15, 2.1.31 and 2.1.42 for the other such rules. The existence and (heavy) use of suchrules in Escher is one important reason I gave up on sharing of nodes. See Comment 1.2.33 for amore detailed discussion on the advantages and disadvantages of sharing.) In a typical programstatement h = b without term substitutions in the body, rewriting a subterm that is α-equivalent(see §2.1.3 for the exact details) to h in the current term with b involves only the creation anddestruction of terms and redirection of pointers to terms. No actual modification to an atomicterm embedded inside the current term actually takes place, which means sharing is always safe.This scenario is no longer true when term substitutions appear in the body of statements.

58 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 57 59 ⊲

bool term::betaReduction(term ∗ parent, uint id) if (lc()→isAbs() ≡ false) return false;

substitution bind(lc()→fields[0]→cname, rc());lc()→fields[1]→subst(bind);term ∗ ret = lc()→fields[1]→reuse();〈simplify update pointers 51a〉return true;

Defines:

betaReduction, used in chunks 77 and 103.Uses isAbs 22f, lc 23a, rc 23a, reuse 34c, subst 42a, and substitution 41.

Comment 2.1.15. This function implements the following conjunction rule:

u ∧ (x = t) ∧ v = ux/t ∧ (x = t) ∧ vx/t. (2.1)

Here, t is not a variable and x is a variable free in u or v or both, but not free in t. The LHS of theequation is supposed to capture every term that has a subterm (x = t) embedded conjunctively(see Definition 2.1.17) inside it. All the variables in the rule are syntactical variables because asubterm that pattern matches with the LHS of the equation can occur inside a term that bindsthe variable x, in which case the standard term substitution routine will not give us what we want.

The condition that t is not a variable is important. If t is a free variable and we interpret (x = t)to stand for (x = t) or (t = x), I think the correct interpretation, then loops can result fromrepeated application of the rule. In [Llo99], the statement

(t = x) = (x = t) where x is a variable, and t is not a variable

is used to capture the symmetry between x and t in the rule. In the current implementation, wedo away with the swapping rule and implement the symmetry directly to gain better efficiency.The condition that t is not a variable does not appear in [Llo03]; this suggests that the rule as itappears in the book is either loopy or incomplete, depending on how one interprets the rule.

There is another small problem with the rule. Note that I have been calling it a rule, not astatement. Why? In any instantiation of the rule, the variable x must occur free in at leasttwo places, which means the instantiation cannot be a statement because of the no repeated freevariables condition. This error appears in every description of Escher before 22 Sep 2005, the dayit was discovered. The use of this rule, among other things, affects the run-time type checking isunnecessary result (See Proposition 5.1.3 in [Llo03]). This is not as bad as it sounds; we only haveto type-check every time we use the conjunction rule, not after every computation step.

Page 62: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 59

Problem 2.1.16. What is the cost, in terms of expressiveness, of omitting this rule?

Definition 2.1.17. A term t is embedded conjunctively in t and, if t is embedded conjunctivelyin r (or s), then t is embedded conjunctively in r ∧ s.

Comment 2.1.18. We could implement the rule completely using the following set of statements.

((x = t) ∧ u) = ((x = t) ∧ ux/t)

(u ∧ (x = t)) = ((x = t) ∧ u)

where u does not have the form (y = s) for some terms y and s.

(((x = t) ∧ u) ∧ v) = ((x = t) ∧ (u ∧ v))

(u ∧ ((x = t) ∧ v)) = ((x = t) ∧ (u ∧ v))

where u does not have the form (y = s) for some terms y and s.

The last three statements can bring out conjunctively embedded equations to the front of the term,which can then be simplified using the first statement. A loop can occur if the side conditions inthe second and fourth statements are not imposed.

Comment 2.1.19. Notice that we do not need the parent pointer for this particular rewriting.

Comment 2.1.20. In the following, we first check that the current term has the right form, thenwe find (using findEq (see Comment 2.1.21)) a variable-instantiating equation inside the currentterm. By a variable-instantiating equation I mean a (sub)term having the form (x = t) embeddedconjunctively inside the current term which satisfies all the side conditions of Equation 2.1. (Ifthere are more than one variable-instantiating equation, the leftmost is selected. Subsequent callsto findEq on the current term (rewritten using Equation 2.1) will find the remaining variable-instantiating equations in the left-to-right order.) If no such equation exists, findEq returns anull pointer. We rename the x in (x = t) temporarily so that it does not get substituted witht by subst. Since we will not call freememory on the current term, we need to reuse the termp->fields[1] when creating bind to make sure the term substitution works as expected.

59 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 58 60a ⊲

bool term::simplifyConjunction() term ∗ p = findEq(this); if (p ≡ NULL) return false;

term ∗ varp = p→lc()→rc();varp→freeze = true;

substitution bind(varp→cname, p→fields[1]→reuse());subst(bind); p→fields[1]→refcount−−;varp→freeze = false;

return true;

Defines:simplifyConjunction, used in chunks 77 and 103.

Uses findEq 60a, freeze 23f, lc 23a, rc 23a, reuse 34c, subst 42a, and substitution 41.

Comment 2.1.21. The function findEq seeks a variable-instantiating equation inside the root

term with the help of isEq. The function findEq assumes that the calling term is a conjunctionof the form t1 ∧ t2. (See Definition 2.1.17.) If t1 is a variable-instantiating equation, we return.Otherwise, we recurse on t1 if it has the right (conjunctive) form. Then we do the same on t2.This gives us the left-to-right selection order.

Page 63: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 60

60a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 59 60b ⊲

term ∗ term::findEq(term ∗ root) term ∗ p = NULL;term ∗ t1 = lc()→rc();if (t1→isEq(root)) return t1;if (t1→isFunc2Args(iAnd)) p = t1→findEq(root); if (p) return p;

term ∗ t2 = rc();if (t2→isEq(root)) return t2;if (t2→isFunc2Args(iAnd)) p = t2→findEq(root); if (p) return p; return NULL;

Defines:findEq, used in chunks 59 and 103.

Uses iAnd 183, isEq 60b 66b, isFunc2Args 24e, lc 23a, and rc 23a.

Problem 2.1.22. Getting findEq to run fast is an interesting search problem. The first questionis whether left-to-right is the right search order? We can implement top-to-bottom search byisEqing t1 and t2 first, followed by recursion into each of them. Would that be better? Anotherquestion is can we improve search time by representing conjunctive terms differently, for examplein a flat representation? Or if we do stick with the tree representation, can we augment nodes (inthe spirit of binary search algorithms) to make the search go faster?

Comment 2.1.23. This function checks whether the current term is a variable-instantiating term,that is, whether it has the form (t1 = t2), where t1 is a variable, t2 is a (non-variable) term suchthat t1 does not occur free in it, and t1 occurs free elsewhere in the term root. Note the symmetrybetween t1 and t2. We must check both because any one of them can turn out to be the variablethat satisfies all the conditions.

60b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 60a 62 ⊲

bool term::isEq(term ∗ root) if (isFunc2Args(iEqual) ≡ false) return false;term ∗ t1 = lc()→rc(), ∗ t2 = rc();if (t1→isVar() ∧ t2→isVar() ≡ false)

if (t2→occursFree(t1→cname) ≡ false) t1→freeze = true;if (root→occursFreeNaive(t1→cname))

t1→freeze = false; return true; t1→freeze = false;

if (t2→isVar() ∧ t1→isVar() ≡ false)

if (t1→occursFree(t2→cname) ≡ false) t2→freeze = true;bool ret = root→occursFreeNaive(t2→cname);t2→freeze = false;if (ret) 〈isEq::switch t1 and t2 61〉 return ret;

return false;

Defines:isEq, used in chunks 60a, 65–67, and 103.

Uses freeze 23f, iEqual 183, isFunc2Args 24e, isVar 22f, lc 23a, occursFree 38a, occursFreeNaive 38b,and rc 23a.

Page 64: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 61

Comment 2.1.24. We use occursFreeNaive for root here because the temporary variable re-naming we do affects the correctness of the caching of computed free variables. (Note: We nolonger do variable renaming, do we still really need to use occursFreeNaive?

Comment 2.1.25. We need to swap t1 and t2 because procedures that call findEq expect thevariable that satisfies all the conditions to be on the LHS of the equation.

61 〈isEq::switch t1 and t2 61〉≡ (60b 66b)

term ∗ temp = t1; lc()→fields[1] = t2; fields[1] = temp;

Uses lc 23a.

Page 65: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 62

Comment 2.1.26. Example execution of simplifyConjunction.

Query: ((&& y) ((&& ((== x) T1)) ((&& ((== T2) y)) x)))

Time = 1 Answer: ((&& y) ((&& ((== x) T1)) ((&& ((== T2) y)) T1)))

Time = 2 Answer: ((&& T2) ((&& ((== x) t1)) ((&& ((== y) T2)) T1)))

There are two variable-instantiating equations in the query. It is easy to get this wrong if one isnot careful.

Comment 2.1.27. We next look at the implementation of the rules

u ∧ (∃x1. · · · ∃xn.v) = ∃x1. · · · ∃xn.(u ∧ v) (2.2)

(∃x1. · · · ∃xn.v) ∧ u = ∃x1. · · · ∃xn.(v ∧ u) (2.3)

Note that the convention on syntactic variables dictate that none of the variables xi can appearfree in u. The two rules can be captured by repeated applications of the following two specialcases of the rules

u ∧ (∃x.v) = ∃x.(u ∧ v) (2.4)

(∃x.v) ∧ u = ∃x.(v ∧ u) (2.5)

and these are what we will actually implement. We choose to implement these easier rules becausechecking that each xi is free in u would be an expensive exercise.

Interestingly, it is actually quite important to get the order of u and v right in the conjunction.For example, implementing

(∃x.v) ∧ u = ∃x.(u ∧ v)

instead of Statement 2.5 will seriously slow down the predicate permute (see Sect. 6.3).

62 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 60b 64a ⊲

bool term::simplifyConjunction2(term ∗ parent, uint id) term ∗ t1 = lc()→rc(), ∗ t2 = rc();term ∗ sigma, ∗ other;if (t2→isApp() ∧ t2→lc()→isF(iSigma))

sigma = t2; other = t1; else if (t1→isApp() ∧ t1→lc()→isF(iSigma))

sigma = t1; other = t2; else return false;

int var = sigma→rc()→fields[0]→cname;if (other→occursFree(var)) return false;

〈simplifyConjunction2::create body 63〉〈simplify update pointers 51a〉return true;

Defines:simplifyConjunction2, used in chunks 77 and 103.

Uses isApp 22f, isF 22f, iSigma 183, lc 23a, occursFree 38a, and rc 23a.

Page 66: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 63

Comment 2.1.28. We could recycle ∃x but choose not to.

63 〈simplifyConjunction2::create body 63〉≡ (62)

term ∗ con = newT2Args(F, iAnd);if (sigma ≡ t2)

con→initT2Args(other→reuse(), sigma→rc()→fields[1]→reuse());else con→initT2Args(sigma→rc()→fields[1]→reuse(), other→reuse());term ∗ abs = new_term(ABS);abs→insert(new_term(V, var)); abs→insert(con);term ∗ ret = new_term(APP);ret→insert(new_term(F, iSigma)); ret→insert(abs);

Uses iAnd 183, initT2Args 25d, insert 23a, iSigma 183, new_term 31b, newT2Args 25c, rc 23a, and reuse 34c.

Comment 2.1.29. Example execution of simplifyConjunction2.

Query: ((&& (sigma \x1.(sigma \x2.v))) u)

Time = 1 Answer: (sigma \x1.((&& u) (sigma \x2.v)))

Time = 2 Answer: (sigma \x1.(sigma \x2.((&& u) v)))

Comment 2.1.30. The use of Statements 2.4 and 2.5 introduces a peculiar behaviour into Escherin that the same query, when asked using two different variable names, can result in two differentcomputation sequences. To illustrate this, consider the following statement:

f : Int× (Int→ Ω)→ Ω

f(x, s) = (x ≤ 8) ∧ ∃z.(z ∈ s ∧ (prime z)).

Now, if we ask Escher to compute the value of f(y, 2, 3), we get

f(y, 2, 3) = (y ≤ 8) ∧ ∃z.(z ∈ 2, 3 ∧ (prime z))

= ∃z.((y ≤ 8) ∧ z ∈ 2, 3 ∧ (prime z))

= . . .

= ∃z.((y ≤ 8) ∧ (z = 2))

= (y ≤ 8).

However, if we ask Escher to compute the value of f(z, 2, 3), we will get

f(z, 2, 3) = (z ≤ 8) ∧ ∃z.(z ∈ 2, 3 ∧ (prime z))

= . . .

= (z ≤ 8) ∧ ∃z.(z = 2)

= (z ≤ 8) ∧ ⊤

= (z ≤ 8).

The two computation sequences are different from the second step onwards. The second sequencetakes one step longer than the first. Just about the only comforting thing is that the end resultsare equivalent. The questions then are

1. Should we retain Statements 2.4 and 2.5 in the booleans module? Judging from the (limittedset of) test programs I have, there is no actual need for the two statements. In general, Ibelieve they make certain computations go faster, although one can also show instanceswhere they actually make things go slightly slower.

2. If we retain the two statements, should we modify the convention so that a variable renamingis done to make them applicable in cases where they cannot be applied?

Page 67: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 64

Comment 2.1.31. This function implements the following existential rules:

∃x1. · · · ∃xn.⊤ = ⊤ (2.6)

∃x1. · · · ∃xn.⊥ = ⊥ (2.7)

∃x1. · · · ∃xn.(x ∧ (x1 = u) ∧ y) = ∃x2. · · · ∃xn.(xx1/u ∧ ⊤ ∧ yx1/u). (2.8)

The other rules are implemented in the Booleans module. See Comment 6.1.1. I suppose they canbe implemented here if we really need to maximise efficiency at the price of complicated code.

Comment 2.1.32. We first check whether the current term starts with ∃x1 · · · ∃xn. We thenmove to the subterm after ∃x1 · · · ∃xn and perform surgery on it if possible.

64a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 62 66a ⊲

bool term::simplifyExistential(term ∗ parent, uint id) if (fields[0]→isF(iSigma) ≡ false) return false;

term ∗ ret = NULL; term ∗ p = NULL;int var = rc()→fields[0]→cname;substitution bind;

〈simplifyExistential::move to the body 64b〉

〈simplifyExistential::case one and two 64c〉〈simplifyExistential::tricky case 65a〉

simplifyExistential_cleanup:〈simplify update pointers 51a〉return true;

Defines:simplifyExistential, used in chunks 77 and 103.

Uses isF 22f, iSigma 183, rc 23a, and substitution 41.

Comment 2.1.33. The following allows us to move past the remaining ∃xi to get to the body ofthe term.

64b 〈simplifyExistential::move to the body 64b〉≡ (64a)

term ∗ body = fields[1]→fields[1];while (body→isApp() ∧ body→lc()→isF(iSigma))

body = body→rc()→fields[1];

Uses isApp 22f, isF 22f, iSigma 183, lc 23a, and rc 23a.

Comment 2.1.34. This handles Statements 2.6 and 2.7. Completeness of specification is not anissue here. The two statements can be captured by repeated application of the statements

∃x.⊤ = ⊤ and ∃x.⊥ = ⊥.

Making them part of the internal simplification routine gives us efficiency advantages.

64c 〈simplifyExistential::case one and two 64c〉≡ (64a)

if (body→isD(iTrue) ∨ body→isD(iFalse)) ret = body→reuse(); goto simplifyExistential_cleanup;

Uses iFalse 183, isD 22f, iTrue 183, and reuse 34c.

Page 68: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 65

Comment 2.1.35. We next discuss Statement 2.8. The pattern in the head of Statement 2.8should be interpreted in the same way as the corresponding pattern in the conjunction rule de-scribed in Comment 2.1.15. Note that the statement is slightly different from that given in [Llo03],which takes the following form:

∃x1. · · · ∃xn.(x ∧ (xi = u) ∧ y) = ∃x1. · · · ∃xi−1.∃xi+1. · · · ∃xn.(xxi/u ∧ yxi/u).

First, restricting xi to be x1 as we did incurs a small computational cost in that we need to moveto the subterm starting with ∃xi during pattern matching to apply Statement 2.8. In return, wecan write simpler code. The second change is that instead of dropping the term (x1 = u), we puta ⊤ in its place. The two expressions are equivalent, of course. The advantage of that is the same:we can write simpler code. Another advantage of this latter change is that, unlike the originalstatement, we do end up with a natural special case. (See Comment 2.1.36.)

65a 〈simplifyExistential::tricky case 65a〉≡ (64a)

〈simplifyExistential::tricky case::special case 65b〉〈simplifyExistential::tricky case::general case 65c〉

Comment 2.1.36. A special case of Statement 2.8 is the following:

∃x1. · · · ∃xn.(x1 = u) = ∃x2. · · · ∃xn.⊤. (2.9)

The body of the statement can be further simplified to ⊤, of course.

65b 〈simplifyExistential::tricky case::special case 65b〉≡ (65a)

if (body→isEq(var)) ret = new_term(D, iTrue);goto simplifyExistential_cleanup;

Uses isEq 60b 66b, iTrue 183, and new_term 31b.

Comment 2.1.37. In the general case, we first check that the body has the overal form t1 ∧ t2.Then we attempt to find in the body an equation that instantiates the first quantified variable andreplaces it with ⊤. (This is performed all at the same time by replaceEq.) If that operation issuccessful, we perform term substitutions on the body and then get rid of the first quantification.

65c 〈simplifyExistential::tricky case::general case 65c〉≡ (65a)

if (body→isFunc2Args(iAnd) ≡ false) return false;p = body→replaceEq(var); if (p ≡ NULL) return false;

bind.first = p→lc()→rc()→cname; bind.second = p→fields[1];body→subst(bind);p→freememory();

ret = rc()→fields[1]→reuse();

Uses iAnd 183, isFunc2Args 24e, lc 23a, rc 23a, replaceEq 66a, reuse 34c, and subst 42a.

Page 69: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 66

Comment 2.1.38. The function replaceEq finds a subterm of the form (x = t) embeddedconjunctively inside a term (with the help of isEq), replaces it with ⊤ and then returns a pointerto (x = t).

Comment 2.1.39. We assume that the calling term is a conjunction of the form t1 ∧ t2. If t1is a variable-instantiating equation, we return. Otherwise, we recurse on t1 if it has the right(conjunctive) form. Then we do the same on t2. (See also Comment 2.1.21.)

66a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 64a 66b ⊲

term ∗ term::replaceEq(int var) term ∗ p = NULL;term ∗ t1 = lc()→rc();if (t1→isEq(var))

lc()→fields[1] = new_term(D, iTrue); return t1; if (t1→isFunc2Args(iAnd)) p = t1→replaceEq(var); if (p) return p;

term ∗ t2 = rc();if (t2→isEq(var)) fields[1] = new_term(D, iTrue); return t2; if (t2→isFunc2Args(iAnd)) p = t2→replaceEq(var); if (p) return p; return NULL;

Defines:replaceEq, used in chunks 65c, 68b, and 103.

Uses iAnd 183, isEq 60b 66b, isFunc2Args 24e, iTrue 183, lc 23a, new_term 31b, and rc 23a.

Comment 2.1.40. This function checks whether the current term has the form (x = t) where xis the input variable and t is a term such that x does not occur free in t. If the current term hasthe form (t = x) where x does not occur free in t, we need to swap the two arguments becauseprocedures that call replaceEq expect the variable x to be on the LHS of the equation.

66b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 66a 67a ⊲

bool term::isEq(int x) if (isFunc2Args(iEqual) ≡ false) return false;term ∗ t1 = lc()→rc(), ∗ t2 = rc();

if (t1→isVar(x) ∧ t2→occursFree(x) ≡ false) return true;if (t2→isVar(x) ∧ t1→occursFree(x) ≡ false)

〈isEq::switch t1 and t2 61〉return true;

return false;

Defines:isEq, used in chunks 60a, 65–67, and 103.

Uses iEqual 183, isFunc2Args 24e, isVar 22f, lc 23a, occursFree 38a, and rc 23a.

Comment 2.1.41. Example execution of simplifyExistential.

Query: (sigma \x3.(sigma \x2.(sigma \x1.((== x3) t1))))

Time = 1 Answer: True

Query: (sigma \x3.(sigma \x.(sigma \y.(&& y (&& (== x T1) (&& (== t2 y) x))))))

Time = 1 Answer: (sigma \x3.(sigma \y.(&& y (&& True (&& (== t2 y) T1)))))

Time = 2 Answer: (sigma \x3.(&& t2 (&& True (&& True T1))))

Time = 3 Answer: (sigma \x3.(&& t2 (&& True T1)))

Time = 4 Answer: (sigma \x3.(&& t2 T1))

Page 70: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 67

Comment 2.1.42. This function implements the following universal rules:

∀x1. · · · ∀xn.(⊥ → u) = ⊤ (2.10)

∀x1. · · · ∀xn.(x ∧ (x1 = u) ∧ y→ v) = ∀x2. · · · ∀xn.((x ∧ ⊤ ∧ y→ v)x1/u). (2.11)

Statement 2.11 is equivalent to the following rule given in [Llo03]:

∀x1. · · · ∀xn.(x ∧ (x1 = u) ∧ y→ v) = ∀x1. · · · ∀xi−1.∀xi+1. · · · ∀xn.((x ∧ y→ v)xi/u).

67a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 66b 69 ⊲

bool term::simplifyUniversal(term ∗ parent, uint id) if (lc()→isF(iPi) ≡ false) return false;

int var = rc()→fields[0]→cname;〈simplifyUniversal::check the form of body 67b〉〈simplifyUniversal::true statement 67c〉〈simplifyUniversal::special case 67d〉〈simplifyUniversal::general case 68b〉

Defines:

simplifyUniversal, used in chunks 77 and 103.Uses iPi 183, isF 22f, lc 23a, and rc 23a.

Comment 2.1.43. We move past the remaining ∀s to get to the body and check whether it hasthe form t1 → t2. If so, we move to t1.

67b 〈simplifyUniversal::check the form of body 67b〉≡ (67a)

term ∗ body = rc()→fields[1];while (body→isApp() ∧ body→lc()→isF(iPi))

body = body→rc()→fields[1];if (body→isFunc2Args(iImplies) ≡ false) return false;term ∗ t1 = body→lc()→rc();

Uses iImplies 183, iPi 183, isApp 22f, isF 22f, isFunc2Args 24e, lc 23a, and rc 23a.

Comment 2.1.44. This code chunk implements Statement 2.10.

67c 〈simplifyUniversal::true statement 67c〉≡ (67a)

if (t1→isD(iFalse)) term ∗ ret = new_term(D, iTrue);〈simplify update pointers 51a〉return true;

Uses iFalse 183, isD 22f, iTrue 183, and new_term 31b.

Comment 2.1.45. A special case of Statement 2.11 is the following:

∀x1. · · · ∀xn.((x1 = u)→ v) = ∀x2. · · · ∀xn.(⊤ → v)x1/u = ∀x2. · · · ∀xn.vx1/u.

67d 〈simplifyUniversal::special case 67d〉≡ (67a)

if (t1→isEq(var)) term ∗ t2 = body→rc();substitution bind(t1→lc()→rc()→cname, t1→rc());t2→subst(bind);body→replace(t2→reuse());t2→freememory();〈simplifyUniversal::change end game 68a〉

Uses isEq 60b 66b, lc 23a, rc 23a, replace 33b, reuse 34c, subst 42a, and substitution 41.

Page 71: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 68

Comment 2.1.46. After changing the body, we remove the quantifier of x1 and return.

68a 〈simplifyUniversal::change end game 68a〉≡ (67d 68b)

term ∗ ret = rc()→fields[1]→reuse();〈simplify update pointers 51a〉return true;

Uses rc 23a and reuse 34c.

Comment 2.1.47. We first check whether the LHS of → has the form t3 ∧ t4. If so, we seek tofind an equation instantiating the first quantified variable and replace it with ⊤. (This is againdone using replaceEq.) Then we make the necessary term substitutions and return.

68b 〈simplifyUniversal::general case 68b〉≡ (67a)

if (t1→isFunc2Args(iAnd) ≡ false) return false;term ∗ p = t1→replaceEq(var); if (p ≡ NULL) return false;

substitution bind(p→lc()→rc()→cname, p→fields[1]);body→subst(bind);p→freememory();

〈simplifyUniversal::change end game 68a〉Uses iAnd 183, isFunc2Args 24e, lc 23a, rc 23a, replaceEq 66a, subst 42a, and substitution 41.

Page 72: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 69

Comment 2.1.48. Example execution of simplifyUniversal.

Query: (pi \x2.(pi \x1.(pi \x3.((implies ((== x1) t1)) ((&& x1) x1)))))

Time = 1 Answer: (pi \x2.(pi \x3.((&& t1) t1)))

Query: (pi \x3.(pi \x1.(pi \x2.((implies ((&& ((== True) x2))

((&& ((== x1) True)) ((&& x1) x2)))) t1))))

Time = 1 Answer: (pi \x3.(pi \x2.((implies ((&& ((== True) x2))

((&& True) ((&& True) x2)))) t1)))

Time = 2 Answer: (pi \x3.((implies ((&& True) ((&& True) (&& True True)))) t1))

Time = 3 Answer: (pi \x3.((implies ((&& True) ((&& True) True))) t1))

Time = 4 Answer: (pi \x3.((implies ((&& True) True)) t1))

Time = 5 Answer: (pi \x3.((implies True) t1))

Time = 6 Answer: (pi \x3.t1)

Comment 2.1.49. This next function implements the rules

it = t if t is rigid.

(is t) = i(s t) if t is rigid.

69 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 67a 71b ⊲

bool term::simplifyModalTerms(term ∗ parent, uint id) if (isModal())

if (¬isRigid()) return false;term ∗ ret = fields[0]→reuse();〈simplify update pointers 51a〉return true;

if (isApp() ∧ lc()→isModal() ∧ lc()→fields[0]→isF())

if (¬rc()→isRigid()) return false;term ∗ ret = new_term(MODAL);ret→modality = lc()→modality;term ∗ temp = new_term(APP);temp→insert(lc()→fields[0]→reuse());temp→insert(rc()→reuse());ret→insert(temp);〈simplify update pointers 51a〉return true;

return false;

Defines:

simplifyModalTerms, used in chunks 77 and 103.Uses insert 23a, isApp 22f, isF 22f, isModal 22f, lc 23a, new_term 31b, rc 23a, and reuse 34c.

Page 73: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 70

2.1.2 Computing and Reducing Candidate Redexes

Comment 2.1.50. We now describe the function reduce that dynamically computes the candi-date redexes inside a term (in the leftmost outermost order) and tries to reduce them.

Definition 2.1.51. A redex of a term t is an occurrence of a subterm of t that is α-equivalent toan instance of the head of a statement.

Comment 2.1.52. Informally, given a term t, every term s represented by a subtree of the syntaxtree representing t, with the exception of the variable directly following a λ, is a subterm of t. Thepath expression leading from the root of the syntax tree representing t to the root of the syntaxtree representing s is called the occurrence of s. For exact formal definitions of these concepts, see[Llo03, pp. 46].

Comment 2.1.53. There is an easy way to count the number of subterms in a term t. A token iseither a left bracket ’(’, a variable, a constant, or an expression of the form λx for some variable x.The number of subterms in a term t is simply the number of tokens in (the string representation)of t. For example, the term ((f (1, (2, 3), 4)) λx.(g x)) has 13 subterms.

Comment 2.1.54. There are obviously many subterms. For redex testing, it is important thatwe rule out as many of these as posible up front. The following result is a start.

Let t be a term. A subterm r of t is probably not a redex if any one of the following is true:

1. r is a variable;

2. r = λx.t for some variable x and term t;

3. r = D t1 . . . tn, n ≥ 0, where D is a data constructor of arity m ≥ n, and each ti is a term;

4. r = (t1, . . . , tn) for some n ≥ 0.

70a 〈cannot possibly be a redex 70a〉≡ (72)

if (isAString()) return false;if (tag ≡ V ∨ tag ≡ D) return false;if (tag ≡ ABS ∨ tag ≡ PROD ∨ isData()) goto not_a_redex;

Uses isAString 25a and isData 70b 71b.

Comment 2.1.55. This function checks whether the current term has the form D t1 . . . tn, n ≥ 1,where D is a data constructor of arity m ≥ n and each ti is a term.

70b 〈term-schema::function declarations 22f〉+≡ (103) ⊳ 37b 71c ⊲

bool isData();

Defines:isData, used in chunk 70a.

Comment 2.1.56. When we see a term t = D t1 . . . tn, n ≥ 1, where D is a data constructor ofarity n and each ti is a term, we can immediately deduce that any prefix of t cannot be a redex.The variable is_data is used to store this information.

70c 〈term-schema parts 22a〉+≡ (103) ⊳ 36b 87b ⊲

bool is_data;

70d 〈term-schema initializations 22c〉+≡ (23b) ⊳ 36c

is_data = false;

70e 〈term-schema clone parts 22d〉+≡ (32b) ⊳ 36d

ret→is_data = is_data;

Page 74: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 71

Comment 2.1.57. We can probably sometimes recycle t->is_data here, but decided to alwaysuse the safe false value instead.

71a 〈term-schema replace parts 22e〉+≡ (33b) ⊳ 37a 89c ⊲

is_data = false;

Comment 2.1.58. If the current term is a data term, then the left subterm of the current termis also a data term.

71b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 69 71d ⊲

bool term::isData() if (tag 6= APP) return false;if (is_data) fields[0]→is_data = true; return true; if (spineTip()→isD())

is_data = true; fields[0]→is_data = true; return true; return false;

Defines:isData, used in chunk 70a.

Uses isD 22f and spineTip 24d.

Comment 2.1.59. We now describe the reduce function. We compute the subterms one by onein the left-to-right, outermost to innermost order. For each subterm, we first determine whetherit can possibly be a candidate redex. If not, we proceed to the next subterm. Otherwise, weattempt to match and reduce it using try_match_n_reduce. If this is successful, we return true.Otherwise, we proceed to the next subterm. The parameter tried records the total number ofcandidate redexes actually tried by this function. All the other parameters are needed only bytry_match_n_reduce.

71c 〈term-schema::function declarations 22f〉+≡ (103) ⊳ 70b

#define Standard 0

#define LastResort 1

#define SwitchModalities 2

bool reduce(vector<int> mpath, term ∗ parent, uint cid,term ∗ root, int & tried, bool usetp);

bool reduce(vector<int> mpath, term ∗ parent, uint cid,term ∗ root, int & tried, int strategy);

bool reduceRpt(int maxstep, int & stepsTaken, bool usetp);bool reduceRpt(bool usetp) int x; return reduceRpt(0, x, usetp); bool reduceRpt() int x; return reduceRpt(0, x, false);

Defines:reduce, used in chunks 71d, 73a, 76a, 77, 83a, and 85.reduceRpt, used in chunks 77, 140a, 145a, 148c, and 149a.

71d 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 71b 72 ⊲

bool term::reduce(vector<int> mpath, term ∗ parent, uint cid,term ∗ root, int & tried, bool usetp)

if (reduce(mpath,parent,cid,root,tried,Standard)) return true;if (usetp)

if (reduce(mpath,parent,cid,root,tried,SwitchModalities))return true;

return reduce(mpath,parent,cid,root,tried,LastResort);

Uses reduce 71c 72.

Page 75: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 72

72 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 71d 73a ⊲

bool term::reduce(vector<int> mpath, term ∗ parent, uint cid,term ∗ root, int & tried, int strategy)

〈cannot possibly be a redex 70a〉

tried++;// if (try_disruptive(mpath, this, parent, cid, root, tried))// return true;if (try_match_n_reduce(mpath,this,parent,cid,root,tried,strategy))

return true;

not_a_redex:if (tag ≡ ABS)

return fields[1]→reduce(mpath,this,1, root, tried,strategy);if (tag ≡ MODAL)

mpath.push_back(modality);return fields[0]→reduce(mpath,this,0,root, tried,strategy);

if (tag ≡ APP)

if (lc()→reduce(mpath, this,0, root, tried, strategy))return true;

return rc()→reduce(mpath,this,1, root, tried, strategy);if (tag ≡ PROD)

uint dimension = fields.size();for (uint i=0; i6=dimension; i++)

if (fields[i]→reduce(mpath, this, i, root, tried, strategy))return true;

return false;if (tag ≡ F) return false;return false;

Defines:reduce, used in chunks 71d, 73a, 76a, 77, 83a, and 85.

Uses lc 23a, rc 23a, and try_match_n_reduce 74.

Page 76: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 73

Comment 2.1.60. It is easy to add code to calculate the occurrence of each subterm if thisinformation is desired.

Comment 2.1.61. The function reduceRpt reduces an expression repeatedly until no furtherreduction is possible. The return value is true if the term is modified in the process; false otherwise.

73a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 72 86a ⊲

bool term::reduceRpt(int maxstep, int & stepsTaken, bool usetp) int tried = 0; vector<int> modalPath;bool reduced = true; bool rewritten = false;int starttime = ltime; char next;while (reduced)

reduced = reduce(modalPath, NULL, 0, this, tried, usetp);if (reduced) rewritten = true;if (tag ≡ D) break;if ((maxstep > 0) ∧ (ltime - starttime) > maxstep) break;if (interrupted) break;if (stepByStep)

setSelector(STDOUT); print(); ioprintln();setSelector(SILENT);cin ≫ next;

stepsTaken = ltime - starttime;return rewritten;

Defines:reduceRpt, used in chunks 77, 140a, 145a, 148c, and 149a.

Uses ioprintln, reduce 71c 72, and setSelector.

Comment 2.1.62. The function reduce uses the following function to try and match and reducea candidate redex. The function try_match_n_reduce works as follows. Given a candidate redex,we first examine whether it can be simplified using the internal simplification routines of Escher.If so, we are done and can return. Otherwise, we try to pattern match (using redex_match) thecandidate redex with the head of suitable statements in the program. If the head of a statementh = b is found to match with candidate using some term substitution θ, then we construct bθand replace candidate with bθ. Depending on whether candidate has a parent, we either onlyneed to redirect a pointer or we need to replace in place.

73b 〈terms.cc::local functions 25c〉+≡ (105a) ⊳ 32a

#include "global.h"

#include "pattern-match.h"

#include "tableaux.h"

// static int nestingdepth = 0;〈try match::helper functions 79c〉〈try match 74〉〈try disruptive 85〉

Page 77: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 74

74 〈try match 74〉≡ (73b)

bool do_local_search = true;bool try_match_n_reduce(vector<int> mpath, term ∗ candidate,

term ∗ parent,uint cid, term ∗ root,int & tried, int strategy)

vector<substitution> theta; ÷∗ this cannot be made global because ofeager statements ∗÷

〈debug matching 1 84b〉〈try match::different simplifications 77〉〈try match::try cached statements first 75a〉

int bm_size = statements.size();for (int j=0; j<bm_size; j++)

〈try match::find special cases where no matching is required 78b〉theta.clear();term ∗ head = statements[j].stmt→lc()→rc();term ∗ body = statements[j].stmt→rc();〈debug matching 2 84c〉if (redex_match(head, candidate, theta))

〈try match::prove modalities-switching theorem 80〉〈try match::side conditions on types 82b〉〈try match::eager statements 83a〉ltime++;〈try match::unimportant things 83b〉term ∗ temp = body→clone();temp→subst(theta);〈try match::reduce temp to simplest form 76a〉〈try match::put reduct in place 75b〉〈try match::output answer 83d〉return true;

〈debug matching 4 84e〉

return false;

Defines:

try_match_n_reduce, used in chunk 72.Uses lc 23a, rc 23a, redex_match 91a 91b 92a, subst 42a, and substitution 41.

Page 78: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 75

Comment 2.1.63. Certain (sub)computations are cached in the vector cachedStatementsduringrun-time. We try out these cached statements first in simplifying a redex. The pattern matchingoperation used in this special case is just identity checking. Maybe we need to check for α-equivalence in general.

75a 〈try match::try cached statements first 75a〉≡ (74)

if (strategy ≡ Standard) int cs_size = cachedStatements.size();for (int j=0; j6=cs_size; j++)

term ∗ head = cachedStatements[j].stmt→lc()→rc();term ∗ body = cachedStatements[j].stmt→rc();theta.clear(); // vector<substitution> theta;if (candidate→equal(head) ∨ redex_match(head, candidate, theta))

// cerr << "Using cached computation." << endl;term ∗ temp = body→clone();if (theta.size()) temp→subst(theta); ltime++; cltime++;〈try match::put reduct in place 75b〉return true;

Uses equal 26a, lc 23a, rc 23a, redex_match 91a 91b 92a, subst 42a, and substitution 41.

Comment 2.1.64. This is how we put the reduct (temp) in place of the redex (candidate). Wefirst need to update the label of temp. Then depending on whether candidate has a parent, weeither change some pointers or do an in-place replacement.

75b 〈try match::put reduct in place 75b〉≡ (74 75a)

if (parent) parent→fields[cid] = temp; candidate→freememory(); else candidate→replace(temp); temp→freememory();

Uses replace 33b.

Page 79: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 76

Comment 2.1.65. In the proposed leftmost outermost reduction scheme, we need to find theleftmost outermost redex in t∗ immediately after rewriting a subterm r in t with s to obtaint∗ = t[s/r]. Given the cost of labelVariables, finding the next redex is an expensive operationif t∗ is big. We are much better off doing localised surgeries on terms. After one rewriting step,instead of looking for the next redex in t∗, we will try to reduce s as much as possible beforejumping out to consider reducing t∗. This simple change in the redex selection order speeds thingsup tremendously, at no cost to correctness. We will have problems with list/set comprehensionthough, in particular infinite lists and infinite sets.

76a 〈try match::reduce temp to simplest form 76a〉≡ (74)

#ifdef ESCHER

if (¬outermost ∧ do_local_search ∧ ¬lastresort ∧ temp→tag 6= D) do_local_search = false;term ∗ temp3 = NULL; // term * temp2 = NULL;if (optimise) // temp2 = head->clone(); temp2->subst(theta);

// temp2->unshare(NULL, 1);temp3 = temp→clone();

nestingdepth++;int time_old = ltime;temp→unshare(NULL, 1); // we should not need to do this operationbool reduced = true;while (reduced)

reduced = temp→reduce(mpath,NULL,0, temp,tried);if (temp→tag ≡ D) break;

nestingdepth−−;if (optimise) 〈try match::cache computation 76b〉 do_local_search = true;

#endif

Uses reduce 71c 72 and subst 42a.

76b 〈try match::cache computation 76b〉≡ (76a)

if (ltime - time_old > 30 ∧ head→isApp() ∧cacheFuncs.find(head→lc()→cname) 6= cacheFuncs.end())

// int osel = getSelector(); setSelector(STDERR);// temp3->print(); ioprint(" = "); temp->print();// ioprint("; – "); ioprint(ltime-time_old); ioprintln();// ioprintln(); setSelector(osel);statementType st;st.stmt = newT2Args(F, iEqual);st.stmt→initT2Args(temp3, temp→clone());temp3→labelStaticBoundVars(); // temp->labelStaticBoundVars();st.stmt→collectSharedVars();cachedStatements.push_back(st);

else temp3→freememory();Uses collectSharedVars 86a, getSelector, iEqual 183, initT2Args 25d, ioprint, ioprintln, isApp 22f,

labelStaticBoundVars 37c, lc 23a, newT2Args 25c, and setSelector.

Page 80: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 77

Comment 2.1.66. The different simplification routines described in 2.1.1 are used here. Wecheck the form of candidate before attempting to apply suitable routines.

77 〈try match::different simplifications 77〉≡ (74)

int msg = -5;if (candidate→isFunc2Args())

int f = candidate→spineTip()→cname;if (f ≡ iEqual)

if (candidate→simplifyEquality(parent, cid)) msg = 1; 〈simpl output 78a〉

else if (f ≡ iAnd) if (candidate→simplifyConjunction())

msg = 2; 〈simpl output 78a〉 if (candidate→simplifyConjunction2(parent, cid))

msg = 3; 〈simpl output 78a〉 if (candidate→simplifyInequalities(parent, cid))

msg = 4; 〈simpl output 78a〉

if (candidate→simplifyArithmetic(parent, cid)) msg = 5; 〈simpl output 78a〉

if (candidate→isApp())

if (candidate→lc()→isF(iTpHelp)) ÷∗ We first reduce the right child using equational reasoning.

If this goes all the way to True or False, we can simplifythe whole thing without calling the theorem prover. ∗÷

candidate→rc()→reduceRpt(false);

if (candidate→rc()→isD()) term ∗ temp = candidate→rc()→clone();candidate→replace(temp);

else candidate→simplifyWithTP2();〈simpl output 78a〉

if (candidate→simplifyExistential(parent, cid))

msg = 6; 〈simpl output 78a〉

if (candidate→simplifyUniversal(parent, cid)) msg = 7; 〈simpl output 78a〉

if (candidate→betaReduction(parent, cid)) msg = 8; 〈simpl output 78a〉

if (candidate→simplifyMath(parent, cid)) msg = 9; 〈simpl output 78a〉

if (candidate→simplifyModalTerms(parent, cid))

msg = 10; 〈simpl output 78a〉

Uses betaReduction 58, iAnd 183, iEqual 183, isApp 22f, isD 22f, isF 22f, isFunc2Args 24e, iTpHelp 183, lc 23a,rc 23a, reduce 71c 72, reduceRpt 71c 73a, replace 33b, simplifyArithmetic 54, simplifyConjunction 59,simplifyConjunction2 62, simplifyEquality 50, simplifyExistential 64a, simplifyInequalities 56,simplifyMath 57, simplifyModalTerms 69, simplifyUniversal 67a, simplifyWithTP2 97, and spineTip 24d.

Page 81: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 78

Comment 2.1.67. The redex is marked out in the answer. We do not print the term before thesimplification; that would be too messy though.

78a 〈simpl output 78a〉≡ (77)

ltime++;if (verbose) int osel = getSelector(); setSelector(STDOUT);

ioprint("Time = "); ioprintln(ltime);switch (msg) case 1: ioprint(eqsimpl); break;case 2: ioprint(andsimpl); break;case 3: ioprint(and2simpl); break;case 4: ioprint(ineqsimpl); break;case 5: ioprint(arsimpl); break;case 6: ioprint(exsimpl); break;case 7: ioprint(uvsimpl); break;case 8: ioprint(betasimpl); break;case 9: ioprint(mathsimpl); break;case 10: ioprint(modalsimpl); break;candidate→redex = true;ioprint("Answer: "); root→print(); ioprint("\n\n");candidate→redex = false; setSelector(osel);

return true;

Uses getSelector, ioprint, ioprintln, and setSelector.

Comment 2.1.68. In the last-resort mode, we only try input equations that are marked forlast-resort use. This should be the first special-case test.

78b 〈try match::find special cases where no matching is required 78b〉≡ (74) 78c ⊲

if (statements[j].lastresort ∧ strategy 6= LastResort) continue;if (strategy ≡ LastResort ∧ ¬statements[j].lastresort) continue;

Comment 2.1.69. We do not want to waste energy pattern matching if we can tell up front thata term and the head of a statement is not going to match. The following proposition provides asimple check for this.

Proposition 2.1.70. Let r = (f r1 . . . rn), n ≥ 0, be a term where is f a function symbol and leth = (g t1 . . . rm), m ≥ 0, be the head of a statement. If n 6= m or f 6= g, then r cannot be a redex.

Proof. In both cases there is no θ such that hθ = r.

78c 〈try match::find special cases where no matching is required 78b〉+≡ (74) ⊳ 78b 79a ⊲

if (statements[j].anchor > 0) candidate→spineTip();if ((candidate→spinelength -1) 6= statements[j].numargs) continue;if (candidate→spinetip→cname 6= statements[j].anchor) continue;

Uses spineTip 24d.

Page 82: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 79

Comment 2.1.71. If the modal path to a subterm does not match the modal context of an inputequation, then it cannot be a redex.

79a 〈try match::find special cases where no matching is required 78b〉+≡ (74) ⊳ 78c 79b ⊲

uint msize = statements[j].modalContext.size();if (msize ∧ strategy 6= SwitchModalities)

if (msize 6= mpath.size() + queryModalContext.size()) continue;bool equal = true;uint i1 = 0;for (i1=0; i16=queryModalContext.size(); i1++)

if (queryModalContext[i1] 6= statements[j].modalContext[i1]) equal = false; break;

for (uint myi=0; myi6=mpath.size(); myi++)

if (mpath[myi] 6= statements[j].modalContext[i1+myi]) equal = false; break;

if (¬equal) continue;

Uses equal 26a.

Comment 2.1.72. The preceding comment is true unless we are in the SwitchModalitiesmode,where we will attempt to pattern match if the modal path to a subterm is a permutation of themodal context of an input equation. We do this because we might be able to prove that the desiredinput equation with the right modal context follows from the given input equation.

79b 〈try match::find special cases where no matching is required 78b〉+≡ (74) ⊳ 79a

if (strategy ≡ SwitchModalities) if (¬isPermutation(queryModalContext,mpath,statements[j].modalContext))

continue;

79c 〈try match::helper functions 79c〉≡ (73b)

static bool isPermutation(vector<int> &x1, vector<int> &x2, vector<int> y) if ((x1.size() + x2.size()) 6= y.size()) return false;bool found = false; uint i=0, j=0;for (i=0; i6=x1.size(); i++)

found = false;for (j=0; j6=y.size(); j++)

if (y[j] ≡ -5) continue;if (y[j] ≡ x1[i]) y[j] = -5; found = true; break;

if (found ≡ false) return false;

for (i=0; i6=x2.size(); i++)

found = false;for (j=0; j6=y.size(); j++)

if (y[j] ≡ -5) continue;if (y[j] ≡ x2[i]) y[j] = -5; found = true; break;

if (found ≡ false) return false;

return true;

Page 83: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 80

Comment 2.1.73.

80 〈try match::prove modalities-switching theorem 80〉≡ (74)

if (strategy ≡ SwitchModalities) // check that we need to prove a theorem to begin with

〈switch modalities::set up conjecture 81〉

// set up premises neededvector<pair<term ∗,bool> > tlist;for (uint i=0; i6=formulas.size(); i++)

term ∗ temp = formulas[i].fml→clone()→normalise();pair<term ∗,bool> ent(temp, formulas[i].globalass);tlist.push_back(ent);setSelector(STDERR);ioprint("premise: "); tlist[tlist.size()-1].first→print();ioprintln(); setSelector(SILENT);

// call theorem proverterm ∗ goal = new_term(APP);goal→insert(new_term(F, iNot)); goal→insert(goalf);pair<term ∗,bool> gent(goal→normalise(), false);tlist.push_back(gent);

Tableaux tab(tlist);TruthValue ret = tab.expand();setSelector(STDERR);ioprint("Attempted proof:\n "); tab.print();ioprint(" Answer: "); ret.print(); ioprintln();setSelector(SILENT);

if (ret.value 6= MYTRUE) return false;

Uses expand 124a, iNot 183, insert 23a, ioprint, ioprintln, new_term 31b, normalise 47a, setSelector,and Tableaux 121.

Page 84: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 81

Comment 2.1.74. Here we form a term representing the desired input equation we want to try toprove. This is done in three steps. We first set up the desired sequence of modalities in the vectormodalities to be used in the construction of the first part of our term. The second step involvesputting together the quantifiers. (Note that this is needed because the quantifiers are strippedfrom input equations during parsing. These stripped quantifiers are stored in the statements.)The final step involves putting the equation part of the desired input equation in the right place.

81 〈switch modalities::set up conjecture 81〉≡ (80)

assert(queryModalContext.size() + mpath.size() > 0);vector<int> modalities = queryModalContext;modalities.insert(modalities.end(), mpath.begin(), mpath.end());

term ∗ goalf = new_term(MODAL);goalf→modality = modalities[0];term ∗ temp = goalf;for (uint i=1; i6=modalities.size(); i++)

temp→insert(new_term(MODAL));temp→fields[0]→modality = modalities[i];temp = temp→fields[0];

〈switch modalities::set up conjecture::set up quantifiers 82a〉

if (quantifiers) temp2→insert(statements[j].stmt→clone());temp→insert(quantifiers);

else temp→insert(statements[j].stmt→clone());

setSelector(STDERR);ioprint("goalf: "); goalf→print(); ioprintln(); setSelector(SILENT);

Uses insert 23a, ioprint, ioprintln, new_term 31b, and setSelector.

Page 85: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 82

Comment 2.1.75. Here we just systematically go through the quantified variables stored instatements[j].quantifiedVars during parsing and construct the term representation of thequantifiers. The resultant term is pointed to by quantifiers, if it exists.

82a 〈switch modalities::set up conjecture::set up quantifiers 82a〉≡ (81)

term ∗ temp2 = NULL, ∗ quantifiers = NULL;if (statements[j].quantifiedVars.size())

quantifiers = new_term(APP);temp2 = quantifiers;temp2→insert(new_term(F, iPi));temp2→insert(new_term(ABS));temp2→fields[1]→insert(new_term(V, statements[j].quantifiedVars[0]));temp2 = temp2→fields[1];

for (uint i=1; i6=statements[j].quantifiedVars.size(); i++) temp2→insert(new_term(APP));temp2 = temp2→fields[1];temp2→insert(new_term(F, iPi));temp2→insert(new_term(ABS));temp2→fields[1]→insert(

new_term(V, statements[j].quantifiedVars[i]));temp2 = temp2→fields[1];

Uses insert 23a, iPi 183, and new_term 31b.

Comment 2.1.76. Some (overloaded) statements have side conditions on the type of subtermsappearing in the head. We will see how this is handled now.

82b 〈try match::side conditions on types 82b〉≡ (74)

if (statements[j].tycond) int sterm_index = statements[j].tycond→sterm;int osel = getSelector(); setSelector(STDOUT);ioprint("Checking type side condition: ");vector<pair<string, type ∗> > slns;bool res = unify(slns, stat_term_types[j][sterm_index].second,

statements[j].tycond→dtype);res ? ioprint(" True\n") : ioprint(" False\n");setSelector(osel);for (uint j=0; j6=slns.size(); j++) delete_type(slns[j].second);slns.clear();if (¬res) return false;

Uses delete_type 2c 3a, getSelector, ioprint, setSelector, and unify 13.

Page 86: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 83

Comment 2.1.77. We now look at how eager statements are handled. When we matched asubterm of the form (f t1 . . . tn) with the head of a statement that is to be evaluated eagerly, weproceed to evaluate the arguments t1 to tn first. The whole expression can only be rewritten ifnone of the tis contain a redex.

83a 〈try match::eager statements 83a〉≡ (74)

if (statements[j].eager ∧ candidate→isApp()) ÷∗ try reduce the arguments first, return

true if any one can be reduced ∗÷for (int i=candidate→spinelength-1; i6=0; i−−)

/∗ go to argument spinelength - i argument ∗/term ∗ arg = candidate;for (int j=1; j6=i; j++) arg = arg→lc();if (arg→rc()→reduce(mpath,arg,1,root, tried, false))

return true;

Uses isApp 22f, lc 23a, rc 23a, and reduce 71c 72.

Comment 2.1.78. We are done talking about important things. We now list the not-so-importantthings like reporting and debugging checks.

83b 〈try match::unimportant things 83b〉≡ (74)

〈try match::debugging code 1 84a〉〈debug matching 3 84d〉〈try match::output pattern matching information 83c〉

83c 〈try match::output pattern matching information 83c〉≡ (83b)

if (verbose) int osel = getSelector(); setSelector(STDOUT);ioprint("Time = "); ioprintln(ltime);ioprint("Matched "); head→print(); ioprintln(); // ioprint(" and ");// candidate->print();// ioprint("\nReplacing with "); body->print(); ioprint(’ ’);// printTheta(theta);

candidate→redex = true;ioprint("Query: "); root→print(); ioprintln();candidate→redex = false; setSelector(osel);

Uses getSelector, ioprint, ioprintln, printTheta 101e 102a, and setSelector.

83d 〈try match::output answer 83d〉≡ (74)

if (verbose) int osel = getSelector(); setSelector(STDOUT);ioprint("Answer: "); root→print(); ioprint("\n\n"); setSelector(osel);

Uses getSelector, ioprint, and setSelector.

Page 87: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 84

Comment 2.1.79. This is a simple check to make sure the candidate redex and the instantiatedhead are really α-equivalent.

84a 〈try match::debugging code 1 84a〉≡ (83b)

#ifdef MAIN_DEBUG1

term ∗ head1 = head→clone();head1→subst(theta);head1→applySubst();assert(head1→equal(candidate));

#endif

Uses equal 26a and subst 42a.

Comment 2.1.80. These code allows us to track what is going on during matching.

84b 〈debug matching 1 84b〉≡ (74)

if (verbose ≡ 3) setSelector(STDOUT);ioprint("Trying to redex match "); candidate→print(); ioprintln();

Uses ioprint, ioprintln, and setSelector.

84c 〈debug matching 2 84c〉≡ (74)

if (verbose ≡ 3) ioprint("\tand "); head→print(); ioprint(" ... "); Uses ioprint.

84d 〈debug matching 3 84d〉≡ (83b)

if (verbose ≡ 3) ioprint("\t[succeed]\n");Uses ioprint.

84e 〈debug matching 4 84e〉≡ (74)

if (verbose ≡ 3) ioprint("\t[failed]\n");Uses ioprint.

Page 88: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 85

Comment 2.1.81. We now look at disruptive operations.

85 〈try disruptive 85〉≡ (73b)

bool try_disruptive(vector<int> mpath, term ∗ candidate,term ∗ parent, uint cid, term ∗ root,int & tried)

int osel = getSelector(); setSelector(SILENT);if (candidate→isFunc2Args(iAssign))

term ∗ arg1 = candidate→lc()→rc();// reduce arg2ioprint("simplifying ");candidate→rc()→print(); ioprintln();int tried2 = 0;bool reduced = true;while (reduced)

// candidate->rc()->labelVariables(changed2);reduced = candidate→rc()→reduce(mpath,NULL,

0, root, tried2, false);if (candidate→rc()→tag ≡ D) break;

;ioprint("result = "); candidate→rc()→print(); ioprintln();// update statementfor (uint i=0; i6=statements.size(); i++)

if (statements[i].anchor ≡ arg1→cname) assert(statements[i].persistent);statements[i].stmt→rc()→freememory();statements[i].stmt→fields[1] = candidate→rc();

statements[i].stmt→print(); ioprintln();setSelector(osel);break;

// candidate = Succeedcandidate→fields[1] = NULL;term ∗ temp = new_term(D, iSucceeded);if (parent) parent→fields[cid] = temp;

candidate→freememory(); else candidate→replace(temp); temp→freememory(); return true;

return false;

Uses getSelector, iAssign 183, ioprint, ioprintln, isFunc2Args 24e, iSucceeded 183, lc 23a, new_term 31b,rc 23a, reduce 71c 72, replace 33b, and setSelector.

Page 89: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 86

2.1.3 Pattern Matching

2.1.3.1 Preprocessing of Statements

Comment 2.1.82. During pattern matching, the name of bound variables in the head of a pro-gram statement s = t needs to be changed repeatedly. The corresponding variables in t must bechanged accordingly to preserve the original meaning of the statement. As this is a key operationthat needs to be done repeatedly very many times, an efficient algorithm is needed. The key ideahere is that we can use the same variable node to represent corresponding variables in s and t.This way, when we change a variable in s during pattern matching, all corresponding variablesin s and t get changed automatically. The term representations produced by the parser are treeswithout shared node. The following function collectSharedVars implements this kind of sharing.The procedure is simple. We first collect together all the shared variables in s and t separatelyusing shareLambdaVars. Then we redirect shared variables in t to their corresponding variablesin s using shareHeadLambdaVars.

Note that only bound variables are shared by this operation. The correctness of the functionlabelStaticBoundVars (see Comment 1.2.43) is thus not affected.

86a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 73a 86b ⊲

void term::collectSharedVars() term ∗ head = fields[0]→fields[1];term ∗ body = fields[1];vector<term ∗> headlvars;head→shareLambdaVars(headlvars, true);body→shareLambdaVars(headlvars, false);body→shareHeadLambdaVars(headlvars);

Defines:

collectSharedVars, used in chunks 76b and 103.Uses shareHeadLambdaVars 88a and shareLambdaVars 86b.

Comment 2.1.83. The input vector lvars is used to collect all the lambda variables in a term.We only need to do this for the head. The input parameter use controls this. The procedure ofshareLambdaVars is as follows: every time we see a term of the form λx.t, we use shareVar toredirect all occurrences of x in t to point to the x straight after the λ sign.

86b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 86a 87a ⊲

void term::shareLambdaVars(vector<term ∗> & lvars, bool use) if (tag ≡ ABS)

if (use) lvars.push_back(fields[0]);fields[1]→shareVar(fields[0], this, 1);fields[1]→shareLambdaVars(lvars, use);return;

int size = fields.size();for (int i=0; i6=size; i++)

fields[i]→shareLambdaVars(lvars, use);

Defines:shareLambdaVars, used in chunks 86a and 103.

Uses shareVar 87a.

Comment 2.1.84. The procedure shareVar with input variable x is only ever called within thecorrect scope t of a term λx.t. (If a subterm λx.t2 occurs inside t, we will skip that subterm.)This guarantees that all the variables that get redirected in the (tag == V) case are exactly those

Page 90: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 87

variables bound by the input variable var. The pointer parent->fields[id] points to the currentterm.

87a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 86b 88a ⊲

void term::shareVar(term ∗ var, term ∗ parent, uint id) if (tag ≡ SV ∨ tag ≡ D ∨ tag ≡ F) return;if (tag ≡ ABS) if (var→cname ≡ fields[0]→cname) return;

fields[1]→shareVar(var, this, 1); return; if (tag ≡ V)

if (cname ≡ var→cname) parent→fields[id] = var→reuse();var→parents.push_back(&parent→fields[id]);this→freememory();

return;uint size = fields.size();for (uint i=0; i6=size; i++) fields[i]→shareVar(var, this, i);

Defines:

shareVar, used in chunks 86b and 103.Uses parents 119a and reuse 34c.

Comment 2.1.85. Pointers to term schema pointers that got redirected in shareVar are storedin parents. These are then used for further redirection in shareHeadLambdaVars. At present, thisis the only place where parents is used. The parents parameter need not be initialized duringterm construction. It need not be copied during cloning. Its value also does not get affected duringreplacing.

87b 〈term-schema parts 22a〉+≡ (103) ⊳ 70c 89b ⊲

vector<term ∗∗> parents;

Uses parents 119a.

Page 91: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 88

Comment 2.1.86. The procedure for shareHeadLambdaVars is as follows. Every time we see aterm of the form λx.t, we redirect x and all occurrences of x in t pointing to it (these are recordedin parents) if x is in hlvars and then we recurse on t.

88a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 87a 88b ⊲

void term::shareHeadLambdaVars(vector<term ∗> & hlvars) if (hlvars.empty()) return;if (tag ≡ ABS)

int size = hlvars.size();for (int i=0; i6=size; i++)

if (fields[0]→cname 6= hlvars[i]→cname) continue;int psize = fields[0]→parents.size();for (int j=0; j6=psize; j++)

∗(fields[0]→parents[j]) = hlvars[i]→reuse();fields[0]→freememory();

fields[0]→freememory();fields[0] = hlvars[i]→reuse();break;

fields[1]→shareHeadLambdaVars(hlvars);return;

int size = fields.size();for (int i=0; i6=size; i++)

fields[i]→shareHeadLambdaVars(hlvars);

Defines:shareHeadLambdaVars, used in chunks 86a and 103.

Uses parents 119a and reuse 34c.

Comment 2.1.87. Free variables in program statements may also need to be changed duringpattern matching. To do away with the need for tree traversal, we employ the same trick to sharecorresponding free variables in the head and body of statements. This is done in a preprocessingstep. The following function performs this task. It works as follows. Every time we see a freevariable x in the head, we traverse the body to redirect all free occurrences of x to the one in thehead. Redirection is accomplished using shareFreeVar. We assume that labelStaticBoundVarshas been called to label the variables.

88b 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 88a 89a ⊲

void term::collectFreeVars(term ∗ bodyparent, uint id) if (tag ≡ V ∧ isFree())

bodyparent→fields[id]→shareFreeVar(this, bodyparent, id);if (tag ≡ SV ∨ tag ≡ D ∨ tag ≡ F) return;if (tag ≡ ABS) fields[1]→collectFreeVars(bodyparent, id);int size = fields.size();for (int i=0; i6=size; i++)

fields[i]→collectFreeVars(bodyparent, id);

Defines:collectFreeVars, used in chunk 103.

Uses isFree 37b and shareFreeVar 89a.

Comment 2.1.88. The return value of shareFreeVar can be used to implement the idea de-scribed in Comment 2.1.90.

Page 92: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 89

89a 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 88b 89d ⊲

bool term::shareFreeVar(term ∗ v, term ∗ parent, uint id)if (tag ≡ V ∧ isFree() ∧ cname ≡ v→cname)

freememory(); parent→fields[id] = v→reuse(); return true;

if (tag ≡ SV ∨ tag ≡ D ∨ tag ≡ F) return false;if (tag ≡ ABS) return fields[1]→shareFreeVar(v, this, 1);bool ret = false;int size = fields.size();for (int i=0; i6=size; i++)

if (fields[i]→shareFreeVar(v, this, i)) ret = true;return ret;

Defines:

shareFreeVar, used in chunks 88b and 103.Uses isFree 37b and reuse 34c.

Comment 2.1.89. The following function pre-computes all the free variables inside a subtermand put them in the vector preFVars. Pointers to terms instead of strings are used to allow us torename free variables directly without doing another traversal.

89b 〈term-schema parts 22a〉+≡ (103) ⊳ 87b

vector<term ∗> preFVars;

89c 〈term-schema replace parts 22e〉+≡ (33b) ⊳ 71a

preFVars.clear();

89d 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 89a 97 ⊲

void term::precomputeFreeVars() if (tag ≡ SV ∨ tag ≡ D ∨ tag ≡ F) return;if (tag ≡ V ∧ isFree()) preFVars.push_back(this);if (tag ≡ ABS)

fields[1]→precomputeFreeVars();preFVars = fields[1]→preFVars; return;

int size = fields.size();for (int i=0; i6=size; i++)

fields[i]→precomputeFreeVars();int size2 = fields[i]→preFVars.size();for (int j=0; j6=size2; j++)

preFVars.push_back(fields[i]→preFVars[j]);

Defines:

precomputeFreeVars, used in chunk 103.Uses isFree 37b.

Comment 2.1.90. UNIMPLEMENTED IDEA: A variable that occurs in the head but not inthe body of a statement can be flagged so that we do not have to put its substitution in θ.

2.1.3.2 Redex Determination

Definition 2.1.91. A redex of a term t is an occurrence of a subterm of t that is α-equivalent toan instance of the head of a statement.

Page 93: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 90

Fact 2.1.92. Two α-equivalent terms can only differ in the names of their bound variables. (Seealso [Llo03, pp. 71].)

Algorithm 2.1.93. To determine whether a term t is a redex with respect to the head h ofa statement, we need to determine whether there exists a term substitution θ such that hθ isα-equivalent to t. There is a simple algorithm for doing that:

θ ← while (hθ 6= t) do

o← leftmost innermost occurrence in t such that o is also in h and hθ|o 6= t|o;if hθ|o and t|o are both λ-terms then

change name of bound variable in hθ|o to that in t|o, renaming freevariables in hθ|o to avoid free-variable capture whenever necessary;

else if hθ|o is a free occurrence of a variable x in h and no free variable in t|owould be captured by the substitution x/t|o then

θ ← θ ∪ x/t|o;

else return failure;

return θ;

Comment 2.1.94. The no-free-variable-capture condition in the else if case is needed to preventmatching on statements like h = λy.x and t = λy.(g y). Without the condition, we would bind(g y) to x, but the end result of doing hx/(g y) is actually λz.(g y), which is not equal to t. (SeeDefinition 2.5.3 in [Llo03].) If this kind of matching is desired, a syntactical variable must be used.

Comment 2.1.95. Algorithm 2.1.93 does not take syntatical variables into account. Concep-tually, given an equation with syntatical variables in it, we should first initialise the syntacticalvariables to obtain a valid statement. This will then allow us to use Algorithm 2.1.93 to do patternmatching on it. In practice, we do the instantiation of syntactical variables and pattern matchingat the same time. The following modified algorithm is used.

Algorithm 2.1.96. Given terms h with syntactical variables in it and a candidate redex t, thealgorithm decides whether there exists θ such that hθ is α-equivalent to t.

θ ← while (hθ 6= t) do

o← leftmost innermost occurrence in t such that o is also in h and hθ|o 6= t|o;if hθ|o and t|o are both λ-terms then

change name of bound variable in hθ|o to that in t|o, renaming freevariables in hθ|o to avoid free-variable capture whenever necessary;

else if hθ|o is a free occurrence of a variable x in h and no free variable in t|owould be captured by the substitution x/t|o then

θ ← θ ∪ x/t|o;

else if hθ|o is a syntactical variable x in h

θ ← θ ∪ x/t|o;

else return failure;

return θ;

Provided syntactical variables only ever occur at places where a (normal) variable can appear, Ithink the algorithm is complete in the sense that if there is a way to instantiate the syntacticalvariables so that a matching can occur, Algorithm 2.1.96 will find it.

Page 94: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 91

Comment 2.1.97. Algorithm 2.1.96 renames variables as necessary when both hθ|o and t|o areλ-terms. Renaming of free variables in h is safe only because the head of a statement cannotcontain more than one occurrence of a free variable.

Comment 2.1.98. Typically, the h considered in Algorithm 2.1.96 is the head of a statementh = b. When we rename free variables in h, we also need to rename the corresponding variablesin b so as not to change the original meaning of the statement. How about the bound variables?When we change a bound variable in h, do we need to rename its corresponding variables in b?

In the presence of syntactical variables, the answer is a definite yes. Consider the statement(f λx.u) = λx.u. Given candidate redex (f λy.(g y)), we will get the incorrect answer λx.(g y) ifwe do not rename the x in the body of the statement during pattern matching. Efficient algorithmsfor doing such renaming of variables are described in Comments 2.1.82 and 2.1.87.

Comment 2.1.99. There is a simple way to realise Algorithm 2.1.96. Start with two pointerspt and ph pointing, respectively, at t and h. Denote by [pt] and [ph] the subterms of t and hpointed to by pt and ph. Move the pointers forward one step at a time to the next subterm in theleft-to-right, outermost-to-innermost order. At each time step, if [ph] 6= [pt] then:

1. if [ph] is a syntactical variable, add [ph]/[pt] to θ;

2. else if [ph] is a variable free in h and the free variable capture condition does not occur, add[ph]/[pt] to θ;

3. else if [pt] and [ph] are both lambda terms and xt and xh are the corresponding lambdavariables, then set all occurrences of xh in [ph] to xt, renaming as necessary free variablesthat get captured as a result;

4. else return failure.

91a 〈pattern-match::function declarations 91a〉≡ (105b) 99b ⊲

bool redex_match(term ∗ head, term ∗ body, vector<substitution> & theta);bool redex_match(term ∗ head, term ∗ body, vector<substitution> & theta,

vector<term ∗> bindingAbss, term ∗ orig_head);

Defines:redex_match, used in chunks 46b, 74, 75a, and 94–96.

Uses substitution 41.

91b 〈pattern-match::functions 91b〉≡ (105c) 92a ⊲

bool redex_match(term ∗ head, term ∗ body, vector<substitution> & theta) vector<term ∗> bindingAbss;return redex_match(head, body, theta, bindingAbss, head);

Defines:redex_match, used in chunks 46b, 74, 75a, and 94–96.

Uses substitution 41.

Page 95: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 92

92a 〈pattern-match::functions 91b〉+≡ (105c) ⊳ 91b 100a ⊲

bool redex_match(term ∗ head, term ∗ body, vector<substitution> & theta,vector<term ∗> bindingAbss, term ∗ orig_head)

kind head_tag = head→tag;kind term_tag = body→tag;

if (head_tag ≡ SV) 〈redex-match::case of SV 92b〉 if (head_tag ≡ V) 〈redex-match::case of V 93c〉 if (head_tag 6= term_tag) return false;

〈redex-match::case of constant 94b〉if (head_tag ≡ APP) 〈redex-match::case of APP 94c〉 if (head_tag ≡ PROD) 〈redex-match::case of PROD 95a〉 if (head_tag ≡ ABS) 〈redex-match::case of ABS 95b〉 if (head_tag ≡ MODAL) 〈redex-match::case of MODAL 96〉 assert(false); return false;

Defines:redex_match, used in chunks 46b, 74, 75a, and 94–96.

Uses substitution 41.

Comment 2.1.100. Here we consider matching on syntactical variables. A syntactical variablematches anything, if all the constraints are obeyed, that is.

92b 〈redex-match::case of SV 92b〉≡ (92a)

〈redex-match::case of SV::check constraints 93a〉substitution sub(head→cname, body);theta.push_back(sub);return true;

Uses substitution 41.

Page 96: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 93

Comment 2.1.101. The constraint /VAR/ means that the term bound to the current syntacticalvariable must be a variable. The constraint /CONST/means that the term bound to the current syn-tactical variable must be a data constructor or a function symbol. The constraint /EQUAL,x_SV/,where x_SV is another syntactical variable appearing before the current one, means that the termbound to the current syntactical variable must be equal to the term bound to x_SV.

93a 〈redex-match::case of SV::check constraints 93a〉≡ (92b)

condition ∗ constraint = head→cond;if (constraint)

int ctag = constraint→tag;

if (ctag ≡ CVAR ∧ term_tag 6= V) return false; // problematic?else if (ctag ≡ CCONST ∧ term_tag 6= D ∧ term_tag 6= F) return false;else if (ctag ≡ CEQUAL)

// if (term_tag != D && term_tag != V) return false;term ∗ bound = findBinding(constraint→svname, theta);〈error handling::get previously bound 93b〉if (body→equal(bound) ≡ false) return false;

else if (ctag ≡ CNOTEQUAL) // if (term_tag != D) return false;term ∗ bound = findBinding(constraint→svname, theta);〈error handling::get previously bound 93b〉if (body→equal(bound) ≡ true) return false;

// assert(ctag != CVAR); assert(ctag != CNOTEQUAL);

Uses equal 26a and findBinding 102b 102c.

93b 〈error handling::get previously bound 93b〉≡ (93a)

if (bound ≡ NULL) setSelector(STDERR);ioprint("The constraint EQUAL or NOTEQUAL on syntactical "

"variables is used incorrectly; it appears before "

"its argument is instantiated.\n");assert(false);

Uses ioprint and setSelector.

Comment 2.1.102. We next examine the case of variables. We do not have to do anything ifhead is identical to body. If head is a bound variable, then body must be identical to head formatching to succeed.

93c 〈redex-match::case of V 93c〉≡ (92a 100b)

if (term_tag ≡ V ∧ head→cname ≡ body→cname) return true;if (head→isFree() ≡ false) return false;

〈redex-match::case of V::check free variable capture condition 94a〉

substitution sub(head→cname, body);theta.push_back(sub);return true;

Uses isFree 37b and substitution 41.

Page 97: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 94

Comment 2.1.103. We need to check that no variable in body would be captured by the sub-stitution head/body.

94a 〈redex-match::case of V::check free variable capture condition 94a〉≡ (93c)

int captd;if (body→captured(bindingAbss, captd))

setSelector(STDERR);cerr ≪" ** Matching Failed: Free variable capture in redex-match.\n";ioprint("head = "); head→print(); ioprintln();ioprint("term = "); body→print(); ioprintln();assert(orig_head);ioprint("orig head = "); orig_head→print(); ioprintln();return false;

Uses captured 39a, ioprint, ioprintln, and setSelector.

Comment 2.1.104. We now look at the case when head is a constant.

94b 〈redex-match::case of constant 94b〉≡ (92a 100b)

if (head_tag ≡ F) return (head→cname ≡ body→cname);if (head_tag ≡ D)

if (head→isfloat 6= body→isfloat) return false;if (head→isint 6= body→isint) return false;if (head→isfloat ∧ body→isfloat) return (head→numf ≡ body→numf);else if (head→isint ∧ body→isint) return (head→numi ≡ body→numi);return (head→cname ≡ body→cname);

Comment 2.1.105. The case of applications is particularly simple. We first try to match theleft child. If successful, we match the right child.

94c 〈redex-match::case of APP 94c〉≡ (92a)

if (¬redex_match(head→lc(),body→lc(), theta, bindingAbss, orig_head))return false;

return redex_match(head→rc(), body→rc(),theta,bindingAbss,orig_head);

Uses lc 23a, rc 23a, and redex_match 91a 91b 92a.

Comment 2.1.106. These can be used to debug redex_match.

94d 〈redex-match::case of APP::debug matching 1 94d〉≡if (verbose ≡ 3)

ioprint("\n\t\tmatching "); head→lc()→print();ioprint(" and "); body→lc()→print(); ioprint(" ... ");

Uses ioprint and lc 23a.

94e 〈redex-match::case of APP::debug matching 2 94e〉≡if (verbose ≡ 3)

ioprint(" successful\n");ioprint("\t\tmatching "); head→rc()→print(); ioprint(" and ");body→rc()→print(); ioprint(" ... ");

Uses ioprint and rc 23a.

Page 98: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 95

Comment 2.1.107. We now look at the case of products. We cannot assume that the dimensionsof head and body are equal even when the type-checker says they have the same types. Why?Well, sometimes we use a function name to represent data.

95a 〈redex-match::case of PROD 95a〉≡ (92a)

uint size = head→fields.size();if (size 6= body→fields.size()) return false;

for (uint i=0; i6=size; i++)if (¬redex_match(head→fields[i],body→fields[i],theta,

bindingAbss,orig_head))return false;

return true;Uses redex_match 91a 91b 92a.

Comment 2.1.108. The last case is that of abstraction. We change the name of lambda variablesto avoid having to worry about α-equivalence later on.

95b 〈redex-match::case of ABS 95b〉≡ (92a)

if (head→fields[0]→tag ≡ SV) redex_match(head→fields[0],body→fields[0],theta,bindingAbss,orig_head);

else 〈redex-match::case of ABS::change variable name 95c〉 bindingAbss.push_back(head);return redex_match(head→fields[1],body→fields[1],theta,bindingAbss,orig_head);

Uses redex_match 91a 91b 92a.

Comment 2.1.109. If necessary, we need to change the name of the bound variable in head sothat it is the same as the bound variable in body. In so doing, we may inadvertently capture afree variable inside head. (This is an extremely rare scenario. I have never seen it happen in anynon-simulated computation.) Another variable renaming is necessary in this case.

Thanks to the preprocessing we did (see Comments 2.1.82 and 2.1.87), we need to set only thename of one variable in each case.

95c 〈redex-match::case of ABS::change variable name 95c〉≡ (95b)

int term_var = body→fields[0]→cname;if (head→fields[0]→cname 6= term_var)

int size = head→preFVars.size();for (int i=0; i6=size; i++)

if (term_var ≡ head→preFVars[i]→cname) 〈redex-match::write a small warning message 95d〉head→preFVars[i]→cname = newPVar();

head→fields[0]→cname = term_var;

Uses newPVar 185b.

95d 〈redex-match::write a small warning message 95d〉≡ (95c)

int osel = getSelector(); setSelector(STDOUT);ioprint(" ** Trouble. Variable "); head→preFVars[i]→print();ioprint(" captured after lambda variable renaming.\n");ioprint("head = "); head→print(); ioprintln();ioprint("term = "); body→print(); ioprintln();setSelector(osel);

Uses captured 39a, getSelector, ioprint, ioprintln, and setSelector.

Page 99: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.1. TERM REWRITING 96

96 〈redex-match::case of MODAL 96〉≡ (92a)

if (head→modality 6= body→modality) return false;return redex_match(head→fields[0],body→fields[0],theta,bindingAbss,orig_head);

Uses redex_match 91a 91b 92a.

Page 100: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.2. INTERACTION WITH THE THEOREM PROVER 97

Comment 2.1.110. We now look at some instructive test cases for the procedure. Evaluatingthe following program

(f \y.x) = True

: (f \y.(g y y))

will result in

** Matching Error: Free variable capture in redex-match.

Final Answer: (f \y.((g y) y)).

To force a matching here, we can use the statement (f \y.x_SV) = True instead. Evaluating thesame query will then result in True.

Evaluating the program

(f \x.(g x y)) = (g y y)

: (f \y.(g y y))

will result in

** Trouble. Variable y captured after lambda variable renaming.

** Matching Failed: Free variable capture in redex-match.

Final Answer: (f \y.((g y) y)).

The lambda variable x in the head of the statement is successfully renamed at first. Matchingfails when we subsequently try to match the free variable y in the head of the statement with thebound variable y in the query. The reader should convince herself that matching should indeedfail in this case.

Evaluating this next prggram

(f \x.(g y x)) = (g y y)

: (f \y.(g z y))

will produce the answer ((g z) z).

2.2 Interaction with the Theorem Prover

2.2.1 Rank k Computations

97 〈term-schema::function definitions 24d〉+≡ (105a) ⊳ 89d

#ifndef ESCHER

#include "tableaux.h"

bool term::simplifyWithTP() return false; // disable this function to begin withif (isVar() ∨ isD() ∨ isF()) return false;if (isAbs()) return fields[1]→simplifyWithTP();if (isProd())

for (uint i=0; i6=fields.size(); i++)if (fields[i]→simplifyWithTP()) return true;

return false;/∗ if done previously, return ∗/if (isApp() ∧ lc()→isApp() ∧ lc()→lc()→isD(iTpTag))

return false;/∗ we don’t do terms with free variables inside ∗/

Page 101: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.2. INTERACTION WITH THE THEOREM PROVER 98

getFreeVars();if (myfreevars.size() > 0)

assert(isApp() ∨ isModal());if (isApp())

if (lc()→simplifyWithTP()) return true;return rc()→simplifyWithTP();

else if (isModal()) assert(false); /∗ check that the type is Bool ∗/pair<type ∗, vector<term_type> > res = mywellTyped(this);if (res.first→getTag() 6= "Bool")

assert(isApp() ∨ isModal());if (isApp())

if (lc()→simplifyWithTP()) return true;return rc()→simplifyWithTP();

else if (isModal()) assert(false); if (¬(isApp() ∧ lc()→isF(iTpHelp)))

assert(isApp() ∨ isModal());if (isApp())

if (lc()→simplifyWithTP()) return true;return rc()→simplifyWithTP();

else if (isModal()) assert(false); 〈simplifyWithTP::call theorem prover 99a〉

bool term::simplifyWithTP2() 〈simplifyWithTP::call theorem prover 99a〉

#endif

Defines:simplifyWithTP, used in chunk 103.simplifyWithTP2, used in chunks 77 and 103.

Uses getFreeVars 36a, isAbs 22f, isApp 22f, isD 22f, isF 22f, isModal 22f, isProd 22f, isVar 22f, iTpHelp 183,iTpTag 183, lc 23a, rc 23a, and term_type 10a.

Page 102: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.2. INTERACTION WITH THE THEOREM PROVER 99

99a 〈simplifyWithTP::call theorem prover 99a〉≡ (97)

term ∗ theorem = rc();vector<pair<term ∗,bool> > tlist;// insert goal formula into tableauterm ∗ goal = new_term(APP);goal→insert(new_term(F, iNot)); goal→insert(theorem→clone());pair<term ∗,bool> gent(goal→normalise(), false);tlist.push_back(gent);

// prove theorembackchain = true;Tableaux tab(tlist);TruthValue ret = tab.expand();if (verbose)

ioprint("Attempted Proof:\n "); tab.print();ioprint(" Answer : "); ret.print(); ioprintln(" ;");

tab.freememory();if (ret.value ≡ MYTRUE)

lc()→freememory(); rc()→freememory(); fields.clear();tag = D; cname = iTrue;return true;

else term ∗ temp = new_term(APP);temp→insert(new_term(APP));temp→insert(theorem→clone());temp→lc()→insert(new_term(D, iTpTag));temp→lc()→insert(new_term(D, iDontKnow));replace(temp);temp→freememory();

return false;

Uses expand 124a, iDontKnow 183, iNot 183, insert 23a, ioprint, ioprintln, iTpTag 183, iTrue 183, lc 23a,new_term 31b, normalise 47a, rc 23a, replace 33b, and Tableaux 121.

2.2.2 Free Variable Instantiation

Comment 2.2.1. In the theorem proving mode, we sometimes need to replace universally quan-tified variables with so-called free variables. These needs to be instantiated at some stage. Oneimportant mechanism used to instantiate them is to find matching between subformulas that wouldallow us to close off branches. The matching algorithm is very similar to redex_match, exceptthat we concentrate only on free variables and do not do α-conversion.

99b 〈pattern-match::function declarations 91a〉+≡ (105b) ⊳ 91a 101e ⊲

bool freevar_match(term ∗ fml1, term ∗ fml2, vector<substitution> & theta);bool freevar_match(term ∗ fml1, term ∗ fml2, vector<substitution> & theta,

vector<term ∗> bindingAbss);

Uses freevar_match 100a and substitution 41.

Page 103: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.2. INTERACTION WITH THE THEOREM PROVER 100

100a 〈pattern-match::functions 91b〉+≡ (105c) ⊳ 92a 100b ⊲

bool freevar_match(term ∗ fml1, term ∗ fml2,vector<substitution> & theta)

vector<term ∗> bindingAbss;return freevar_match(fml1, fml2, theta, bindingAbss);

Defines:freevar_match, used in chunks 99–101 and 129c.

Uses substitution 41.

100b 〈pattern-match::functions 91b〉+≡ (105c) ⊳ 100a 102a ⊲

bool freevar_match(term ∗ head, term ∗ body, vector<substitution> & theta,vector<term ∗> bindingAbss)

kind head_tag = head→tag;kind term_tag = body→tag;assert(head_tag 6= SV /∗&& head_tag != MODAL ∗/);if (head_tag ≡ V)

if (isUVar(head→cname) ∧ ¬body→occursFree(head→cname)) term ∗ orig_head = NULL;head→validfree = true; head→free = true;〈redex-match::case of V 93c〉

if (term_tag ≡ V)

if (isUVar(body→cname) ∧ ¬head→occursFree(body→cname)) term ∗ headtemp = head;head = body;body = headtemp;term ∗ orig_head = NULL;head→validfree = true; head→free = true;〈redex-match::case of V 93c〉

if (head_tag 6= term_tag) return false;if (head_tag ≡ V ∧ term_tag ≡ V)

if (head→cname 6= body→cname) return false;return true;

〈redex-match::case of constant 94b〉if (head_tag ≡ APP) 〈freevar-match::case of APP 101b〉 if (head_tag ≡ PROD) 〈freevar-match::case of PROD 101d〉 if (head_tag ≡ ABS) 〈freevar-match::case of ABS 101a〉 if (head_tag ≡ MODAL) 〈freevar-match::case of MODAL 101c〉 assert(false); return false;

Uses freevar_match 100a, isUVar 44b, occursFree 38a, and substitution 41.

Page 104: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.2. INTERACTION WITH THE THEOREM PROVER 101

Comment 2.2.2. One significant case where redex_match and freevar_match differs is in thecase of abstractions. In redex_match, we will rename the name of bound variables in the head

if necessary. (We are looking for a substitution that achieves α-equivalence). This is not done infreevar_match. We may or may not want to do this in the future.

101a 〈freevar-match::case of ABS 101a〉≡ (100b)

if (head→fields[0]→cname 6= body→fields[0]→cname) return false;bindingAbss.push_back(head);return freevar_match(head→fields[1], body→fields[1], theta, bindingAbss);

Uses freevar_match 100a.

Comment 2.2.3. These cases are identical for redex_match and freevar_match.

101b 〈freevar-match::case of APP 101b〉≡ (100b)

if (¬freevar_match(head→lc(),body→lc(),theta,bindingAbss)) return false;return freevar_match(head→rc(), body→rc(), theta, bindingAbss);

Uses freevar_match 100a, lc 23a, and rc 23a.

101c 〈freevar-match::case of MODAL 101c〉≡ (100b)

if (head→modality 6= body→modality) return false;return freevar_match(head→fields[0], body→fields[0],theta, bindingAbss);

Uses freevar_match 100a.

101d 〈freevar-match::case of PROD 101d〉≡ (100b)

uint size = head→fields.size();if (size 6= body→fields.size()) return false;

for (uint i=0; i6=size; i++) setSelector(SILENT);ioprint("unifying "); head→fields[i]→print();ioprint(" and "); body→fields[i]→print(); ioprint(" ");if (¬freevar_match(head→fields[i],body→fields[i],theta,bindingAbss))

setSelector(SILENT); ioprint(" false\n"); setSelector(SILENT);return false;

setSelector(SILENT); ioprint(" true\n"); setSelector(SILENT);

return true;

Uses freevar_match 100a, ioprint, and setSelector.

2.2.2.1 Manipulating Substitutions

101e 〈pattern-match::function declarations 91a〉+≡ (105b) ⊳ 99b 102b ⊲

void printTheta(vector<substitution> & theta);

Defines:printTheta, used in chunks 83c and 129e.

Uses substitution 41.

Page 105: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.2. INTERACTION WITH THE THEOREM PROVER 102

102a 〈pattern-match::functions 91b〉+≡ (105c) ⊳ 100b 102c ⊲

void printTheta(vector<substitution> & theta) if (getSelector() ≡ SILENT) return;ioprint(’’);int size = theta.size();if (size ≡ 0) ioprint("\n"); return; for (int i=0; i6=size-1; i++)

ioprint(’(’);if (theta[i].first ≥ 5000)

ioprint(pve); ioprint(theta[i].first-5000); else ioprint(getString(theta[i].first));ioprint(’/’);theta[i].second→print(); ioprint("), ");

ioprint(’(’);if (theta[size-1].first ≥ 5000)

ioprint(pve); ioprint(theta[size-1].first-5000); else ioprint(getString(theta[size-1].first));ioprint(’/’);theta[size-1].second→print(); ioprint(’)’);ioprint("\n");

Defines:printTheta, used in chunks 83c and 129e.

Uses getSelector, ioprint, and substitution 41.

102b 〈pattern-match::function declarations 91a〉+≡ (105b) ⊳ 101e

term ∗ findBinding(int svname, vector<substitution> & theta);

Defines:findBinding, used in chunk 93a.

Uses substitution 41.

102c 〈pattern-match::functions 91b〉+≡ (105c) ⊳ 102a

term ∗ findBinding(int svname, vector<substitution> & theta) int size = theta.size();for (int i=0; i6=size; i++)

if (theta[i].first ≡ svname) return theta[i].second;return NULL;

Defines:findBinding, used in chunk 93a.

Uses substitution 41.

Page 106: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.2. INTERACTION WITH THE THEOREM PROVER 103

2.2.2.2 File Organization

103 〈terms.h 103〉≡#ifndef _TERM_H

#define _TERM_H

#include <iostream>#include <string>#include <vector>#include <set>#include <utility>#include <cassert>#include <stdlib.h>#include <ctype.h>#include <math.h>#include "io.h"

using namespace std;

#define uint unsigned int

struct term;

〈term-schema::definitions 30a〉〈term-schema::supporting types 30b〉typedef vector<int> occurrence;〈term-schema::type defs 21c〉

extern const string & getString(int code);extern bool isUVar(term ∗ t);extern bool isUVar(int cn);

struct term 〈term-schema parts 22a〉〈term-schema::constructors 23b〉〈term-schema::function declarations 22f〉term ∗ clone();void freememory();void replace(term ∗ t);bool equal(term ∗ t);bool isFunc2Args();bool isFunc2Args(int f);term ∗ spineTip();term ∗ spineTip(int & x);bool isChar();bool isString();bool isAString();bool isRigid();void print();void printVertical(uint level);vector<int> & getFreeVars();void unmarkValidfree();void labelStaticBoundVars();void labelBound(int x);bool occursFree(int var);

Page 107: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.2. INTERACTION WITH THE THEOREM PROVER 104

bool occursFreeNaive(int var);bool occursFreeNaive(int var, vector<int> boundv);bool captured(vector<term ∗> & bvars, int & captd);void rename(int var1, int var2);void renameLambdaVar(int var1, int var2);void subst(vector<substitution> & subs);void subst(substitution & sub);void subst2(vector<substitution> & subs, vector<term ∗> bv,

term ∗∗ pointer);// bool containsQuantifiers();bool isNegation();bool isNegationOf(term ∗ t2);bool isDiamond();void stripNegations();bool containsFreeVariable();void collectFreeVariables(set<int> & fvars);term ∗ normalise();term ∗ normalise1();term ∗ normalise2();void collectFunctionNames(set<int> & x);bool termReplace(term ∗ s, term ∗ r,

term ∗ parent,int id);bool matchReplace(term ∗ s, term ∗ r,

term ∗ parent,int id);bool simplifyEquality(term ∗ parent, uint id);bool simplifyArithmetic(term ∗ parent, uint id);bool simplifyInequalities(term ∗ parent, uint id);bool simplifyMath(term ∗ parent, uint id);bool betaReduction(term ∗ parent, uint id);bool simplifyConjunction();bool simplifyConjunction2(term ∗ parent, uint id);bool simplifyExistential(term ∗ parent, uint id);bool simplifyUniversal(term ∗ parent, uint id);bool simplifyModalTerms(term ∗ parent, uint id);term ∗ findEq(term ∗ root);bool isEq(term ∗ root);bool isEq(int var);term ∗ replaceEq(int var);void collectSharedVars();void shareLambdaVars(vector<term ∗> & lvars, bool use);void shareVar(term ∗ var, term ∗ parent, uint id);void shareHeadLambdaVars(vector<term ∗> & hlvars);void collectFreeVars(term ∗ bodyparent, uint id);void collectLambdaVars(multiset<int> & ret);bool shareFreeVar(term ∗ v, term ∗ parent, uint id);void precomputeFreeVars();bool simplifyWithTP();bool simplifyWithTP2();

;

〈term-schema::memory management 31a〉extern int newPVar();extern int newUVar();extern term ∗ newT2Args(kind k, const string & f);

Page 108: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

2.2. INTERACTION WITH THE THEOREM PROVER 105

extern term ∗ newT2Args(kind k, int f);

#endif

Uses betaReduction 58, captured 39a, collectFreeVariables 44b, collectFreeVars 88b, collectSharedVars 86a,containsFreeVariable 44b, equal 26a, findEq 60a, getFreeVars 36a, isAString 25a, isChar 25a,isDiamond 45b, isEq 60b 66b, isFunc2Args 24e, isNegation 45a, isNegationOf 45c, isString 25a,isUVar 44b, labelBound 37d, labelStaticBoundVars 37c, matchReplace 46b, newPVar 185b, newT2Args 25c,newUVar 185b, normalise 47a, normalise1 47b, normalise2 48c, occursFree 38a, occursFreeNaive 38b,precomputeFreeVars 89d, printVertical 29a, rename 39c, renameLambdaVar 40, replace 33b, replaceEq 66a,shareFreeVar 89a, shareHeadLambdaVars 88a, shareLambdaVars 86b, shareVar 87a, simplifyArithmetic 54,simplifyConjunction 59, simplifyConjunction2 62, simplifyEquality 50, simplifyExistential 64a,simplifyInequalities 56, simplifyMath 57, simplifyModalTerms 69, simplifyUniversal 67a,simplifyWithTP 97, simplifyWithTP2 97, spineTip 24d, stripNegations 45d, subst 42a, subst2 42b,substitution 41, and termReplace 46a.

105a 〈terms.cc 105a〉≡#include "terms.h"

〈terms.cc::local functions 25c〉〈term-schema::function definitions 24d〉

105b 〈pattern-match.h 105b〉≡#ifndef _PATTERN_MATCH_H

#define _PATTERN_MATCH_H

#include "terms.h"

〈pattern-match::function declarations 91a〉

#endif

105c 〈pattern-match.cc 105c〉≡#include <iostream>#include <utility>#include <vector>#include "io.h"

#include "pattern-match.h"

#include "global.h"

〈pattern-match::functions 91b〉

Page 109: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

Chapter 3

Theorem Proving in Bach

3.1 Introduction

Comment 3.1.1. The theorem prover we describe here is based on one originally developedby Josh Cole. His program was written in Java and I have translated that into C++ and addedmodifications and extensive documentation.

3.2 Labels and Worlds

Comment 3.2.1. A label is essentially a formula that is true in a World. This class stores anidentifier, the derivation of the label and whether it depends on the initial goal, as well as a historyof rules that have been tried or applied on this label.

106 〈class Label 106〉≡ (113b)

#include <set>#include "global.h"

class Label public:

term ∗ fml;string derivation;int id;bool depOnGoal;Label(term ∗ t, string derivation, bool dependsOnGoal);void freememory();Label ∗ clone();Label ∗ clone2();void print();bool isTried(const string & rule, int worldname);bool isApplied(const string & rule, int worldname);int appliedCount(const string & rule, int worldname);bool anyApplied();void setApplied(const string & rule, int worldname);void setTried(const string & rule, int worldname);void setTried(const string & rule);bool isTried(const string & rule);void resetRuleHistory();string getDrv() return derivation; bool globalAssumption;

106

Page 110: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.2. LABELS AND WORLDS 107

private:set<string> ∗ rulesTried;multiset<string> ∗ rulesApplied;string key(const string & rule, int worldname)

string ret = rule + "@" + numtostring(worldname);return ret;

;extern void resetId();

Defines:Label, used in chunks 107–113, 117, 121, 125–27, and 130–43.

Uses anyApplied 109b, appliedCount 108c, isApplied 108c, isTried 109a, resetId 109c, setApplied 109b,and setTried 109b.

Comment 3.2.2. Here are the constructor, freememory and cloning functions.

107a 〈label.cc body 107a〉≡ (113c) 107b ⊲

Label::Label(term ∗ t, string d, bool depends) fml = t; id = nextId++; derivation = d; depOnGoal = depends;rulesTried = new set<string>; rulesTried→clear();rulesApplied = new multiset<string>; rulesApplied→clear();globalAssumption = false;

Uses Label 106.

107b 〈label.cc body 107a〉+≡ (113c) ⊳ 107a 108a ⊲

void Label::freememory() if (fml) fml→freememory();delete rulesTried; delete rulesApplied;

Uses Label 106.

Page 111: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.2. LABELS AND WORLDS 108

Comment 3.2.3. We decrement nextId at the end to offset the increment done in the constructor.

108a 〈label.cc body 107a〉+≡ (113c) ⊳ 107b 108b ⊲

Label ∗ Label::clone() Label ∗ ret = new Label(fml→clone(), derivation, depOnGoal);// ret->rulesTried = rulesTried;copy(rulesTried→begin(), rulesTried→end(),

inserter(∗(ret→rulesTried), ret→rulesTried→begin())) ;

// ret->rulesApplied = rulesApplied;copy(rulesApplied→begin(), rulesApplied→end(),

inserter(∗(ret→rulesApplied), ret→rulesApplied→begin())) ;

ret→id = id; nextId−−;return ret;

Label ∗ Label::clone2()

string drv = "D," + derivation;Label ∗ ret = new Label(fml→clone(), drv, depOnGoal);// ret->rulesTried = rulesTried;copy(rulesTried→begin(), rulesTried→end(),

inserter(∗(ret→rulesTried), ret→rulesTried→begin())) ;

// ret->rulesApplied = rulesApplied;copy(rulesApplied→begin(), rulesApplied→end(),

inserter(∗(ret→rulesApplied), ret→rulesApplied→begin())) ;

return ret;

Uses Label 106.

Comment 3.2.4. The print function given here serves as a substitute for toString in the Javacode.

108b 〈label.cc body 107a〉+≡ (113c) ⊳ 108a 108c ⊲

void Label::print() ioprint(id); ioprint(".");ioprint("["); ioprint(derivation); ioprint("] "); fml→print();

Uses ioprint and Label 106.

Comment 3.2.5. This function checks whether a rule has been applied to the current label ina certain world.

108c 〈label.cc body 107a〉+≡ (113c) ⊳ 108b 109a ⊲

bool Label::isApplied(const string & rule, int worldname) if (rulesApplied→find(key(rule,worldname)) 6= rulesApplied→end())

return true;return false;

int Label::appliedCount(const string & rule, int worldname)

return rulesApplied→count(key(rule,worldname));

Defines:appliedCount, used in chunks 106, 134a, and 136a.isApplied, used in chunks 106 and 143.

Uses Label 106.

Page 112: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.2. LABELS AND WORLDS 109

Comment 3.2.6. This function checks whether a rule has been tried on the current label in acertain world. The pair (label,world) defines a unique node in the tableaux.

109a 〈label.cc body 107a〉+≡ (113c) ⊳ 108c 109b ⊲

bool Label::isTried(const string & rule, int worldname) return (rulesTried→find(key(rule,worldname)) 6= rulesTried→end());

bool Label::isTried(const string & rule)

return (rulesTried→find(rule) 6= rulesTried→end());

Defines:isTried, used in chunks 106, 111a, 130b, 136a, 139d, and 141a.

Uses Label 106.

Comment 3.2.7. These functions allow us to keep a record of what rules has been applied tothe current label.

109b 〈label.cc body 107a〉+≡ (113c) ⊳ 109a 109c ⊲

bool Label::anyApplied() return ¬rulesApplied→empty();

void Label::setApplied(const string & rule, int worldname) rulesApplied→insert(key(rule,worldname));

void Label::setTried(const string & rule, int worldname)

rulesTried→insert(key(rule,worldname));void Label::setTried(const string & rule) rulesTried→insert(rule);

void Label::resetRuleHistory() rulesTried→clear(); rulesApplied→clear();

Defines:anyApplied, used in chunk 106.setApplied, used in chunks 106, 130c, 134a, 136a, 139c, and 141–43.setTried, used in chunks 106, 130b, 136a, 139c, and 141a.

Uses insert 23a and Label 106.

109c 〈label.cc body 107a〉+≡ (113c) ⊳ 109b 110b ⊲

void resetId() nextId = 0; Defines:

resetId, used in chunks 106 and 122a.

Page 113: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.2. LABELS AND WORLDS 110

Comment 3.2.8. We have seen how a label is implemented. Now we look at the management ofa collection of labels.

110a 〈class LabelSet 110a〉≡ (113b)

#include <list>struct LabelSet

LabelSet() labels.clear(); void freememory();void print();void add(Label & label);int size() return labels.size(); Label & get(int i);void remove(int i);bool isClosed();bool contains(term ∗ term);bool subset(LabelSet ∗ labelset);list<Label> labels;

;Defines:

LabelSet, used in chunks 110–12.Uses add 110c, contains 112a, isClosed 111e 118a 122c, Label 106, and subset 112b.

110b 〈label.cc body 107a〉+≡ (113c) ⊳ 109c 110c ⊲

void LabelSet::freememory() list<Label>::iterator p = labels.begin();while (p 6= labels.end()) p→freememory(); p++;

void LabelSet::print()

list<Label>::iterator p = labels.begin();while (p 6= labels.end()) p→print(); ioprint("\n"); p++;

Uses ioprint, Label 106, and LabelSet 110a.

Comment 3.2.9. We do not store the formulas in the exact order in which they are inserted intothe world. Certain formulas (the R-labels, see Comment 3.2.10) are kept at the end of the labelset. (See Comment 3.2.10.) We insert the formula right before such formulas, relabelling formulasas we go along. It is assumed that the R-labels occur as a block at the end of the current labelset.

110c 〈label.cc body 107a〉+≡ (113c) ⊳ 110b 111c ⊲

〈RLabel 111a〉

void LabelSet::add(Label & label) 〈LabelSet::add::special cases 111b〉

list<Label>::iterator p = labels.end(); p−−;while (RLabel(∗p)) label.id = p→id; p→id++; p−−; p++;labels.insert(p, 1, label);

Defines:add, used in chunks 110a, 113a, 117, 130c, 134a, 136a, 139–43, and 183.

Uses insert 23a, Label 106, LabelSet 110a, and RLabel 111a.

Page 114: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.2. LABELS AND WORLDS 111

Comment 3.2.10. Copies of formulas newly created by the universal rule are always pushed tothe end of the label set. These formulas are characterised by a derivation string that ends inthe character ’R’ and they have not been processed by the universal rule again.

111a 〈RLabel 111a〉≡ (110c)

static char lastch(string x) assert(x.size()); return x[x.size()-1];

static bool RLabel(Label & l) return (lastch(l.derivation) ≡ ’R’ ∧ ¬l.isTried(universalRuleId));

Defines:lastch, used in chunk 111b.RLabel, used in chunk 110c.

Uses isTried 109a and Label 106.

Comment 3.2.11. If the set is currently empty or if the label to be inserted is a copy of anexisting one created by the universal rule then we just insert the label at the end of the set.

111b 〈LabelSet::add::special cases 111b〉≡ (110c)

if (labels.empty() ∨ lastch(label.derivation) ≡ ’R’) labels.push_back(label); return;

Uses lastch 111a.

Comment 3.2.12. We have to work a bit to get and remove Labels from the list. An ArrayListwas used in the Java code and the following two operations are straightforward library calls.

111c 〈label.cc body 107a〉+≡ (113c) ⊳ 110c 111e ⊲

Label & LabelSet::get(int i) 〈list::goto the right pointer 111d〉 return ∗p;

void LabelSet::remove(int i) 〈list::goto the right pointer 111d〉 p→fml→freememory(); labels.erase(p);

Uses Label 106 and LabelSet 110a.

111d 〈list::goto the right pointer 111d〉≡ (111c)

assert(0 ≤ i ∧ i < (int)labels.size());list<Label>::iterator p = labels.begin();int j=0; while (j6=i) p++; j++;

Uses Label 106.

Comment 3.2.13. The following function checks whether the term ⊥ is inside the current set oflabels. This function is actually a special case of the function contains and can be removed.

111e 〈label.cc body 107a〉+≡ (113c) ⊳ 111c 112a ⊲

bool LabelSet::isClosed() list<Label>::iterator p = labels.begin();while (p 6= labels.end())

if (p→fml→isD(iFalse)) return true; p++; return false;

Defines:isClosed, used in chunks 110a, 113a, 116a, 120b, 121, and 123–28.

Uses iFalse 183, isD 22f, Label 106, and LabelSet 110a.

Page 115: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.2. LABELS AND WORLDS 112

Comment 3.2.14. The following function checks whether a given term appears inside the currentset of labels. In the Java program, there is code for handling interruption of the thread executingthis function. ∗ ∗ ∗ Do we need something like that here?

112a 〈label.cc body 107a〉+≡ (113c) ⊳ 111e 112b ⊲

bool LabelSet::contains(term ∗ term) list<Label>::iterator p = labels.begin();while (p 6= labels.end())

if (p→fml→equal(term)) return true; p++; return false;

Defines:contains, used in chunks 110a, 112b, 136a, and 140a.

Uses equal 26a, Label 106, and LabelSet 110a.

Comment 3.2.15. This function checks whether one label set is a subset of another.

112b 〈label.cc body 107a〉+≡ (113c) ⊳ 112a 113a ⊲

bool LabelSet::subset(LabelSet ∗ labelset) list<Label>::iterator p = labels.begin();while (p 6= labels.end())

if (¬labelset→contains(p→fml)) return false;p++;

return true;

Defines:subset, used in chunks 110a, 118b, 178, and 181.

Uses contains 112a, Label 106, and LabelSet 110a.

Comment 3.2.16. We now look at worlds. A world is a node in a tableau. Each world has aname attribute and contains a LabelSet that represents the formulas that hold in the world.

112c 〈class World 112c〉≡ (113b)

struct World int name;LabelSet ∗ labels;World(int n) name = n; labels = new LabelSet; World ∗ clone();void freememory() labels→freememory(); void print(int n);

;

Uses LabelSet 110a.

Page 116: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.2. LABELS AND WORLDS 113

113a 〈label.cc body 107a〉+≡ (113c) ⊳ 112b

World ∗ World::clone() World ∗ ret = new World(name); assert(ret→labels→size() ≡ 0);

list<Label>::iterator p = labels→labels.begin();while (p 6= labels→labels.end())

ret→labels→add(∗(p→clone())); p++;

return ret;void World::print(int n)

for (int i=0; i6=2∗n; i++) ioprint(" ");ioprint("World "); ioprint(name);assert(labels 6= NULL);if (labels→isClosed()) ioprintln(" (closed) ");else ioprintln(" (open) ");labels→print(); ioprint("\n");

Uses add 110c, ioprint, ioprintln, isClosed 111e 118a 122c, and Label 106.

Comment 3.2.17. All the classes we have described so far are located in the label module.

113b 〈label.h 113b〉≡#ifndef _LABEL_H_

#define _LABEL_H_

#include <string>#include "io.h"

#include "terms.h"

#include "types.h"

class World;〈class Label 106〉〈class LabelSet 110a〉〈class World 112c〉

#endif

113c 〈label.cc 113c〉≡#include "label.h"

static int nextId = 0;〈label.cc body 107a〉

Page 117: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.3. ACCESSIBILITY RELATION 114

3.3 Accessibility Relation

Comment 3.3.1. An accessibility relation is a binary relation on Worlds (nodes) in a Tableau.In a multi-modal logic multiple labelled relations are defined. This class defines an accessibilityrelation as a set of Links where each link is a triple (index, w1, w2); w1 and w2 are the namesof the related worlds.

114 〈accessibilityRelation.h 114〉≡#ifndef _ACCESSIBILITYRELATION_H_

#define _ACCESSIBILITYRELATION_H_

#include <string>#include <vector>#include <set>#include <cassert>#include "io.h"

struct Link int index;int w1; int w2;Link(int ind, int x1, int x2) index = ind; w1 = x1; w2 = x2; Link ∗ clone() Link ∗ ret = new Link(index, w1, w2); return ret; void print()

ioprint("("); ioprint(index); ioprint(","); ioprint(w1);ioprint(","); ioprint(w2); ioprint(")");

;

class AccessibilityRelation // R and indices should be private

public: vector<Link ∗> R;// set<string> indices;

public:AccessibilityRelation() R.clear(); /∗ indices.clear(); ∗/ ∼AccessibilityRelation()

for (uint i=0; i6=R.size(); i++) delete R[i];void addLink(int index, int w1, int w2)

/∗ indices.insert(index); ∗/Link ∗ l = new Link(index,w1,w2); R.push_back(l);

int size() return R.size(); Link ∗ getLink(int i) assert(i ≥ 0 ∧ i < size()); return R[i]; bool isLinked(int index, int w1, int w2);// bool isLinked(int i1, int i2, int w1, int w2);// vector<string> getIndices();// AccessibilityRelation * clone();void print();

;

#endif

Defines:AccessibilityRelation, used in chunks 115 and 116.Link, used in chunks 118 and 119.

Uses getIndices 115b, insert 23a, ioprint, and isLinked 115a.

Page 118: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.4. TABLEAU 115

115a 〈accessibilityRelation.cc 115a〉≡ 115b ⊲

#include "accessibilityRelation.h"

bool AccessibilityRelation::isLinked(int index, int w1, int w2) for (uint i=0; i6=R.size(); i++)

if (R[i]→index ≡ index ∧ R[i]→w1 ≡ w1 ∧ R[i]→w2 ≡ w2)return true;

return false;

// bool AccessibilityRelation::isLinked(int i1, int i2, int w1, int w2) // for (uint i=0; i!=R.size(); i++) // if (R[i]->index==i1 && R[i]->w1==w1 && isLinked(i2,R[i]->w2,w2))// return true;// // return false;//

Defines:isLinked, used in chunks 114 and 142.

Uses AccessibilityRelation 114.

115b 〈accessibilityRelation.cc 115a〉+≡ ⊳ 115a

÷∗ vector<string> AccessibilityRelation::getIndices() vector<string> ret;set<string>::iterator p = indices.begin();while (p 6= indices.end()) ret.push_back(∗p); p++; return ret;

∗÷

// AccessibilityRelation * AccessibilityRelation::clone() // AccessibilityRelation * ret = new AccessibilityRelation();// for (uint i=0; i!=R.size(); i++)// ret->R.push_back(R[i]);

// set<string>::iterator p = indices.begin();// while (p != indices.end()) ret->indices.insert(*p); p++; // return ret;//

void AccessibilityRelation::print() ioprint("Acc Relation: ");for (uint i=0; i6=R.size(); i++) R[i]→print(); ioprint(" "); ioprint("");

Defines:getIndices, used in chunk 114.

Uses AccessibilityRelation 114, insert 23a, and ioprint.

3.4 Tableau

Comment 3.4.1. A tableau is a set of Worlds with an AccessibilityRelation linking pairs ofworlds. Each tableau in a Tableaux is identified by its name attribute. Worlds are also known asnodes of the tableau. The stucture defined by the accessibility relation is processed by the someof the methods of this class to compute child, parent and ancestor sets of formulas.

Page 119: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.4. TABLEAU 116

116a 〈tableau.h 116a〉≡#ifndef _TABLEAU_H_

#define _TABLEAU_H_

#include <vector>#include <string>#include "io.h"

#include "label.h"

#include "accessibilityRelation.h"

class Tableau public:

Tableau(int n);Tableau(int n, term ∗ t);Tableau(int n, vector<pair<term ∗,bool> > l);Tableau ∗ clone();void print(int n);int addWorld();World & getWorld(int i)

assert(i ≥0 ∧ i < (int)worlds.size()); return worlds[i]; void setName(int n) name = n; int size() return worlds.size(); void addLink(int index, int w1, int w2);bool isClosed();bool isLoop(World & world);vector<World ∗> children(int index, int worldname);// vector<World *> parents(int index, int worldname);vector<World ∗> ancestors(World & world);void freeResources();void freememory()

for (uint i=0; i6=worlds.size(); i++) worlds[i].freememory(); AccessibilityRelation ∗ accRel;int name;vector<World> worlds;

private:int nextWorld;bool isFree;string toStringFinal;bool isClosedFinal;

;

#endif

Defines:getWorld, used in chunks 125b, 127a, 128b, 132, 138b, 139c, and 141b.Tableau, used in chunks 116–22, 124b, 126c, and 141–43.

Uses AccessibilityRelation 114, addWorld 117c, ancestors 119b, children 118c, freeResources 120c,isClosed 111e 118a 122c, isFree 37b, isLoop 118b, and parents 119a.

116b 〈tableau.cc 116b〉≡ 117a ⊲

#include "tableau.h"

Tableau::Tableau(int n) 〈Tableau init 116c〉 Uses Tableau 116a.

Page 120: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.4. TABLEAU 117

116c 〈Tableau init 116c〉≡ (116 117)

name = n; nextWorld = 0; isFree = false; toStringFinal = "";isClosedFinal = false; accRel = new AccessibilityRelation();

Uses AccessibilityRelation 114 and isFree 37b.

Comment 3.4.2. Here is another constructor for Tableau. It takes in another term to add as alabel to the first world, usually the negation of the theorem to be proved.

117a 〈tableau.cc 116b〉+≡ ⊳ 116b 117b ⊲

Tableau::Tableau(int n, term ∗ t) 〈Tableau init 116c〉int w = addWorld(); Label l(t,"AG",true); worlds[w].labels→add(l);

Uses add 110c, addWorld 117c, Label 106, and Tableau 116a.

Comment 3.4.3. There is also a constructor that takes in a vector of terms, which contains theaxioms followed by the negation of the formula to be proved.

117b 〈tableau.cc 116b〉+≡ ⊳ 117a 117c ⊲

Tableau::Tableau(int n, vector<pair<term ∗,bool> > l) assert(l.size() > 0);〈Tableau init 116c〉int w = addWorld();for (uint i=0; i6=l.size()-1; i++)

Label lb(l[i].first, "A", false);if (l[i].second) lb.globalAssumption = true;worlds[w].labels→add(lb);

Label lb(l[l.size()-1].first, "A", true); worlds[w].labels→add(lb);

Uses add 110c, addWorld 117c, Label 106, and Tableau 116a.

Comment 3.4.4. This adds a new (empty) world to the tableau.

117c 〈tableau.cc 116b〉+≡ ⊳ 117b 117d ⊲

int Tableau::addWorld() World world(nextWorld);worlds.push_back(world);nextWorld++;return (nextWorld - 1);

Defines:

addWorld, used in chunks 116, 117, and 141b.Uses Tableau 116a.

Comment 3.4.5. This function adds an indexed link between two worlds to the accessibilityrelation of this tableau.

117d 〈tableau.cc 116b〉+≡ ⊳ 117c 118a ⊲

void Tableau::addLink(int index, int w1, int w2) accRel→addLink(index, w1, w2);

Uses Tableau 116a.

Page 121: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.4. TABLEAU 118

Comment 3.4.6. The next function tests whether this tableau is closed. A tableau is closed if itcontains some world containing the label False (bottom). Note there is a slight complication heredue to the fact that a tableau can have had its resources freed, in which case no computation ispossible and a cached value is read instead. See the method freeResources.

118a 〈tableau.cc 116b〉+≡ ⊳ 117d 118b ⊲

bool Tableau::isClosed() if (isFree) return isClosedFinal; // cached valuefor (int i=0; i6=size(); i++)

if (worlds[i].labels→isClosed()) return true;return false;

Defines:isClosed, used in chunks 110a, 113a, 116a, 120b, 121, and 123–28.

Uses isFree 37b and Tableau 116a.

Comment 3.4.7. This next function tests whether a world in this tableau is a loop node. Aworld is a loop node iff all of its labels are contained in some ancestral node (world).

118b 〈tableau.cc 116b〉+≡ ⊳ 118a 118c ⊲

bool Tableau::isLoop(World & world) vector<World ∗> ancs = ancestors(world);for (uint i=0; i6=ancs.size(); i++)

if (world.labels→subset(ancs[i]→labels)) return true;return false;

Defines:isLoop, used in chunks 116a, 120b, and 126c.

Uses ancestors 119b, subset 112b, and Tableau 116a.

Comment 3.4.8. The next function computes the children of a world in this tableau defined bythe relation with a given index.

118c 〈tableau.cc 116b〉+≡ ⊳ 118b 119a ⊲

vector<World ∗> Tableau::children(int index, int worldname) vector<World ∗> list;for (int i=0; i6=accRel→size(); i++)

Link ∗ link = accRel→getLink(i);if (link→index ≡ index ∧ link→w1 ≡ worldname)

list.push_back(&(worlds[link→w2]));return list;

Defines:

children, used in chunks 116a and 143.Uses Link 114 and Tableau 116a.

Page 122: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.4. TABLEAU 119

Comment 3.4.9. This function computes the parents of a world in this tableau defined by therelation with a given index.

119a 〈tableau.cc 116b〉+≡ ⊳ 118c 119b ⊲

÷∗vector<World ∗> Tableau::parents(int index, int worldname)

vector<World ∗> list;for (int i=0; i6=accRel→size(); i++)

Link & link = accRel→getLink(i);if (link→index ≡ index ∧ link→w2 ≡ worldname)

list.push_back(&(worlds[link→w1]));return list;

∗÷

Defines:parents, used in chunks 87, 88a, and 116a.

Uses Link 114 and Tableau 116a.

Comment 3.4.10. This function computes the ancestors of a world in this tableau defined bythe relation, regardless of index.

119b 〈tableau.cc 116b〉+≡ ⊳ 119a 120a ⊲

vector<World ∗> Tableau::ancestors(World & world) vector<World ∗> list;for (int i=0; i6=accRel→size(); i++)

Link ∗ link = accRel→getLink(i);

if (link→w2 ≡ world.name) list.push_back(&(worlds[link→w1]));vector<World ∗> Ancestors = ancestors(worlds[link→w1]);for (uint j=0; j6=Ancestors.size(); j++)

list.push_back(Ancestors[j]);

return list;

Defines:ancestors, used in chunks 116a and 118b.

Uses Link 114 and Tableau 116a.

Page 123: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 120

Comment 3.4.11. We now look at how a Tableau is cloned.

120a 〈tableau.cc 116b〉+≡ ⊳ 119b 120b ⊲

Tableau ∗ Tableau::clone() Tableau ∗ ret = new Tableau(name); ret→worlds.clear();for (int i=0; i6=size(); i++)

ret→worlds.push_back(∗(worlds[i].clone())); ret→accRel→R.clear();for (unsigned int i=0; i6=accRel→R.size(); i++)

ret→accRel→R.push_back(accRel→R[i]→clone());

// ret->accRel->indices.clear();// set<string>::iterator p = accRel->indices.begin();// while (p != accRel->indices.end()) // ret->accRel->indices.insert(*p); p++; ret→nextWorld = nextWorld;return ret;

Uses insert 23a and Tableau 116a.

Comment 3.4.12. Here is how we print a tableau.

120b 〈tableau.cc 116b〉+≡ ⊳ 120a 120c ⊲

void Tableau::print(int n) if (isFree) ioprint(toStringFinal);for (int i=0; i6=2∗n; i++) ioprint(" ");ioprint("Tableau "); ioprint(name); ioprint(" ("); ioprint(size());ioprint(" worlds, "); accRel→print(); ioprint(") ");isClosed() ? ioprintln("(closed)") : ioprintln("(open)");for (int i=0; i6=size(); i++)

worlds[i].print(n+1); // ioprint(" ");if (isLoop(worlds[i])) ioprint("(loops)\n");

Uses ioprint, ioprintln, isClosed 111e 118a 122c, isFree 37b, isLoop 118b, and Tableau 116a.

Comment 3.4.13. The next function frees the resources used by this tableau. Once a tableauhas been processed by the expansion algorithm its resources can be (mostly) freed. This involvesreleasing the memory storing its nodes and accessibility relation.

120c 〈tableau.cc 116b〉+≡ ⊳ 120b

void Tableau::freeResources() /∗ do nothing ∗/ Defines:

freeResources, used in chunk 116a.Uses Tableau 116a.

3.5 Tableaux

Comment 3.5.1. A tableaux is a set of Tableaus where each member is a branch of the traditionaltree structure in tableau theorem proving. Tableau theorem proving is a proof-by-refutationstyle of theorem proving, involving a systematic search for a contradiction implicit in a set offormulas. The approach here follows the one taken in [dCG02]. The tableaux-expansion algorithm

Page 124: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 121

implemented by the expand function is given on page 65 of this paper. The tableau rules are givenon pages 58 and 59.

The higher-order part comes from the Modal Higher-order Logic for Agents paper. In partic-ular, the tableau rules are summarised on page 27 of this paper.

The algorithm here is probably sound, but not complete. So claims of theoremhood given bythis algorithm can be trusted. However, a failure to establish a theorem does not entail that theinput is not a theorem, nor indeed that its negation is a theorem.

121 〈tableaux.h 121〉≡#ifndef _TABLEAUX_H_

#define _TABLEAUX_H_

#include <utility>#include <vector>#include "tableau.h"

#include "constraints-solver.h"

〈TruthValue 123b〉

class Tableaux public:

Tableaux(vector<pair<term ∗,bool> > l);void addClone(Tableau tableau);void addFormula(Label label);bool isClosed();void print();TruthValue expand();bool expandStep(int tableau);bool isClosable();// bool instantiateFreeVariables();bool applyNegationRule(Label & label, World & world);bool applyConjunctionRule(Label & label, World & world);bool applyDisjunctionRule(Label &label, int world, int tindex, bool lr);bool applyExistentialRule(Label & label, World & world);bool applyUniversalRule(Label & label, World & world);bool applyUniversalRuleSPImplies(Label & label, World & world);bool applySubstitutivityRule(Label & label, World & world);bool applyReflexiveRule(Label & label, World & world);bool applyEqReasoningRule(Label & label, World & world);bool applyDiamondRule(Label & label, int world, Tableau & tableau);bool applyNecessityRule(Label & label, int world, Tableau &tableau);bool applyClosureRule(World & world);bool applyKRule(Label & label, World & world, Tableau & tableau);void printInst() solver.printInst(); void freememory()

for (uint i=0; i6=tableaux.size();i++) tableaux[i].freememory();solver.freememory();

private:

vector<Tableau> tableaux;int nextTableau;// set<int> goalRelated;constraints_solver solver;

;

Page 125: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 122

#endif

Defines:Tableaux, used in chunks 80, 99a, 122–25, 127c, 130–35, 137b, 138b, and 140–43.

Uses addClone 122b, applyClosureRule 140b, applyConjunctionRule 131a, applyDiamondRule 141b,applyDisjunctionRule 132a, applyEqReasoningRule 140a, applyExistentialRule 134b, applyKRule 143,applyNecessityRule 142, applyNegationRule 130a, applyReflexiveRule 137b, applySubstitutivityRule 135b,applyUniversalRule 133a, applyUniversalRuleSPImplies 138b, constraints_solver 144, expand 124a,expandStep 125a, isClosable 127c, isClosed 111e 118a 122c, Label 106, printInst 150c, and Tableau 116a.

Comment 3.5.2. The tableaux constructor takes in a vector of formulas, the last of which is thenegation of the theorem to be proved. The remaining formulas are axioms on which a proof of thetheorem may depend.

122a 〈tableaux.cc 122a〉≡ 122b ⊲

#include "tableaux.h"

#include "unification.h"

Tableaux::Tableaux(vector<pair<term ∗,bool> > l) nextTableau = 0; resetId();Tableau tb(nextTableau, l);tableaux.push_back(tb);nextTableau++;// if (backchain) l[l.size()-1]->collectFunctionNames(goalRelated);

Uses resetId 109c, Tableau 116a, and Tableaux 121.

Comment 3.5.3. This function adds a clone of a tableau to this tableaux. Usually the tableauto be cloned is already a member of this tableaux and a second copy is added. This is useful whencreating another branch which is identical to an existing branch up to a certain point, after whichit diverges.

122b 〈tableaux.cc 122a〉+≡ ⊳ 122a 122c ⊲

void Tableaux::addClone(Tableau tableau) Tableau ∗ clone = tableau.clone();clone→setName(nextTableau); nextTableau++;tableaux.push_back(∗clone);

Defines:addClone, used in chunks 121 and 132c.

Uses Tableau 116a and Tableaux 121.

Comment 3.5.4. The tableaux is closed if every tableau in it is closed.

122c 〈tableaux.cc 122a〉+≡ ⊳ 122b 123a ⊲

bool Tableaux::isClosed() for (uint i=0; i6=tableaux.size(); i++)

if (¬tableaux[i].isClosed()) return false;return true;

Defines:isClosed, used in chunks 110a, 113a, 116a, 120b, 121, and 123–28.

Uses Tableaux 121.

Page 126: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 123

Comment 3.5.5. Here is how we print a tableaux.

123a 〈tableaux.cc 122a〉+≡ ⊳ 122c 124a ⊲

void Tableaux::print() int tsize = tableaux.size();ioprint("Tableaux ("); ioprint(tsize); ioprint(" tableaus) ");isClosed() ? ioprintln("(closed)") : ioprintln("(open)");for (int i=0; i6=tsize; i++) tableaux[i].print(0);

Uses ioprint, ioprintln, isClosed 111e 118a 122c, and Tableaux 121.

3.5.1 The Tableaux Expansion Algorithm

Comment 3.5.6. The tableaux expansion algorithm returns a value indicating whether the ex-pansion closes the tableaux. The algorithm

• returns MYTRUE if the tableaux is closed;

• returns MYFALSE if the tableaux is open and no quantifiers appear in the input set of terms;

• returns DONTKNOW otherwise.

We introduce this next data structure to encode the return values of the algorithm.

123b 〈TruthValue 123b〉≡ (121)

enum truthvalue MYFALSE, MYTRUE, DONTKNOW ;struct TruthValue

truthvalue value;void print() if (value ≡ MYFALSE) ioprint("False");

else if (value ≡ MYTRUE) ioprint("True");else if (value ≡ DONTKNOW) ioprint("Don’t Know");else assert(false);

;

Uses ioprint.

Page 127: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 124

Comment 3.5.7. We expand each tableau in turn until either it is closed or no more rules canbe applied to it. Since new tableaus can be inserted into tableaux by the disjunction rule, weneed to recompute the number of tableaus there is in the outermost for loop.

To ensure fairness when using free-variable tableau that allows multiple application of theuniversal rule, we need to switch to the next tableau after each application of the universal rule assuggested in [Fit90]. It is also not sufficient to have a single top-most for loop that goes througheach tableau in turn. We have to keep visiting the tableaus until no more rule can be applied toany of them. This has not been implemented.

124a 〈tableaux.cc 122a〉+≡ ⊳ 123a 125a ⊲

TruthValue Tableaux::expand() TruthValue result; int j=0;for (uint i=0; i6=tableaux.size(); i++)

while (true) if (tableaux[i].isClosed() ∨ ¬expandStep(i)) break;〈expand::debugging messages 124b〉if (isClosable())

cerr ≪ "Closable!\n";result.value = MYTRUE; return result;

if (isClosed()) result.value = MYTRUE;else result.value = DONTKNOW;return result;

Defines:

expand, used in chunks 80, 99a, and 121.Uses expandStep 125a, isClosable 127c, isClosed 111e 118a 122c, and Tableaux 121.

124b 〈expand::debugging messages 124b〉≡ (124a)

if (verbose ≡ 2) setSelector(STDOUT);ioprint("Tableau: "); ioprint(tableaux[i].name);ioprint(" Iteration: "); ioprintln(j); print();setSelector(SILENT); j++;

Uses ioprint, ioprintln, setSelector, and Tableau 116a.

Page 128: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 125

Comment 3.5.8. We now look at how a tableau can be expanded in incremental steps. This isthe core of the theorem proving algorithm.

In [dCG02], three kinds of tableaux rules are distinguished. They are the classical rules, thepropagation rules and the structural rules. The classical rules include the negation, conjunction,disjunction, existential, universal, abstraction, and substitutivity rules.

Propagation rules have the following general formulation: if there is a certain formula φ ina node having a certain pattern, then propagate a formula (either φ or some other one). Thenecessity rules are examples of propagation rules.

Structural rules, in constrast, have the following general formulation: if there is such a paternthen add some new node(s) and edge(s). The possibility rules are examples of structural rules.

These three types of tableaux rules are executed in sequence.

125a 〈tableaux.cc 122a〉+≡ ⊳ 124a 127c ⊲

bool Tableaux::expandStep(int tindex) bool appliedRule = false;〈expandStep::classical saturation step 125b〉〈expandStep::structural step 126c〉〈expandStep::propagation step 127a〉return appliedRule;

Defines:expandStep, used in chunks 121 and 124a.

Uses Tableaux 121.

Comment 3.5.9. In the classical saturation step, for each world in the tableau, we keep ap-plying the classical rules until either the world is closed or no more rules apply. We then applythe substitution and Escher rules, as a last resort. Why do we not apply these last two rulescontinually?

125b 〈expandStep::classical saturation step 125b〉≡ (125a)

int numworlds = tableaux[tindex].size();for (int j=0; j6=numworlds; j++)

World & worldj = tableaux[tindex].getWorld(j);if (worldj.labels→isClosed()) continue;

int mysize = worldj.labels→size();for (int k=0; k6=mysize; k++)

Label & label = worldj.labels→get(k);〈expandStep::apply any of the classical rules 126a〉

if (applyClosureRule(worldj)) appliedRule = true;// is applyClosureRule necessary here? incorporate that into isClosable?

if (¬appliedRule) 〈expandStep::apply last resort rules 126b〉 if (tableaux[tindex].isClosed()) return appliedRule;

Uses applyClosureRule 140b, getWorld 116a, isClosable 127c, isClosed 111e 118a 122c, and Label 106.

Comment 3.5.10. The classical rules are the negation, conjunction, disjunction, reflexive, exis-tential and universal rules. The disjunction rule constructs a new tableau out of the existing one;it is probably for this reason that we want to delay it as much as possible.

Is this really the right place for applyUniversalRuleSPImplies? It deals with modal formulasand can probably be thought of as a propagation rule.

Page 129: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 126

126a 〈expandStep::apply any of the classical rules 126a〉≡ (125b)

if (applyNegationRule(label, worldj) ∨ applyConjunctionRule(label, worldj) ∨applyReflexiveRule(label, worldj) ∨applySubstitutivityRule(label, worldj) ∨applyExistentialRule(label, worldj) ∨applyUniversalRuleSPImplies(label, worldj) ∨applyUniversalRule(label, worldj))

appliedRule = true;Uses applyConjunctionRule 131a, applyExistentialRule 134b, applyNegationRule 130a,

applyReflexiveRule 137b, applySubstitutivityRule 135b, applyUniversalRule 133a,and applyUniversalRuleSPImplies 138b.

Comment 3.5.11. We delay the disjunction rule as much as possible because it creates morework. See Comment 3.5.29 to understand why there are two cases of applyDisjunctionRule here.

126b 〈expandStep::apply last resort rules 126b〉≡ (125b)

for (int k=0; k6=mysize; k++) Label & label = worldj.labels→get(k);if (applyDisjunctionRule(label, j, tindex, false))

appliedRule = true; break; if (¬appliedRule)

for (int k=0; k6=mysize; k++) Label & label = worldj.labels→get(k);if (applyDisjunctionRule(label, j, tindex, true))

appliedRule = true; break;

Uses applyDisjunctionRule 132a and Label 106.

Comment 3.5.12. In the structural step, we apply the diamond rule – the only structural rulewe have at the moment – to every label in every world (even newly created worlds in this step) inthe tableau as long as the world is not closed and it is not a loop node.

126c 〈expandStep::structural step 126c〉≡ (125a)

Tableau & brch = tableaux[tindex];int j = 0;while (j < brch.size())

if (¬brch.worlds[j].labels→isClosed() ∧ ¬brch.isLoop(brch.worlds[j]))for (int k=0; k6=brch.worlds[j].labels→size();k++)

Label & label = brch.worlds[j].labels→get(k);if (applyDiamondRule(label, brch.worlds[j].name, brch))

appliedRule = true;

j++;if (tableaux[tindex].isClosed()) return appliedRule;

Uses applyDiamondRule 141b, isClosed 111e 118a 122c, isLoop 118b, Label 106, and Tableau 116a.

Page 130: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 127

Comment 3.5.13. The last step of the procedure is the propagation step.

127a 〈expandStep::propagation step 127a〉≡ (125a)

for (int j=0; j6=tableaux[tindex].size(); j++) World & world = tableaux[tindex].getWorld(j);if (world.labels→isClosed()) continue;for (int k=0; k6=world.labels→size(); k++)

Label & label = world.labels→get(k);〈expandStep::apply propagation rules 127b〉

Uses getWorld 116a, isClosed 111e 118a 122c, and Label 106.

127b 〈expandStep::apply propagation rules 127b〉≡ (127a)

if (applyNecessityRule(label, world.name, tableaux[tindex])) appliedRule = true;// if (applyKRule(label, world, tableaux[tindex])) appliedRule = true;

Uses applyKRule 143 and applyNecessityRule 142.

Comment 3.5.14. The function isClosable checks whether there is an instantiation of thefree variables inside a tableaux that can be used to close all the branches simultaneously. Thisalgorithm is an adaptation of the one introduced in [Gie01]. This is how it works. We go downeach branch looking for substitutions that can close the branch. All such substitutions are thenput together in a disjunction since each one of them can close the branch on its own. We thenput together the candidate substitutions so obtained from each branch in a conjunction (which iseffectively a constraint on the values the free variables can take) and then try to see whether theconstraint is satisfiable.

127c 〈tableaux.cc 122a〉+≡ ⊳ 125a 130a ⊲

#include "pattern-match.h"

〈tableaux::atomic formula 129b〉vector<term ∗> constraints;

bool Tableaux::isClosable() solver.reset(); constraints.clear(); uint branchesClosable = 0;

for (uint k=0; k6=tableaux.size(); k++) 〈isClosable::work on branch k 128a〉

if (branchesClosable < tableaux.size()) return false;

return solver.solvable();

Defines:isClosable, used in chunks 121, 124a, and 125b.

Uses solvable 145a and Tableaux 121.

Page 131: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 128

Comment 3.5.15. We now see how each branch is processed. If the branch is already closed,we are done. Otherwise, we find all possible instantiations of the free variables that can close thebranch and make a disjunction out of all of them. The disjunction is then added to the overallconstraint on the tableaux. The variable branchesClosable keeps track of how many branchesare at present locally closable.

128a 〈isClosable::work on branch k 128a〉≡ (127c)

if (tableaux[k].isClosed()) branchesClosable++; continue;

constraints.clear(); bool foundContradiction = false;〈isClosable::find possible instantiations of free variables 128b〉

if (foundContradiction) branchesClosable++;term ∗ bc = solver.makeOrConstraint(constraints);solver.addConstraint(bc);

Uses addConstraint 148b, isClosed 111e 118a 122c, and makeOrConstraint 147c.

Comment 3.5.16. We examine all pairs of the form p and (¬ q), where p and q are atomicformulas, to try and find instantiations that unifies p and q using a modified version of the matchingalgorithm. The reason we restrict p and q to atomic formulas is because the resulting substitutionwe found needs to be applied to the whole tableaux, and substitutions that result in variablecapture are not allowed. This no-variable-capture condition is automatically satisfied if we restrictourselves to atomic p’s and q’s. Also, it turns out the method is still complete in the first-ordercase. See Section 7.5 of [Fit90] for details.

128b 〈isClosable::find possible instantiations of free variables 128b〉≡ (128a)

for (int wi=0; wi6=tableaux[k].size(); wi++) World & w = tableaux[k].getWorld(wi);for (int i=w.labels→size()-1; i≥0; i−−)

term ∗ fml = w.labels→get(i).fml;bool sex1 = fml→isNegation();〈isClosable::ignore non-applicable formulas 128c〉

for (int j=w.labels→size()-1; j≥0; j−−) if (i ≡ j) continue;term ∗ fml2 = w.labels→get(j).fml;bool sex2 = fml2→isNegation();〈isClosable::ignore non-applicable formula pairs 129a〉〈isClosable::try to match the labels 129c〉

Uses getWorld 116a and isNegation 45a.

Comment 3.5.17. We can safely ignore non-atomic formulas and those that contain no freevariables. They play no part in finding suitable instantiations of the free variables.

128c 〈isClosable::ignore non-applicable formulas 128c〉≡ (128b)

if (¬fml→containsFreeVariable()) continue;if (sex1 ∧ ¬isAtomicFormula(fml→rc())) continue;if (¬sex1 ∧ ¬isAtomicFormula(fml)) continue;

Uses containsFreeVariable 44b, isAtomicFormula 129b, and rc 23a.

Page 132: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 129

Comment 3.5.18. We only look at pairs p and (¬ q) where p and q are both atomic formulasand ignore others.

129a 〈isClosable::ignore non-applicable formula pairs 129a〉≡ (128b)

if (sex1 ≡ sex2) continue;if (sex2 ∧ ¬isAtomicFormula(fml2→rc())) continue;if (¬sex2 ∧ ¬isAtomicFormula(fml2)) continue;

Uses isAtomicFormula 129b and rc 23a.

Comment 3.5.19. An atomic formula is a term having the form (f t1 · · · tn), n ≥ 1, where f isnot one of ∧,∨,¬,−→, Π, Σ.

129b 〈tableaux::atomic formula 129b〉≡ (127c)

static bool isAtomicFormula(term ∗ t) if (¬t→isApp()) return false;if (¬t→spineTip()→isF()) return false;int fname = t→spineTip()→cname;return ¬(fname ≡ iAnd ∨ fname ≡ iOr ∨ fname ≡ iNot ∨

fname ≡ iPi ∨ fname ≡ iSigma ∨ fname ≡ iImplies);

Defines:isAtomicFormula, used in chunks 128c, 129a, and 136b.

Uses iAnd 183, iImplies 183, iNot 183, iOr 183, iPi 183, isApp 22f, isF 22f, iSigma 183, and spineTip 24d.

Comment 3.5.20. Here we check whether there is a substitution θ that can unify fml and fml2

by instantiating the free variables inside fml and fml2. We modified the redex-matching algorithmto act only on free variables.

129c 〈isClosable::try to match the labels 129c〉≡ (128b)

〈isClosable::try match labels::error reporting 1 129d〉vector<substitution> theta; bool ret;

if (sex1 ≡ 0) ret = freevar_match(fml, fml2→rc(), theta);else ret = freevar_match(fml→rc(), fml2, theta);

if (ret) 〈isClosable::try match labels::error reporting 2 129e〉constraints.push_back(solver.makeAndConstraint(theta));foundContradiction = true;// cout << "i = " << i << " j = " << j << endl;

// else ioprintln(" ... failed");

Uses freevar_match 100a, ioprintln, makeAndConstraint 147b, rc 23a, and substitution 41.

Comment 3.5.21. Here are some code to help with tracking progress.

129d 〈isClosable::try match labels::error reporting 1 129d〉≡ (129c)

// setSelector(STDERR); ioprint("trying "); fml->print(); ioprint(" and ");// fml2->print(); ioprintln(); setSelector(SILENT);

Uses ioprint, ioprintln, and setSelector.

129e 〈isClosable::try match labels::error reporting 2 129e〉≡ (129c)

// setSelector(STDERR); ioprint("\t"); fml->print(); ioprint("\n\t");// fml2->print();ioprintln(); ioprint("\ttheta = "); printTheta(theta); setSelector(SILENT);

Uses ioprint, ioprintln, printTheta 101e 102a, and setSelector.

Page 133: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 130

3.5.2 Classical Rules

Comment 3.5.22. We now look at how basic tableau rules are implemented, starting with theclassical rules.

Comment 3.5.23. First we look at the negation rule. The input parameters are the label to bechecked and the world to which we should add a new label if the negation rule applies.

130a 〈tableaux.cc 122a〉+≡ ⊳ 127c 131a ⊲

#include "global.h"

bool Tableaux::applyNegationRule(Label & label, World & world) const string & ruleId = negationRuleId; 〈return if rule tried before 130b〉

if (label.fml→isNegation() ∧ label.fml→rc()→isNegation()) term ∗ temp = label.fml→rc()→rc()→clone();〈insert new label into world 130c〉return true;

return false;

Defines:

applyNegationRule, used in chunks 121 and 126a.Uses isNegation 45a, Label 106, rc 23a, and Tableaux 121.

Comment 3.5.24. Have we tried the rule on this label before? If so, we return without doinganything else.

130b 〈return if rule tried before 130b〉≡ (130–34 137b 140–42)

if (label.isTried(ruleId)) return false;label.setTried(ruleId);

Uses isTried 109a and setTried 109b.

Comment 3.5.25. We insert a newly created formula into a world. This code chunk is reused injust about all the other tableau rules.

130c 〈insert new label into world 130c〉≡ (130–34 137c 138a 140a)

temp→stripNegations();string drv = numtostring(label.id) + "," + ruleId;Label lbl(temp, drv, label.depOnGoal);//if (backchain && label.depOnGoal) temp->collectFunctionNames(goalRelated);world.labels→add(lbl);label.setApplied(ruleId, world.name);

Uses add 110c, Label 106, setApplied 109b, and stripNegations 45d.

Page 134: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 131

Comment 3.5.26. We next look at the conjunction rule. As usual, we are given the label to bechecked and the world to which we should add the new labels if the rule applies. Since formulasare normalized up front, conjunctions take the form of ¬(p ∨ q). That is equivalent to (¬p ∧ ¬q).

131a 〈tableaux.cc 122a〉+≡ ⊳ 130a 132a ⊲

bool Tableaux::applyConjunctionRule(Label & label, World & world) const string & ruleId = conjunctionRuleId; 〈return if rule tried before 130b〉

if (label.fml→isNegation() ∧ label.fml→rc()→isFunc2Args(iOr))term ∗ temp = new_term(APP);temp→insert(new_term(F, iNot));temp→insert(label.fml→rc()→lc()→rc()→clone()); 〈insert new label into world 130c〉

temp = new_term(APP);temp→insert(new_term(F, iNot));temp→insert(label.fml→rc()→rc()→clone()); 〈insert new label into world 130c〉

return true;return false;

Defines:applyConjunctionRule, used in chunks 121 and 126a.

Uses iNot 183, insert 23a, iOr 183, isFunc2Args 24e, isNegation 45a, Label 106, lc 23a, new_term 31b, rc 23a,and Tableaux 121.

Comment 3.5.27. We are currently working with normalised forms of formulas. This makes theformulas somewhat difficult to read and we may want to change that at some stage. The followingmay be useful in the future.

131b 〈applyConjunctionRule::unnormalised form 131b〉≡if (label.fml→isFunc2Args(iAnd))

term ∗ temp = label.fml→lc()→rc()→clone(); 〈insert new label into world 130c〉 temp = label.fml→rc()→clone(); 〈insert new label into world 130c〉 return true;

Uses iAnd 183, isFunc2Args 24e, lc 23a, and rc 23a.

Page 135: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 132

Comment 3.5.28. The next function implements the disjunction rule. The third argumenttindex is the tableau containing the world indexed by w. It is the one we need to clone whencreating a new branch. We pass in the index w instead of a reference to a World object like in theother rules because the disjunction rule can change the underlying structure storing the differentworlds, and hence cause the reference to become a wild pointer.

132a 〈tableaux.cc 122a〉+≡ ⊳ 131a 133a ⊲

bool Tableaux::applyDisjunctionRule(Label & label, int w, int tindex, bool lr) 〈applyDisjunctionRule::skip unless we are down to our last resorts 132b〉

const string & ruleId = disjunctionRuleId; 〈return if rule tried before 130b〉

if (label.fml→isFunc2Args(iOr)) term ∗ t2 = label.fml→lc()→rc(), ∗ t3 = label.fml→rc();〈applyDisjunctionRule::add a tableau n add t3 132c〉〈applyDisjunctionRule::add t2 132d〉return true;

return false;

Defines:

applyDisjunctionRule, used in chunks 121 and 126b.Uses iOr 183, isFunc2Args 24e, Label 106, lc 23a, rc 23a, and Tableaux 121.

Comment 3.5.29. The disjunction rule is something you do not want to do unless it is strictlynecessary. Even when it is strictly necessary, given a choice of two disjunctive formulas to split,you want to be careful about which one you choose. As a rule, we want to be goal directed and notsplit a disjunctive formula obtained from a rule in the database unless we cannot make progressany other way. The derivation of such rules take the form of "A" or "x,U" where x is an integer.

132b 〈applyDisjunctionRule::skip unless we are down to our last resorts 132b〉≡ (132a)

string ldrv = label.getDrv(); char lch = ldrv[ldrv.size()-1];if (lr ≡ false ∧ (lch ≡ ’A’ ∨ lch ≡ ’U’)) return false;

Comment 3.5.30. After addClone, the reference world becomes a wild pointer because theunderlying tableaux data structure got changed. The formula t3 appears in the cloned tableaubut is removed from the current one.

132c 〈applyDisjunctionRule::add a tableau n add t3 132c〉≡ (132a)

term ∗ temp = t3→clone(); World & world = tableaux[tindex].getWorld(w); 〈insert new label into world 130c〉

addClone(tableaux[tindex]);

int idx = tableaux[tindex].getWorld(w).labels→size()-1;tableaux[tindex].getWorld(w).labels→remove(idx);

Uses addClone 122b and getWorld 116a.

Comment 3.5.31. Adding t2 to the current branch is easy. We just have to be careful torecalculate the pointer to the world we want to insert the formula in.

132d 〈applyDisjunctionRule::add t2 132d〉≡ (132a)

temp = t2→clone(); World & world = tableaux[tindex].getWorld(w); 〈insert new label into world 130c〉

Uses getWorld 116a.

Page 136: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 133

Comment 3.5.32. We now examine the universal rule. We use the free variable tableau methodhere. Given a universally quantified formula ∀x.φ, we add a new formula φx/X to the tableauwhere X is a new free variable. Instantiation of these free variables are handled incrementallyusing the algorithm introduced in [Gie01]. (See also Comment 3.5.14.)

133a 〈tableaux.cc 122a〉+≡ ⊳ 132a 134b ⊲

bool Tableaux::applyUniversalRule(Label & label, World & world) const string & ruleId = universalRuleId; 〈return if rule tried before 130b〉

if (label.fml→isNegation() ∧ label.fml→rc()→isApp() ∧label.fml→rc()→lc()→isF(iSigma))

term ∗ temp = label.fml→rc()→rc()→fields[1];〈applyUniversalRule::introduce negation if necessary 133b〉

int varn = label.fml→rc()→rc()→fields[0]→cname;substitution theta(varn, new_term(V, newUVar()));temp→subst(theta);〈insert new label into world 130c〉

〈applyUniversalRule::add a copy of old label if necessary 134a〉return true;

return false;

Defines:applyUniversalRule, used in chunks 121 and 126a.

Uses isApp 22f, isF 22f, iSigma 183, isNegation 45a, Label 106, lc 23a, new_term 31b, newUVar 185b, rc 23a,subst 42a, substitution 41, and Tableaux 121.

Comment 3.5.33. After passing the filter above, we have a formula of the form ¬∃x.p. If p hasthe form (¬ q) for some q, then we assign the temp pointer to q. Otherwise, we need to treat theformula as ¬∃x.(¬ (¬ p)) and construct (¬ p) from p before assigning temp to (¬ p).

133b 〈applyUniversalRule::introduce negation if necessary 133b〉≡ (133a)

if (temp→isNegation()) temp = temp→rc()→clone(); else term ∗ t2 = temp→clone();

temp = new_term(APP); temp→insert(new_term(F,iNot)); temp→insert(t2);

Uses iNot 183, insert 23a, isNegation 45a, new_term 31b, and rc 23a.

Page 137: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 134

Comment 3.5.34. To prove certain theorems, we need to apply the universal rule on the sameformula multiple times. (We fix it at two for now; this should be made a parameter later.) Thecomplication is that we want to delay subsequent applications of the universal rule to the sameformula as much as possible because we do not want to clutter up the tableaux unnecessarily andmake the resultant task of finding a closing substitution more difficult than necessary. This canbe achieved by having a more complicated control algorithm that can take into account what hasbeen done to universally quantified formulas. A simpler alternative which we adopt here is to adda new copy of a universally quantified formula that has just been processed to the end of worldand use the same the control algorithm as before. We need to record in the new copy that theuniversal rule has been applied to it before. The newly created copy will be inserted right at theend of the queue so that they are not processed again until as late as possible. See Comment 3.2.9for details on how this works.

134a 〈applyUniversalRule::add a copy of old label if necessary 134a〉≡ (133a)

if (label.appliedCount(ruleId, world.name) < 2) Label l2(label.fml→clone(), label.getDrv() + ",R", label.depOnGoal);l2.setApplied(ruleId, world.name);world.labels→add(l2);

Uses add 110c, appliedCount 108c, Label 106, and setApplied 109b.

Comment 3.5.35. The next function implements the existential rule. Given a formula of theform σ ∃x.φ, we add a formula σ φx/(fs (u1, . . . , un)), where fs is a new Skolem functionand the uis are the free variables in φ introduced by the universal rule. (See Comment 3.5.32.)We need to introduce a Skolem term instead of just a fresh variable because we need to avoidmaking substitutions of the form x/y, where y is actually dependent on x, when trying to findsubstitutions that will close the tableaux. Basically, when we have a formula of the form ∀x.∃y.φ,the actual witness to the variable y is dependent on the term chosen for x. Skolemization inhigher-order logic is not sound.

134b 〈tableaux.cc 122a〉+≡ ⊳ 133a 135b ⊲

static set<int> fvars;bool Tableaux::applyExistentialRule(Label & label, World & world)

const string & ruleId = existentialRuleId; 〈return if rule tried before 130b〉

if (label.fml→isApp() ∧ label.fml→lc()→isF(iSigma)) term ∗ skfunc = NULL; fvars.clear();label.fml→rc()→collectFreeVariables(fvars);

if (fvars.size() > 0) 〈create Skolem constant 135a〉 else skfunc = new_term(V, newPVar());

term ∗ temp = label.fml→rc()→fields[1]→clone();substitution theta(label.fml→rc()→fields[0]→cname, skfunc);temp→subst(theta);〈insert new label into world 130c〉return true;

return false;

Defines:

applyExistentialRule, used in chunks 121 and 126a.Uses collectFreeVariables 44b, isApp 22f, isF 22f, iSigma 183, Label 106, lc 23a, new_term 31b, newPVar 185b,

rc 23a, subst 42a, substitution 41, and Tableaux 121.

Page 138: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 135

Comment 3.5.36. Instead of introducing a Skolem function, we use a fresh variable (with theappropriate function type). The argument to the function variable is created by stepping throughthe free variables collected in fvars and forming a product out of them.

135a 〈create Skolem constant 135a〉≡ (134b)

term ∗ tmp = NULL;term ∗ arg2 = new_term(PROD);set<int>::iterator p = fvars.begin();while (p 6= fvars.end()) tmp = new_term(V, ∗p); arg2→insert(tmp); p++; skfunc = new_term(APP);skfunc→insert(new_term(V, newPVar())); skfunc→insert(arg2);

Uses insert 23a, new_term 31b, and newPVar 185b.

Comment 3.5.37. This next function performs the substitutivity rule. (This is also calledJeffrey’s rule.) If the input label is a term of the form s = r, where s and r are not identical, therule adds to the world any new labels generated by replacing the s with r in all the other labels inthis world that has not been touched before. The function applySubstitutivityRule does thisincrementally one Label at a time. Note that, to avoid free-variable capture, an occurs check isusually needed. To avoid having to do this, we impose the additional constraint that both s andr do not contain free variables except those introduced by the universal rule. We ignore questionsof admissibility for now.

135b 〈tableaux.cc 122a〉+≡ ⊳ 134b 137b ⊲

〈support functions 136b〉

bool Tableaux::applySubstitutivityRule(Label & lbl, World & world) if (¬lbl.fml→isFunc2Args(iEqual)) return false;term ∗ s = lbl.fml→lc()→rc(), ∗ r = lbl.fml→rc();if (¬(isHarmless(s) ∧ isHarmless(r))) return false;if (s→equal(r)) return false;

string key1 = numtostring(lbl.id) + ",";for (int i=0; i6=world.labels→size(); i++)

〈applySubstitutivityRule::main body 136a〉 return false;

Defines:applySubstitutivityRule, used in chunks 121 and 126a.

Uses equal 26a, iEqual 183, isFunc2Args 24e, isHarmless 137a, Label 106, lc 23a, rc 23a, and Tableaux 121.

Page 139: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 136

Comment 3.5.38. We just go through the world and do the first applicable substitution. Note:only one substitution is done at a time.

136a 〈applySubstitutivityRule::main body 136a〉≡ (135b)

Label lbl2 = world.labels→get(i);if (lbl.id ≡ lbl2.id ∨ ¬isSimple(lbl2.fml)) continue;if (lbl2.derivation.substr(0,key1.size()) ≡ key1 ∧

lbl2.derivation[lbl2.derivation.size()-1] ≡ ’=’ ∧lbl2.appliedCount(key1, world.name) > 2)

continue;

string key = key1 + numtostring(lbl2.id) + "," + substitutionRuleId ;if (lbl.isTried(key)) continue;lbl.setTried(key);

term ∗ t3 = lbl2.fml→clone();if ((t3→termReplace(s, r, NULL, -5) ∨ t3→termReplace(r, s, NULL, -5))∧ ¬isTrivialEq(t3) ∧ ¬world.labels→contains(t3))

Label nlbl(t3, key, lbl.depOnGoal ∨ lbl2.depOnGoal);world.labels→add(nlbl);lbl.setApplied(key, world.name);// lbl2.setApplied(key1, world.name);for (int ks=0; ks 6=lbl2.appliedCount(key1, world.name)+1; ks++)

nlbl.setApplied(key1, world.name);return true;

else t3→freememory();Uses add 110c, appliedCount 108c, contains 112a, isSimple 136b, isTried 109a, isTrivialEq 136c, Label 106,

setApplied 109b, setTried 109b, and termReplace 46a.

Comment 3.5.39. [Fit90] A formula is simple if it is atomic, or the negation of an atomic formulawhose head is =.

136b 〈support functions 136b〉≡ (135b) 136c ⊲

bool isSimple(term ∗ t) if (isAtomicFormula(t)) return true;return (t→isNegation() ∧ isAtomicFormula(t→rc()) ∧

t→rc()→spineTip()→cname ≡ iEqual);

Defines:isSimple, used in chunk 136a.

Uses iEqual 183, isAtomicFormula 129b, isNegation 45a, rc 23a, and spineTip 24d.

136c 〈support functions 136b〉+≡ (135b) ⊳ 136b 137a ⊲

bool isTrivialEq(term ∗ t) return (t→isFunc2Args(iEqual) ∧ t→lc()→rc()→equal(t→rc())) ;

Defines:

isTrivialEq, used in chunk 136a.Uses equal 26a, iEqual 183, isFunc2Args 24e, lc 23a, and rc 23a.

Comment 3.5.40. A term is harmless if it only contains free variables introduced by the universalrule or Skolem variables. When inserted into another term, harmless terms cannot cause a variablecapture.

Page 140: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 137

137a 〈support functions 136b〉+≡ (135b) ⊳ 136c

bool isHarmless(term ∗ t) vector<int> fv = t→getFreeVars();for (uint i=0; i6=fv.size(); i++)

if (fv[i] 6= -5 ∧ ¬(isUVar(fv[i]) ∨ fv[i] ≥ 5000))return false;

return true;

Defines:isHarmless, used in chunk 135b.

Uses getFreeVars 36a and isUVar 44b.

Comment 3.5.41. This next function implements a version of the reflexivity rule. The versionin the paper says if t is a term of type α and the prefix σ already occurs on the branch, thenwe can add the labelled formula σ t = t to the branch. The rule as implemented by Josh is asfollows. We check for a formula of the form ¬(t = t) and, if found, add a formula t = t to close thetableau. An extension added to deal with free variables is the following rule: if we find a formulaof the form ¬(x = t), where x is a free variable introduced via the universal rule, then we can adda formula t = t to close the branch since x can be instantiated to t to close the branch.

137b 〈tableaux.cc 122a〉+≡ ⊳ 135b 138b ⊲

bool Tableaux::applyReflexiveRule(Label & label, World & world) const string & ruleId = reflexiveRuleId; 〈return if rule tried before 130b〉

term ∗ t1 = label.fml;if (t1→isNegation() ∧ t1→rc()→isFunc2Args(iEqual))

〈applyReflexiveRule::first case 137c〉〈applyReflexiveRule::second case 138a〉

return false;

Defines:

applyReflexiveRule, used in chunks 121 and 126a.Uses iEqual 183, isFunc2Args 24e, isNegation 45a, Label 106, rc 23a, and Tableaux 121.

137c 〈applyReflexiveRule::first case 137c〉≡ (137b)

if (t1→rc()→lc()→rc()→equal(t1→rc()→rc())) term ∗ temp = newT2Args(F, iEqual);term ∗ temp2 = t1→rc()→rc()→clone();temp→initT2Args(temp2, temp2→clone());〈insert new label into world 130c〉return true;

Uses equal 26a, iEqual 183, initT2Args 25d, lc 23a, newT2Args 25c, and rc 23a.

Page 141: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 138

Comment 3.5.42. We have to deal with the symmetric cases (x = t) and (t = x).

138a 〈applyReflexiveRule::second case 138a〉≡ (137b)

if (isUVar(t1→rc()→lc()→rc()→cname)) term ∗ temp = newT2Args(F, iEqual);term ∗ temp2 = t1→rc()→rc()→clone();temp→initT2Args(temp2, temp2→clone());〈insert new label into world 130c〉return true;

else if (isUVar(t1→rc()→rc()→cname)) term ∗ temp = newT2Args(F, iEqual);term ∗ temp2 = t1→rc()→lc()→rc()→clone();temp→initT2Args(temp2, temp2→clone());〈insert new label into world 130c〉return true;

Uses iEqual 183, initT2Args 25d, isUVar 44b, lc 23a, newT2Args 25c, and rc 23a.

Comment 3.5.43. The following deals with the special case of universally quantified formulasthat are global assumptions and have the following general form:

∀x.(i1 · · ·ijx −→ j1 · · ·jk

x).

Everytime we find such a formula, we go through each other formula t in the tableaux looking fora subterm s and θ such that s = (i1 · · ·ij

x)θ. If we can find such a pair, we replace s in t by(j1 · · ·jk

x)θ.

138b 〈tableaux.cc 122a〉+≡ ⊳ 137b 140a ⊲

bool Tableaux::applyUniversalRuleSPImplies(Label & label, World & world) if (¬label.globalAssumption) return false;const string & ruleId = universalSPImpliesRuleId;string drv1 = numtostring(label.id) + ",";

〈applyURuleSPI::recognise formula form 139a〉bool applied = false;for (uint i=0; i6=tableaux.size(); i++)

for (int j=0; j6=tableaux[i].size(); j++) uint ksize = tableaux[i].getWorld(j).labels→size();for (uint k=0; k6=ksize; k++)

Label & lbl = tableaux[i].getWorld(j).labels→get(k);if (lbl.id ≡ label.id) continue;〈applyUniversalRuleSPImplies::process label 139c〉

return applied;

Defines:applyUniversalRuleSPImplies, used in chunks 121 and 126a.

Uses getWorld 116a, Label 106, and Tableaux 121.

Comment 3.5.44. We only deal with the specific case when that formula is represented as

¬∃x.¬(¬i1 · · ·ijx ∨j1 · · ·jk

x).

We need to change the code to deal with the different forms of the formula in the future. We calllabelStaticBoundVars on p because that is needed by matchReplace later.

Page 142: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 139

139a 〈applyURuleSPI::recognise formula form 139a〉≡ (138b)

term ∗ fml = label.fml;if (¬(fml→isNegation() ∧ fml→rc()→isApp() ∧ fml→rc()→lc()→isF(iSigma)∧ fml→rc()→rc()→fields[1]→isNegation())) // \not \exists x. \notreturn false;

term ∗ body = fml→rc()→rc()→fields[1]→rc();if (¬(body→isFunc2Args(iOr) ∧ body→lc()→rc()→isNegation()))

return false; // (\not p) \vee q

term ∗ p = body→lc()→rc()→rc(), ∗ q = body→rc();term ∗ temp = p; 〈applyUniversalRuleSPImplies::has leading modalities 139b〉temp = q; 〈applyUniversalRuleSPImplies::has leading modalities 139b〉p→labelStaticBoundVars();

Uses iOr 183, isApp 22f, isF 22f, isFunc2Args 24e, iSigma 183, isNegation 45a, labelStaticBoundVars 37c,lc 23a, and rc 23a.

Comment 3.5.45. Here we check that a term has the form i1 · · ·ijx, for some variable x.

139b 〈applyUniversalRuleSPImplies::has leading modalities 139b〉≡ (139a)

if (¬temp→isModal()) return false;while (temp→isModal()) temp = temp→fields[0];if (¬temp→isVar()) return false;

Uses isModal 22f and isVar 22f.

Comment 3.5.46. Given a label t, we replace all subterms s of t where there exists θ such thatpθ = s in the label with qθ and create a new label. The main work is done in the matchReplace

function.

139c 〈applyUniversalRuleSPImplies::process label 139c〉≡ (138b)

〈applyUniversalRuleSPImplies::irrelevant cases 139d〉

lbl.setTried(drv, world.name);term ∗ t = lbl.fml→clone();if (t→matchReplace(p, q, NULL, 0))

Label nlbl(t, drv, label.depOnGoal);tableaux[i].getWorld(j).labels→add(nlbl);lbl.setApplied(drv, world.name);applied = true;

else t→freememory();

Uses add 110c, getWorld 116a, Label 106, matchReplace 46b, setApplied 109b, and setTried 109b.

Comment 3.5.47. We ignore formulas that we have tried this rule on before.

139d 〈applyUniversalRuleSPImplies::irrelevant cases 139d〉≡ (139c)

string drv = drv1 + numtostring(lbl.id) + "," + ruleId;if (lbl.isTried(drv, world.name)) continue;

Uses isTried 109a.

Comment 3.5.48. This next function calls the equational reasoning component to reduce a label.This is currently not being used.

Page 143: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 140

140a 〈tableaux.cc 122a〉+≡ ⊳ 138b 140b ⊲

bool Tableaux::applyEqReasoningRule(Label & label, World & world) const string & ruleId = bachRuleId; 〈return if rule tried before 130b〉

term ∗ temp = label.fml→clone();bool rewritten = temp→reduceRpt(false);if (rewritten) temp = temp→normalise();if (rewritten ∧ ¬world.labels→contains(temp))

〈insert new label into world 130c〉return true;

else temp→freememory();return false;

Defines:

applyEqReasoningRule, used in chunk 121.Uses contains 112a, Label 106, normalise 47a, reduceRpt 71c 73a, and Tableaux 121.

Comment 3.5.49. This next function implements the closure rule. We check that all pairs oflabels in a given world for direct contradictories and enter False (bottom) as a new label if twocontradictory labels are found. The parameter world is the world we will add a new label Falseto if the rule applies. The procedure works as follows. For each label, we first check whether it isequals to false. If so, we are done. Otherwise, we compare the label with all labels following itto check for direct contradiction.

140b 〈tableaux.cc 122a〉+≡ ⊳ 140a 141b ⊲

bool Tableaux::applyClosureRule(World & world) const string & ruleId = closureRuleId;for (int i=0; i6=world.labels→size(); i++)

Label label1 = world.labels→get(i);string key = numtostring(label1.id) + "," + ruleId;〈applyClosureRule::case when label1 is false 140c〉〈applyClosureRule::check pairs 141a〉

return false;

Defines:applyClosureRule, used in chunks 121 and 125b.

Uses Label 106 and Tableaux 121.

Comment 3.5.50. We first check whether label1 is ⊥ or ¬⊤. In the latter case, we insert ⊥ tothe world.

140c 〈applyClosureRule::case when label1 is false 140c〉≡ (140b)

if (label1.fml→isD(iFalse)) return true;

if (label1.fml→isNegation() ∧ label1.fml→rc()→isD(iTrue)) Label lbl(new_term(D, iFalse), key, label1.depOnGoal);world.labels→add(lbl);return true;

Uses add 110c, iFalse 183, isD 22f, isNegation 45a, iTrue 183, Label 106, new_term 31b, and rc 23a.

Comment 3.5.51. We return straight after finding a contradiction.

Page 144: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 141

141a 〈applyClosureRule::check pairs 141a〉≡ (140b)

for (int j=i+1; j6=world.labels→size(); j++) Label label2 = world.labels→get(j);key = numtostring(label1.id) + "," + numtostring(label2.id) + "," + ruleId;if (¬label1.isTried(key, world.name))

label1.setTried(key, world.name);if (label1.fml→isNegationOf(label2.fml))

Label lbl(new_term(D, iFalse), key,label1.depOnGoal ∨ label2.depOnGoal);

world.labels→add(lbl);return true;

Uses add 110c, iFalse 183, isNegationOf 45c, isTried 109a, Label 106, new_term 31b, and setTried 109b.

3.5.3 Structural Rules

Comment 3.5.52. The next function implements the possibility rule.

141b 〈tableaux.cc 122a〉+≡ ⊳ 140b 142 ⊲

bool Tableaux::applyDiamondRule(Label & label, int w1, Tableau & tableau) const string & ruleId = diamondRuleId; 〈return if rule tried before 130b〉

term ∗ t1 = label.fml;if (t1→isNegation() ∧ t1→rc()→isModal())

int w2 = tableau.addWorld();term ∗ t2 = NULL;if (t1→rc()→fields[0]→isNegation())

t2 = t1→rc()→fields[0]→rc()→clone(); else

t2 = new_term(APP);t2→insert(new_term(F, iNot));t2→insert(t1→rc()→fields[0]→clone());

string drv = numtostring(label.id) + "," + ruleId;Label lbl(t2, drv, label.depOnGoal);

tableau.getWorld(w2).labels→add(lbl);tableau.addLink(t1→rc()→modality, w1, w2);label.setApplied(ruleId, w1);

return true;return false;

Defines:applyDiamondRule, used in chunks 121 and 126c.

Uses add 110c, addWorld 117c, getWorld 116a, iNot 183, insert 23a, isModal 22f, isNegation 45a, Label 106,new_term 31b, rc 23a, setApplied 109b, Tableau 116a, and Tableaux 121.

Page 145: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 142

3.5.4 Propagation Rules

Comment 3.5.53. We now examine the propagation rules. The following implements the neces-sity rule. Given a formula iϕ in world j, we go through each world k that is accessible from theworld j and insert ϕ into it.

142 〈tableaux.cc 122a〉+≡ ⊳ 141b 143 ⊲

bool Tableaux::applyNecessityRule(Label & label, int cur_w, Tableau & tableau)const string & ruleId = boxRuleId; 〈return if rule tried before 130b〉if (¬label.fml→isModal()) return false;

bool applied = false;int index = label.fml→modality;for (uint i=0; i6=tableau.worlds.size(); i++)

int next_w = tableau.worlds[i].name;if (cur_w ≡ next_w) continue;if (tableau.accRel→isLinked(index, cur_w, next_w))

string drv = numtostring(label.id) + "," + ruleId;Label lbl(label.fml→fields[0]→clone(), drv, label.depOnGoal);tableau.worlds[i].labels→add(lbl);label.setApplied(ruleId, cur_w);applied = true;

return applied;

Defines:

applyNecessityRule, used in chunks 121 and 127b.Uses add 110c, isLinked 115a, isModal 22f, Label 106, setApplied 109b, Tableau 116a, and Tableaux 121.

Page 146: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.5. TABLEAUX 143

Comment 3.5.54. Next we look at the K rule (2(p −→ q) −→ (2p −→ 2q)). The parametertableau contains world and the children worlds into which we will propagate label. Note that¬3iφ is equivalent to ¬¬2i¬φ. So when we see a term of the form σ ¬¬2i¬φ, we can add σ2 ¬φto the tableau if world σ2 is a child of world σ.

143 〈tableaux.cc 122a〉+≡ ⊳ 142

bool Tableaux::applyKRule(Label & label, World & world, Tableau & tableau) const string & ruleId = kRuleId;term ∗ t1 = label.fml;if (¬(t1→isNegation() ∧ t1→rc()→isDiamond())) return false;int index = t1→rc()→rc()→modality;vector<World ∗> children = tableau.children(index, world.name);bool applied = false;for (uint i=0; i6=children.size(); i++)

if (¬label.isApplied(ruleId, children[i]→name)) term ∗ phi = t1→rc()→rc()→fields[0];string drv = numtostring(label.id) + "," + ruleId;Label lbl(phi→clone(), drv, label.depOnGoal);children[i]→labels→add(lbl);label.setApplied(ruleId, children[i]→name);applied = true;

return applied;

Defines:applyKRule, used in chunks 121 and 127b.

Uses add 110c, children 118c, isApplied 108c, isDiamond 45b, isNegation 45a, Label 106, rc 23a,setApplied 109b, Tableau 116a, and Tableaux 121.

Page 147: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.6. A CONSTRAINTS SOLVER 144

3.6 A Constraints Solver

Comment 3.6.1. Instead of an independent unification solver, we use the equational reasoningcomponent of Bach to solve unification problems. This solver is fairly restricted in its capabilitybut it is good enough for now.

144 〈constraints-solver.h 144〉≡#ifndef _CONSTRAINTS_SOLVER_H_

#define _CONSTRAINTS_SOLVER_H_

#include <vector>#include "terms.h"

#include "global.h"

class constraints_solver public:

constraints_solver() constraintsTerm = NULL; term ∗ makeAndConstraint(vector<substitution> &);term ∗ makeOrConstraint(vector<term ∗> &);term ∗ makeEqConstraint(substitution);void addConstraint(term ∗);bool solvable();vector<substitution> getTheta();void printInst();void freememory() if (constraintsTerm) constraintsTerm→freememory();void reset() freememory(); constraintsTerm = NULL;

private:term ∗ constraintsTerm;bool solutionRightForm(term ∗ t, bool orAllowed);vector<substitution> getTheta2(term ∗ t);

;

#endif

Defines:constraints_solver, used in chunks 121 and 145–50.

Uses addConstraint 148b, getTheta 149c, getTheta2 150a, makeAndConstraint 147b, makeEqConstraint 147a,makeOrConstraint 147c, printInst 150c, solvable 145a, and substitution 41.

Page 148: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.6. A CONSTRAINTS SOLVER 145

Comment 3.6.2. Given two constraints univ0 = t1 and univ0 = t2, we need to see whetherit is possible to unify t1 and t2. We use the equational reasoner to check this.

145a 〈constraints-solver.cc 145a〉≡ 146a ⊲

#include "constraints-solver.h"

#define MAXSTEP 2000

bool constraints_solver::solvable() if (¬constraintsTerm) return false;〈solvable::error reporting 1 145b〉int stepstaken = 0;constraintsTerm→reduceRpt(MAXSTEP, stepstaken, false);〈solvable::error reporting 2 145c〉if (stepstaken > MAXSTEP ∨ constraintsTerm→isD(iFalse)) return false;if (¬solutionRightForm(constraintsTerm, true)) return false;return true;

Defines:solvable, used in chunks 127c and 144.

Uses constraints_solver 144, iFalse 183, isD 22f, and reduceRpt 71c 73a.

Comment 3.6.3. Here are some code that can be used for debugging.

145b 〈solvable::error reporting 1 145b〉≡ (145a)

int osel = getSelector(); setSelector(STDERR); ioprint("solving ");constraintsTerm→print(); ioprintln(); setSelector(osel);

Uses getSelector, ioprint, ioprintln, and setSelector.

145c 〈solvable::error reporting 2 145c〉≡ (145a)

setSelector(STDERR); ioprint("solution "); ioprint("(");ioprint(stepstaken); ioprint(") : "); constraintsTerm→print();ioprintln(); setSelector(osel);

Uses ioprint, ioprintln, and setSelector.

Page 149: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.6. A CONSTRAINTS SOLVER 146

Comment 3.6.4. We expect the result of a unification process to have the general form

s1 ∨ · · · ∨ sn,

where at least one of the sis has the following general form

(x1 = t1) ∧ · · · ∧ (xn = tn), n ≥ 1, xi 6= xj if i 6= j

Subterms of the form (ti = ti), which sometimes cannot be simplified by the equational reasonerwhen there are function symbols inside ti, are also allowed to occur in the answer. An answer thathas the above form is said to have the right form. The argument orAllowed is there to make surewe have disjunctions followed by conjunctions.

146a 〈constraints-solver.cc 145a〉+≡ ⊳ 145a 146b ⊲

static bool uniqueInst(term ∗ t);bool constraints_solver::solutionRightForm(term ∗ t, bool orAllowed)

if (t→isFunc2Args(iOr)) if (¬orAllowed) return false;return (solutionRightForm(t→lc()→rc(), true) ∨

solutionRightForm(t→rc(), true)) ;if (t→isFunc2Args(iAnd))

if (¬(solutionRightForm(t→lc()→rc(), false) ∧solutionRightForm(t→rc(), false))) return false;

return uniqueInst(t);if (¬t→isFunc2Args(iEqual)) return false;return (isUVar(t→rc()) ∨ isUVar(t→lc()→rc()) ∨

t→rc()→equal(t→lc()→rc()));

Uses constraints_solver 144, equal 26a, iAnd 183, iEqual 183, iOr 183, isFunc2Args 24e, isUVar 44b, lc 23a,and rc 23a.

Comment 3.6.5. The function uniqueInst takes in a conjunction of variable instantiations (inthe form of equalities) and check whether there exists variables that are instantiated multipletimes.

146b 〈constraints-solver.cc 145a〉+≡ ⊳ 146a 147a ⊲

set<int> vars;bool uniqueInst2(term ∗ t);bool uniqueInst(term ∗ t) vars.clear(); return uniqueInst2(t);

bool uniqueInst2(term ∗ t) if (t→isFunc2Args(iAnd))

return (uniqueInst2(t→lc()→rc()) ∧ uniqueInst2(t→rc()));

if (t→isFunc2Args(iEqual)) int fv;if (isUVar(t→lc()→rc())) fv = t→lc()→rc()→cname;else fv = t→rc()→cname;if (vars.find(fv) ≡ vars.end()) vars.insert(fv); return true; else return false;

assert(false); return false;

Uses iAnd 183, iEqual 183, insert 23a, isFunc2Args 24e, isUVar 44b, lc 23a, and rc 23a.

Page 150: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.6. A CONSTRAINTS SOLVER 147

Comment 3.6.6. Given a substitution x/t, we make a constraint term (x = t) out of it. Inthe special case where t is itself a variable, we choose to construct (x = t) or (t = x) dependingon a lexicographic order on the names of the variables. This canonical form eases the detection ofduplicate constraints later on.

147a 〈constraints-solver.cc 145a〉+≡ ⊳ 146b 147b ⊲

term ∗ constraints_solver::makeEqConstraint(substitution x) term ∗ ret = newT2Args(F, iEqual);

if (x.second→isVar() ∧ x.second→cname > x.first) ret→initT2Args(x.second→clone(), new_term(V, x.first));

else ret→initT2Args(new_term(V, x.first), x.second→clone());return ret;

Defines:makeEqConstraint, used in chunks 144 and 147b.

Uses constraints_solver 144, iEqual 183, initT2Args 25d, isVar 22f, new_term 31b, newT2Args 25c,and substitution 41.

Comment 3.6.7. This turns a substitution into a conjunction of equality statements.

147b 〈constraints-solver.cc 145a〉+≡ ⊳ 147a 147c ⊲

term ∗ constraints_solver::makeAndConstraint(vector<substitution> & x) if (x.size() ≡ 0) return new_term(D,iTrue);term ∗ ret = makeEqConstraint(x[0]);for (uint i=1; i6=x.size(); i++)

term ∗ temp = newT2Args(F, iAnd);temp→initT2Args(ret, makeEqConstraint(x[i]));ret = temp;

return ret;

Defines:makeAndConstraint, used in chunks 129c and 144.

Uses constraints_solver 144, iAnd 183, initT2Args 25d, iTrue 183, makeEqConstraint 147a, new_term 31b,newT2Args 25c, and substitution 41.

Comment 3.6.8. This turns a vector of terms (each a constraint) into a disjunction of constraints.

147c 〈constraints-solver.cc 145a〉+≡ ⊳ 147b 148b ⊲

term ∗ constraints_solver::makeOrConstraint(vector<term ∗> & x) assert(x.size() ≥ 1); 〈makeOrConstraint::remove duplicates 148a〉

term ∗ ret = x[0];for (uint i=1; i6=x.size(); i++)

if (x[i] ≡ NULL) continue;term ∗ temp = newT2Args(F, iOr);temp→initT2Args(ret, x[i]);ret = temp;

return ret;

Defines:

makeOrConstraint, used in chunks 128a and 144.Uses constraints_solver 144, initT2Args 25d, iOr 183, and newT2Args 25c.

Page 151: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.6. A CONSTRAINTS SOLVER 148

Comment 3.6.9. To simplify the constraint satisfaction problem, we remove duplicates from thevector x.

148a 〈makeOrConstraint::remove duplicates 148a〉≡ (147c)

for (uint i=0; i6=x.size(); i++) if (x[i] ≡ NULL) continue;for (uint j=i+1; j<x.size(); j++)

if (x[j] ≡ NULL) continue;if (x[i]→equal(x[j])) x[j]→freememory(); x[j] = NULL;

Uses equal 26a.

Comment 3.6.10. The constraints introduced by each branch of the tableaux are collected to-gether in a conjunction by repeated calls to the function addConstraint. The input to addConstraintis a branch constraint, which takes the form of a disjunction of conjunctions. The general caseis easy to handle: we just add the current branch constraint to the conjunction we have alreadycollected so far (constraintsTerm). The interesting cases are when the branch constraint is asingle equality and when constraintsTerm contains single equality branch constraints. These twocases are dealt with below.

148b 〈constraints-solver.cc 145a〉+≡ ⊳ 147c 149c ⊲

〈addConstraint helper function 149b〉

static vector<term ∗> eqs;void constraints_solver::addConstraint(term ∗ c)

if (¬constraintsTerm) constraintsTerm = c; return; 〈addConstraint::case when c is a single equality 148c〉〈addConstraint::case when constraintsTerm contains single equalities 149a〉

term ∗ temp = newT2Args(F, iAnd);temp→initT2Args(constraintsTerm, c);constraintsTerm = temp;

Defines:

addConstraint, used in chunks 128a and 144.Uses constraints_solver 144, iAnd 183, initT2Args 25d, and newT2Args 25c.

Comment 3.6.11. If the input branch constraint c is a single equality, then the input constraintmust be satisfiable for the whole tableaux constraint to be satisfiable. We can exploit this factto replace all occurrences of c in constraintsTerm with True and then simplify. If the resultantconstraintsTerm can be reduced to True, then we just set constraintsTerm to c and return.Otherwise, we keep going.

148c 〈addConstraint::case when c is a single equality 148c〉≡ (148b)

if (c→isFunc2Args(iEqual)) term ∗ tr = new_term(D, iTrue);if (constraintsTerm→termReplace(c, tr, NULL,-5))

constraintsTerm→reduceRpt(false);if (constraintsTerm→isD(iTrue))

constraintsTerm→freememory();constraintsTerm = c;return;

else tr→freememory();

Uses iEqual 183, isD 22f, isFunc2Args 24e, iTrue 183, new_term 31b, reduceRpt 71c 73a, and termReplace 46a.

Page 152: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.6. A CONSTRAINTS SOLVER 149

Comment 3.6.12. Next is the symmetric case where there exists single equalities in constraintsTerm

which we use to simplify c.

149a 〈addConstraint::case when constraintsTerm contains single equalities 149a〉≡ (148b)

eqs.clear(); getEqs(constraintsTerm, eqs);for (uint i=0; i6=eqs.size(); i++)

term ∗ tr = new_term(D, iTrue);if (c→termReplace(eqs[i], tr, NULL, -5))

c→reduceRpt(false);if (c→isD(iTrue)) c→freememory(); return;

else tr→freememory();

Uses isD 22f, iTrue 183, new_term 31b, reduceRpt 71c 73a, and termReplace 46a.

Comment 3.6.13. This retrieves all the single equalities from a term that is a conjunction ofdisjunctions, like constraintsTerm is.

149b 〈addConstraint helper function 149b〉≡ (148b)

static void getEqs(term ∗ t, vector<term ∗> & eqs) if (t→isFunc2Args(iEqual)) eqs.push_back(t); return; if (t→isFunc2Args(iAnd))

getEqs(t→lc(), eqs); getEqs(t→rc(), eqs);

Uses iAnd 183, iEqual 183, isFunc2Args 24e, lc 23a, and rc 23a.

Comment 3.6.14. We would like to convert constraintsTerm into a substitution θ to instantiateformulas in the tableau.

In the case when there is one substitution that can be used to close the tableaux, the termconstraintsTerm will have the general form ∧i(xi = ti), where each xi is a free variable. Ingeneral, there could be multiple substitutions that can be used to close the tableaux. In this case,the input term t would have the general form ∨iθi, where each θi has the form ∧j(xj = tj). Inthis case, we return the substitution form of θ1.

149c 〈constraints-solver.cc 145a〉+≡ ⊳ 148b 150a ⊲

vector<substitution> constraints_solver::getTheta() term ∗ t = constraintsTerm;if (t→isD(iTrue)) vector<substitution> theta; return theta; assert(t→isFunc2Args(iEqual) ∨ t→isFunc2Args(iAnd) ∨

t→isFunc2Args(iOr));while (t→lc()→lc()→isF(iOr)) t = t→lc()→rc();return getTheta2(t);

Defines:getTheta, used in chunk 144.

Uses constraints_solver 144, getTheta2 150a, iAnd 183, iEqual 183, iOr 183, isD 22f, isF 22f, isFunc2Args 24e,iTrue 183, lc 23a, rc 23a, and substitution 41.

Page 153: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

3.6. A CONSTRAINTS SOLVER 150

150a 〈constraints-solver.cc 145a〉+≡ ⊳ 149c 150c ⊲

vector<substitution> constraints_solver::getTheta2(term ∗ t) vector<substitution> theta;if (t→isFunc2Args(iEqual)) 〈getTheta::base case 150b〉 return theta; assert(t→isFunc2Args(iAnd));theta = getTheta2(t→lc()→rc());vector<substitution> theta2 = getTheta2(t→rc());for (uint i=0; i6=theta2.size(); i++) theta.push_back(theta2[i]);return theta;

Defines:getTheta2, used in chunks 144 and 149c.

Uses constraints_solver 144, iAnd 183, iEqual 183, isFunc2Args 24e, lc 23a, rc 23a, and substitution 41.

Comment 3.6.15. The base case of getTheta is a term of the form (t1 = t2). If either t1 or t2is a variable, then we construct a substitution. Otherwise, we just ignore the equality. This latterform of equality comes about as a side effect of the constraints imposed on the variables.

150b 〈getTheta::base case 150b〉≡ (150a)

if (¬(t→lc()→rc()→isVar() ∨ t→rc()→isVar())) setSelector(STDOUT); ioprint("Found side condition: "); t→print();ioprint("\nPress any key to continue. "); setSelector(SILENT);char ch; cin ≫ ch;assert(t→lc()→rc()→isVar() ∨ t→rc()→isVar());return theta;

substitution s;if (t→lc()→rc()→isVar()) s.first = t→lc()→rc()→cname; s.second = t→rc(); else s.first = t→rc()→cname; s.second = t→lc()→rc(); theta.push_back(s);

Uses ioprint, isVar 22f, lc 23a, rc 23a, setSelector, and substitution 41.

Comment 3.6.16. Given a set of variable names vars, this procedure picks out the possibleinstantiations of variables in vars given in constraintsTerm. The actual printing is done in thesubsidiary function that takes an additional term as argument.

150c 〈constraints-solver.cc 145a〉+≡ ⊳ 150a

void constraints_solver::printInst() if (constraintsTerm)

int osel = getSelector(); setSelector(STDOUT);constraintsTerm→print();setSelector(osel);

else ioprint("");

Defines:printInst, used in chunks 121 and 144.

Uses constraints_solver 144, getSelector, ioprint, and setSelector.

Page 154: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

Chapter 4

The Parser

151

Page 155: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 152

4.1 Parsing using Lex and Yacc

4.1.1 Scanner

152 〈escher-scan.l 152〉≡%

#include <iostream>

#include <string.h>

#include <stack>

#include "terms.h"

#include "unification.h"

#include "y.tab.h"

using namespace std;

char linebuf[5000];

int tokenpos = 0;

int import_level = 0;

bool quiet;

bool interactive;

%

〈flex options 155b〉%x CMNT

%%

\\- BEGIN CMNT;

<CMNT>.|\n ;

<CMNT>\-\ BEGIN INITIAL;

[\t ]+ 〈lex:tpos 154c〉

\-\-.* // cout « "–\n";

〈lex:tpos 154c〉

\n.* 〈lex error reporting hackery 154b〉 tokenpos = 0;

Query 〈lex:tpos 154c〉 return QUERY;

Answer 〈lex:tpos 154c〉 return ANSWER;

LastResort 〈lex:tpos 154c〉 return LASTRESORT;

Cache 〈lex:tpos 154c〉 return CACHE;

Eager 〈lex:tpos 154c〉 return EAGER;

Persistent 〈lex:tpos 154c〉 return PERSISTENT;

GlobalAssumption 〈lex:tpos 154c〉 return GLOBALASS;

Nonrigid 〈lex:tpos 154c〉 return NONRIGID;

using 〈lex:tpos 154c〉 return USING;

type 〈lex:tpos 154c〉 return TYPE;

typeof 〈lex:tpos 154c〉 return TYPEOF;

prove 〈lex:tpos 154c〉 return PROVE;

KB 〈lex:tpos 154c〉 return KB;

Bool 〈lex:tpos 154c〉 return BOOL;

Int 〈lex:tpos 154c〉 return INT;

Float 〈lex:tpos 154c〉 return FLOAT;

Char 〈lex:tpos 154c〉 return CHAR;

String 〈lex:tpos 154c〉 return STRING;

ListString 〈lex:tpos 154c〉 return LISTRING;

\-\> 〈lex:tpos 154c〉 return ARROW;

import 〈lex:tpos 154c〉 return IMPORT;

quit 〈lex:tpos 154c〉 return QUIT;

VAR 〈lex:tpos 154c〉 return VAR;

CONST 〈lex:tpos 154c〉 return CONST;

Page 156: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 153

EQUAL 〈lex:tpos 154c〉 return EQUAL;

NOTEQUAL 〈lex:tpos 154c〉 return NOTEQUAL;

StrList 〈lex:tpos 154c〉 return STRLIST;

add 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return ADD;

sub 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return SUB;

max 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return MAX;

min 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return MIN;

mul 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return MUL;

div 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return DIV;

mod 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return MOD;

sin 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return SIN;

cos 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return COS;

sqrt 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return SQRT;

exp 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return EXP;

atan2 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return ATAN2;

if 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return IF;

then 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return THEN;

else 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return ELSE;

ite 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return ITE;

\&\& 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return AND;

\|\| 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return OR;

not 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return NOT;

implies 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return IMPLIES;

iff 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return IFF;

sigma 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return SIGMA;

pi 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return PI;

exists 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return EXISTS;

forall 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return FORALL;

box 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return BOX;

\|\- 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return TURNSTILE;

assign 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return ASSIGN;

tpHelp 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return TPHELP;

TpTag 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return TPTAG;

\= 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return MYEQ;

\/\= 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return MYNEQ;

\<\= 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return MYLTE;

\< 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return MYLT;

\>\= 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return MYGTE;

\> 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return MYGT;

True 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return TRUE;

False 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return FALSE;

\# 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return CONS;

\[\] 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return EMPTYLIST;

-?[0-9]+ 〈lex:tpos 154c〉 yylval.numint = atoi(yytext);

return DATA_CONSTRUCTOR_INT;

-?[0-9]+\.[0-9]+ 〈lex:tpos 154c〉 yylval.num = atof(yytext);

return DATA_CONSTRUCTOR_FLOAT;

\’[^’]\’ 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉return DATA_CONSTRUCTOR_CHAR; /*’*/

\"[^"]*\" 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉return DATA_CONSTRUCTOR_STRING; /*"*/

[a-zA-Z\/0-9\_\.\-]+\.es 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return FILENAME;

[m-z][0-9\_]* 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return VARIABLE;

pv(e|t)[0-9]* 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return VARIABLE;

Page 157: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 154

[a-zA-Z][0-9]*\_SV 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return SYN_VARIABLE;

[a-z][a-zA-Z0-9\-\_\’]* 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return IDENTIFIER1;

[A-Z][a-zA-Z0-9\-\_\’]* 〈lex:tpos 154c〉 〈lex:copy yytext 154a〉; return IDENTIFIER2;

. 〈lex:tpos 154c〉 return yytext[0];

%%

#define YY_NO_UNPUT 1 // suppose to get rid of warning message

〈facilities for handling multiple input files 155a〉

154a 〈lex:copy yytext 154a〉≡ (152)

yylval.name = new char[strlen(yytext)+1] ;

strcpy(yylval.name, yytext);

Comment 4.1.1. I learned this trick for achieving better error recovery from [LMB92, p. 246].The regular expressionn.* matches a newline and the next line, which is saved in linebuf before being returned to thescanner by yyless. The variable tokenpos remembers the current position on the current line.

154b 〈lex error reporting hackery 154b〉≡ (152)

if (!quiet) cerr « "prompt> ";

assert(strlen(yytext+1) <= 5000);

strcpy(linebuf, yytext+1); yyless(1);

154c 〈lex:tpos 154c〉≡ (152)

tokenpos += yyleng;

154d 〈yacc token definitions 154d〉≡ (156) 175c ⊲

%token QUERY ANSWER IMPORT QUIT ARROW PROVE KB TYPE TYPEOF

%token EAGER CACHE LASTRESORT PERSISTENT GLOBALASS NONRIGID USING

%token DATA VAR CONST EQUAL NOTEQUAL BOX EXISTS FORALL TURNSTILE

%token AND OR NOT IMPLIES IFF ADD SUB MAX MIN MUL DIV MOD SIN COS SQRT EXP ATAN2

%token IF THEN ELSE ITE

%token SIGMA PI MYLT MYLTE MYGT MYGTE MYEQ MYNEQ ASSIGN TPHELP TPTAG

%token TRUE FALSE CONS EMPTYLIST

%token BOOL INT FLOAT CHAR STRING LISTRING STRLIST

%token <name> FILENAME

%token <name> VARIABLE

%token <name> DATA_CONSTRUCTOR

%token <numint> DATA_CONSTRUCTOR_INT

%token <num> DATA_CONSTRUCTOR_FLOAT

%token <name> DATA_CONSTRUCTOR_STRING

%token <name> DATA_CONSTRUCTOR_CHAR

%token <name> SYN_VARIABLE

%token <name> IDENTIFIER1

%token <name> IDENTIFIER2

Comment 4.1.2. Escher allows nested import statements in program files. Unfortunately, wecannot simply switch input files every time we see an import statement to read from the correctfile because flex scanners do a lot of buffering. That is to say, the next token comes from thebuffer, not the file yyin.

The solution provided by flex is a mechanism to create and switch between input buffers, andthis is what we used here. A stack of input buffers is used to handle multiply nested import

Page 158: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 155

statements. Every time we see an import statement, we call switchBuffer to push the currentbuffer onto stack, and then create a new buffer and switch to it. When we are done with thecurrent buffer, the scanner will call yywrap to delete the existing buffer and then revert to theprevious buffer stored on top of the stack.

See, for more details on flex, [Pax95].

155a 〈facilities for handling multiple input files 155a〉≡ (152)

stack<YY_BUFFER_STATE> import_stack;

void switchBuffer(FILE * in)

YY_BUFFER_STATE current = YY_CURRENT_BUFFER;

import_stack.push(current);

// cout « "Switching to new file.\n";

YY_BUFFER_STATE newf = yy_create_buffer(in, YY_BUF_SIZE);

yy_switch_to_buffer(newf);

import_level++;

int yywrap()

import_level–;

cerr « "done "; if (import_level == 0) cerr « endl;

if (import_level == 0 && interactive) quiet = false;

if (!quiet) cerr « "prompt> ";

YY_BUFFER_STATE current = YY_CURRENT_BUFFER;

yy_delete_buffer(current);

if (import_stack.size())

yy_switch_to_buffer(import_stack.top());

import_stack.pop();

return 0;

return 1;

int mywrap()

if (interactive) quiet = false;

if (!quiet) cerr « "prompt> ";

// YY_BUFFER_STATE current = YY_CURRENT_BUFFER;

// yy_delete_buffer(current);

if (import_stack.size())

yy_switch_to_buffer(import_stack.top());

import_stack.pop();

return 0;

return 1;

// »

Defines:mywrap, used in chunk 156.switchBuffer, used in chunk 157.

Comment 4.1.3. The option yylineno allows us to track down the line number of an offendingcommand.

155b 〈flex options 155b〉≡ (152)

%option yylineno

Page 159: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 156

4.1.2 Parser

156 〈escher-parser.y 156〉≡%

#include <iostream>

#include <vector>

#include <stack>

#include <string>

#include "global.h"

#include "io.h"

#include "terms.h"

#include "unification.h"

#include "pattern-match.h"

#include "tableaux.h"

using namespace std;

#define YYMAXDEPTH 50000

extern int yylex(); extern FILE * yyin;

extern bool quiet; extern bool interactive;

int mywrap();

int yyparse();

〈parser::function declarations 157d〉〈parser::variables 157c〉%

%union char * name;

int cname;

float num;

int numint;

term * trm;

type * c_type;

condition * cond;

〈yacc token definitions 154d〉%type <trm> term_schema

%type <cond> sv_condition

%type <c_type> type

%type <name> dataconstructor

%type <cname> functionsymbol

%type <numint> stmt_ctrl_directive

%%

input : program_statements ;

program_statements : /* empty */ | program_statements program_statement ;

program_statement : import | type_info | statement_schema | query | quit ;

〈parser::quit 157a〉〈parser::import 157b〉〈parser::query 158a〉〈parser::type info 173〉〈parser::statement schema 162〉〈parser::term schema 167〉

Page 160: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 157

%%

〈parser::error reporting 177a〉〈bach main program 176a〉

Uses mywrap 155a.

Comment 4.1.4. Quiting is easy. We clean up the memory occupied by the program and thenexit.

157a 〈parser::quit 157a〉≡ (156)

quit : QUIT ’;’ cout « "Quiting...\n"; cleanup(); exit(0); ;

Uses cleanup 177b 177c.

Comment 4.1.5. We allow nested import statements. See Comment 4.1.2 on how this works.

157b 〈parser::import 157b〉≡ (156)

import : IMPORT FILENAME ’;’

if (imported.find($2) == imported.end())

FILE * in = fopen($2, "r");

if (!in)

cerr « "Error reading from " « $2 « endl; assert(false);

quiet = true;

switchBuffer(in); cerr « " Reading " « $2 « "...";

imported.insert($2); //$

;

Uses switchBuffer 155a.

157c 〈parser::variables 157c〉≡ (156) 166b ⊲

#include <set>

set<string> imported;

157d 〈parser::function declarations 157d〉≡ (156) 176c ⊲

extern int switchBuffer(FILE * in);

Uses switchBuffer 155a.

Page 161: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 158

Comment 4.1.6. This is where a computation/proof starts.

158a 〈parser::query 158a〉≡ (156)

query : ’:’ term_schema query_context ’;’

〈parser::perform a computation 158b〉if (answer) answer->freememory();

queryModalContext.clear();

| ’:’ term_schema query_context ’|’ CACHE ’;’

term * oquery = $2->clone();

〈parser::perform a computation 158b〉〈parser::cache computed result 159〉queryModalContext.clear();

| 〈theorem-proving queries 160a〉;

query_context : /* empty */ | USING modalities ;

modalities : BOX DATA_CONSTRUCTOR_INT queryModalContext.push_back($2);

| modalities BOX DATA_CONSTRUCTOR_INT

queryModalContext.push_back($3);

;

Comment 4.1.7. Given a query, we repeatedly simplify it using reduce until nothing can bedone anymore. If the end result is a data constructor, we print it. Otherwise, we seek the help ofthe theorem prover to simplify it further. See Comment 4.1.12 for further details.

The variable tried is the total number of redexes tried throughout the computation.

158b 〈parser::perform a computation 158b〉≡ (158a)

bool program_okay = typeCheck();

type * ret = NULL;

term * answer = NULL;

if (program_okay && typecheck) ret = wellTyped($2);

if (program_okay)

if (ret) delete_type(ret);

〈parser::query::output query 161b〉while (true)

bool change1 = $2->reduceRpt(true);

if ($2->isD()) break;

if (interrupted) interrupted = false; break;

/* query::seek help from TP */

bool change2 = $2->simplifyWithTP();

if (!change1 && !change2) break;

answer = $2;

〈parser::query::output result 161c〉 else

cerr « " Error: Query not evaluated.\n";

if (!quiet) cerr « "prompt> ";

Comment 4.1.8. Here we form a statement from the original query and the answer obtainedand insert that into the cacheStatements vector.

Page 162: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 159

159 〈parser::cache computed result 159〉≡ (158a)

statementType st;

term * head = oquery;

term * body = answer;

term * p = newT2Args(F, iEqual); p->initT2Args(head, body);

st.stmt = p;

〈parser::preprocess statements 165a〉term * leftmost = head->spineTip(st.numargs);

if (leftmost->isF())

st.anchor = leftmost->cname;

insert_ftable(leftmost->cname, st.numargs);

cachedStatements.push_back(st);

Page 163: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 160

Comment 4.1.9. A theorem-proving query can take several forms, depending on the premiseswe want to use.

160a 〈theorem-proving queries 160a〉≡ (158a)

’:’ TURNSTILE term_schema ’;’

typeCheck();

vector<pair<term *, bool> > tlist;

int osel = getSelector(); setSelector(STDOUT);

ioprint("Prove |- "); $3->print(); ioprintln();

term * goalf = $3;

〈parser::insert goal formula into tableau 160b〉〈parser::prove a theorem 161a〉

| ’:’ KB TURNSTILE term_schema ’;’

typeCheck();

vector<pair<term *,bool> > tlist; // ’

int osel = getSelector(); setSelector(STDOUT);

ioprint("Prove KB |- "); $4->print(); ioprintln();

for (uint i=0; i!=formulas.size(); i++)

term * fml = formulas[i].fml->clone()->normalise();

pair<term *, bool> ent(fml, formulas[i].globalass);

tlist.push_back(ent);

term * goalf = $4;

〈parser::insert goal formula into tableau 160b〉〈parser::prove a theorem 161a〉

| ’:’ ’’ term_schemas_product ’’ TURNSTILE term_schema ’;’

typeCheck();

vector<pair<term *,bool> > tlist;

int osel = getSelector(); setSelector(STDOUT);

ioprint("Prove ");

int i = temp_fields.size()-1;

while (temp_fields[i] != NULL)

pair<term *, bool>

ent(temp_fields[i]->normalise(),false);

tlist.push_back(ent);

temp_fields[i]->print(); ioprint(" "); i–;

ioprint(" |- "); $6->print(); ioprintln();

term * goalf = $6;

〈parser::insert goal formula into tableau 160b〉〈parser::prove a theorem 161a〉

Comment 4.1.10. Here we negate the formula we want to prove and then insert that into thetableau.

160b 〈parser::insert goal formula into tableau 160b〉≡ (160a)

term * goal = new_term(APP);

goal->insert(new_term(F, iNot)); goal->insert(goalf);

pair<term *,bool> gent(goal->normalise(), false);

tlist.push_back(gent);

Comment 4.1.11. Here we set up the initial tableau and then extend it.

Page 164: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 161

161a 〈parser::prove a theorem 161a〉≡ (160a)

Tableaux tab(tlist);

// setSelector(STDERR);ioprint("Init tableau ");tab.print();setSelector(SILENT);

TruthValue ret = tab.expand();

setSelector(STDOUT);

ioprint("Attempted Proof:\n "); tab.print();

ioprint(" Answer : "); ret.print(); ioprintln(" ;");

ioprint(" Theta(s) : "); tab.printInst(); ioprintln(" ; \n");

setSelector(osel);

tab.freememory();

if (!quiet) cerr « "prompt> ";

161b 〈parser::query::output query 161b〉≡ (158b)

int osel = getSelector(); setSelector(STDOUT);

ioprint(" Query: "); $2->print(); ioprintln(); /*$*/

setSelector(osel);

161c 〈parser::query::output result 161c〉≡ (158b)

// cout « "Total candidate redexes tried = " « tried « endl;

int osel2 = getSelector(); setSelector(STDOUT);

ioprint("Steps = "); ioprint(ltime);

if (optimise) ioprint(" ("); ioprint(cltime); ioprint(" cached step(s))");

ioprint("\n Answer: ");

answer->print(); ioprintln(" ;");

setSelector(osel2);

// answer->freememory();

ltime = 0; cltime = 0;

cerr « "prompt> ";

Page 165: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 162

Comment 4.1.12. We currently send the whole query term to the theorem prover. What weneed to do is actually to identify a formula t that occurs as a subterm in the query term and getthe theorem prover to prove the validity of either t or ¬t. We should then substitute t with theappropriate value and reduce the simplified query term further.

The query term is put onto a stack so that we can retrieve it further. A stack also allowsdeeper nesting of recursive calls between Escher and TP. We do not currently need that.

Here, we have the code for sending a query message to the theorem prover. Possible responsesfrom the theorem prover are discussed in Comment 4.1.13.

Comment 4.1.13. There are two possible responses from the theorem prover after a querymessage is sent from Escher. We can get an answer back straightaway, in which case we pop thestack and perform the necessary surgery on the query term.

We can also get a query message from the theorem prover asking for a term to be evaluated.This can potentially involve further calls to the theorem prover. At present, we just reduce theterm and then give the result back to the theorem prover.

Comment 4.1.14. We now move on to the parsing of Escher statements. Two different kinds ofinput are supported. We can accept a vanilla Escher statement with syntactic variables in it. Wecan also accept Bach input equations. System defined statements are used in Alkemy for otherpurposes.

162 〈parser::statement schema 162〉≡ (156)

statement_schema :

term_schema MYEQ term_schema ’;’

〈escher-parser::statement schema 163〉

| term_schema MYEQ term_schema ’;’ stmt_ctrl_directive ’;’

〈escher-parser::statement schema 163〉〈statement schema::control directives 165b〉

| term_schema MYEQ term_schema ’;’

TYPEOF ’(’ DATA_CONSTRUCTOR_INT ’)’ ’~’ type ’;’

〈escher-parser::statement schema typeof 166a〉

| term_schema ’;’

〈bach-parser::bach formula 166c〉

| term_schema ’;’ stmt_ctrl_directive ’;’

〈bach-parser::bach formula 166c〉int lis = statements.size()-1; int lif = formulas.size()-1;

if ($3 == LASTRESORT)

if (bftype == INEQ) statements[lis].lastresort = true;

if ($3 == GLOBALASS)

if (bftype == RULE) formulas[lif].globalass = true;

;

stmt_ctrl_directive : EAGER $$ = EAGER ;

| LASTRESORT $$ = LASTRESORT;

| CACHE $$ = CACHE;

| PERSISTENT $$ = PERSISTENT;

| GLOBALASS $$ = GLOBALASS;

;

Comment 4.1.15. In the case of Escher statements, we just put the head and the body togetherand do the necessary preprocessing.

Page 166: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 163

163 〈escher-parser::statement schema 163〉≡ (162 166a)

statementType st; term * head = $1; term * body = $3;

〈parser::make sure statement head has the right form 164〉st.stmt = newT2Args(F, iEqual); st.stmt->initT2Args(head, body);

〈parser::preprocess statements 165a〉statements.push_back(st);

Page 167: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 164

Comment 4.1.16. Here, we check that

1. Every free variable appearing in the body also appears in the head. This is part of thetype-weaker condition on statements.

2. Every free variable in the head appears exactly once.

3. Every lambda variable in the head is unique. This extra condition is needed to make surethe preprocessing code behaves alright.

164 〈parser::make sure statement head has the right form 164〉≡ (163)

term * leftmost = head->spineTip(st.numargs); //$

assert(leftmost->isF());

// make sure all free variables in body appears in the head

// head->labelVariables(0); body->labelVariables(0);

head->getFreeVars(); body->getFreeVars();

for (uint i=0; i!=body->myfreevars.size(); i++)

if (body->myfreevars[i] == -5) continue;

bool done = false;

for (uint j=0; j!=$1->myfreevars.size(); j++)

if (body->myfreevars[i] == head->myfreevars[j])

done = true; break;

if (!done)

setSelector(STDERR);

cerr « " *** Error parsing statement: ";

head->print(); cerr « " = "; body->print(); cerr « endl;

cerr « "Variable " « getString(body->myfreevars[i]) «

" free in body but not free in head.\n";

assert(false);

// make sure every free variable occurs only once in the head

for (uint i=0; i!=head->myfreevars.size(); i++)

if (head->myfreevars[i] == -5) continue;

for (uint j=i+1; j!=head->myfreevars.size(); j++)

if (head->myfreevars[i] == head->myfreevars[j])

setSelector(STDERR);

cerr « " *** Error parsing statement: ";

head->print(); cerr « " = "; body->print(); cerr«endl;

cerr « "Variable " « head->myfreevars[i] «

" occurs multiple times in head.\n";

assert(false);

// make sure lambda variables in the head are unique

multiset<int> lvars; head->collectLambdaVars(lvars);

multiset<int>::iterator p = lvars.begin();

while (p != lvars.end())

if (lvars.count(*p) > 1)

setSelector(STDERR);

cerr « " *** Error parsing statement: ";

head->print(); cerr « " = "; body->print(); cerr«endl;

cerr « "Lambda variable " « getString(*p) «

" occurs multiple times in head.\n";

Page 168: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 165

assert(false);

p++;

// everything is okay now

st.anchor = leftmost->cname;

insert_ftable(leftmost->cname, st.numargs);

Comment 4.1.17. Here we perform different kinds of preprocessing on statements talked aboutin §2.1.3.1 and other places.

165a 〈parser::preprocess statements 165a〉≡ (159 163 166c)

head->labelStaticBoundVars(); body->labelStaticBoundVars();

st.stmt->collectSharedVars();

head->collectFreeVars(st.stmt, 1);

head->precomputeFreeVars();

Comment 4.1.18. We have some control directives that can be used to control the evaluationorder of Bach.

We provide a mechanism to specify that certain statements should be evaluated in an eagerfashion. The default Escher evaluation strategy is a lazy one: the leftmost outermost redex ispicked at any step. The eager strategy stipulates that the leftmost innermost redex be picked atany step. Eager statements are processed in the same way as normal statements. We just set a tagto say the statement is eager. The try_match_n_reduce function will take this tag into accountwhen doing computations.

Some statements in the booleans module increase the length of the resulting term. (This isusually the case for user-defined statements, but statements in the booleans module, in most cases,should not be like that.) We provide here a mechanism to delay the application of such statementsas a last resort.

We provide a control directive to specify that computations involving certain functions shouldbe cached to improve efficiency.

We need persistent objects in database applications. This is done by labelling certain state-ments as persistent. The RHS of a persistent statement can change during run-time.

165b 〈statement schema::control directives 165b〉≡ (162)

int li = statements.size()-1;

if ($5 == EAGER) statements[li].eager = true;

if ($5 == LASTRESORT) statements[li].lastresort = true;

if ($5 == PERSISTENT) statements[li].persistent = true;

if ($5 == CACHE) cacheFuncs.insert(leftmost->cname);

Comment 4.1.19. In addition to side conditions on syntactic variables, we also have side con-ditions on the type of subterms residing in the head of statements. This mechanism is designedto allow us to overload a function with definitions that are type dependent. For example, supposewe want to write a function card : (a− > Bool)− > Nat to compute the cardinality of a givenset. Depending on the actual type of a, we may have different definitions. For example, we mayhave

(card λx .v) = (card (enumerate2D λx .v)); if λx.v : (a× b)→ Ω

(card λx .v) = (card (enumerate3D λx .v)); if λx.v : (a× b× c)→ Ω

if we have different ways of enumerating sets of tuples. The typeof(x) ∼ atype construct allowsus to specify such side conditions. Here, x is an integer that specifies the location of a subterminside the statement. In particular, x is the label given to the node in the syntax tree representing

Page 169: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 166

the subterm using a postorder traversal of the tree. The condition typeof(x) ∼ atype is satisfiedif the relative type of the subterm at position x unifies with atype.

Here, we just add the type condition to the statement. This will be checked during patternmatching; see Comment 2.1.62.

166a 〈escher-parser::statement schema typeof 166a〉≡ (162)

〈escher-parser::statement schema 163〉statements[statements.size()-1].tycond = new type_condition($7,$10);

Comment 4.1.20. We now look at Bach formulas. A Bach input equation has the general formj1j2 . . . jk

∀(u = v).

166b 〈parser::variables 157c〉+≡ (156) ⊳ 157c 169d ⊲

enum BFType INEQ, RULE, FACT ;

166c 〈bach-parser::bach formula 166c〉≡ (162)

BFType bftype;

statementType st; term * head = NULL, * body = NULL, * p = NULL;

〈parser::check that the formula has the right form 166d〉if (bftype == INEQ || bftype == FACT)

st.stmt = p;

〈parser::preprocess statements 165a〉term * leftmost = head->spineTip(st.numargs);

if (leftmost->isF())

st.anchor = leftmost->cname;

insert_ftable(leftmost->cname, st.numargs);

statements.push_back(st);

formulaType ft; ft.fml = $1;

formulas.push_back(ft);

Comment 4.1.21. After this step, we have

166d 〈parser::check that the formula has the right form 166d〉≡ (166c)

p = $1;

while (p->isModal()) st.modalContext.push_back(p->modality); p=p->fields[0];

while (p->isApp() && p->fields[0]->isF(iPi))

st.quantifiedVars.push_back(p->fields[1]->fields[0]->cname);

p = p->fields[1]->fields[1];

if (p->isFunc2Args(iEqual)) bftype = INEQ;

else if (p->isFunc2Args(iImplies)) bftype = RULE;

else bftype = FACT;

if (bftype == INEQ)

p = p->clone(); head = p->lc()->rc(); body = p->rc();

else if (bftype == FACT)

head = p->clone(); body = new_term(D, iTrue);

p = newT2Args(F, iEqual); p->initT2Args(head, body);

Page 170: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 167

Comment 4.1.22. We next look at term schemas.

167 〈parser::term schema 167〉≡ (156)

term_schema : SYN_VARIABLE $$ = new_term(SV, insert_symbol($1));

| SYN_VARIABLE sv_condition

$$=new_term(SV, insert_symbol($1)); $$->cond = $2;

| VARIABLE $$ = new_term(V, insert_symbol($1));

| SIGMA $$ = new_term(F, iSigma);

| PI $$ = new_term(F, iPi);

| AND $$ = new_term(F, iAnd);

| OR $$ = new_term(F, iOr);

| NOT $$ = new_term(F, iNot);

| IMPLIES $$ = new_term(F, iImplies);

| ITE $$ = new_term(F, iIte);

| IFF $$ = new_term(F, iIff);

| ADD $$ = new_term(F, iAdd);

| SUB $$ = new_term(F, iSub);

| MAX $$ = new_term(F, iMax);

| MIN $$ = new_term(F, iMin);

| MUL $$ = new_term(F, iMul);

| DIV $$ = new_term(F, iDiv);

| MOD $$ = new_term(F, iMod);

| SIN $$ = new_term(F, iSin);

| COS $$ = new_term(F, iCos);

| SQRT $$ = new_term(F, iSqrt);

| EXP $$ = new_term(F, iExp);

| ATAN2 $$ = new_term(F, iAtan2);

| MYLT $$ = new_term(F, iLT);

| MYLTE $$ = new_term(F, iLTE);

| MYGT $$ = new_term(F, iGT);

| MYGTE $$ = new_term(F, iGTE);

| MYEQ $$ = new_term(F, iEqual);

| MYNEQ $$ = new_term(F, iNEqual);

| ASSIGN $$ = new_term(F, iAssign);

| TPHELP $$ = new_term(F, iTpHelp);

| TPTAG $$ = new_term(D, iTpTag);

| TRUE $$ = new_term(D, iTrue);

| FALSE $$ = new_term(D, iFalse);

| CONS $$ = new_term(D, iHash);

| EMPTYLIST $$ = new_term(D, iEmptyList);

| DATA_CONSTRUCTOR_INT $$ = new_term_int($1);

| DATA_CONSTRUCTOR_FLOAT $$ = new_term_float($1);

| DATA_CONSTRUCTOR_CHAR

int code = insert_symbol($1); $$ = new_term(D, code);

| 〈term schema::strings 168a〉| IDENTIFIER1 $$ = new_term(F, insert_symbol($1));

| IDENTIFIER2 $$ = new_term(D, insert_symbol($1));

| ’\\’ VARIABLE ’.’ term_schema

$$ = new_term(ABS);

$$->insert(new_term(V,insert_symbol($2))); $$->insert($4);

| ’\\’ SYN_VARIABLE ’.’ term_schema

$$ = new_term(ABS);

$$->insert(new_term(SV,insert_symbol($2))); $$->insert($4);

| 〈term schema::if-then-else statements 168b〉

Page 171: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 168

| 〈term schema::existential statements 169a〉| 〈term schema::universal statements 169b〉| BOX DATA_CONSTRUCTOR_INT term_schema

$$ = new_term(MODAL); $$->modality = $2; $$->insert($3);

| ’(’ term_schema term_schema ’)’

$$ = new_term(APP); $$->insert($2); $$->insert($3);

| 〈term schema::syntactic sugar 169e〉| ’(’ ’)’ $$ = new_term(PROD);

| 〈term schema::products 170b〉| 〈term schema::sets 171〉| 〈term schema::lists 172〉;

〈parser::term schemas 170a〉〈parser::term schema products 170c〉〈parser::sv condition 169c〉

Comment 4.1.23. We have two kinds of strings: the first kind atomic; the second, composite.An atomic string is a single data constructor; just like characters, one can only define equalityfunction on atomic strings, nothing else.

To define functions like substring that access the individual characters of a string, we need torepresent a string as a composite object. A natural thing to do here is to represent a string as alist of characters. An atomic string is written "This is a string". A composite string is writtenStrList "This is a string".

168a 〈term schema::strings 168a〉≡ (167)

DATA_CONSTRUCTOR_STRING

int code = insert_symbol($1); strings.insert(code);

$$ = new_term(D, code);

| STRLIST DATA_CONSTRUCTOR_STRING

string x($2); int size = x.size();

term * elist = new_term(D, iEmptyList);

for (int i=size-2; i!=0; i–)

term * temp = newT2Args(D, iHash);

string character("’"); character += x[i]; character += "’";

int code = insert_symbol(character);

temp->initT2Args(new_term(D, code), elist);

elist = temp;

$$ = elist; /*$*/

Comment 4.1.24. We provide syntactic sugar for writing if-then-else statements here. Thefunction have the following signature:

if − then − else : Ω × a× a→ a.

Note that the domain is a tuple – this function should not be written in curried form.

168b 〈term schema::if-then-else statements 168b〉≡ (167)

IF term_schema THEN term_schema ELSE term_schema

$$ = new_term(APP);

$$->insert(new_term(F, iIte));

term * temp = new_term(PROD);

temp->insert($2); temp->insert($4); temp->insert($6);

$$->insert(temp);

Page 172: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 169

Comment 4.1.25. We provide syntactic sugars for writing existentially and universally quantifiedstatements in a natural way.

169a 〈term schema::existential statements 169a〉≡ (167)

’\\’ EXISTS VARIABLE ’.’ term_schema

$$ = new_term(APP); $$->insert(new_term(F, iSigma));

term * abs = new_term(ABS);

abs->insert(new_term(V, insert_symbol($3))); abs->insert($5);

$$->insert(abs);

169b 〈term schema::universal statements 169b〉≡ (167)

’\\’ FORALL VARIABLE ’.’ term_schema

$$ = new_term(APP); $$->insert(new_term(F, iPi));

term * abs = new_term(ABS);

abs->insert(new_term(V, insert_symbol($3))); abs->insert($5);

$$->insert(abs);

Comment 4.1.26. There is a small language for imposing side conditions on syntactical variables.See Comment 1.2.25.

169c 〈parser::sv condition 169c〉≡ (167)

sv_condition : ’/’ VAR ’/’ $$ = new condition; $$->tag = CVAR;

| ’/’ CONST ’/’ $$ = new condition; $$->tag = CCONST;

| ’/’ EQUAL ’,’ SYN_VARIABLE ’/’

$$ = new condition; $$->tag = CEQUAL;

$$->svname = insert_symbol($4);

| ’/’ NOTEQUAL ’,’ SYN_VARIABLE ’/’

$$ = new condition; $$->tag = CNOTEQUAL;

$$->svname = insert_symbol($4);

;

Comment 4.1.27. A function applied to multiple arguments is painful to write. Here weintroduce a syntactic sugar to allow users to write terms of the form (f t1 . . . tn) to mean(· · · (f t1) · · · tn). The following variable is needed to remember terms.

169d 〈parser::variables 157c〉+≡ (156) ⊳ 166b 174b ⊲

vector<term *> temp_fields;

169e 〈term schema::syntactic sugar 169e〉≡ (167)

’(’ term_schema term_schema term_schemas ’)’

$$ = new_term(APP); $$->insert($2); $$->insert($3);

int size = temp_fields.size(); int psize = 0;

while (temp_fields[size-1-psize] != NULL) psize++;

term * temp;

for (int i=size-psize; i!=size; i++)

temp = new_term(APP);

temp->insert($$);

temp->insert(temp_fields[i]);

$$ = temp;

while (psize+1) temp_fields.pop_back(); psize–;

Page 173: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 170

170a 〈parser::term schemas 170a〉≡ (167)

term_schemas : term_schema

temp_fields.push_back(NULL); // start a new mult app

temp_fields.push_back($1);

| term_schemas term_schema

temp_fields.push_back($2);

;

Comment 4.1.28. Products are handled in about the same way, except that we do not have toconstruct application nodes.

170b 〈term schema::products 170b〉≡ (167)

’(’ term_schemas_product ’)’

$$ = new_term(PROD);

int size = temp_fields.size(); int psize = 0;

while (temp_fields[size-1-psize] != NULL) psize++;

for (int i=size-psize; i!=size; i++) $$->insert(temp_fields[i]);

while (psize+1) temp_fields.pop_back(); psize–;

170c 〈parser::term schema products 170c〉≡ (167)

term_schemas_product : term_schema

temp_fields.push_back(NULL); // start a new product

temp_fields.push_back($1);

| term_schemas_product ’,’ term_schema

temp_fields.push_back($3);

;

Page 174: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 171

Comment 4.1.29. We also provide syntactic sugar for extensional sets.

171 〈term schema::sets 171〉≡ (167)

’’ ’’

$$ = new_term(ABS); $$->insert(new_term(V, newPVar()));

$$->insert(new_term(D, iFalse));

| ’’ term_schemas_product ’’

int pv = newPVar();

$$ = new_term(ABS); $$->insert(new_term(V, pv));

term * arg2 = new_term(D, iFalse);

int i = temp_fields.size()-1;

while (temp_fields[i] != NULL)

term * ite = new_term(APP);

ite->insert(new_term(F, iIte));

ite->insert(new_term(PROD));

term * eq = newT2Args(F, iEqual);

eq->initT2Args(new_term(V, pv), temp_fields[i]);

ite->fields[1]->insert(eq);

ite->fields[1]->insert(new_term(D, iTrue));

ite->fields[1]->insert(arg2);

arg2 = ite;

i–;

$$->insert(arg2); // $

setSelector(STDERR); $$->print(); ioprintln(); setSelector(SILENT);

int size = temp_fields.size(); int psize = 0;

while (temp_fields[size-1-psize] != NULL) psize++;

while (psize+1) temp_fields.pop_back(); psize–;

Page 175: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 172

Comment 4.1.30. In the good tradition of functional programmingm we provide syntactic sugarfor lists as well.

172 〈term schema::lists 172〉≡ (167)

’[’ ’]’

| ’[’ term_schemas_product ’]’

int tsize = temp_fields.size();

term * tail = newT2Args(D, iHash);

tail->initT2Args(temp_fields[tsize-1], new_term(D, iEmptyList));

int i = tsize - 2;

while (temp_fields[i] != NULL)

term * current = newT2Args(D, iHash);

current->initT2Args(temp_fields[i], tail);

tail = current;

i–;

$$ = tail;

int size = temp_fields.size(); int psize = 0;

while (temp_fields[size-1-psize] != NULL) psize++;

while (psize+1) temp_fields.pop_back(); psize–;

Page 176: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 173

Comment 4.1.31. We need to declare the signature of every constant we use. This informationis need for proper type checking of the program.

173 〈parser::type info 173〉≡ (156) 174a ⊲

type_info : functionsymbol ’:’ type ’;’ insert_constant($1, $3);

| constructordecl

| syndecl

| functionsymbol ’:’ NONRIGID ’;’ insert_nonrigid_constant($1);

;

functionsymbol : IDENTIFIER1 $$ = insert_symbol($1);

| SIGMA $$ = iSigma;

| PI $$ = iPi;

| AND $$ = iAnd;

| OR $$ = iOr;

| NOT $$ = iNot;

| IMPLIES $$ = iImplies;

| ITE $$ = iIte;

| IFF $$ = iIff;

| ADD $$ = iAdd;

| SUB $$ = iSub;

| MAX $$ = iMax;

| MIN $$ = iMin;

| MUL $$ = iMul;

| DIV $$ = iDiv;

| MOD $$ = iMod;

| MYLT $$ = iLT;

| MYLTE $$ = iLTE;

| MYGT $$ = iGT;

| MYGTE $$ = iGTE;

| MYEQ $$ = iEqual;

| MYNEQ $$ = iNEqual;

| ASSIGN $$ = iAssign;

| TPHELP $$ = iTpHelp;

| TPTAG $$ = iTpTag;

| TRUE $$ = iTrue;

| FALSE $$ = iFalse;

| CONS $$ = iHash;

| EMPTYLIST $$ = iEmptyList;

;

Page 177: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 174

Comment 4.1.32. A collection of data constructors with a common signature can be declared bylisting them followed by their common signature. We need to insert the signature as a user-definedtype here so that we can recognise it when we see it again later. Each data constructor togetherwith its signature is recorded for later type checking use as well.

174a 〈parser::type info 173〉+≡ (156) ⊳ 173 174c ⊲

constructordecl : dataconstructors ’:’ type ’;’

string tname($3->getName());

type * t = new type_udefined(tname, vec_constants);

if ($3->isUdefined())

insert_type(tname, UDEFINED, t);

insert_constant(insert_symbol(vec_constants[0]), $3);

for (uint i=1; i!=vec_constants.size(); i++)

insert_constant(insert_symbol(vec_constants[i]),

$3->clone());

vec_constants.clear();

// if (!quiet) cerr « "prompt> ";

;

dataconstructors : dataconstructor vec_constants.push_back($1);

| dataconstructors ’,’ dataconstructor

vec_constants.push_back($3);

;

dataconstructor : IDENTIFIER2 $$ = $1;

| DATA_CONSTRUCTOR $$ = $1;

;

Comment 4.1.33. We record the list of data constructors in this temporary vector.

174b 〈parser::variables 157c〉+≡ (156) ⊳ 169d 175b ⊲

vector<string> vec_constants;

174c 〈parser::type info 173〉+≡ (156) ⊳ 174a 175a ⊲

syndecl : TYPE IDENTIFIER2 MYEQ type ’;’

string t($2); insert_type(t,SYNONYM,$4);

;

Page 178: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 175

Comment 4.1.34. We now give the grammar for types. The -> function-forming operator isright associative; the * product-forming operator is left associative.

There are six system-defined types: Bool, Int, Float, Char, String and ListString. Thefirst five are atomic types. The type ListString is translated into (List Char) by the system.

175a 〈parser::type info 173〉+≡ (156) ⊳ 174c

type : IDENTIFIER1 string tname($1); $$ = new type_parameter(tname);

| BOOL $$ = new type("Bool");

| INT $$ = new type("Int");

| FLOAT $$ = new type("Float");

| CHAR $$ = new type("Char");

| STRING $$ = new type("String");

| LISTRING $$ = new type_alg("List"); $$->addAlpha(new type("Char"));

| IDENTIFIER2

string tname($1);

pair<int,type *> p = get_type(tname);

if (p.second == NULL) $$ = new type_udefined(tname);

else if (p.first == UDEFINED) $$ = p.second->clone();

else $$ = new type_synonym(tname, p.second->clone());

| ’(’ IDENTIFIER2 types ’)’

string tname($2);

type_tuple * rem = dcast<type_tuple *>(tempTuples.top());

tempTuples.pop();

$$ = new type_alg(tname, rem);

delete_type(rem);

| ’(’ products ’)’ $$ = tempTuples.top(); tempTuples.pop();

| type ARROW type $$ = new type_abstraction($1, $3);

| ’(’ type ’)’ $$ = $2;

;

products : products ’*’ type tempTuples.top()->addAlpha($3);

| type ’*’ type tempTuples.push(new type_tuple);

tempTuples.top()->addAlpha($1);

tempTuples.top()->addAlpha($3);

;

types : type

tempTuples.push(new type_tuple); tempTuples.top()->addAlpha($1);

| types type

tempTuples.top()->addAlpha($2);

;

175b 〈parser::variables 157c〉+≡ (156) ⊳ 174b 176b ⊲

stack<type *> tempTuples;

175c 〈yacc token definitions 154d〉+≡ (156) ⊳ 154d

%right ARROW

%left ’*’

Page 179: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 176

4.1.3 Bach Main Program

176a 〈bach main program 176a〉≡ (156) 177c ⊲

#ifndef __APPLE__

#ifndef __sun

#include <getopt.h>

#endif

#endif

#include <signal.h>

static void handle_signal(int sig)

cout « "Interrupted....\n"; interrupted = true;

int main(int argc, char ** argv)

interactive = false; quiet = true;

char c;

while ((c = getopt(argc, argv, "vitobds")) != EOF)

switch (c)

case ’v’: verbose++; break;

case ’i’: interactive = true; break;

case ’t’: typecheck = false; break;

case ’o’: optimise = true; break;

case ’b’: backchain = true; break;

case ’d’: outermost = true; break;

case ’s’: stepByStep = true; break;

if (verbose) setSelector(STDOUT); else setSelector(SILENT);

initFuncTable();

initialise_constants();

signal(SIGINT, handle_signal);

logcache = fopen("log.cache", "r+"); assert(logcache);

if (interactive) cerr « "prompt> ";

do yyparse(); while (!feof(yyin));

fclose(logcache);

cleanup();

return 0;

Uses cleanup 177b 177c.

Comment 4.1.35. Error reporting is not quite right yet. The line number reported is wrongbecause of nested imports.

176b 〈parser::variables 157c〉+≡ (156) ⊳ 175b

extern int yylineno; extern char * yytext; extern char linebuf[500];

extern int tokenpos;

Page 180: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

4.1. PARSING USING LEX AND YACC 177

176c 〈parser::function declarations 157d〉+≡ (156) ⊳ 157d 177b ⊲

void yyerror(const char * s);

177a 〈parser::error reporting 177a〉≡ (156)

void yyerror(const char * s)

cerr « yylineno « ": " « s « " at " « yytext « " in this line\n";

cerr « linebuf « endl;

for (int i=0; i!=tokenpos-1; i++) cerr « " ";

cerr « "^" « endl;

if (!quiet) cerr « "prompt> ";

Comment 4.1.36. This function frees the memory held by the program modules.

177b 〈parser::function declarations 157d〉+≡ (156) ⊳ 176c

void cleanup();

Defines:cleanup, used in chunks 157a and 176a.

177c 〈bach main program 176a〉+≡ (156) ⊳ 176a

void cleanup()

cleanup_statements(); cleanup_formulas();

cleanup_constants(); cleanup_synonyms();

mem_report();

Defines:cleanup, used in chunks 157a and 176a.

Page 181: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

Chapter 5

Global Data Structures

178 〈global.h 178〉≡#ifndef _ESCHER_GLOBAL_H_

#define _ESCHER_GLOBAL_H_

#include <vector>#include <string>#include <set>#include "terms.h"

#include "types.h"

#include "unification.h"

// this is used to record side conditions on types for statementsstruct type_condition

int sterm;type ∗ dtype;type_condition(int t, type ∗ d) sterm = t; dtype = d; void freememory() delete_type(dtype);

;// these are the escher statementsstruct statementType

vector<int> modalContext; // this is used in Bach onlyvector<int> quantifiedVars; // this is used in Bach onlyterm ∗ stmt;int numargs;int anchor;bool typechecked;bool lastresort;bool eager;bool persistent;type_condition ∗ tycond;statementType()

anchor = -5; typechecked = false; lastresort = false;eager = false; persistent = false; tycond = NULL;

void freememory()

stmt→freememory();if (tycond) tycond→freememory();

;

178

Page 182: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

179

struct formulaType term ∗ fml;bool globalass;formulaType() globalass = false; void freememory() fml→freememory();

;

extern vector<statementType> statements;extern vector<vector<term_type> > stat_term_types;extern vector<formulaType> formulas;extern vector<statementType> cachedStatements;

extern vector<int> queryModalContext;

extern bool typeCheck();extern void cleanup_statements();extern void cleanup_formulas();

extern set<int> cacheFuncs;extern set<int> strings;

extern void initialise_constants();extern void insert_constant(int name, type ∗ sig);extern type ∗ get_signature(int name);extern void cleanup_constants();

extern void insert_nonrigid_constant(int name);extern bool is_rigid_constant(int name);

extern void insert_type(const string & tname, int x, type ∗ tp);extern pair<int, type ∗> get_type(const string & tname);extern void cleanup_synonyms();

/∗ function symbol table ∗/extern void initFuncTable();extern void insert_ftable(int func, int earity);extern pair<int,int> getFuncEArity(int func);extern void print_ftable();

extern int ltime; extern int cltime;/∗ options ∗/extern int verbose; extern bool typecheck;extern bool optimise; extern bool backchain; extern bool outermost;

extern bool interrupted;extern bool stepByStep;

/∗ some global strings ∗/〈global symbol constants 185a〉

extern const string pve;

extern const string substitutionRuleId, negationRuleId, conjunctionRuleId,

Page 183: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

180

disjunctionRuleId, reflexiveRuleId, kRuleId,existentialRuleId, universalRuleId, universalSPImpliesRuleId,bachRuleId, closureRuleId, uclosureRuleId, diamondRuleId, boxRuleId;

extern const string eqsimpl, andsimpl, and2simpl, ineqsimpl, arsimpl,exsimpl, uvsimpl, betasimpl, mathsimpl, modalsimpl;

/∗ symbol table ∗/extern int insert_symbol(const string & symbol);extern const string & getString(int code);

/∗ log files ∗/extern FILE ∗ logcache;

#define UDEFINED 0

#define SYNONYM 1

/∗ miscellaneous functions ∗/#include <sstream>inline std::string numtostring(const int i)

std::stringstream s; s ≪ i; return s.str(); inline std::string numtostring(const double i)

std::stringstream s; s ≪ i; return s.str();

bool inVector(int x, vector<int> & v);bool subset(vector<int> v1, vector<int> v2);

#endif

Uses cleanup_statements 186c, delete_type 2c 3a, get_signature 189d, get_type 193, getFuncEArity 192a,initFuncTable 191a, initialise_constants 188a, insert_constant 189c, insert_ftable 191b,insert_type 193, print_ftable 192b, subset 112b, and term_type 10a.

Page 184: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

181

Comment 5.0.37. The variable ltime records the total number of computation steps taken tosimplify the query. The variable cltime records the total number of steps computed using cachedinformation. Statements in the input Escher program are stored in a vector. Each statement isstored in a structure called statementType. The fields numargs and anchor are used to pick outunsuitable statements during pattern matching. (See Comment 2.1.69 for more details.)

181 〈global.cc 181〉≡#include "global.h"

vector<statementType> statements;vector<vector<term_type> > stat_term_types;vector<formulaType> formulas;vector<statementType> cachedStatements;vector<int> queryModalContext;set<int> cacheFuncs;set<int> chars;set<int> strings;

int ltime = 0; int cltime = 0;int verbose = 0; bool typecheck = true; bool optimise = false;bool backchain = false; bool outermost = false;FILE ∗ logcache = NULL;bool interrupted = false;bool stepByStep = false;

const string underscore = "_";const string alpha = "alpha";const string Parameter = "Parameter";const string Tuple = "Tuple";const string Arrow = "Arrow";const string gBool = "Bool";const string gInt = "Int", gFloat = "Float", gChar = "Char", gString = "String";const string pve = "pve";

const string substitutionRuleId = "=";const string negationRuleId = "~~";const string conjunctionRuleId = "&";const string disjunctionRuleId = "v";const string reflexiveRuleId = "Id";const string existentialRuleId = "E";const string universalRuleId = "U";const string universalSPImpliesRuleId = "USI";const string bachRuleId = "Bc";const string closureRuleId = "C";const string uclosureRuleId = "UI";const string diamondRuleId = "<>";const string boxRuleId = "[]";const string kRuleId = "K";

const string eqsimpl = "Equalities simplification\n";const string andsimpl = "And rule simplification\n";const string and2simpl = "And2 rule simplification\n";const string ineqsimpl = "Inequalities simplification\n";const string arsimpl = "Arithmetic simplification\n";

Page 185: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

182

const string exsimpl = "Existential rule simplification\n";const string uvsimpl = "Universal rule simplification\n";const string betasimpl = "Beta reduction\n";const string mathsimpl = "Math library function call\n";const string modalsimpl = "Modal term simplification\n";

#include <stdlib.h>#include <cassert>#include <set>#include <vector>#include <string>using namespace std;

〈symbols and their integer representations 183〉〈statements and type checking 186a〉〈constants and their signatures 188a〉〈function symbol table 190b〉〈nonrigid constants 192c〉〈type name to type objects mapping 193〉

bool inVector(int x, vector<int> & v) vector<int>::iterator p = find(v.begin(), v.end(), x);return (p 6= v.end());

bool subset(vector<int> v1, vector<int> v2)

int size = v1.size();for (int i=0; i6=size; i++)

if (¬inVector(v1[i], v2)) return false;return true;

// <itoa>

Uses subset 112b and term_type 10a.

Page 186: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

183

Comment 5.0.38. For efficiency reasons, we do not want to deal with the string representationsof symbols in the system. Each symbol is mapped to an integer, and the mappings are recordedhere.

183 〈symbols and their integer representations 183〉≡ (181) 185b ⊲

const int iNot = 1001; const std::string gNot = "not";const int iAnd = iNot + 1; const std::string gAnd = "&&";const int iOr = iAnd + 1; const std::string gOr = "||";const int iImplies = iOr + 1; const std::string gImplies = "implies";const int iIff = iImplies +1; const std::string gIff = "iff";const int iPi = iIff + 1; const std::string gPi = "pi";const int iSigma = iPi + 1; const std::string gSigma = "sigma";const int iEqual = iSigma +1; const std::string gEqual = "=";const int iIte = iEqual + 1; const std::string gIte = "ite";const int iTrue = iIte + 1; const std::string gTrue = "True";const int iFalse = iTrue + 1; const std::string gFalse = "False";const int iHash = iFalse + 1; const std::string gHash = "#";const int iEmptyList = iHash + 1; const std::string gEmptyList = "[]";const int iInfinity = iEmptyList + 1; const std::string gInfinity = "Infinity";const int iAdd = iInfinity + 1; const std::string gAdd = "add";const int iSub = iAdd + 1; const std::string gSub = "sub";const int iMax = iSub + 1; const std::string gMax = "max";const int iMin = iMax + 1; const std::string gMin = "min";const int iMul = iMin + 1; const std::string gMul = "mul";const int iDiv = iMul + 1; const std::string gDiv = "div";const int iMod = iDiv + 1; const std::string gMod = "mod";const int iAtan2 = iMod + 1; const std::string gAtan2 = "atan2";const int iLT = iAtan2 + 1; const std::string gLT = "<";const int iLTE = iLT + 1; const std::string gLTE = "<=";const int iGT = iLTE + 1; const std::string gGT = ">";const int iGTE = iGT + 1; const std::string gGTE = ">=";const int iNEqual = iGTE +1; const std::string gNEqual = "/=";const int iAssign = iNEqual+1; const std::string gAssign = ":=";const int iTpHelp = iAssign+1; const std::string gTpHelp = "tpHelp";const int iTpTag = iTpHelp +1; const std::string gTpTag = "TpTag";const int iSucceeded = iTpTag+1; const std::string gSucceeded = "Succeeded";const int iFailed = iSucceeded+1; const std::string gFailed = "Failed";const int iDontKnow = iFailed+1; const std::string gDontKnow = "DontKnow";const int iSin = iDontKnow+1; const std::string gSin = "sin";const int iCos = iSin + 1; const std::string gCos = "cos";const int iSqrt = iCos + 1; const std::string gSqrt = "sqrt";const int iExp = iSqrt + 1; const std::string gExp = "exp";

vector<string> symbolsMap;vector<string> charsMap; // characters are encoded using numbers in the range

// [2000,2999]int insert_symbol(const string & symbol)

if (symbol[0] ≡ ’\”) for (uint i=0; i6=charsMap.size(); i++)

if (charsMap[i] ≡ symbol) return 2000+i;charsMap.push_back(symbol);int csize = charsMap.size(); assert(csize ≤ 1000);return 2000+csize-1;

Page 187: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

184

for (uint i=0; i6=symbolsMap.size(); i++)if (symbolsMap[i] ≡ symbol) return i+1;

symbolsMap.push_back(symbol);int csize = symbolsMap.size(); assert(csize < 1000);return csize;

const string gError = "Error";const string & getString(int code)

if (0 < code ∧ code ≤ (int)symbolsMap.size())return symbolsMap[code-1];

if (2000 ≤ code ∧ code < 2000+(int)charsMap.size())return charsMap[code];

switch (code) case iNot: return gNot; case iAnd: return gAnd;case iOr: return gOr; case iImplies: return gImplies;case iIff: return gIff; case iPi: return gPi;case iSigma: return gSigma; case iEqual: return gEqual;case iIte: return gIte; case iTrue: return gTrue;case iFalse: return gFalse; case iHash: return gHash;case iEmptyList: return gEmptyList;case iInfinity: return gInfinity;case iAdd: return gAdd;case iSub: return gSub; case iMax: return gMax;case iMin: return gMin; case iMul: return gMul;case iDiv: return gDiv; case iMod: return gMod;case iLT: return gLT; case iLTE: return gLTE;case iGT: return gGT; case iGTE: return gGTE;case iNEqual: return gNEqual; case iAssign: return gAssign;case iTpHelp: return gTpHelp; case iTpTag: return gTpTag;case iSucceeded: return gSucceeded; case iFailed: return gFailed;case iDontKnow: return gDontKnow;case iSin: return gSin;case iCos: return gCos;case iSqrt: return gSqrt;case iExp: return gExp;case iAtan2: return gAtan2;cerr ≪ "code = " ≪ code ≪ endl; assert(false);return gError;

Defines:iAdd, used in chunks 54, 55, 185a, 188b, and 191a.iAnd, used in chunks 47b, 48b, 52a, 53a, 60a, 63, 65c, 66a, 68b, 77, 129b, 131b, 146–50, and 185a.iAssign, used in chunks 85, 185a, 189b, and 191a.iAtan2, used in chunks 54, 185a, 188b, and 191a.iCos, used in chunks 57, 185a, 188b, and 191a.iDiv, used in chunks 55, 185a, 188b, and 191a.iDontKnow, used in chunks 99a and 185a.iEmptyList, used in chunks 28, 51d, 185a, and 188a.iEqual, used in chunks 52a, 53a, 60b, 66b, 76b, 77, 135–38, 146–50, and 185a.iExp, used in chunks 57, 185a, 188b, and 191a.iFailed, used in chunks 185a and 189b.iFalse, used in chunks 45c, 47b, 51d, 53a, 56, 64c, 67c, 111e, 140c, 141a, 145a, 185a, and 188a.iGT, used in chunks 56, 185a, 189a, and 191a.iGTE, used in chunks 56, 185a, 189a, and 191a.iHash, used in chunks 25a, 28b, 185a, and 188a.

Page 188: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

185

iIff, used in chunks 47b and 185a.iImplies, used in chunks 47b, 67b, 129b, and 185a.iInfinity, used in chunks 54, 56, 185a, and 188a.iIte, used in chunks 28c and 185a.iLT, used in chunks 56, 185a, 189a, and 191a.iLTE, used in chunks 56, 185a, 189a, and 191a.iMax, used in chunks 54, 185a, 188b, and 191a.iMin, used in chunks 54, 185a, 188b, and 191a.iMod, used in chunks 54, 185a, 188b, and 191a.iMul, used in chunks 55, 185a, 188b, and 191a.iNEqual, used in chunk 185a.iNot, used in chunks 45, 47, 48, 80, 99a, 129b, 131a, 133b, 141b, and 185a.iOr, used in chunks 47, 48, 129b, 131a, 132a, 139a, 146a, 147c, 149c, and 185a.iPi, used in chunks 27, 47b, 67, 82a, 129b, and 185a.iSigma, used in chunks 27, 47b, 62–64, 129b, 133a, 134b, 139a, and 185a.iSin, used in chunks 57, 185a, 188b, and 191a.iSqrt, used in chunks 57, 185a, 188b, and 191a.iSub, used in chunks 55, 185a, 188b, and 191a.iSucceeded, used in chunks 85, 185a, and 189b.iTpHelp, used in chunks 77, 97, and 185a.iTpTag, used in chunks 97, 99a, and 185a.iTrue, used in chunks 45c, 47b, 51–53, 56, 64–67, 99a, 140c, 147–49, 185a, and 188a.

Uses add 110c.

185a 〈global symbol constants 185a〉≡ (178)

extern const int iNot, iAnd, iOr, iImplies, iIff, iPi, iSigma, iEqual, iIte,iTrue, iFalse, iHash, iEmptyList, iInfinity, iAdd, iSub, iMax, iMin, iMul,iDiv, iMod, iLT, iLTE, iGT, iGTE, iNEqual, iAssign, iTpHelp, iTpTag,iSucceeded, iFailed, iDontKnow, iSin, iCos, iSqrt, iExp, iAtan2;

Uses iAdd 183, iAnd 183, iAssign 183, iAtan2 183, iCos 183, iDiv 183, iDontKnow 183, iEmptyList 183,iEqual 183, iExp 183, iFailed 183, iFalse 183, iGT 183, iGTE 183, iHash 183, iIff 183, iImplies 183,iInfinity 183, iIte 183, iLT 183, iLTE 183, iMax 183, iMin 183, iMod 183, iMul 183, iNEqual 183, iNot 183,iOr 183, iPi 183, iSigma 183, iSin 183, iSqrt 183, iSub 183, iSucceeded 183, iTpHelp 183, iTpTag 183,and iTrue 183.

Comment 5.0.39. We now see how variables are handled. System-generated variables haveinteger representations above 5000. Standard variables generated by the system are encoded usingvalues in the range 5000 to 99999. Fresh variables of this type are obtained using newPVar(). Avariable with code 5013, for example, corresponds to a variable pve13. Free universal variablesgenerated by the universal rule in the theorem proving part of the system are encoded using valuesabove 100000.

185b 〈symbols and their integer representations 183〉+≡ (181) ⊳ 183

static unsigned int varInt = 5000;static unsigned int uvarInt = 100000;int newPVar() assert(varInt < 100000); return varInt++; int newUVar() return uvarInt++;

Defines:newPVar, used in chunks 43c, 95c, 103, 134b, and 135a.newUVar, used in chunks 103 and 133a.

Page 189: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

186

Comment 5.0.40. Here we just systematically go through the statements and type check eachone. We need to record the inferred type for each subterm of the statement. There is a checkto make sure that the indices for statements and stat_term_types matches; that is, the i-thelement in the latter contains information about the i-th element in the former.

186a 〈statements and type checking 186a〉≡ (181) 186c ⊲

bool typeCheck() if (¬typecheck) return true;cerr ≪ "Type checking statements...";int size = statements.size();for (int i=0; i6=size; i++)

if (statements[i].typechecked) continue;pair<type ∗, vector<term_type> > res;res = mywellTyped(statements[i].stmt);type ∗ t = res.first;if (t) delete_type(t);

statements[i].typechecked = true;stat_term_types.push_back(res.second);int k = stat_term_types.size() - 1;if (i 6= k) cerr ≪ "(i,k) = " ≪ i ≪ "," ≪ k ≪endl;assert(i ≡ k);〈typeCheckStatements::print inferred types 186b〉

else return false;cerr ≪ "done.\n";cerr ≪ "Type checking formulas...";size = formulas.size();for (int i=0; i6=size; i++)

pair<type ∗, vector<term_type> > res;res = mywellTyped(formulas[i].fml);type ∗ t = res.first;if (t) delete_type(t); else return false;

cerr ≪ "done.\n";return true;

Defines:

typeCheckStatements, never used.Uses delete_type 2c 3a and term_type 10a.

186b 〈typeCheckStatements::print inferred types 186b〉≡ (186a)

int osel = getSelector(); setSelector(SILENT);for (uint j=0; j6=stat_term_types[k].size();j++)

ioprint((int)j); stat_term_types[k][j].first→print();ioprint(" : "); ioprintln(stat_term_types[k][j].second→getName());

setSelector(osel);

Uses getSelector, ioprint, ioprintln, and setSelector.

Comment 5.0.41. Here we release the memory occupied by the statements and the data struc-tures supporting side conditions on them. We do not have to free the term part of stat_term_typesbecause they point to subterms of terms residing in the statements vector.

Page 190: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

187

186c 〈statements and type checking 186a〉+≡ (181) ⊳ 186a 187 ⊲

void cleanup_statements() cerr ≪ "Cleaning up statements...";

#ifndef ALKEMY

assert(stat_term_types.size() ≡ 0 ∨statements.size() ≥ stat_term_types.size());

#endif

for (uint i=0; i6=statements.size(); i++) statements[i].freememory();

#ifndef ALKEMY

if (statements[i].typechecked /∗&& stat_term_types.size() > 0 ∗/)for (uint j=0; j6=stat_term_types[i].size(); j++)

delete_type(stat_term_types[i][j].second);#endif

cerr ≪ "Done.\n";cerr ≪ "Cleaning up " ≪ cachedStatements.size() ≪

" cached statements...";for (uint i=0; i6=cachedStatements.size(); i++)

cachedStatements[i].freememory();cerr ≪ "Done.\n";

Defines:cleanup_statements, used in chunk 178.

Uses delete_type 2c 3a.

187 〈statements and type checking 186a〉+≡ (181) ⊳ 186c

void cleanup_formulas() cerr ≪ "Cleaning up formulas...";for (uint i=0; i6=formulas.size(); i++) formulas[i].freememory();cerr ≪ "Done.\n";

Page 191: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

188

Comment 5.0.42. We now describe a facility that supports the storage and retrieval of thedeclared signatures of constants.

188a 〈constants and their signatures 188a〉≡ (181) 189c ⊲

struct constant_sig int name; type ∗ signature; ;vector<constant_sig> constants;

void initialise_constants() constant_sig temp;temp.name = iTrue; temp.signature = new type("Bool");constants.push_back(temp);temp.name = iFalse; temp.signature = new type("Bool");constants.push_back(temp);type ∗ a = new type_parameter("a");type ∗ lista = new type_alg("List"); lista→addAlpha(a);temp.name = iEmptyList; temp.signature = lista;constants.push_back(temp);temp.name = iHash;temp.signature =

new type_abstraction(a→clone(),new type_abstraction(lista→clone(), lista→clone()));

constants.push_back(temp);

temp.name = iInfinity; temp.signature = new type_parameter("number");constants.push_back(temp);

〈initialise constants::arithmetic operations 188b〉〈initialise constants::relational operations 189a〉〈initialise constants::disruptive operations 189b〉

Defines:

initialise_constants, used in chunk 178.Uses iEmptyList 183, iFalse 183, iHash 183, iInfinity 183, and iTrue 183.

188b 〈initialise constants::arithmetic operations 188b〉≡ (188a)

type ∗ number = new type_parameter("number");type ∗ number2 = new type_parameter("number2");type ∗ number3 = new type_parameter("number3");type ∗ algtype = new type_abstraction(number,

new type_abstraction(number2,number3));temp.name = iAdd; temp.signature = algtype; constants.push_back(temp);temp.name = iSub; temp.signature = algtype→clone(); constants.push_back(temp);temp.name = iMax; temp.signature = algtype→clone(); constants.push_back(temp);temp.name = iMin; temp.signature = algtype→clone(); constants.push_back(temp);temp.name = iMul; temp.signature = algtype→clone(); constants.push_back(temp);temp.name = iMod; temp.signature = algtype→clone(); constants.push_back(temp);temp.name = iDiv; temp.signature = algtype→clone(); constants.push_back(temp);temp.name = iAtan2;temp.signature = algtype→clone(); constants.push_back(temp);type ∗ tempsig = new type_abstraction(number2→clone(),number3→clone()) ;temp.name = iSin; temp.signature = tempsig; constants.push_back(temp);temp.name = iCos; temp.signature = tempsig→clone(); constants.push_back(temp);temp.name = iSqrt; temp.signature = tempsig→clone(); constants.push_back(temp);temp.name = iExp; temp.signature = tempsig→clone(); constants.push_back(temp);

Uses iAdd 183, iAtan2 183, iCos 183, iDiv 183, iExp 183, iMax 183, iMin 183, iMod 183, iMul 183, iSin 183,iSqrt 183, and iSub 183.

Page 192: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

189

189a 〈initialise constants::relational operations 189a〉≡ (188a)

type ∗ reltype = new type_abstraction(number→clone(),new type_abstraction(number2→clone(), new type("Bool")));

temp.name = iGT; temp.signature = reltype; constants.push_back(temp);temp.name = iGTE; temp.signature = reltype→clone(); constants.push_back(temp);temp.name = iLT; temp.signature = reltype→clone(); constants.push_back(temp);temp.name = iLTE; temp.signature = reltype→clone(); constants.push_back(temp);

Uses iGT 183, iGTE 183, iLT 183, and iLTE 183.

Comment 5.0.43. The following constants are for disruptive operations, that is, operations thatchanges persistent objects.

189b 〈initialise constants::disruptive operations 189b〉≡ (188a)

temp.name = iSucceeded; temp.signature = new type("Success");constants.push_back(temp);temp.name = iFailed; temp.signature = new type("Success");constants.push_back(temp);temp.name = iAssign;temp.signature = new type_abstraction(a→clone(),

new type_abstraction(a→clone(), new type("Success")));constants.push_back(temp);

Uses iAssign 183, iFailed 183, and iSucceeded 183.

189c 〈constants and their signatures 188a〉+≡ (181) ⊳ 188a 189d ⊲

void insert_constant(int name, type ∗ sig) assert(name > 0);for (uint i=0; i6=constants.size(); i++)

if (constants[i].name ≡ name) int osel = getSelector(); setSelector(STDERR);cerr ≪ "The constant "≪ getString(name)≪" has been "

≪ "defined before. " ≪ getString(name) ≪ " : ";cerr ≪ constants[i].signature→getName();cerr ≪ ".\nInstruction ignored.\n"; setSelector(osel);return;

constant_sig temp; temp.name = name; temp.signature = sig;constants.push_back(temp);

Defines:

insert_constant, used in chunk 178.Uses getSelector and setSelector.

189d 〈constants and their signatures 188a〉+≡ (181) ⊳ 189c 190a ⊲

type ∗ get_signature(int name) for (uint i=0; i6=constants.size(); i++)

if (constants[i].name ≡ name) return constants[i].signature;cerr ≪ "Unknown constant: " ≪ getString(name) ≪ endl;// assert(false);return NULL;

Defines:get_signature, used in chunks 16a and 178.

Page 193: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

190

190a 〈constants and their signatures 188a〉+≡ (181) ⊳ 189d

void cleanup_constants() cerr ≪ "Cleaning up constants...";for (uint i=0; i6=constants.size(); i++)

delete_type(constants[i].signature);cerr ≪ "Done.\n";

Uses delete_type 2c 3a.

Comment 5.0.44. Information about function symbols (collected during parsing) are stored ina hash table for quick and easy access. We now describe this function symbol table.

190b 〈function symbol table 190b〉≡ (181) 190c ⊲

struct fEntry int name;int minEffectArity;int maxEffectArity;fEntry(int n, int min, int max)

name = n; minEffectArity = min; maxEffectArity = max; ;

#define TABLESIZE 501

static vector<fEntry> func_info[TABLESIZE];

Comment 5.0.45. Clearly, we want a hash function that can be computed efficiently. Lookingat the first and last characters in the function name seemed a reasonable idea. (Looking at everycharacter seemed expensive, but there is probably not much in it.) We need to add size to makesure functions that begin and end with the same characters are hashed to different indices withhigh probability.

190c 〈function symbol table 190b〉+≡ (181) ⊳ 190b 191a ⊲

static int hash(int name) // int size = name.size();// int ret = name[0] * name[size-1] - (name[0] + name[size-1]) + size;// ret = ret % TABLESIZE;// return ret;return name % TABLESIZE;

Defines:

hash, used in chunks 191b and 192a.

Page 194: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

191

Comment 5.0.46. We can probably have a scheme whereby we try out different hash functionsat run time and decide on one that induces the best distribution of functions in the table.

Comment 5.0.47. Here we need to initialise information for functions that are implementedinside the code.

191a 〈function symbol table 190b〉+≡ (181) ⊳ 190c 191b ⊲

void initFuncTable() insert_ftable(iAdd, 2); insert_ftable(iSub, 2);insert_ftable(iMax, 2); insert_ftable(iMin, 2);insert_ftable(iMul, 2); insert_ftable(iDiv, 2);insert_ftable(iMod, 2); insert_ftable(iAtan2, 2);insert_ftable(iSin, 1); insert_ftable(iCos, 1);insert_ftable(iSqrt, 1); insert_ftable(iExp, 1);insert_ftable(iLT, 2); insert_ftable(iLTE, 2);insert_ftable(iGT, 2); insert_ftable(iGTE, 2);insert_ftable(iAssign, 2);

Defines:initFuncTable, used in chunk 178.

Uses iAdd 183, iAssign 183, iAtan2 183, iCos 183, iDiv 183, iExp 183, iGT 183, iGTE 183, iLT 183, iLTE 183,iMax 183, iMin 183, iMod 183, iMul 183, insert_ftable 191b, iSin 183, iSqrt 183, and iSub 183.

Comment 5.0.48. Basic insertion is okay. We first check whether func is already present beforeinserting.

191b 〈function symbol table 190b〉+≡ (181) ⊳ 191a 192a ⊲

void insert_ftable(int func, int earity) int index = hash(func);int size = func_info[index].size();for (int i=0; i6=size; i++)

if (func_info[index][i].name ≡ func) if (earity < func_info[index][i].minEffectArity)

func_info[index][i].minEffectArity = earity;else if (earity > func_info[index][i].maxEffectArity)

func_info[index][i].maxEffectArity = earity;return;

fEntry f(func, earity, earity);func_info[index].push_back(f);// print_ftable();

Defines:insert_ftable, used in chunks 178 and 191a.

Uses hash 190c and print_ftable 192b.

Page 195: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

192

192a 〈function symbol table 190b〉+≡ (181) ⊳ 191b 192b ⊲

pair<int,int> getFuncEArity(int func) assert(func > 0);pair<int,int> ret(-1,-1);int index = hash(func);int size = func_info[index].size();for (int i=0; i6=size; i++)

if (func ≡ func_info[index][i].name) ret.first = func_info[index][i].minEffectArity;ret.second = func_info[index][i].maxEffectArity;return ret;

cerr ≪ "Error: Function " ≪ getString(func) ≪ " unknown. "

"Effective arity could not be determined.\n";// assert(false);return ret;

Defines:getFuncEArity, used in chunk 178.

Uses hash 190c.

192b 〈function symbol table 190b〉+≡ (181) ⊳ 192a

void print_ftable() for (int j=0; j6=TABLESIZE; j++)

cout ≪ j ≪ ": ";int size = func_info[j].size();for (int i=0; i6=size; i++)

cout ≪ "(" ≪ func_info[j][i].name ≪ " "

≪ func_info[j][i].minEffectArity ≪ " "

≪ func_info[j][i].maxEffectArity ≪ ")\t";cout ≪ endl;

Defines:print_ftable, used in chunks 178 and 191b.

192c 〈nonrigid constants 192c〉≡ (181)

set<int> nonrigid_constants;

void insert_nonrigid_constant(int name) nonrigid_constants.insert(name); bool is_rigid_constant(int name)

return (nonrigid_constants.find(name) ≡ nonrigid_constants.end());

Uses insert 23a.

Page 196: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

193

Comment 5.0.49. This facility is used to provide mappings from type names to type objects.The initial assignment was performed in the parser.

193 〈type name to type objects mapping 193〉≡ (181)

#include <map>static map<string, pair<int, type ∗> > type_fac;

void insert_type(const string & tname, int x, type ∗ tp) assert(type_fac.find(tname) ≡ type_fac.end());pair<int, type ∗> temp(x, tp);type_fac[tname] = temp;

pair<int, type ∗> get_type(const string & tname) map<string, pair<int,type ∗> >::iterator p = type_fac.find(tname);if (p ≡ type_fac.end()) pair<int,type ∗> ret(-5,NULL); return ret; return p→second;

void cleanup_synonyms() cerr ≪ "Cleaning up type synonyms...";map<string, pair<int, type ∗> >::iterator p = type_fac.begin();while (p 6= type_fac.end()) delete_type(p→second.second); p++; cerr ≪ "Done.\n";

Defines:cleanup_signatures, never used.get_type, used in chunk 178.insert_type, used in chunk 178.

Uses delete_type 2c 3a.

Page 197: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

194

194 〈itoa 194〉≡÷∗ char ∗ itoa(int value, char ∗strg, int radix)

char tmp[33];char ∗tp = tmp;int i;unsigned v;int sign;char ∗sp;

if (radix > 36 ∨ radix ≤ 1) assert(false);

sign = (radix ≡ 10 ∧ value < 0);if (sign) v = -value; assert(false); else v = (unsigned)value;

while (v ∨ tp ≡ tmp) i = v % radix;v = v ÷ radix;if (i < 10) ∗tp++ = i+’0’;else ∗tp++ = i + ’a’ - 10;

if (strg ≡ 0) strg = (char ∗)malloc((tp-tmp)+sign+1);assert(strg);

sp = strg;

if (sign) ∗sp++ = ’-’; assert(false); while (tp > tmp)

∗sp++ = ∗−−tp;∗sp = 0;return strg; ∗÷

÷∗ Extract from stlsoft_integer_to_string.h∗∗ www: http://www.synesis.com.au/stlsoft∗ http://www.stlsoft.org/∗∗ Copyright (C) 2002, Synesis Software Pty Ltd.∗ (Licensed under the Synesis Software Standard Source License:∗ http://www.synesis.com.au/licenses/ssssl.html)∗∗ ...∗∗ ////////////////////////////////////////////////////////// */static const char s_characters[20] =

’9’, ’8’, ’7’, ’6’, ’5’, ’4’, ’3’, ’2’, ’1’, ’0’,’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’, ’8’, ’9’, 0

;static const char ∗s_mid = s_characters + 9;

Page 198: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

195

char ∗ uint_to_string(char ∗buf, size_t cchBuf, unsigned int i,const char ∗ prefix)

char ∗psz = buf + cchBuf - 1; // Set psz to last char∗psz = 0; // Set terminating null

do unsigned lsd = i % 10; // Get least significant digit

i ÷= 10; // Prepare for next most significant digit

−−psz; // Move back

∗psz = s_mid[lsd]; // Place the digit

while (i 6= 0);

do −−psz; ∗psz = ∗prefix; prefix++; while (∗prefix 6= 0);

return psz;

Page 199: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

Chapter 6

System Modules

6.1 The Booleans Module

196 〈booleans.es 196〉≡– Equality and Disequality

remove : (a -> Bool) -> (a -> b) -> (a -> b) ;

(remove s \x.d_SV/CONST/) = \x.d_SV ;

– where d_SV is a default term (FIX THIS)

(remove s \x.if u_SV then v else w_SV) =

\x.if (&& u_SV (not (s x))) then v else ((remove s \x.w_SV) x) ;

= : a -> a -> Bool ;

(= \x.u_SV \z.v_SV) = (&& (subset \x.u_SV \z.v_SV)

(subset \z.v_SV \x.u_SV)) ; typeof(4) ~ (a -> Bool) ;

import sets.es ;

(= \x.u_SV \y.v_SV) =

(&& (less \x.u_SV \y.v_SV) (less \y.v_SV \x.u_SV)) ;

less : (a -> b) -> (a -> b) -> Bool ;

(less \x.d_SV/CONST/ z) = True ;

– where d_SV is a default term (FIX THIS)

(less \x.if u_SV then v else w_SV z) =

\forall x.(&& (implies u_SV (= v (z x)))

(less (remove \x.u_SV \x.w_SV) z)) ;

ite : (Bool * a * a) -> a ;

if True then u else v = u ;

if False then u else v = v ;

if x then x_SV else y_SV/EQUAL,x_SV/ = x_SV ;

if if x then y else w then True else z = if x then y else (glueite w z) ;

glueite : b -> a -> b ;

(glueite False w) = w ;

(glueite 0.0 w) = w ;

196

Page 200: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.1. THE BOOLEANS MODULE 197

(glueite if x then y else z w) = if x then y else (glueite z w) ; Eager ;

– this Eagerness is necessary to ensure correctness

– if (if u then True else False) then True else v = if u then True else v ;

– if u_SV then False else v_SV/EQUAL,u_SV/ = False ;

– if u_SV then True else (if v_SV/EQUAL,u_SV/ then True else w_SV =

– if v_SV then True else w_SV ;

– this something useful to convert if-then-else’s to ||’s and &&’s

– if u then v else w = (|| (&& u v) (&& (not u) w)) ;

– (if x then y else z w) = if x then (y w) else (z w) ; LastResort ;

– \forall w.\forall x.\forall y.\forall z.(= (w if x then y else z) if x then (w y) else (w z))

&& : Bool -> Bool -> Bool ;

(&& True x) = x ;

(&& x True) = x ;

(&& False x) = False ;

(&& x False) = False ;

– Do we really need these? Apparently permute need them.

(&& (|| x y) z) = (|| (&& x z) (&& y z)) ; LastResort ;

(&& x (|| y z)) = (|| (&& x y) (&& x z)) ; LastResort ;

(&& if u then v else w t) = if (&& u t) then v else (&& w t) ; LastResort ;

(&& t if u then v else w) = if (&& t u) then v else (&& t w) ; LastResort ;

– The following are specialized versions of the two rules above.

– In computations, I find that the two rules given above tend to work

– (really) badly when used in conjunction with Escher’s leftmost outermost

– reduction order. A more in-depth analysis of this phenomenon is called for.

(&& if (= z u) then v else w t) =

if (&& (= z u) t) then v else (&& w t) ; LastResort ;

(&& t if (= x u) then v else w) =

if (&& t (= x u)) then v else (&& t w) ; LastResort ;

|| : Bool -> Bool -> Bool ;

(|| True x) = True ;

(|| x True) = True ;

(|| False x) = x ;

(|| x False) = x ;

(|| if u then True else w t) = if u then True else (|| w t) ; LastResort ;

(|| if u then False else w t) = (|| (&& (not u) w) t) ; LastResort ;

(|| t if u then True else w) = if u then True else (|| t w) ; LastResort ;

(|| t if u then False else w) = (|| t (&& (not u) w)) ; LastResort ;

– this is needed when using rmdup2

(|| (= x_SV u_SV) (= y_SV/EQUAL,x_SV/ v_SV/EQUAL,u_SV/)) = (= x_SV u_SV) ;

not : Bool -> Bool ;

(not False) = True ;

(not True) = False ;

(not (not x)) = x ;

(not (&& x y)) = (|| (not x) (not y)) ; LastResort ;

Page 201: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.1. THE BOOLEANS MODULE 198

(not (|| x y)) = (&& (not x) (not y)) ; LastResort ;

(not if u then v else w) = if u then (not v) else (not w) ; LastResort ;

sigma : (a -> Bool) -> Bool ;

〈existential statements 199〉

pi : (a -> Bool) -> Bool ;

〈universal statements 200〉

implies : Bool -> Bool -> Bool ;

(implies True x) = x ; – these are needed by queries 8 and 9 in

(implies False x) = True ; – the database example

– (implies p q) = (|| (not p) q) ; LastResort ; – this affects pi, bad.

/= : a -> a -> Bool ;

(/= x y) = (not (= x y)) ;

comp : (a -> b) -> (b -> c) -> a -> c ;

– (comp p1 p2) = \x.(p2 (p1 x)) ;

(comp p1 p2 x) = (p2 (p1 x)) ;

proj1 : (a * b) -> a ;

(proj1 (t1,t2)) = t1 ;

proj2 : (a * b) -> b ;

(proj2 (t1,t2)) = t2 ;

identity : a -> a ;

(identity x) = x ; LastResort ;

– These are used by the theorem prover

TpTag : ProveStatus -> Bool -> Bool ;

DontKnow : ProveStatus ;

Page 202: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.1. THE BOOLEANS MODULE 199

Comment 6.1.1. The rules for Σ as presented in [Llo03] are as follows:

∃x1. · · · ∃xn.⊤ = ⊤ (6.1)

∃x1. · · · ∃xn.⊥ = ⊥ (6.2)

∃x1. · · · ∃xn.(x ∧ (xi = u) ∧ y) = ∃x1. · · · ∃xi−1.∃xi+1. · · · ∃xn.(xxi/u ∧ yxi/u) (6.3)

∃x1. · · · ∃xn.(u ∨ v) = (∃x1. · · · ∃xn.u) ∨ (∃x1. · · · ∃xn.v) (6.4)

∃x1. · · · ∃xn.(if u then ⊤ else v) = (if ∃x1. · · · ∃xn.u then ⊤ else ∃x1. · · · ∃xn.v (6.5)

∃x1. · · · ∃xn.(if u then ⊤ else v) = ∃x1. · · · ∃xn.(¬u ∨ v) (6.6)

Statements 6.1 to 6.3 are implemented in the internal simplification routines. We now look at howthe remaining statements are implemented in the booleans module. The expression ∃x1 · · · ∃xn inthe heads is a bit worrying. How can we capture that in a finite number of statements? The answeris very simple: replace ∃x1 · · · ∃xn with ∃x in Statements 6.4 to 6.6. Here are some questions forthe reader. Why can we do that? What is the cost of doing that? Why do we bother with thecase of ∃x1 · · · ∃x3 for Statements 6.4 and 6.5 but not Statement 6.6? Is the number 3 special orcan (should?) it be so some other number?

199 〈existential statements 199〉≡ (196)

(sigma \x.(|| u_SV v_SV)) = (|| (sigma \x.u_SV) (sigma \x.v_SV)) ;

(sigma \x1.(sigma \x2.(sigma \x3.(|| u_SV v_SV)) ) ) =

(|| (sigma \x1.(sigma \x2.(sigma \x3.u_SV)))

(sigma \x1.(sigma \x2.(sigma \x3.v_SV)))) ;

(sigma \x.if u_SV then True else v_SV) =

if \exists x.u_SV then True else \exists x.v_SV ;

(sigma \x1.(sigma \x2.(sigma \x3.if u_SV then True else v_SV))) =

if (sigma \x1.(sigma \x2.(sigma \x3.u_SV))) then True

else (sigma \x1.(sigma \x2.(sigma \x3.v_SV))) ;

(sigma \x.if u_SV then False else v_SV) = (sigma \x.(|| (not u_SV) v_SV)) ;

(sigma \x2.(sigma \x1.(sigma \x.if u_SV then False else v_SV))) =

(sigma \x2.(sigma \x1.(sigma \x.(|| (not u_SV) v_SV)))) ;

(sigma \x.if u_SV then v_SV else w_SV) =

if (sigma \x.(&& u_SV v_SV)) then True

else (sigma \x.(&& (not u_SV) w_SV)) ; LastResort ;

– \exists x.if u_SV then v_SV else w_SV =

– if \exists x.(&& u_SV v_SV) then True else \exists x.(&& (not u_SV) w_SV) ;

Comment 6.1.2. The rules for Π as stated in [Llo03] are as follows:

∀x1. · · · ∀xn.(⊥ → u) = ⊤ (6.7)

∀x1. · · · ∀xn.(x ∧ (xi = u) ∧ y→ v) =

∀x1. · · · ∀xi−1.∀xi+1. · · · ∀xn.((x ∧ y→ v)xi/u) (6.8)

∀x1. · · · ∀xn.(u ∨ v→ t) = (∀x1. · · · ∀xn.(u→ t)) ∧ (∀x1. · · · ∀xn.(v→ t)) (6.9)

∀x1. · · · ∀xn.((ite u ⊤ v)→ t) = (∀x1. · · · ∀xn.(u→ t)) ∧ (∀x1. · · · ∀xn.(v→ t)) (6.10)

∀x1. · · · ∀xn.((ite u ⊥ v)→ t) = ∀x1. · · · ∀xn.(¬u ∧ v→ t) (6.11)

Statements 6.7 and 6.1.2 are implemented as part of the internal simplification routine. Noticethat the body of Statements 6.9 and 6.10 are identical. If we include the statement

if u then ⊤ else v = u ∨ v (6.12)

Page 203: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.1. THE BOOLEANS MODULE 200

as part of the ite rules, then we can get Statement 6.10 from Statement 6.9 via Statement 6.12.Proceeding in a similar fashion, we can use

if u then ⊥ else v = ¬u ∧ v (6.13)

to get rid of Statement 6.11 (and Statement 6.6). One thing with Statements 6.12 and 6.13 is thatthey force the a in the type of ite to be boolean. Can we do this simplification? If we want toretain the flexibility of handling sets represented both as nested ites and disjunctions, the answeris unfortunately no. This is because Statement 6.12 will transform any set represented in ite forminto its corresponding disjunctive form. This will, for example, affect the operation of less , whichis only defined for sets represented in ite form.

So we cannot do that simplification. (We can probably still safely use Statement 6.13, but we willnot try.) That means we have to find a way to represent Statements 6.9–6.11. The following rulestogether capture them finitely.

∀x.(u ∨ v→ t) = ∀x.(u→ t) ∧ ∀x.(v→ t) (6.14)

∀x.((if u then ⊤ else v)→ t) = ∀x.(u → t) ∧ ∀x.(v → t) (6.15)

∀x.(∀y.u ∧ ∀z.v) = ∀x.∀y.u ∧ ∀x.∀z.v (6.16)

∀x.((if u then ⊥ else v)→ t) = ∀x.(¬u ∧ v→ t) (6.17)

Question 6.1.3. Why do not we need the following counterpart

∀x1. · · · ∀xn.(⊤ → u) = ∀x1. · · · ∀xn.u

to Statement 6.7? I did see the need to put in ⊤ → u = u in the module for some queries to work.

200 〈universal statements 200〉≡ (196)

(pi \x.False) = False ;

(pi \x.True) = True ;

(pi \x1.(implies (|| u_SV v_SV) t_SV)) =

(&& (pi \x1.(implies u_SV t_SV)) (pi \x1.(implies v_SV t_SV))) ;

(pi \x1.(implies if u_SV then True else v_SV t_SV)) =

(&& (pi \x1.(implies u_SV t_SV)) (pi \x1.(implies v_SV t_SV))) ;

(pi \x.(&& (pi \x1.u_SV) (pi \x2.v_SV))) =

(&& (pi \x.(pi \x1.u_SV)) (pi \x.(pi \x2.v_SV))) ;

(pi \x.(implies if u_SV then True else v_SV t_SV)) =

(pi \x.(implies (&& (not u_SV) v_SV) t_SV)) ;

– if u then True else v = (|| u v)

– if u then False else v = (&& (not u) v)

Page 204: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.2. THE NUMBERS MODULE 201

6.2 The Numbers Module

201 〈numbers.es 201〉≡Infinity : a ;

NegInfinity : a ;

(add x_SV/CONST/ Infinity) = Infinity ;

(add Infinity x_SV/CONST/) = Infinity ;

(sub x_SV/CONST/ Infinity) = NegInfinity ;

(sub Infinity x_SV/CONST/) = Infinity ;

(min x_SV/CONST/ Infinity) = x_SV ;

(min Infinity x_SV/CONST/) = x_SV ;

(add x 0.0) = x;

(add 0.0 x) = x ;

(mul x 0.0) = 0.0 ;

(mul 0.0 x) = 0.0 ;

(div 0 x) = 0 ;

(div 0.0 x) = 0.0 ;

(mul 1 x) = x ;

(mul 1.0 x) = x ;

Power : (number1 * Int) -> number2 ;

power : (number1 * Int) -> number3 ;

(power (1,n)) = 1;

(power (m_SV/CONST/,0)) = 1 ;

(power (m_SV/CONST/,1)) = m_SV ;

(power (m_SV/CONST/,n_SV/CONST/)) = if (&& (<= m_SV 16) (< n_SV 16)) then

(power2 (m_SV,n_SV))

else (Power (m_SV,n_SV)) ;

power2 : (number1 * Int) -> number2 ;

(power2 (m_SV/CONST/,n_SV/CONST/)) = (mul m_SV (power (m_SV,(sub n_SV 1)))) ;

integer : number1 -> number2 -> number3 ;

(integer x y) = (sub (div x y) (remainder x y)) ;

remainder : number1 -> number2 -> number3 ;

(remainder x y) = (div (mod x y) y) ;

monus : number1 -> number2 -> number3 ;

(monus x_SV/CONST/ y_SV/CONST/) = (max 0 (sub x_SV y_SV)) ;

– this produces a loop

– (> (add u_SV/CONST/ (card v)) v_SV/CONST/) =

– if (> u_SV v_SV) then True else (> (add u_SV (card v)) v_SV) ;

– > : number -> number -> Bool ;

– (> if u then v_SV/CONST/ else w_SV/CONST/ x_SV/CONST/) =

– if u then (> v_SV x_SV) else (> w_SV x_SV) ;

– >= : number -> number -> Bool ;

Page 205: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.2. THE NUMBERS MODULE 202

– (>= if u then v_SV/CONST/ else w_SV/CONST/ x_SV/CONST/) =

– if u then (>= v_SV x_SV) else (>= w_SV x_SV) ;

– < : number -> number -> Bool ;

– (< if u then v_SV/CONST/ else w_SV/CONST/ x_SV/CONST/) =

– if u then (< v_SV x_SV) else (< w_SV x_SV) ;

– <= : number -> number -> Bool ;

– (<= if u then v_SV/CONST/ else w_SV/CONST/ x_SV/CONST/) =

– if u then (<= v_SV x_SV) else (<= w_SV x_SV) ;

– (< x_SV/CONST/ Infinity) =

– if (/= x_SV Infinity) then True else (< x_SV Infinity) ;

(< Infinity x_SV/CONST/) = False ;

(< x_SV/CONST/ Infinity) = True ;

abs : number -> number ;

(abs x_SV/CONST/) = if (>= x_SV 0) then x_SV else (add x_SV (mul -2 x_SV)) ;

fabs : number -> number ;

(fabs x_SV/CONST/) = if (>= x_SV 0.0) then x_SV else (add x_SV (mul -2.0 x_SV));

mChooseN : Int -> Int -> Int ;

(mChooseN m_SV/CONST/ 0) = 1 ;

(mChooseN m_SV/CONST/ n_SV/CONST/) =

(div (facl m_SV (sub m_SV n_SV)) (fac n_SV)) ;

facl : Int -> Int -> Int ;

(facl m_SV/CONST/ n_SV/CONST/) = if (> m_SV n_SV)

then (mul m_SV (facl (sub m_SV 1) n_SV))

else 1 ;

fac : Int -> Int ;

(fac 1) = 1 ;

(fac m_SV/CONST/) = (mul m_SV (fac (sub m_SV 1))) ;

Page 206: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.3. THE LIST MODULE 203

6.3 The List Module

203 〈lists.es 203〉≡– [] : (List a) ;

– # : a -> (List a) -> (List a) ;

import numbers.es ;

head : (List a) -> a ;

(head (# x y)) = x ;

tail : (List a) -> (List a) ;

(tail (# x y)) = y ;

last : (List a) -> a ;

(last (# x [])) = x;

(last (# x (# y z))) = (last (# y z)) ;

elem : Int -> (List a) -> a ;

(elem 1 (# x y)) = x ;

(elem z_SV/CONST/ (# x y)) = (elem (sub z_SV 1) y) ;

enumList : Int -> (List Int) ;

(enumList x_SV/CONST/) = (enumList2 x_SV x_SV) ;

enumList2 : Int -> Int -> (List Int) ;

(enumList2 0 x) = [] ;

(enumList2 x_SV/CONST/ y_SV/CONST/) =

(# (add (sub y_SV x_SV) 1) (enumList2 (sub x_SV 1) y_SV)) ;

inList : a -> (List a) -> Bool ;

(inList x []) = False ;

(inList x (# y z)) = if (= x y) then True else (inList x z) ;

length : (List a) -> Int ;

(length []) = 0 ;

(length (# x y)) = (add 1 (length y)) ;

zip : (List a) -> (List b) -> (List (a * b)) ;

(zip [] []) = [] ;

(zip (# x1 y1) (# x2 y2)) = (# (x1,x2) (zip y1 y2)) ;

zipWith : a -> (List b) -> (List (a * b)) ;

(zipWith x []) = [] ;

(zipWith x (# y z)) = (# (x,y) (zipWith x z)) ;

concat : ((List a) * (List a)) -> (List a) ;

(concat ([],x)) = x ;

(concat ((# u x), y)) = (# u (concat (x, y))) ;

concat2 : (List a) -> (List a) -> (List a) ;

(concat2 [] x) = x ;

(concat2 (# u x) y) = (# u (concat2 x y)) ;

Page 207: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.3. THE LIST MODULE 204

reverse : (List a) -> (List a) ;

(reverse []) = [] ;

(reverse (# x y)) = (concat ((reverse y),[x])) ;

append : ((List a) * (List a) * (List a)) -> Bool ;

(append (u,v,w)) =

(|| (&& (= u []) (= v w))

(sigma \r.

(sigma \x.

(sigma \y.(&& (&& (= u (# r x)) (= w (# r y)))

(append (x,v,y))))))) ;

permute : ((List a) * (List a)) -> Bool ;

(permute ([], x)) = (= x []) ;

(permute ((# x y), w)) =

(sigma \u.(sigma \v.(sigma \z.

(&& (= w (# u v)) (&& (delete (u,(# x y),z)) (permute (z,v))))))) ;

delete : (a * (List a) * (List a)) -> Bool ;

(delete (x,[],y)) = False ;

(delete (x,(# y z),w)) =

(|| (&& (= x y) (= w z))

(sigma \v.(&& (= w (# y v)) (delete (x,z,v))))) ;

sorted : (List a) -> Bool ;

(sorted []) = True ;

(sorted (# x y)) =

if (= y []) then True

else (sigma \u.(sigma \v.(&& (&& (= y (# u v)) (<= x u)) (sorted y)))) ;

isort : (List a) -> (List a) ;

(isort []) = [] ;

(isort (# x y)) = (ins x (isort y)) ;

ins : a -> (List a) -> (List a) ;

(ins x []) = (# x []) ;

(ins x (# y z)) = if (<= x y) then (# x (# y z)) else (# y (ins x z)) ;

isort2 : (a -> a -> Bool) -> (List a) -> (List a) ;

(isort2 p []) = [] ;

(isort2 p (# x y)) = (ins2 p x (isort2 p y)) ;

ins2 : (a -> a -> Bool) -> a -> (List a) -> (List a) ;

(ins2 p x []) = (# x []) ;

(ins2 p x (# y z)) = if (p x y) then (# x (# y z)) else (# y (ins2 p x z)) ;

fold : (a -> b -> b) -> b -> (List a) -> b ;

(fold m v []) = v ;

(fold m v (# x y)) = (m x (fold m v y)) ;

foldr : (a -> b -> b) -> b -> (List a) -> b;

(foldr m s []) = s ;

(foldr m s (# x y)) = (m x (foldr m s y)) ;

Page 208: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.3. THE LIST MODULE 205

filter : (a -> Bool) -> (List a) -> (List a) ;

(filter p []) = [] ;

(filter p (# x y)) = if (p x) then (# x (filter p y)) else (filter p y) ;

map : (a -> b) -> (List a) -> (List b) ;

(map m []) = [] ;

(map m (# x [])) = (# (m x) []) ;

(map m (# x y)) = (# (m x) (map m y)) ;

rmduplicates : (List a) -> (List a) ;

(rmduplicates []) = [] ;

(rmduplicates (# x y)) = (# x (rmduplicates (removeListEle x y))) ;

removeListEle : a -> (List a) -> (List a) ;

(removeListEle x []) = [] ;

(removeListEle x (# y z)) = if (= x y) then (removeListEle x z)

else (# y (removeListEle x z)) ;

neg : (a -> Bool) -> a -> Bool ;

(neg p x) = (not (p x)) ;

qsort : (List a) -> (List a) ;

(qsort []) = [] ;

(qsort (# x y)) =

(concat ((qsort (filter (neg (< x)) y)),

(# x (qsort (filter (< x) y))))) ;

listExists : (a -> Bool) -> (List a) -> Bool ;

(listExists p []) = False ;

(listExists p (# x y)) = if (p x) then True else (listExists p y) ;

sublist : Int -> (List a) -> (List a) ;

(sublist n []) = [] ;

(sublist n (# x y)) = if (> n 0) then (# x (sublist (sub n 1) y)) else [] ;

isSublist : (List a) -> (List a) -> Bool;

(isSublist [] x) = True ;

(isSublist (# x y) []) = False ;

(isSublist (# x1 y1) (# x2 y2)) =

if (= x1 x2) then (isSublist y1 y2) else False ;

ints : Int -> Int -> (List Int) ;

(ints x y) = if (< x y) then (# x (ints (add x 1) y)) else (# x []) ;

Page 209: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.3. THE LIST MODULE 206

206 〈sets.es 206〉≡import numbers.es ;

union : (a -> Bool) -> (a -> Bool) -> (a -> Bool) ;

(union s t) = \x.(|| (s x) (t x)) ;

intersect : (a -> Bool) -> (a -> Bool) -> (a -> Bool) ;

(intersect s t) = \x.(&& (s x) (t x)) ;

minus : (a -> Bool) -> (a -> Bool) -> (a -> Bool) ;

(minus s t) = \x.(&& (s x) (= (t x) False)) ;

subset : (a -> Bool) -> (a -> Bool) -> Bool ;

(subset s t) = (pi \x.(implies (s x) (t x))) ;

powerset : (a -> Bool) -> ((a -> Bool) -> Bool) ;

(powerset \x.False) = \s.(= s \x.False) ;

(powerset \x.if u_SV then True else v_SV) =

\s.(sigma \t.(sigma \r.(&& ((powerset \x.u_SV) t)

(&& ((powerset \x.v_SV) r) ((= s) (union t r)))))) ;

(powerset \x.if u_SV then False else v_SV) = (powerset \x.(&& (not u_SV) v_SV)) ;

(powerset \x.(= x t)) = \s.(|| (= s \y.False) (= s \x.(= x t))) ;

(powerset \x.(|| u_SV v_SV)) =

\s.(sigma \t.(sigma \r.(&& ((powerset \x.u_SV) t)

(&& ((powerset \x.v_SV) r) (= s (union t r)))))) ;

linearise : (a -> Bool) -> (a -> Bool) ;

(linearise \x.False) = \x.False ;

(linearise \x.if u_SV then True else v_SV) =

(union (linearise \x.u_SV) (linearise \x.v_SV)) ;

(linearise \x.if u_SV then False else v_SV) =

(linearise \x.(&& (not u_SV) v_SV)) ;

(linearise \x.(= x t)) = \x.if (= x t) then True else False ;

(linearise \x.(|| u_SV v_SV)) = (union (linearise \x.u_SV) (linearise \x.v_SV)) ;

rmdup : (a -> Bool) -> (a -> Bool) ;

(rmdup \x.t_SV) = \x.(rmdup2 t_SV) ;

rmdup2 : Bool -> Bool ;

(rmdup2 False) = False ;

(rmdup2 True) = True ;

(rmdup2 (= x t_SV)) = (= x t_SV) ;

(rmdup2 if (= x t_SV) then True else False) =

if (= x t_SV) then True else False ;

(rmdup2 if (= x t_SV) then True else u) =

if (= x t_SV) then True else (rmdup2 (&& (/= x t_SV) u)) ; Eager ;

(rmdup2 (|| (= x t_SV) u)) = (|| (= x t_SV) (rmdup2 (&& (/= x t_SV) u))) ;

(rmdup2 (|| (|| (= x t_SV) u) v)) = (rmdup2 (|| (= x t_SV) (|| u v))) ;

rmdupCustom : (a -> a -> Bool) -> (a -> Bool) -> (a -> Bool) ;

(rmdupCustom p \x.t_SV) = \x.(rmdupCustom2 p t_SV) ;

rmdupCustom2 : (a -> a -> Bool) -> Bool -> Bool ;

(rmdupCustom2 p False) = False ;

Page 210: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.3. THE LIST MODULE 207

(rmdupCustom2 p if (= x t_SV) then True else False) =

if (= x t_SV) then True else False ;

(rmdupCustom2 p if (= x t_SV) then True else u) =

if (= x t_SV) then True else (rmdupCustom2 p (&& (not (p x t_SV)) u)) ; Eager;

card : (a -> Bool) -> Int ;

(card s) = (card2 (rmdup s)) ;

card2 : (a -> Bool) -> Int ;

(card2 \x.(= x u)) = 1 ;

(card2 \x.(= u x)) = 1 ;

(card2 \x.(|| (= x u) v_SV)) = (add 1 (card2 \x.v_SV)) ;

(card2 \x.(|| (= u x) v_SV)) = (add 1 (card2 \x.v_SV)) ;

(card2 \x.(|| u_SV v_SV)) = (add (card2 \x.u_SV) (card2 \x.v_SV)) ;

(card2 \x.False) = 0 ;

(card2 \x.if (= x u_SV) then True else v_SV) = (add 1 (card2 \x.v_SV)) ;

(card2 \x.if (&& (<= u_SV/CONST/ x) (<= x v_SV/CONST/)) then True else t_SV) =

(add (sub v_SV u_SV) (card2 \x.t_SV)) ;

(card2 \x.if (>= x u_SV/CONST/) then True else t_SV) = Infinity ;

(card2 \x.if (<= x u_SV/CONST/) then True else t_SV) = Infinity ;

(card2 \x.if x_SV then True else v_SV) = (add (card2 \x.x_SV) (card2 \x.v_SV)) ;

– typeof(x) - which x? may need to use occurrence.

(card2 \x.(&& (= (proj1 x) u_SV/CONST/)

(= (proj2 x) v_SV/CONST/))) = 1 ; typeof(19) ~ (a * b) -> Bool;

mapFn : (a -> b) -> (a -> Bool) -> (b -> Bool) ;

(mapFn t s) = \x.\exists y.(&& (s y) (= (t y) x)) ;

filterSet : (a -> Bool) -> (a -> Bool) -> (a -> Bool) ;

(filterSet p \x.False) = \x.False ;

(filterSet p \x.(= x v)) = if (p v) then \x.(= x v) else \x.False ;

(filterSet p \x.(|| u_SV v_SV)) = (union (filterSet p \x.u_SV)

(filterSet p \x.v_SV)) ;

(filterSet p \x.if (= x v) then True else v_SV) =

if (p v) then (union \x.(= x v) (filterSet p \x.v_SV))

else (filterSet p \x.v_SV) ;

pickAnElement : (a -> Bool) -> a ;

(pickAnElement \x.(= x u)) = u ;

(pickAnElement \x.if (= x u) then True else v_SV) = u ;

switch : Int -> Bool -> Bool -> Bool -> Bool ;

(switch 1 t1 t2 t3) = t1 ;

(switch 2 t1 t2 t3) = t2 ;

(switch 3 t1 t2 t3) = t3 ;

compare : a -> a -> Int ;

(compare x y) = if (= x y) then 1 else if (< x y) then 2 else 3 ;

makeBTree : (a -> Bool) -> (a -> Bool) ;

– we expect the first argument to be in list form

(makeBTree \x.s_SV) = \x.(makeBTree2 (sortIte s_SV)) ;

Page 211: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.3. THE LIST MODULE 208

makeBTree2 : Bool -> Bool ;

(makeBTree2 False) = False ;

(makeBTree2 if (= x y) then True else False) = (= x y) ;

(makeBTree2 if (= x y) then True else v2) =

(switch (compare x (midEle if (= x y) then True else v2)) True

(makeBTree2 (lessthan (midEle if (= x y) then True else v2)

if (= x y) then True else v2))

(makeBTree2 (greaterthan (midEle if (= x y) then True else v2)

if (= x y) then True else v2))) ;

sortIte : Bool -> Bool ;

(sortIte False) = False ;

(sortIte if (= x y) then True else v) = (insIte (= x y) (sortIte v)) ;

insIte : Bool -> Bool -> Bool ;

(insIte (= x y) False) = if (= x y) then True else False ;

(insIte (= x_SV y) if (= z_SV/EQUAL,x_SV/ y2) then True else v) =

if (< y y2)

then if (= x_SV y) then True else if (= z_SV y2) then True else v

else if (= z_SV y2) then True else (insIte (= x_SV y) v) ;

cardBool : Bool -> Int ;

(cardBool False) = 0 ;

(cardBool if (= x y) then True else v) = (add 1 (cardBool v)) ;

get : Float -> Bool -> Bool ;

(get 1.0 if (= x y) then True else v) = y ;

(get n_SV/CONST/ if (= x y) then True else z) = (get (sub n_SV 1.0) z) ;

midEle : Bool -> a ;

(midEle s) = (get (integer (cardBool s) 2) s) ;

lessthan : a -> Bool -> Bool ;

(lessthan z False) = False ;

(lessthan z if (= x y) then True else v2) =

if (< y z) then if (= x y) then True else (lessthan z v2)

else (lessthan z v2) ;

greaterthan : a -> Bool -> Bool ;

(greaterthan z False) = False ;

(greaterthan z if (= x y) then True else v2) =

if (> y z) then if (= x y) then True else (greaterthan z v2)

else (greaterthan z v2) ;

removeBound : (a -> Bool) -> Bool ;

(removeBound \x.x_SV) = x_SV ;

simplify2D : ((a * a) -> Bool) -> ((a * a) -> Bool) ;

(simplify2D \x.(&& (= (proj1 x) v1) (= (proj2 x) v2))) = \x.(= x (v1,v2)) ;

(simplify2D \x.(|| u_SV v_SV)) = (union (simplify2D \x.u_SV)

(simplify2D \x.v_SV)) ;

〈multiset functions 209〉

Page 212: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

6.3. THE LIST MODULE 209

209 〈multiset functions 209〉≡ (206)

msetunion : (a -> Int) -> (a -> Int) -> (a -> Int) ;

(msetunion \x.0 m) = m ;

(msetunion \x.if (= x t) then v else w_SV m) =

\x.if (= x t) then (add v (m t))

else ((msetunion \x.w_SV (remove \x.(= x t) m)) x) ;

msetdiff : (a -> Int) -> (a -> Int) -> (a -> Int) ;

(msetdiff \x.0 m) = \x.0 ;

(msetdiff \x.if (= x t) then v else w_SV m) =

\x.if (= x t) then (monus v (m t)) else ((msetdiff \x.w_SV m) x) ;

msetmax : (a -> Int) -> (a -> Int) -> (a -> Int) ;

(msetmax \x.0 m) = m ;

(msetmax \x.if (= x t) then v else w_SV m) =

\x.if (= x t) then (max v (m t))

else ((msetmax \x.w_SV (remove \x.(= x t) m)) x) ;

msetmin : (a -> Int) -> (a -> Int) -> (a -> Int) ;

(msetmin \x.0 m) = \x.0 ;

(msetmin \x.if (= x t) then v else w_SV m) =

\x.if (= x t) then (min v (m t)) else ((msetmin \x.w_SV m) x) ;

msetinc : (a -> Int) -> (a -> Int) -> Bool ;

(msetinc \x.0 m) = True ;

(msetinc \x.if u_SV then v else w_SV m) =

(&& (pi \x.(implies u_SV (<= v (m x))))

(msetinc (remove \x.u_SV \x.w_SV) m)) ;

msetmember : a -> (a -> Int) -> Bool ;

(msetmember x m) = (< 0 (m x)) ;

Page 213: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

Chapter 7

A Listing of the Code Chunks

〈apply (x,t) to each eqn in eqns, extend eqns and return true 12a〉〈delete eqns of the form x = x 11c〉〈if x appears in t, return false 11d〉〈type checking 21a〉〈type checking actual 15a〉〈type checking subsidiary functions 20b〉〈type checking variables 15b〉〈type::abstractions 7a〉〈type::abstractions::implementation 7b〉〈type::algebraic types 9a〉〈type::algebraic types::implementation 9c〉〈type::composite types 3b〉〈type::composite types::implementation 3c〉〈type::function declarations 5e〉〈type::functions 3a〉〈type::parameters 4c〉〈type::parameters::implementation 4d〉〈type::synonyms 6a〉〈type::tuples 6b〉〈type::tuples::implementation 6c〉〈type::type 2b〉〈types.cc 2a〉〈types.h 1〉〈unification body 10c〉〈unification.cc 10b〉〈unification.h 10a〉〈unify::case of both non-parameters 14a〉〈unify::verbose 1 14b〉〈unify::verbose 2 14c〉〈variable case::lookup previous occurrence 17b〉〈wellTyped2::application::error reporting2 19a〉〈wellTyped2::application::t1 should have right form 18b〉〈wellTyped2::case of t a constant 16a〉〈wellTyped2::case of t a modal term 19c〉〈wellTyped2::case of t a tuple 20a〉〈wellTyped2::case of t a variable 17a〉〈wellTyped2::case of t an abstraction 19b〉〈wellTyped2::case of t an application 18a〉

210

Page 214: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

211

〈wellTyped2::save n return 16b〉〈cannot possibly be a redex 70a〉〈debug matching 1 84b〉〈debug matching 2 84c〉〈debug matching 3 84d〉〈debug matching 4 84e〉〈error handling::get previously bound 93b〉〈freememory error checking 33a〉〈freevar-match::case of ABS 101a〉〈freevar-match::case of APP 101b〉〈freevar-match::case of MODAL 101c〉〈freevar-match::case of PROD 101d〉〈isEq::switch t1 and t2 61〉〈memory debugging code 31c〉〈normalise1::and 48a〉〈normalise1::iff 48b〉〈pattern-match.cc 105c〉〈pattern-match.h 105b〉〈pattern-match::function declarations 91a〉〈pattern-match::functions 91b〉〈print error handling 28d〉〈print extra information 29c〉〈print white spaces 29b〉〈redex-match::case of ABS 95b〉〈redex-match::case of ABS::change variable name 95c〉〈redex-match::case of APP 94c〉〈redex-match::case of APP::debug matching 1 94d〉〈redex-match::case of APP::debug matching 2 94e〉〈redex-match::case of constant 94b〉〈redex-match::case of MODAL 96〉〈redex-match::case of PROD 95a〉〈redex-match::case of SV 92b〉〈redex-match::case of SV::check constraints 93a〉〈redex-match::case of V 93c〉〈redex-match::case of V::check free variable capture condition 94a〉〈redex-match::write a small warning message 95d〉〈simpl output 78a〉〈simplify update pointers 51a〉〈simplifyArithmetic::add, subtract, multiply and divide 55〉〈simplifyConjunction2::create body 63〉〈simplifyEquality::case of applications 53a〉〈simplifyEquality::case of products 52a〉〈simplifyEquality::case of products::empty tuples 52b〉〈simplifyEquality::case of products::error handling 52c〉〈simplifyEquality::case of strings 51d〉〈simplifyEquality::check whether we have data constructors 53b〉〈simplifyEquality::identical variables and function symbols 51b〉〈simplifyEquality::irrelevant cases 51c〉〈simplifyEquality::local variables 52d〉〈simplifyExistential::case one and two 64c〉〈simplifyExistential::move to the body 64b〉〈simplifyExistential::tricky case 65a〉〈simplifyExistential::tricky case::general case 65c〉〈simplifyExistential::tricky case::special case 65b〉

Page 215: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

212

〈simplifyUniversal::change end game 68a〉〈simplifyUniversal::check the form of body 67b〉〈simplifyUniversal::general case 68b〉〈simplifyUniversal::special case 67d〉〈simplifyUniversal::true statement 67c〉〈simplifyWithTP::call theorem prover 99a〉〈subst2::case of SV 42c〉〈subst2::case of V 43b〉〈subst2::free variable captured 43c〉〈subst2::replace by ti 43a〉〈switch modalities::set up conjecture 81〉〈switch modalities::set up conjecture::set up quantifiers 82a〉〈term schema::equal::numbers 26b〉〈term schema::print if-then-else 28c〉〈term schema::print lists 28b〉〈term schema::print strings 28a〉〈term-schema clone parts 22d〉〈term-schema initializations 22c〉〈term-schema parts 22a〉〈term-schema replace parts 22e〉〈term-schema::constructors 23b〉〈term-schema::definitions 30a〉〈term-schema::function declarations 22f〉〈term-schema::function definitions 24d〉〈term-schema::memory management 31a〉〈term-schema::supporting types 30b〉〈term-schema::type defs 21c〉〈terms.cc 105a〉〈terms.cc::local functions 25c〉〈terms.h 103〉〈try disruptive 85〉〈try match 74〉〈try match::cache computation 76b〉〈try match::debugging code 1 84a〉〈try match::different simplifications 77〉〈try match::eager statements 83a〉〈try match::find special cases where no matching is required 78b〉〈try match::helper functions 79c〉〈try match::output answer 83d〉〈try match::output pattern matching information 83c〉〈try match::prove modalities-switching theorem 80〉〈try match::put reduct in place 75b〉〈try match::reduce temp to simplest form 76a〉〈try match::side conditions on types 82b〉〈try match::try cached statements first 75a〉〈try match::unimportant things 83b〉〈accessibilityRelation.cc 115a〉〈accessibilityRelation.h 114〉〈addConstraint helper function 149b〉〈addConstraint::case when c is a single equality 148c〉〈addConstraint::case when constraintsTerm contains single equalities 149a〉〈applyClosureRule::case when label1 is false 140c〉〈applyClosureRule::check pairs 141a〉〈applyConjunctionRule::unnormalised form 131b〉

Page 216: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

213

〈applyDisjunctionRule::add a tableau n add t3 132c〉〈applyDisjunctionRule::add t2 132d〉〈applyDisjunctionRule::skip unless we are down to our last resorts 132b〉〈applyReflexiveRule::first case 137c〉〈applyReflexiveRule::second case 138a〉〈applySubstitutivityRule::main body 136a〉〈applyUniversalRule::add a copy of old label if necessary 134a〉〈applyUniversalRule::introduce negation if necessary 133b〉〈applyUniversalRuleSPImplies::has leading modalities 139b〉〈applyUniversalRuleSPImplies::irrelevant cases 139d〉〈applyUniversalRuleSPImplies::process label 139c〉〈applyURuleSPI::recognise formula form 139a〉〈class Label 106〉〈class LabelSet 110a〉〈class World 112c〉〈constraints-solver.cc 145a〉〈constraints-solver.h 144〉〈create Skolem constant 135a〉〈expand::debugging messages 124b〉〈expandStep::apply any of the classical rules 126a〉〈expandStep::apply last resort rules 126b〉〈expandStep::apply propagation rules 127b〉〈expandStep::classical saturation step 125b〉〈expandStep::propagation step 127a〉〈expandStep::structural step 126c〉〈getTheta::base case 150b〉〈insert new label into world 130c〉〈isClosable::find possible instantiations of free variables 128b〉〈isClosable::ignore non-applicable formula pairs 129a〉〈isClosable::ignore non-applicable formulas 128c〉〈isClosable::try match labels::error reporting 1 129d〉〈isClosable::try match labels::error reporting 2 129e〉〈isClosable::try to match the labels 129c〉〈isClosable::work on branch k 128a〉〈label.cc 113c〉〈label.cc body 107a〉〈label.h 113b〉〈LabelSet::add::special cases 111b〉〈list::goto the right pointer 111d〉〈makeOrConstraint::remove duplicates 148a〉〈return if rule tried before 130b〉〈RLabel 111a〉〈solvable::error reporting 1 145b〉〈solvable::error reporting 2 145c〉〈support functions 136b〉〈Tableau init 116c〉〈tableau.cc 116b〉〈tableau.h 116a〉〈tableaux.cc 122a〉〈tableaux.h 121〉〈tableaux::atomic formula 129b〉〈TruthValue 123b〉〈bach main program 176a〉〈bach-parser::bach formula 166c〉

Page 217: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

214

〈escher-parser.y 156〉〈escher-parser::statement schema 163〉〈escher-parser::statement schema typeof 166a〉〈escher-scan.l 152〉〈facilities for handling multiple input files 155a〉〈flex options 155b〉〈lex error reporting hackery 154b〉〈lex:copy yytext 154a〉〈lex:tpos 154c〉〈parser::cache computed result 159〉〈parser::check that the formula has the right form 166d〉〈parser::error reporting 177a〉〈parser::function declarations 157d〉〈parser::import 157b〉〈parser::insert goal formula into tableau 160b〉〈parser::make sure statement head has the right form 164〉〈parser::perform a computation 158b〉〈parser::preprocess statements 165a〉〈parser::prove a theorem 161a〉〈parser::query 158a〉〈parser::query::output query 161b〉〈parser::query::output result 161c〉〈parser::quit 157a〉〈parser::statement schema 162〉〈parser::sv condition 169c〉〈parser::term schema 167〉〈parser::term schema products 170c〉〈parser::term schemas 170a〉〈parser::type info 173〉〈parser::variables 157c〉〈statement schema::control directives 165b〉〈term schema::existential statements 169a〉〈term schema::if-then-else statements 168b〉〈term schema::lists 172〉〈term schema::products 170b〉〈term schema::sets 171〉〈term schema::strings 168a〉〈term schema::syntactic sugar 169e〉〈term schema::universal statements 169b〉〈theorem-proving queries 160a〉〈yacc token definitions 154d〉〈constants and their signatures 188a〉〈function symbol table 190b〉〈global symbol constants 185a〉〈global.cc 181〉〈global.h 178〉〈initialise constants::arithmetic operations 188b〉〈initialise constants::disruptive operations 189b〉〈initialise constants::relational operations 189a〉〈itoa 194〉〈nonrigid constants 192c〉〈statements and type checking 186a〉〈symbols and their integer representations 183〉〈type name to type objects mapping 193〉

Page 218: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

215

〈typeCheckStatements::print inferred types 186b〉〈booleans.es 196〉〈existential statements 199〉〈lists.es 203〉〈multiset functions 209〉〈numbers.es 201〉〈sets.es 206〉〈universal statements 200〉

Page 219: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

Bibliography

[dCG02] Luis Fariñas del Cerro and Olivier Gasquet. A general framework for pattern-drivenmodal tableaux. Logic Journal of the IGPL, 10(1):51–83, 2002.

[Fit90] Melvin Fitting. First-Order Logic and Automated Theorem Proving. Springer-Verlag,1990.

[GHJV95] Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. Design Patterns -Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.

[Gie01] Martin Giese. Incremental closure of free variable tableaux. In Rajeev Goré, AlexanderLeitsch, and Tobias Nipkow, editors, Proceedings of International Joint Conference onAutomated Reasoning, Siena, Italy, LNCS2083, pages 545–560. Springer-Verlag, 2001.

[Llo99] John W. Lloyd. Programming in an integrated functional and logic language. Journalof Functional and Logic Programming, 3, 1999.

[Llo03] John W. Lloyd. Logic for Learning: Learning Comprehensible Theories from StructuredData. Cognitive Technologies. Springer, 2003.

[LMB92] John R. Levine, Tony Mason, and Doug Brown. lex & yacc. O’Reilly, 1992.

[Mit96] John C. Mitchell. Foundations for Programming Languages. MIT Press, Cambridge,MA, 1996.

[Pax95] Vern Paxson. Flex: A fast scanner generator, 2.5 edition, March 1995.

[Pey87] Simon L. Peyton Jones. The Implementation of Functional Programming Languages.Prentice-Hall, 1987.

216

Page 220: bach - Australian National Universityusers.cecs.anu.edu.au/~kee/bach.pdf · Title: bach.dvi Created Date: 4/22/2008 9:55:06 AM

Index

β-reduction rule, 57

atomic terms, 22

caching of computation steps, 34composite terms, 22

embedded conjunctively, 59

node sharingunsafe, 58

term substitution, 41

217