/* This is a literate program.

This section consists of:

   1.  Declarations of all built-in operators.
   2.  A 'preprocess' predicate which converts external to internal format.

Please note that it does not make a thorough check of the syntax of a source program, as yet.

Operators
=========
The operators are declared first so they can be used to make the rest of this program more readable.  Although some of Prolog's operators are used in the language being defined, their precedences are undisturbed, so that the program here still complies with Prolog conventions.  Some operators and commands are so fundamental to the language structure that they are reserved, in the sense that they cannot be overloaded by additional definitions.  We make 'drop' reserved too, so anything really stupid can be undone.

 */ reserved(','). /*
 */ reserved(';'). /*
 */ reserved('{}'). /*
 */ reserved('->'). /*
 */ reserved(':'). /*
 */ reserved((drop)). /*

The usual Prolog operators are listed here as comments, because they differ somewhat between different implementations of Prolog.

   :-op(200,xfy,[^]).
   :-op(300,yfx,[mod]).
   :-op(400,yfx,[*,/,<<,>>,//]).
   :-op(500,fx,[-,+]).
   :-op(500,yfx,[+,-,/\,\/]).
   :-op(700,xfx,[=,\=,@>=,@=<,@>,@<,\==,==,=..,is,=\=,=:=,<,>,>=,=<]).
   :-op(900,fy,[\+,'Â',not,nospy,spy]).
   :-op(1000,xfy,[',']).
   :-op(1050,xfy,['->']).
   :-op(1100,xfy,[;]).
   :-op(1150,fx,[(mode),(discontiguous),(dynamic),(public),(multifile)]).
   :-op(1200,fx,[(:-),(?-)]).
   :-op(1200,xfx,[(:-),-->]).

The Prolog arithmetic and comparison operators are used in much the same way within Libra.  However ',', '->', and ';' are used quite differently.  The comma is used in pair formation, and so is '->', which is internally translated into a comma anyway.  The semicolon is used to separate elements of sets.  The advantage is that we can define a relation, e.g., that permutes the elements of a pair as '{X,Y -> Y,X; X,Y -> X,Y}' rather than '{((X,Y), (Y,X)),((X,Y), (X,Y))}', which both saves parentheses and seems clearer in intention.  However, the use of comma in separating terms of sequences is so well established by Prolog lists that a semicolon would seem perverse in [a,b] and is not allowed.  The distinction is that 'a,b' is ordered, but 'a;b' is unordered.  In fact [a,b] is always equivalent to {1,a; 2,b}, and is actually represented in the same way in this implementation.

The basic scheme is:

  Relations and Sets
  Arithmetic
  Comparison
  Booleans
  Structures
  Commands

The reasons why we place relations and sets at the top of the list is that functional application binds more tightly than arithmetic operators do, and that operators like # yield numbers.

The following is meant to be a complete list of all Libra's built-in relations, including the operators.

 */ predefined(!). /* relational application
 */ predefined(@). /* generate all members of a set
 */ predefined(i). /* generate an arbitrary member of a set
 */ predefined(~). /* 'functional' application
 */ predefined((?)). /* set membership
 */ predefined(\?). /* set non-membership
 */ predefined(>>->). /* set reduction
 */ predefined(>>=>). /* sequence reduction
 */ predefined(image). /* relational image
 */ predefined(values). /* set of values
 */ predefined(\\). /* vector application (zip)
 */ predefined(dom). /* domain of a relation
 */ predefined(codom). /* codomain of a relation
 */ predefined(o). /* relational composition
 */ predefined(^*). /* reflexive transitive closure
 */ predefined(^+). /* transitive closure and powers
 */ predefined(^-). /* relational inverse and powers
 */ predefined(^^). /* limit of reflexive transitive closure
 */ predefined(<-). /* reverse of a sequence
 */ predefined(id). /* identity function on a set
 */ predefined(join). /* union of sets
 */ predefined(meet). /* intersection of sets
 */ predefined(omit). /* asymmetric difference
 */ predefined(else). /* relational extension
 */ predefined(but). /* relational over-ride
 */ predefined(<?). /* left restriction
 */ predefined(<\?). /* left anti-restriction
 */ predefined(?>). /* right restriction
 */ predefined(\?>). /* right anti-restriction
 */ predefined(x). /* cartesian product
 */ predefined(sets_of). /* powerset
 */ predefined(seqs_of). /* all sequences of a given type
 */ predefined(&&). /* concatenation
 */ predefined(equal). /* set equality
 */ predefined(unequal). /* set inequality
 */ predefined(subset). /* subset
 */ predefined(inside). /* proper subset
 */ predefined(disjoint). /* dijointness
 */ predefined(includes). /* set inclusion
 */ predefined(encloses). /* proper inclusion
 */ predefined(#). /* cardinality
 */ predefined(head). /* first element of a sequence
 */ predefined(tail). /* 2nd to last elements of a sequence
 */ predefined(front). /* 1st to penultimate elements of a sequence
 */ predefined(last). /* last element of a sequence
 */ predefined(unique). /* unique string with given prefix
 */ predefined(sort). /* a set in standard order
 */ predefined(+). /* addition
 */ predefined(-). /* subtraction
 */ predefined(*). /* multiplication
 */ predefined(/). /* integer division
 */ predefined(^). /* exponentiation
 */ predefined(mod). /* modulo remainder
 */ predefined(/\). /* bitwise and
 */ predefined(\/). /* bitwise or
 */ predefined(<<). /* left shift
 */ predefined(>>). /* right shift
 */ predefined(>). /* greater than
 */ predefined(>=). /* at least (\< is removed by the preprocessor)
 */ predefined(<). /* less than
 */ predefined(=<). /* at most (\> is removed by the preprocessor)
 */ predefined(=). /* equality
 */ predefined(\=). /* inequality
 */ predefined(max). /* maximum
 */ predefined(min). /* minimum
 */ predefined(&). /* boolean and
 */ predefined(v). /* boolean or
 */ predefined(\). /* boolean complement
 */ predefined(=>). /* implication
 */ predefined(<=>). /* boolean equivalence
 */ predefined(succeeds). /* Prolog hook
 */ predefined(random). /* random integer in given range
 */ predefined(integers). /* the set of integers
 */ predefined(naturals). /* the set of non-negative integers
 */ predefined(positives). /* the set of positive integers
 */ predefined(characters). /* the set of characters
 */ predefined(literals). /* the set of capitalised atoms
 */ predefined(booleans). /* {'True'; 'False'}
 */ predefined(any). /* the universal set
 */ predefined(sets). /* the set of all sets
 */ predefined(relations). /* the set of all relations
 */ predefined(sequences). /* the set of all sequences
 */ predefined(strings). /* the set of all strings
 */ predefined(grounded). /* the set of variable free structures
 */ predefined(symbolic). /* the complement of grounded
 */ predefined(tell). /* acquire output file
 */ predefined(told). /* release output file
 */ predefined(see). /* acquire input file
 */ predefined(seen). /* release input file
 */ predefined(speak). /* acquire user input-output
 */ predefined(spoken). /* release user input-output
 */ predefined(write). /* display expression to current output
 */ predefined(debug). /* display expression to user
 */ predefined(put). /* output a string of characters
 */ predefined(nl). /* output a new line
 */ predefined(read). /* read an expression
 */ predefined(get). /* read a line of characters
 */ predefined(null). /* null (to suppress output of 'find')
 */ predefined((unary_prec)). /* precedence of unary operator
 */ predefined((binary_prec)). /* precedence of binary operator
 */ predefined(int_to_str). /* convert integer to string
 */ predefined(str_to_int). /* convert string to integer

Relations and Sets
==================
The notations for closure are normally written as exponents in mathematical notation, so they are given lowest precedence of the relatonal operators.  R^* denotes the reflexive transitive closure of R, i.e., the application of R zero or more times.  R^+ is similar, except that R is applied at least once.  Another view is that if each pair in R is visualised as an edge of graph G, there is an edge in the graph of R^+ corresponding to every path in G of length 1 or more.  R^* is similar, but includes paths of length zero.  These are tricky operators to implement; R^* and R^+ have trouble with cycles.  S<- is the reverse of sequence S.  

 */ :-op(50,yf,[^*,^+,<-,^^]). /*

The expression R^+N is equivalent to applying R to its own output N times.  R^+0 is the identity function on R, R^+1 is R itself.  Note that '^+' is one operator, not to be confused with '^ +N' (exponentiation).    R^-1 is the reverse of R, i.e., if X!R = Y, then Y!R^-1 = X.  In terms of graphs, the graph of R^-N is the same as the graph of R^+N, but with every edge reversed.  R^-1 models solution by exhaustive search.

 */ :-op(50,yfx,[^+,^-]). /*

The expression 'sets_of S' denotes any set (including the empty set) that is a subset of S.  The expression 'seqs_of S' denotes any sequence (including the empty sequence) whose terms are in S.  Because 'sets_of X' is sometimes written as '2^X', it is given the same precedence as ^*, etc.

 */ :-op(50,fy,[sets_of,seqs_of]). /*

We also allow 'dom R' to stand for the domain of relation R, and 'codom R' to stand for its codomain.  The expression 'values X' is the set of all values generated by X.

 */ :-op(50,fy,[dom,codom,values]). /*

X is an element of 'r meet s' iff X is an element of 'r' and an element of 's'.  X is is an element of 'r join s' iff it is an element of either.  X is an element of 'r omit s' iff it is an element of 'r' but not an element of 's'.  The cartesian product 'r x s' denotes every pair whose first member is drawn from 'r' and whose second member is drawn from 's'.  So that the 'tuple' (1,2,3) will be considered to be a member of 'integers x integers x integers', 'x' has to be right-associative.  

The composition of 'r' and 's', written as 'r o s' is the set of all pairs (X,Z) such that there exists a Y, such that (X,Y) is in 'r' and (Y,Z) is in 's'.  By definition, 'X!(r o s)' is identical to '(X!r)!s'.  The extension of 'r' to 's', written as 'r else s' is such that in 'X!(r else s)', if 'X!r' yields at least one result, the results of 'X!r' are the results of 'X!(r else s)'; otherwise the results (if any) are those of 'X!s'.  The 'over-ride' expression 's but r' is a syntactic variant of 'r else s'.

Finally, 'r && s' denotes the concatenation of two sequences.  (In Prolog terms, append(r,s,r && s).)

However, 'meet', 'x' and 'o' have a multiplicative flavour, whereas 'join', 'else', 'but' and '&&' have an additive flavour, and 'omit' has a subtractive flavour.  They are therefore given the same precedences as the analogous arithmetic operators.  This means, for example, that 'r join s meet t' means 'r join (s meet t)'.

There are four 'restriction' operators: 'S <? R' means R restricted to the input set S, 'R ?> S' means R restricted to the output set S, 'S <\? R' means R restricted to exclude the input set S, and 'R \?> S' means R restricted to exclude the output set S.  These ought to bind more tightly than the other operators, so that 'X!(s <? r1 join s <\? r2)' would apply either r1 or r2 to X depending on whether X is a member of 's' or not a member of 's'.  These seem to have a similar flavour to meet.

 */ :-op(100,yfx,[?>,\?>]). /*
 */ :-op(100,xfy,[<?,<\?]). /*
 */ :-op(100,yfx,[meet,o,x]). /*

 */ :-op(125,yfx,[&&,join,omit,else,but]). /*

The expression 'X!r' means relation 'r' applied to argument X, and 'X!r!s' means '(X!r)!s'.  Relational differs from functional application in that '!' may yield any number of results (including zero) in turn.  By analogy, '!' is given the same precedence as '~'.  If 'r' is a relation, 'r~X' yields one arbitrary result from X!r.  A tight binding is useful, because a record may be represented by a relation, and X!a+Y!a should therefore denote the sum of the 'a' attributes of X and Y.

The expression '@S' yields all the members of S in turn; 'i S' yields an arbitrary member.  These are analogous enough to '!' and '~' to deserve the same precedence.

The double application operator '(S,T)\\Op' applies Op to two relations in parallel, applying Op to each pair of values from S and T having the same argument.  It makes most sense when the relations are sequences.  For example, '(S,T)\\(+)' forms the vector sum of vectors S and T.  ('Vector' is just another name for a sequence.)

 */ :-op(150,yfx,[!,\\,~]). /*
 */ :-op(150,fx,[i,@]). /*

The cardinality (#) of a set is number, so we would like '#S' to bind more tightly than the arithmetic operators.
  
 */ :-op(175,fy,[#]). /*

The set reduce operator 'S>>->Op' combines the elements of S using 'Op', in some order.  For example, '@{1..N}>>->(*)' finds factorial N, for positive N, by multiplying together the set of integers in the range 1 to N.  (The notation '>>->' is meant to convey the many-to-one nature of the operator.  The sequence reduce 'S>>=>Op' operator is similar, but combines successive terms of a sequence in left-to-right order.  For example, 'S>>=>(&&)' flattens a sequence of sequences into a single sequence, as in converting a sequence of words into a sentence.  The expression 'S image R' denotes the set of all Y such that X R Y and X is a member of S.  Since '#' is basically a reduction operator, the reduction operators are given the same precedence as '#'.

 */ :-op(175,yfx,[>>->,>>=>,image]). /*

The postfix '>>->' operator is such that 'X>>->' is equivalent to '{X}>>->join'.

 */ :-op(175,yf,[>>->]). /*

Arithmetic
==========
For convenience, the expression 'X max Y' yields the greater of X or Y, and 'X min Y' their lesser, mainly because it's my language, and I have never understood why they aren't standard.  The notation {M..N} denotes the set of all integers in the range M--N inclusive, and [M..N] denotes the sequence [M,M+1,.. N].  Each of these notations is given looser binding than the additive operators.

 */ :-op(600,yfx,[max,min]). /*

Comparison
==========
The language has a syntactical feature that allows X<Y<Z as a shorthand for X<Y & Y<Z, into which it is translated by the preprocessor.  This means that the comparison operators must be redefined as right associative.  In addition to these operators, the symbols '\>' and '\<' are allowed as synonyms for '=<' and '>='. 

 */ :-op(700,xfy,[=,\=,<,>,>=,=<,\<,\>]). /*

Some additional comparison operators are needed for comparing sets.  For example, 'X subset Y' is 'True' iff every element of X is also a member of Y.  Although it would be tempting to write 'X=<Y', a sequence is a set, and 'X=<Y' would make better sense as a vector comparison, being true if X does not follow Y in lexicographic order.  'Subset' and '=<' are therefore different conditions. 

The expression 'X inside Y' is true iff X is a proper subset of Y, i.e., Y has at least one element that is not in X.  'X disjoint Y' is true iff X and Y have no common elements.  'X includes Y' is a syntactic variant of 'Y subset X'.  'X encloses Y' is a variant of 'Y inside X'.  It seems reasonable, since all these are words, to allow 'X equal Y' as a variant of 'X = Y'.

 */ :-op(700,xfy,[equal,unequal,subset,inside,encloses,includes,disjoint]). /*

The expression X?S is 'True' if X is a member of set S, and 'False' otherwise.  It is convenient to give this looser binding than the set operators so that one may write 'X?S meet R', and so on.  'X\?S' is equivalent to '\X?S'.

 */ :-op(700,xfx,[..,(?),\?]). /*

Boolean
=======
The boolean operators are given weaker binding than the comparison operators, so that one may write 'X<0 & Y<0', for example.  Negation (\) binds stronger than 'and' (&), then 'or' (v), and 'implies' (=>) and 'iff' (<=>) bind weakest.  Negation is given the same precedence as the 'not' operator in some Prologs.

 */ :-op(900,fy,\). /*
 */ :-op(925,yfx,&). /*
 */ :-op(950,yfx,v). /*
 */ :-op(975,xfx,[=>,<=>]). /*

Structure
=========
The notation {S:P} denotes the set S such that P is 'True'.  Since a typical relation has the form {X,Y->X:X>=Y; X,Y->Y:X<Y} (to find the minimum of a pair), ':' should have a precedence intermediate between '->' and ';'.

 */ :-op(1075,xfx,[':']). /*

Commands
========
The commands that can be typed by a user need to have loose binding power so that their operands can be complete expressions.  However, they should not bind looser than ';', because 'let x->y;z.' is nonsense, for example.  A semi-colon should occur only between '{' and '}'.

 */ :-op(1080,fx,[(let),(edit),(find),(?),(use),(reuse),(show),(dump),(drop)]). /*
 */ :-op(1080,xfx,[(fx),(fy),(xf),(yf),(xfx),(xfy),(yfx)]). /*

The 'help x' command displays information about the use of command 'x'.  It needs a higher precedence than any potential operand.

 */ :-op(1090,fx,[(help)]). /*

(11)  The 'unary_prec' and 'binary_prec' should have higher precedence than any potential operand.

 */ :-op(1200,fx,[(unary_prec),(binary_prec)]). /*

Preprocessing
=============
The preprocessor has three main functions:

(1) It converts sets and sequences to internal format (currently, sorted lists).
(2) It applies scope rules.
(3) It standardises syntactic variants, e.g., '\<' becomes '>='.

The point about scope rules needs some introductory explanation.  Suppose that 'add1' is defined as '{X->X+1}'.  Then '1!add1!add1 ought to yield 3.  But from the definition of 'add1', one should also be able to write '1!{X->X+1}!{X->X+1}'.  However, a simple interpretation binds all the X's to the same value, resulting in '1!{1->1+1}!{1->1+1}'.  The subexpression '1!{1->1+1}' simplifies to '2', resulting in '2!{1->1+1}' for the whole expression.  Since '{1->1+1}' is not applicable to '2', no result would be found.  Therefore, in this context, the X's must be read as different.  This suggests that a variable should have a scope limited by the enclosing braces.  On the other hand, in a context such as '1!{X->[X-1,X,X+1]}' the result should clearly be '[0,1,2]'.  The rule adopted is that a variable has the scope of the member of the outermost set in which it appears, including all expressions nested within it.

To aid in identifying scopes, the preprocessor uses a 'symbol table', which is list of lists.  Each term of the table represents a scope, and each of a scope's terms gives the translation from an external variable to an internal variable that is valid for that scope.  The first term of the table is for the innermost current scope.

A variable is in scope if either it is defined locally or local to an enclosing term.

 */ in_scope(X,Y,[Locals|_]) :- in_locals(X,Y,Locals),!. /*
 */ in_scope(X,Y,[_|Globals]) :- in_scope(X,Y,Globals),!. /*

Each scope is a list of internal/external pairs.

 */ in_locals(X,Z,[(Y,Z)|_]) :- Y==X,!. /*
 */ in_locals(X,Z,[_|T])     :- in_locals(X,Z,T),!. /*

A variable is preprocessed by translating it into its internal equivalent, if it is already in scope.

 */ preprocess(X,Y,SymTab,SymTab,_) :- var(X),in_scope(X,Y,SymTab),!. /*

An input variable that is not in scope is added to the local symbol table.  It is necessary to use copy_term, otherwise it will remain bound to all other occurences of the same variable in the term.

 */ preprocess(X,Y,[Lcl|Gbl],[[(X,Y)|Lcl]|Gbl],Context):- 
               var(X),!,copy_term(X,Y),display_line(Context),write('An undeclared variable was found in an expression.'),nl. /*

 */ display_line(X) :- numbervars(X,1,_),write(X),nl,fail. /*
 */ display_line(_) :- true. /*

The special notations for ranges and shift sequences must be preserved, and caught before they can be confused with ordinary sets.  But before that, we must check that the set or sequence does not consist of a simple variable, which would match anything!

 */ preprocess({H},[H1],SymTab,SymTab,Context):- var(H),!,preprocess_term(H,H1,[[]|SymTab],_,Context). /*
 */ preprocess([H],[(1,H1)],SymTab,SymTab,Context):- var(H),!,preprocess(H,H1,[[]|SymTab],_,Context). /*

 */ preprocess({M..N},{M1..N1},SymTab,SymTab,Context) :- 
               !,preprocess(M,M1,[[]|SymTab],SymTab1,Context),preprocess(N,N1,SymTab1,_,Context). /*
 */ preprocess([M..N],[M1..N1],SymTab,SymTab,Context) :- 
               !,preprocess(M,M1,[[]|SymTab],SymTab1,Context),preprocess(N,N1,SymTab1,_,Context). /*

Sets are represented internally by sorted lists.  Each member of a set creates a new local scope, which is discarded after preprocessing it.

 */ preprocess({},[],SymTab,SymTab,_) :- !. /*
 */ preprocess({H;T},Z,SymTab,SymTab,Context) :- 
               !,preprocess_term(H,H1,[[]|SymTab],_,Context),preprocess({T},T1,SymTab,_,Context),sort([H1|T1],Z). /*
 */ preprocess({H},[H1],SymTab,SymTab,Context):- !,preprocess_term(H,H1,[[]|SymTab],_,Context). /*

Terms are pattern matched to decide if they are generators, constructors or filters.

A variable on its own will almost certainly be already declared.

 */ preprocess_term(X,X1,SymTab,SymTab1,Context) :- var(X),!,preprocess(X,X1,SymTab,SymTab1,Context). /*

A variable with a condition is a filter.

 */ preprocess_term((X:B),(X1:B1),SymTab,SymTab2,Context) :- 
                    var(X),!,preprocess_vars(X,X1,SymTab,SymTab1,Context),preprocess(B,B1,SymTab1,SymTab2,Context). /*

Anything with an arrow is a generator.

 */ preprocess_term((X->Y:B),((X1,Y1):B1),SymTab,SymTab3,Context) :- 
                    !,preprocess_vars(X,X1,SymTab,SymTab1,Context),preprocess(Y,Y1,SymTab1,SymTab2,Context),
                    preprocess(B,B1,SymTab2,SymTab3,Context). /*
 */ preprocess_term((X->Y),(X1,Y1),SymTab,SymTab2,Context) :- 
                    !,preprocess_vars(X,X1,SymTab,SymTab1,Context),preprocess(Y,Y1,SymTab1,SymTab2,Context). /*

The pattern with a condition is a filter.

 */ preprocess_term((X:B),(X1:B1),SymTab,SymTab2,Context) :- 
                    !,preprocess_vars(X,X1,SymTab,SymTab1,Context),preprocess(B,B1,SymTab1,SymTab2,Context). /*

Anything else is just a tuple, and should almost certainly include only declared variables.

 */ preprocess_term(X,X1,SymTab,SymTab1,Context) :- preprocess(X,X1,SymTab,SymTab1,Context). /*

Sequences are represented as sets of pairs and are mappings from 1-n onto their terms.

 */ preprocess([],[],SymTab,SymTab,_) :- !. /*
 */ preprocess([H|T],Z,SymTab,SymTab1,Context):- !,preprocess_seq(1,[H|T],Z,SymTab,SymTab1,Context). /*

 */ preprocess_seq(_,[],[],SymTab,SymTab,_)   :- !. /*
 */ preprocess_seq(N,[H|T],[(N,H1)|T1],SymTab,SymTab2,Context):- 
                   preprocess(H,H1,SymTab,SymTab1,Context),N1 is N+1,preprocess_seq(N1,T,T1,SymTab1,SymTab2,Context). /*

Pairs translate to pairs, and '->' is converted to ','.  This takes care of tuples too.

 */ preprocess((X,Y),(X1,Y1),SymTab,SymTab2,Context) :- 
               !,preprocess(X,X1,SymTab,SymTab1,Context),preprocess(Y,Y1,SymTab1,SymTab2,Context). /*
 */ preprocess(((X->Y)),(X1,Y1),SymTab,SymTab2,Context) :- 
               !,preprocess(X,X1,SymTab,SymTab1,Context),preprocess(Y,Y1,SymTab1,SymTab2,Context). /*

An atom or integer denotes itself.

 */ preprocess(X,X,SymTab,SymTab,_) :- atomic(X),!. /*

Comparison operators are given special treatment.  Alternative operators need to be converted to a common form.  The predicate 'comparison(C,X,R,Y)' succeeds if C is a comparison, setting X to the left operand, R to the normalised relation, and Y to the right operand.

 */ comparison(C,_,_,_)  :- var(C),!,fail. /*
 */ comparison(X<Y,X,<,Y)  :- !. /*
 */ comparison(X=<Y,X,=<,Y) :- !. /*
 */ comparison(X\>Y,X,=<,Y) :- !. /*
 */ comparison(X>=Y,X,>=,Y) :- !. /*
 */ comparison(X\<Y,X,>=,Y) :- !. /*
 */ comparison(X>Y,X,>,Y)  :- !. /*
 */ comparison(X=Y,X,=,Y)  :- !. /*
 */ comparison(X\=Y,X,\=,Y) :- !. /*
 */ comparison(X subset Y,X,subset,Y) :- !. /*
 */ comparison(X inside Y,X,inside,Y) :- !. /*
 */ comparison(X encloses Y,X,encloses,Y) :- !. /*
 */ comparison(X includes Y,X,includes,Y) :- !. /*
 */ comparison(X disjoint Y,X,disjoint,Y) :- !. /*
 */ comparison(X equal Y,X,equal,Y) :- !. /*
 */ comparison(X unequal Y,X,unequal,Y) :- !. /*

Shorthands such as X<Y<Z must be expanded to X<Y&Y<Z.  After decomposing a comparison, 'preprocess_comparison' is called to check if the right operand is also a comparison.  If it is, it is preprocessed recursively, then linked to the first comparison by '&'.  In any case, the left and right operands of the comparison are preprocessed, then combined with the normalised relation.

 */ preprocess(C,C1,SymTab,SymTab1,Context) :- comparison(C,X,R,Y),!,preprocess_comparison(X,R,Y,C1,SymTab,SymTab1,Context). /*

 */ preprocess_comparison(X,R,Y,T1 & T2,SymTab,SymTab3,Context) :- comparison(Y,X2,_,_),!,
                          preprocess(X,X1,SymTab,SymTab1,Context),preprocess(X2,X3,SymTab1,SymTab2,Context),T1=..[R,X1,X3],
                          preprocess(Y,T2,SymTab2,SymTab3,Context). /*
 */ preprocess_comparison(X,R,Y,T1,SymTab,SymTab2,Context) :- 
                          preprocess(X,X1,SymTab,SymTab1,Context),preprocess(Y,Y1,SymTab1,SymTab2,Context),T1=..[R,X1,Y1]. /*

Any other function is processed by operating on its operands recursively.

 */ preprocess(X,Y,SymTab,SymTab1,Context) :- 
               compound(X),!,X =.. [F|A],preprocess_args(A,A1,SymTab,SymTab1,Context),Y =..[F|A1]. /*

 */ preprocess_args([],[],SymTab,SymTab,_). /*
 */ preprocess_args([H|T],[H1|T1],SymTab,SymTab2,Context) :- 
                    preprocess(H,H1,SymTab,SymTab1,Context),preprocess_args(T,T1,SymTab1,SymTab2,Context). /*

If the preprocessor is working correctly, no other case needs to be allowed for.

 */ preprocess(X,X,SymTab,SymTab,Context) :- write('Bug: '),nl,write(preprocess(X,X,SymTab,SymTab,Context)),nl. /*

Within 'preprocess_vars' we expect a pattern of tuples of variables, and nothing else.

It is OK if the variable has already been declared locally.

 */ preprocess_vars(X,Y,SymTab,SymTab,_) :- var(X),in_locals(X,Y,SymTab),!. /*

It is bad news if the variable has already been declared globally.

 */ preprocess_vars(X,Y,SymTab,SymTab,Context) :- 
                    var(X),in_scope(X,Y,SymTab),!,display_line(Context),
                    write('An argument is already declared in an enclosing context.'),nl. /*

An input variable that is not in scope is added to the local symbol table.  It is necessary to use copy_term, otherwise it will remain bound to all other occurences of the same variable in the term.

 */ preprocess_vars(X,Y,[Lcl|Gbl],[[(X,Y)|Lcl]|Gbl],_):- var(X),!,copy_term(X,Y). /*

Pairs translate to pairs.  This takes care of tuples too.

 */ preprocess_vars((X,Y),(X1,Y1),SymTab,SymTab2,Context) :- 
                     !,preprocess_vars(X,X1,SymTab,SymTab1,Context),preprocess_vars(Y,Y1,SymTab1,SymTab2,Context). /*

An atom or integer denotes itself.

 */ preprocess_vars(X,X,SymTab,SymTab,_) :- atomic(X),!. /*

It is an error for any other function to be present.  It is processed by operating on its operands recursively.  

 */ preprocess_vars(X,Y,SymTab,SymTab1,Context) :- 
                     compound(X),!,display_line(Context),X =.. [F|L],write('A '''),write(F),
                     write(''' operation appears in an argument pattern.'),nl,
                     list_to_tuple(L,A),preprocess_vars(A,A1,SymTab,SymTab1,Context),tuple_to_list(A1,L1),Y =..[F|L1]. /*

End of literate program. */
