[net.sources] Prolog library: cai.pl

pereira@sri-unix.UUCP (08/15/83)

/* Main menu presentation statementes  */


present_menu(Title, Menu, New_menu) :-
  {repeat((nl,
           print_line(Title),
           print_menu(Menu),
           select_menu_option(Menu, Next_menu, Until, Done)),
         Until)},
  present_new_menu(Done, Title, Next_menu, New_menu).

present_new_menu(done, Title, Next_menu, Next_menu).
present_new_menu(_, Title, Next_menu, New_menu) :-
  !,
  present_menu(Title, Next_menu, New_menu).


print_menu(List) :-
  nl,
  print_menu_list(1, List).

print_menu_list(_, []) :-
  nl.
print_menu_list(Index, [[Flag, Title, Function]|R]):-
  menu_flag(Flag, Print_flag),
  write(Print_flag),
  write(' '),
  write(Index),
  write('. '),
  print_line(Title),
  New_index is Index + 1,
  print_menu_list(New_index, R).

menu_flag(done, '*').
menu_flag(not_done, ' ').
menu_flag(_, '?').

select_menu_option(Menu, New_menu, Until, Done) :-
  {read_in([Command| _], lowercase, 'Please select an option: ',
           'End with a period (.).  help. for options.  ')},
  execute_menu_command(Command, Menu, New_menu, Until, Done).


select_menu_option(_, _, fail, _) :-
  print_paragraph([
    ' ',
    'Please select an option number like 1. or 2. or one of the following:',
    ' ',
    'save.       Saves the state of the session so you can continue later.',
    'quit.       Quit the session.  You can continue if you have done a save.',
    'done.       Done with this menu.  Back up to the previous menu.',
    'next.       Select the next option not starred (*).',
    '.           Same as next.'
    ]),
  continue_prompt.

execute_menu_command(save, _, _, fail, _) :-
  save_in_a_file.
execute_menu_command(quit, _, _, _, _) :-
  abort.
execute_menu_command(done, _, _, true, done).
execute_menu_command(next, Menu, New_menu, true, not_done) :-
  execute_next_menu_selection(Menu, New_menu).
execute_menu_command(next, _, _, true, done).
execute_menu_command(., Menu, New_menu, true, not_done) :-
  execute_next_menu_selection(Menu, New_menu).
execute_menu_command(., _, _, true, done).
execute_menu_command(Index, Menu, New_menu, true, not_done) :-
  integer(Index),
  Index>0,
  execute_indexed_menu_selection(Index, Menu, New_menu).

execute_next_menu_selection(
  [[not_done| Rest]| Not_done],
  [New_selection| Not_done]) :-
    execute_menu_selection([not_done| Rest], New_selection).
execute_next_menu_selection(
  [Current_option| Not_checked],
  [Current_option| New_menu]) :-
    execute_next_menu_selection(Not_checked, New_menu).

execute_indexed_menu_selection(
  1,
  [Selection| Not_done],
  [New_selection| Not_done]) :-
    execute_menu_selection(Selection, New_selection).
execute_indexed_menu_selection(
  Index,
  [Current_option| Not_checked],
  [Current_option| New_menu]) :-
    New_index is Index - 1,
    execute_indexed_menu_selection(New_index, Not_checked, New_menu).

execute_menu_selection(
  [_, Title, [Menu| Rest_of_menu]],
  [done, Title, [New_menu| Rest_of_new_menu]]) :-
    present_menu(Title, [Menu| Rest_of_menu], [New_menu| Rest_of_new_menu]).
execute_menu_selection(
  [_, Title, Function],
  [done, Title, Function]) :-
    Function.
execute_menu_selection([_, Title, Function], [?, Title, Function]).


new_menu([], []).
new_menu([[not_done| Item]| New_rest], [Item| Rest]) :-
  new_menu(New_rest, Rest).


/* Cai lessons */

help :- hi.

hi :-
  {hello,
   main_menu(Menu)},
  present_menu('Main Prolog CAI Menu', Menu, _),
  good_luck.


main_menu(Menu) :-
  basic_prolog_menu(Basic_prolog_menu),
  built_in_predicates_menu(Built_in_predicates_menu),
  examples_menu(Examples_menu),
  meta_logic_menu(Meta_logic_menu),
  new_menu(Menu,
   [['Basic Prolog', Basic_prolog_menu],
    ['Built-in Predicates', Built_in_predicates_menu],
    ['Examples', Examples_menu],
    ['Meta Logic', Meta_logic_menu]]).

basic_prolog_menu(Menu) :-
  new_menu(Menu,
    [['Introduction', prolog_introduction],
     ['Prolog Format', prolog_format],
     ['Prolog Matching', prolog_matching],
     ['Prolog Predicates', prolog_predicates],
     ['Prolog Variables', prolog_variables],
     ['Prolog Implications', prolog_implications],
     ['Built-in Predicates', prolog_builtin_predicates],
     ['Entering a Prolog Data Base', entering_a_data_base]]).

built_in_predicates_menu(Menu) :-
  add_remove_menu(Add_remove_menu),
  input_output_menu(Input_output_menu),
  new_menu(Menu,
    [['Adding and Removing Clauses from the Data Base', Add_remove_menu],
     ['Input and Output Predicates', Input_output_menu],
     ['Arithmetic Predicates', arithmetic_lesson],
     ['Saving and Restoring a Session', save_restore_lesson]]).

input_output_menu(Menu) :-
  new_menu(Menu,
    [['Reading in a DataBase', consult_lesson],
     ['Reading and Writing Data', read_write_lesson],
     ['File Access', file_lesson]]).

add_remove_menu(Menu) :-
  new_menu(Menu,
    [['Introduction', assert_retract_introduction],
     ['Assert Example', assert_example],
     ['Retract Example', retract_example]]).

meta_logic_menu(Menu) :-
  new_menu(Menu,
    [['Introduction', meta_logic_introduction],
     ['Meta Logic Predicates', meta_logic_predicates],
     ['Clause Manipulation', meta_logic_clause_manipulation]]).

examples_menu(Menu) :-
  new_menu(Menu,
    [['Graph/Edge Example', edge_lesson],
     ['List Operations', list_operation_lesson],
     ['Descendant Relation Example', descendant_relation_lesson],
     ['Mini-Prolog Interpreter', mini_interpreter_lesson]]).



hello :-
  nl,
  read_in([yes|_], lowercase, 'Have you used this CAI course before? ',
          'Please answer yes or no and terminate the line with a period. :'),
  nl.
hello :-
  print_title('HELLO'),
  print_paragraph([
    'Welcome to the Prolog CAI course.  This course is still in the',
    'experimental stages.  Please direct any comments or problems to WONG',
    'on the Green machine at Rutgers.  Thank you.',
    ' ',
    'This course is implemented in Prolog and uses the Prolog I/O routines.',
    'These routines require that each response be terminated by a period (.)',
    'and a carrige return (CR).',
    ' ',
    'The following prompt will be given at the end of each page so that you',
    'can read the material.  If you want to continue enter  yes.  to the',
    'prompt and  no.  if you want to backup and read the previous material.'
    ]),
  continue_prompt,
  print_paragraph([
    'The following is a list of available options to this prompt.',
    ' '
    ]),
  continue_response(help, _, _),
  print_paragraph([
    ' ',
    'Ok, on to the heart of the matter',
    ' '
    ]),
  continue_prompt.

  

prolog_introduction :-
  print_title('Introduction'),
  print_paragraph([
    'Prolog stands for PROgramming and LOGic.  The language is based on', 
    'a modified Horn clauses (x:-y,z. where x is implied by y and z).  The',
    'The language was developed at the University of Marseille as a practical',
    'tool for programming in logic.  Prolog does not include explicit loops',
    'or assignments but instead incorporates built-in backtracking which will',
    'be covered in more detail later.',
    ' ',
    'The DEC-10 Prolog system is based on an interpreter with a compiler for',
    'additional speed of debugged programs.  The compiler reduces execution',
    'time by a factor of 10 to 20, however, the interpreter will be used here.'
    ]),
  continue_prompt,
  print_paragraph([
    'A Prolog program consists of a set Horn clauses.  A clause is made up',
    'of a head and a body with the basic form of head :- body.  where',
    'the head is implied by the body of the clause.  Logicians often write',
    'this as BODY -> HEAD.',
    ' ',
    'Some examples are:',
    ' ',
    'all_wet :- covered_with_water.',
    'venerable :- over_sixty.'
    ]),
  continue_prompt.

prolog_format :-
  print_title('Prolog Format'),
  print_paragraph([
    'The Prolog Horn clauses have one basic format:',
    ' ',
    'left :- right.',
    ' ',
    'This states that the left side is implied by the right side.  Of course',
    'either the left side or right side can be null which gives rise to the',
    'two following forms:',
    ' ',
    'left :-.         an assertation that the left is true.',
    ' ',
    ':- right.        a question, Is the right side true?'
    ]),
  continue_prompt,
  print_paragraph([
    'The following is a summary of the Prolog format.',
    ' ',
    'Description    Example         Basic Syntax    Alternate Syntax',
    '-----------    -------         ------------    ----------------',
    'Assertation    apple.          HEAD:-.         HEAD.',
    'Implication    fruit:-apple.   HEAD:-BODY.',
    'Question       ?-fruit.        :-BODY.         ?-BODY.',
    ' ',
    'The assertation states that an apple exists.  The implication states',
    'that an apple is a fruit.  The question asks: Is there a fruit?  With',
    'these facts the answer to the question is yes because there is an apple',
    'and a fruit is implied by an apple'
    ]),
  continue_prompt.


prolog_matching :-
  print_title('Prolog Matching'),
  print_paragraph([
    'Prolog allows you to enter statments into the data base and',
    'also query the data base.  I have put together a small data',
    'base of fruits for you to query.  The following is a list of the',
    'fruity data base:'
    ]),
  Database=[apple, grape, orange, banana],
  write_listing(Database),
  continue_prompt,
  print_paragraph([
    'You can query this data base using the Prolog question form.  This',
    'is done by entering a question to the ?- prompt.  Remember that the',
    'question must end in a period (.).  Prolog will print out the result',
    'if it is in the data base.  It will then ask you if you would like to',
    'try to find another possibility.  You can answer yes. or no.',
    ' ',
    'If the information is not in the data base then Prolog will give a no',
    'response and allow you to ask another question.',
    ' ',
    'You will get a yes response to ?-apple. since it is in the sample data',
    'base.  You can quit the query by entering done. as a question.'
    ]),
  ask_prolog_questions([],Database).

apple.
orange.
grape.
banana.


prolog_predicates :-
  print_title('Prolog Predicates'),
  print_paragraph([
    'A data base of single facts has a few uses but if one adds',
    'a predicate relationship then the number of facts which',
    'can be represented greatly increases.  The form of a',
    'predicate is predicate(argument1, argument2, ...) where the',
    'predicate represents a relationship between the arguments.',
    ' ',
    'Note that a predicate can have any number of arguments and',
    'that this number need not be constant within a data base.'
    ]),
  continue_prompt,
  print_paragraph([
    'For example:  fruit(apple) and fruit(orange) are relationships',
    'as are tree(maple) and tree(orange, fruit).  Note the last',
    'predicate can have either one or two arguments.',
    ' ',
    'Note that predicates must be Prolog constants which are',
    'numbers or names beginning with a lowercase letter.  The',
    'arguments can be either variables or constants.  Variables',
    'will be covered later.'
    ]),
  continue_prompt,
  print_paragraph([
    'The following data base is available for your use:'
    ]),
  listing(tree),
  ask_prolog_questions(
    ['Try querying this data base which uses predicates as before.'],
    [tree]).

tree(maple).
tree(pine).
tree(orange).
tree(spruce).
tree(cherry).


prolog_variables :-
  print_title('Prolog Variables'),
  print_paragraph([
    'Working with constants is fine if you know what you want and',
    'simply wish to verify the contents of the data base, however,',
    'we usually want to get information from the data base.  This',
    'is done using Prolog variables in place of constants.',
    ' ',
    'A Prolog variable is a name which begins with a _ or a capital',
    'letter.  Examples are: Variable, _100, and HELLO.  Note that',
    'only arguments of predicates can be variables and that a',
    'predicate must be a constant.',
    ' ',
    'Prolog will attempt to find a binding for a variable when it',
    'is used in a question.  You can therefore query a data base',
    'using a variable and Prolog will fill it in.'
    ]),
  continue_prompt,
  print_paragraph([
    'Note that the sytem will normally prompt with the variable',
    'name = value as in X = cherry where X is the variable you',
    'have used in your query.  If you respond with a carrige return',
    '(CR) then the system will ask for another question, but if you',
    'respond with a semicolon and a CR then the system will try to',
    'find another binding for the variable.',
    ' ',
    'This is a useful way to interactively query the data base.'
    ]),
  continue_prompt,
  print_paragraph([
    'An example with one variable (X) and two bindings is:',
    ' ',
    '?-tree(X).',
    ' ',
    'X = maple ;',
    ' ',
    'X = spruce'
    ]),
  continue_prompt,
  print_paragraph([
    'Unfortunately this cannot be simulated here so you will have',
    'to live with the following method of asking for alternatives.',
    'After you ask a question the system will try to find a binding',
    'for the variables in the question and write out the question',
    'predicate with bound constants found in the data base.  The system',
    'will then wait for a response from you to tell it what to do next.',
    ' ',
    'A question mark (?) will cause the routine to search for more',
    'bindings while a period (.) will case the search to end and you will',
    'be able to ask another question.'
    ]),
  continue_prompt,
  print_paragraph([
    'Try accessing this data base using variables and constants:'
    ]),
  Database=[tree, contains],
  write_listing(Database),
  ask_prolog_questions(['Try ?-contains(X,Y)'], Database).

contains(book, pages).
contains(magazine, pages).
contains(apple, seeds).
contains(jug, water).
contains(glass, water).
contains(box, marbles).

prolog_implications :-
  print_title('Prolog Implications'),
  print_paragraph([
    'The real power of Prolog is provided by the ability to',
    'provide implications within a data base instead of just',
    'plain vanilla facts.  For example, we can have a number of',
    'animals of different types such as fish, birds, and elephants.',
    ' ',
    'Now we could put statements to the affect that a particular',
    'fish is also an animal or we can say that all fish are animals',
    'but this would lead to a large number of facts which could be',
    'represented in a much more compact way using implications.',
    ' ',
    'The Prolog implication is of the form X :- Y.',
    'This implication makes the previous task very easy and in fact',
    'we can state that animals are fish, birds, and elephants in',
    'just three statements.'
    ]),
  continue_prompt,
  print_paragraph([
    'The following data base includes implications.  Note that the',
    'implications use the variable X on both sides of :- and that',
    'the predicates can be used in implications or plain facts.',
    ' ',
    'If you ask about an animal Prolog will try each',
    'statement in order until it finds one that matches.  If you',
    'asked a question like ?-animal(Y). then Prolog would then',
    'see the first implication and use Y for X.  Since it is an',
    'implication it will be true if the item to the right of :-',
    'is true.  In this case it is fish(Y).  Prolog then tries to',
    'find a fish(Y) and bind Y to a fish and return it as a result.',
    'If Prolog cannot find a fish it will try the next implication.'
    ]),
  nl,
  listing(animal),
  continue_prompt,
  print_paragraph([
    'The remainder of the data base includes:'
    ]),
  nl,
  write_listing([fish, bird, elephant]),
  ask_prolog_questions(['Try ?-animal(X).'], [animal, fish, bird, elephant]).

animal(X) :- fish(X).
animal(X) :- bird(X).
animal(X) :- elephant(X).
animal(freemont).
animal(mergatroid).

fish(tuna).
fish(bass).
fish(charlie).

bird(tweety).
bird(eagle).
bird(penguin).

elephant(clyde).
elephant(dumbo).

prolog_builtin_predicates :-
  print_title('Built-in Prolog Predicates and Infix Notation'),
  print_paragraph([
    'Prolog has a number of built-in predicates. Prolog also has',
    'the ability to put the predicates and arguments into infix',
    'notation which increase readability and ease of entry.',
    ' ',
    'The most commonly used built-in predicate other than ?-',
    'and :- (yes they are actually predicates) is the comma.',
    'The comma is actually the Prolog version of the logical AND.',
    'A predicate format of a comma is AND(a,b).  Note that the AND',
    'is used in place of the comma in the example and the comma',
    'which is between the a and b is an argument seperator.',
    'The infix form of this example is a,b.',
    ' ',
    'The following infix example:  a, b, c, d',
    'is the same as the predicate form:  AND(a, AND(b, AND(c, d))).'
    ]),
  continue_prompt,
  print_paragraph([
    'You can therefore ask a question like:',
    ' ',
    '?-fruit(X), contains(X, seeds).',
    ' ',
    'The question is: find an X such that X is a fruit and X',
    'contains seeds.  Note that Prolog will evauluate the',
    'expression from left to right so that it will try to find',
    'a fruit X before checking about the X containing seeds.',
    'If it cannot find a fruit it will fail.'
    ]),
  continue_prompt,
  print_paragraph([
    'Prolog also includes a number of other built-in predictes',
    'which can be used.  They include:',
    ' ',
    'X , Y               X and Y',
    'X ; Y               X or Y',
    'write(X)            writes X to the output stream (terminal)',
    'nl                  starts a new line on the output stream',
    'read(X)             reads X from the input stream (terminal)',
    '                        Note: the input must end with a .',
    'X = Y               X equals Y',
    'listing(X)          lists all clauses headed by predicate X',
    'listing             lists all clauses',
    'trace               enables tracing of searches',
    ' ',
    'This list is extremely short but it does include a few',
    'frequently used predicates.  Please consult the Prolog Users',
    'for a more complete list.'
    ]),
  continue_prompt,
  print_title('Questions with compound values'),
  print_paragraph([
    'Try asking a few questions about this data base:'
    ]),
  Database=[owns, location],
  write_listing(Database),
  ask_prolog_questions(['Try ?-owns(X,Y),location(Y,city).'], Database).

owns(john, johns_house).
owns(bill, bills_house).
owns(bill, bills_cottage).
owns(mary, marys_house).
owns(mary, marys_cottage).
owns(mary, car).

location(johns_house, city).
location(marys_house, city).
location(bills_house, town).
location(marys_cottage, mountains).
location(bills_cottage, mountains).
location(car, city).

entering_a_data_base :-
  print_title('Using Prolog and Entering a Data Base'),
  print_paragraph([
    'To access Prolog from the DEC-20 monitor type PROLOG.',
    'The top level prompt is | ?- to which you can enter questions.',
    'Note that you do not have to type the ?- to ask a question.',
    'To get to the data entry level enter [user]. to the prompt.',
    'The prompt will now be | only. You can ask questions by typing',
    'the ?- and then the question.',
    ' ',
    'The | only prompt will allow you to enter facts into the data',
    'base.  Facts include basic statements such as tree(pine) and',
    'implications such as plant(X) :- tree(X).',
    ' ',
    'A control-Z will get you back to the top level.'
    ]),
  continue_prompt.

edge_lesson :-
  print_title('Edge Data Base Example'),
  print_paragraph([
    'Prolog allows implications and assertations to be searched',
    'built-in backtracking when it cannot find the answer to a',
    'question.  The following data base consists of asserations',
    'about edges between nodes.  edge(x,y) states that node x is',
    'connected to node y by an edge.',
    ' ',
    'The implications are connected(_1, _2) where _1 and _2 are',
    'Prolog variables.  The value is true if _1 and _2 are nodes',
    'which are connected by an edge or connected through other',
    'connected nodes.  Note that the example contains two trees',
    'which are not connected.'
    ]),
  continue_prompt,
  print_paragraph([
    'The example edge data base consists of the following:'
    ]),
  Database=[edge, connected],
  write_listing(Database),
  ask_prolog_questions(
        ['Try ?-connected(letters,Y). and Try ?-connected(1,a).'],
        Database).

connected(X, Y) :- edge(X, Y).
connected(X, Y) :- edge(Y, X).
connected(X, Z) :- edge(X, Y), connected(Y, Z).

edge(letters, a).
edge(letters, b).
edge(b, c).
edge(c, d).
edge(numbers, 1).
edge(numbers, 2).
edge(2,4).
edge(1,3).


list_operation_lesson :-
  print_title('List Operation Example'),
  print_paragraph([
    'Prolog has a list structure available to the user.  A four',
    'element list is [1,2,3,4] with elements 1,2,3, and 4.  A list',
    'can be divided into two parts: the first element and the rest',
    'of the list.  This is done using the following format:',
    ' ',
    '[1,2,3,4]=[First|Rest]',
    ' ',
    'Fist=1 and Rest=[2,3,4]',
    ' ',
    'This method is used to extract items from a list.'
    ]),
  continue_prompt,
  print_line('The following statments can be used to manipulate lists:'),
  Database=[append, member, reverse, reverse_append],
  write_listing(Database),
  ask_prolog_questions(
        ['Try ?-reverse([1,2,3],X) and ?-member(a,[a,b,c,a,b,c]).'],
        Database).

append([], L, L).
append([X|L1], L2, [X|L3]) :-
  append(L1, L2, L3).

member(X, [X|L]).
member(X, [_|L]) :-
  member(X, L).

reverse(L1, L2) :-
  reverse_append(L1, [], L2).

reverse_append([], L, L).
reverse_append([X|L1], L2, L3) :-
  reverse_append(L1, [X|L2], L3).



descendant_relation_lesson :-
  print_title('Descendant Relationship Example'),
  print_paragraph([
    'A typical application in Prolog entails representing a',
    'family tree via an offspring relationship.  This involves',
    'a data base of offspring and an implication of who a',
    'descendant is.  In addition we can find any siblings of',
    'a person in a similar manner.  (\== is not equals)',
    ' ',
    'The implications in the data base are:'
    ]),
  listing(descendant),
  listing(sibling),
  continue_prompt,
  print_paragraph([
    'The offspring assertations are:'
    ]),
  listing(offspring),
  ask_prolog_questions(
       ['Try ?-descendant(abraham,X).'],
       [descendant, sibling, offspring]).

descendant(X, Y) :- offspring(X, Y).
descendant(X, Z) :- descendant(X, Y), descendant(Y, Z).

sibling(X, Y) :- offspring(P, X), offspring(P, Y), X \== Y.

offspring(abraham, ishmael).
offspring(abraham, isaac).
offspring(isaac, esau).
offspring(issac, jacob).


new(sample).


assert_retract_introduction :-
  print_title('Prolog assert/retract Predicates'),
  print_paragraph([
    'Prolog has two ways of adding clauses into the data base and one',
    'way remove it.  The two ways of adding clauses are by entering the',
    'assertation form to the Prolog user level and using the Prolog assert',
    'predicate.  Clauses may be removed using the Prolog retract predicate.',
    'An example of these methods is:',
    ' ',
    'Assertation form:        predicate(X).                    Adds',
    'Alternate form:          predicate(X):-.                  Adds',
    'Assert predicate:        ?-assert(predicate(X)).          Adds',
    'Retract predicate:       ?-retract(predicate(X)).         Removes'
    ]),
  continue_prompt.

assert_example :-
  print_title('Prolog Assert Predicate'),
  print_paragraph([
    'The assert predicate is used to add Horn clauses to the data base.',
    'The form of the assert predicate is:',
    ' ',
    '?-assert(X).',
    ' ',
    'where X is the clause to be added to the data base.',
    ' ',
    'NOTE:  The assert predicate cannot be backed up.  Once it is',
    'evaluated it will not be undone if a failure occurs during the',
    'evaluation of the rest of clause.'
    ]),
  continue_prompt,
  print_paragraph([
    'An example using the assert with a subsequent failure is:',
    ' ',
    'a(X) :- something(X), assert(new(X)), fail.',
    ' ',
    'If we evaluate ?-a(1). and something(1) exists then we will',
    'add new(1) to the data base. The fail predicate is a built-in',
    'Prolog predicate which always fails and would do so after we',
    'added new(1) to the data base.  Prolog would then backup but',
    'it would not undo the assert.'
    ]),
  continue_prompt,
  print_paragraph([
    'Try asserting statements of the form new(X) into the',
    'data base using the assert predicate.  You will be able',
    'to list your additions by typing help. to the prompt.'
    ]),
  nl,
  ask_prolog_questions(['Try ?-assert(new(1))'], [new]).

retract_example :-
  print_title('Prolog retract predicate'),
  print_paragraph([
    'The Prolog retract predicate works in the opposite manner to the assert',
    'predicate.  The form is retract(X) where X is a Horn clause.  The',
    'retract predicate result will be true if there is a clause in the data',
    'base that matches X.  The predicate has a false value if no statement',
    ' in the data base matches X.',
    ' ',
    'An example is:  ?-retract(new(1)).'
    ]),
  continue_prompt,
  print_paragraph([
    'Try retracting some of the statements you have added to the data base.'
    ]),
  write_listing([new]),
  ask_prolog_questions(['Try ?-retract(new(sample)).'],[new]).


mini_interpreter_lesson :-
  print_title('Mini-Interpreter Example'),
  print_paragraph([
    'The Mini-interpreter consists of four statements:'
    ]),
  write_listing([execute]),
  print_paragraph([
    'The first is that basic statement that true is true.',
    'The second executes compound statements, the third',
    'executes implications and the last one allows the use',
    'evaluatable Prolog predicates.'
    ]),
  continue_prompt,
  write_listing([execute]),
  print_paragraph([
    'Note the use of the cut predicate ! which is also a built-in',
    'Prolog predicate.  It tells the Prolog interpreter that it',
    'not backtrack past (to the left) the cut symbol if a failure',
    'is detected to its right.  But let us test out the execute',
    'statements.'
    ]),
  continue_prompt,
  print_paragraph([
    'Try using the execute statements on this data base.'
    ]),
  Database=[tree, contains],
  write_listing(Database),
  ask_prolog_questions(
       ['Try ?-execute((tree(X),contains(Y,pages))).'],
       Database).

execute(true) :- !.
execute((P,Q)) :- !, execute(P), execute(Q).
execute(P) :- clause((P :- Q)), execute(Q).
execute(P) :- P.


arithmetic_lesson :-
  print_title('Built-in Arithmetic Predicates'),
  print_paragraph([
    'Prolog has a number of built-in arithmetic predicates such as add (+)',
    'subtract (-).  These predicates can be used to compute values of integer',
    'equations when used in conjunction with other built-in functions such as',
    'the function is (i.e. Z is 1+2 where Z is 3).',
    ' ',
    'Note that 3=1+2 is a false statement because = is an exact comparision',
    'function, however, 3 is 1+2 is a true statement because is evaluates the',
    'right parameter (1+2).  The left parameter can be a variable or an',
    'integer of the appropriate value for the statement to be true.']),
  continue_prompt,
  print_paragraph([
    'The following is a short description of the built-in Prolog arithmetic',
    'functions.  A more detailed description appears in the Prolog manual.',
    'Note: integers range from -2^17 to 2^17-1.',
    ' ',
    'Z is X       X is an evaluable integer expression to be unified with Z.',
    'Z = X        Defined as Z=Z.  It does not evaluate Z.',
    'X =:= Y      The values of X and Y are equal.',
    'X =\= Y      The values of X and Y are not equal.',
    'X =< Y       The value of X is less than or equal to  Y.',
    'X >= Y       The value of X is greater than or equal to Y.',
    'X < Y        The value of X is less than Y.',
    'X > Y        The value of X is greater than Y.']),
  continue_prompt,
  print_paragraph([
    'Prolog also includes a number of other useful arithmetic operations.',
    ' ',
    '-X        negation           \X       bitwise negation',
    'X + Y     addition           X /\ Y   bitwise AND',
    'X - Y     subtraction        X \/ Y   bitwise OR',
    'X * Y     multiplication     X << Y   bitwise left shift of X by Y bits',
    'X / Y     division           X >> Y   bitwise right shift of X by Y bits',
    'X mod Y   remainder          !(X)     X is >= 0',
    '[X]       evaluates to X     $(X)     X is an integer']),
  ask_prolog_questions(['Try ?-Z=1+2.  and ?-X is 1+2.'],[]).

save_restore_lesson :-
  print_title('Saving and Restoring a Prolog Session'),
  print_paragraph([
    'Prolog has two built-in predicates which are used to save and restore',
    'the state of a Prolog session.  These are:',
    ' ',
    'save(File)             saves a Prolog session in File.',
    'restore(File)          restores a Prolog session which was saved.',
    ' ',
    'In either case File must be instantiated to a file name.  If a constant',
    'is used and the name contains a period then the name must be in quotes.',
    'The session will be saved in the exact state that execution is in so',
    'a partial evaluation will be continued from the same point.',
    ' ',
    'All assertations and implications are saved and restored.']),
  continue_prompt.


consult_lesson :-
  print_title('Consulting Prolog Text Files'),
  print_paragraph([
    'Prolog can read in Prolog statements in two ways: one is via the',
    'restore(File) predicate which requires the entire session to be saved',
    'using the save(File) predicate and the other method uses file',
    'consultation.',
    ' ',
    'The consultation method reads text files with the same format as user',
    'entries to Prolog.  That is, assertations and implications such as',
    'father(john, mark). and parent(X,Y) :- father(X,Y).   These statements',
    'are read in a processed just as if the user entered them which means',
    'any errors will be noted by the system.']),
  continue_prompt,
  print_paragraph([
    'Since Prolog converts all variable names to numeric names the text',
    'file input tends to be the most universal way of entering a data base',
    'which is to be modified by the user since the text editing facilities',
    'of Prolog are very limited.',
    ' ',
    'Prolog provides three predicates to facilitate entry of text files.',
    ' ',
    'consult(File)           read the text File and enter the new',
    '                        clauses into the data base.',
    '[File|Rest]             consult all Files in the list in order.',
    'reconsult(File)         consult the File but any clauses found in the',
    '                        file will replace those in the data base instead',
    '                        of being appended to the existing ones.']),
  continue_prompt,
  print_paragraph([
    'The file names can be any normally used iwith the operating system',
    'with the stipulation that constant file names over six characters,',
    'with periods or special characters must be in quotes.',
    ' ',
    'There is one special file which allows the user to enter clauses in this',
    'fashion.  The name of this file is user so ?-[user]. or ?-consult(user).',
    'will allow entry from the terminal.  The prompt is | and questions can',
    'be asked by supplying the ?- portion of a clause.',
    ' ',
    'The entry from the user file is terminated by ^Z or :-end.']),
  continue_prompt.

read_write_lesson :-
  print_title('Reading and Writing from Prolog'),
  print_paragraph([
    'Prolog has the ability to read and write data between the data base and',
    'a file.  The file can be either the terminal or a disk file.  The normal',
    'file is the terminal.  I/O can be redirected via tell and see predicates',
    'covered in the next session.  The only difference in the operation of',
    'predicates covered in this session is the source or destination of the',
    'information is a file instead of the terminal.']),
  continue_prompt,
  print_paragraph([
    'The basic input operators are:',
    ' ',
    'read(X)     X is unified with the clause from the current input file.',
    '            the clause must be terminated with a full stop which is a',
    '            period immediately followed by a <cr>.  An end of file is',
    '            noted when X is unified with :-end.',
    'get(C)      C is unified with the numeric value of the next ASCII',
    '            character in the input stream.',
    'get0(C)     same as get(C) except it is the first non-blank character.',
    'skip(N)     skip to the next ASCII character or value N']),
  continue_prompt,
  print_paragraph([
    'The following operations can be used to output information.',
    ' ',
    'write(X)     X is written according to current operator declarations.',
    '             Infix notation can be used.',
    'display(X)   X is written in prefix notation.',
    'nl           A new line is started.',
    'put(C)       C must be an ASCII integer value to be printed.',
    'tab(N)       output N (integer) spaces.',
    'putatom(X)   write the atom X.']),
  continue_prompt,
  print_paragraph([
    'In addition to the input and output file Prolog can direct data',
    'to and from the terminal irrespective of the current input or output',
    'file.  These functions are:',
    ' ',
    'ttynl       prints a new line on the terminal.',
    'ttyflush    print the current output buffer now.',
    'ttyget(C)   same as get(C) to the terminal.',
    'ttyget(C)   same as get0(C) to the terminal.',
    'ttyput(C)   same as put(C) to the terminal.']),
  continue_prompt.

file_lesson :-
  print_title('File Operations from Prolog'),
  print_paragraph([
    'Prolog has the ability to redirect I/O operations from the terminal to',
    'a file.  It allows one input file and one output file.  Any I/O directed',
    'at the terminal can therefore be saved or supplied from a file.  Prolog',
    'has the following predicates to perform this task.',
    ' ',
    'see(File)     File becomes the current input stream.',
    'seeing(File)  File unifies with the name of the current input stream.',
    'seen          closes the current input stream and reopens the last one.',
    ' ',
    'tell(File)    File becomes the current output stream.',
    'telling(File) File unifies with the name of the current output stream.',
    'told          closes the current output stream and reopens the last one.',
    ' ',
    'close(File)   closes a currently opened file.']),
  continue_prompt.


meta_logic_introduction :-
  print_title('Introduction to Meta-Logic Predicates'),
  print_paragraph([
    'The predefined meta-logic predicates in Prolog provide a method to',
    'control the search for a solution.  For example, you may wish to',
    'have certain implications in the data base operate only on integers',
    'or when a certain variable is still uninstantiated.',
    ' ',
    'For these instances Prolog has provided predicates such as integer(X)',
    'and var(X) (among others).  In addition to doing these types of checks',
    'you may want to examine the data base for implications or assertions',
    'in order to perform a hueristic search.  For this example Prolog has',
    'the clause(Head,Implication) predicate to match a Head with the head',
    'of an Implication in the data base thereby allowing you to extract',
    'information from the data base without trying to execute it.',
    ' ']),
  continue_prompt,
  print_paragraph([
    'The meta-logic predicates are useful in building higher level',
    'systems on Prolog.  Since clauses can now check the status of',
    'each argument, they can also control the search procedure used',
    'by the system.  A discussion of the methods which can be employed',
    'is beyond the scope of this program but please feel free to',
    'experiment with the meta-logic predicates described later.'
    ]),
  continue_prompt.

meta_logic_predicates :-
  print_title('Meta-Logic Predicates'),
  print_paragraph([
    'The following is a brief list of the built-in meta-logic functions:',
    ' ',
    'var(X)          checks if X is instantiated with a variable (i.e. _1)',
    'novar(X)        not(var(X))',
    'atom(X)         checks if X is instantiated with an atom (i.e. apple)',
    'integer(X)      checks if X is instantiated with an integer',
    'X==Y            succeeds if X and Y are the same including variables',
    'X\==Y           not(X==Y)',
    ' ']),
  ask_prolog_questions(['Try ?-integer(a). and ?-A=1,integer(A).'],[]).

meta_logic_clause_manipulation :-
  print_title('Meta Logic Clause Manipulation'),
  print_paragraph([
    'The following predicates can be used to convert predicates and there ',
    'arguments into lists and lists into predicates thus allowing you to',
    'search the data base and extract information which may be hidden',
    'from the normal Prlog form of evaluation.',
    ' ']),
  continue_prompt,
  print_paragraph([
    'The following is a list of clause manipulation functions:',
    ' ',
    'functor(Term,Name,Arity)    Term is instantiated, Name is instantiated',
    '                            with the predicate name of Term and Arity',
    '                            is instantiated with the number of arguments',
    '                            in Term.',
    'arg(Index,Term,Argument)    like functor but used to instantiate the',
    '                            indexed argument in Term.',
    'Term=..List                 instantiates Term with a List where the list',
    '                            elements are the predicate of the Term and',
    '                            each of its arguments.',
    'name(Atom,List)             instantiates the Atom with a List of numbers',
    '                            which are the ASCII codes for the name of',
    '                            atom',
    ' ']),
  ask_prolog_questions(['Try ?-pred(1,2,a,b)=..A.'],[]),
  print_paragraph([
    'The following functions can be used to execute or extract information',
    'from the data base:',
    ' ',
    'call(X)                    Instantiates X with an assertion or',
    '                           implication in the data base.  If it is',
    '                           an implication then the tail of the',
    '                           implication is executed.',
    'X                          same as call(X).',
    'clause(Head,Implication)   attempts to find Head as a head of an',
    '                           implication in the data base and instantiates',
    '                           Implication with the entire implication.',
    ' ']),
  ask_prolog_questions(['Try asserting some cluases and then accessing them.'],
                       []).


not_done_yet :-
  print_paragraph([
    ' ',
    '      Sorry, this lesson is not done yet.  Please try later.',
    ' ',
    '      If you have any ideas which I could incoporate please let',
    '      me know by sending mail to WONG@GREEN, thank you.',
    ' ']),
  continue_prompt.


good_luck :-
  print_paragraph([
    'I hope this has been a helpful session.  Good luck with Prolog.',
    ' ',
    'For more information check out the following:',
    ' ',
    'HELP PROLOG',
    ' '
    ]).

/* Prolog question input loop */

ask_prolog_questions(Hint, Listing) :-
  nl,
  print_paragraph(Hint),
  repeat((
      nl,
      print_line('Enter query (help. for commands)'),
      read(('| ?-'), Response),
      check_prolog_question(Response, Listing, Until)),
     Until).

check_prolog_question(Question, Listing, Result) :-
  Question =.. [(?-), New_question],
  print_paragraph([
    'You do not need to prefix you question with ?- because',
    'The system does it for you.  Now attempting:',
    Question]),
  check_prolog_question(New_question, Listing, Result).
check_prolog_question(done, _, true).
check_prolog_question(listing, Listing, fail) :-
  print_line('The current data base is:'),
  write_listing(Listing).
check_prolog_question(quit, _, _) :- abort.
check_prolog_question(save, _, fail) :- save_in_a_file.
check_prolog_question(help, _, fail) :-
  print_paragraph([
    'You can ask a question about the data base by entering it to the',
    '?- prompt.  Remember that the question must end with a period (.) and',
    'a carrige return.',
    ' ',
    'Other options are:',
    ' ',
    'help.       prints this message.',
    'done.       gets you out of the question mode.',
    'save.       saves the current state so you can continue later.',
    'quit.       gets you to the top level of Prolog, you can continue',
    '              if you did a save. before this.',
    'listing.    lists the current data base.'
    ]).
check_prolog_question(Clause, _, fail) :-
  call(Clause),
  nl,
  write(Clause),
  write('  '),
  check_for_more.
check_prolog_question(_, _, fail) :-
  nl,
  print_line(no).


check_prolog_question(X, _, _) :-
  atomic(X),
  nl,
  write_list_line([no, '   Try ?-', X, '(constant).']).

check_for_more :-
  {repeat((ttyflush,
           read_in(Input, lowercase, '  ', 'Try help.  '),
           check_for_more_response(Input, Until, Response)),
          Until)},
  Response.

check_for_more_response([more|_], true, fail).
check_for_more_response([?|_], true, fail).
check_for_more_response([yes|_], true, fail).
check_for_more_response([.|_],  true, true).
check_for_more_response([no|_],  true, true).
check_for_more_response([save|_], fail, _) :-
  save_in_a_file,
  write('Would you like more answers').
check_for_more_response([quit|_], _, _) :- abort.
check_for_more_response(_, fail, _) :-
  print_paragraph([
    'At this point you can ask Prolog to backtrack and find more',
    'answers to your question or allow you to ask another question.',
    'You have the following options:',
    ' ',
    'more.        Find more answers.',
    'yes.         same as more.',
    '.            same as more.',
    'no.          No more answers, ask another question.',
    '?            same as no.',
    'save.        save the state so you can continue later.',
    'quit.        go to the Prolog top level, you can continue if you',
    '               saved the state of this session first.',
    'help.        lists this message.'
    ]).

write_listing([]).
write_listing([L|Rest]) :-
  listing(L),
  write_listing(Rest).

  
/* Utility functions */

{X} :-
  X,
  !.

repeat(Body, Until) :-
  repeat,
  {Body},
  Until.

set_of(X,Q,R) :-
  !,
  setof(X,Q,R).     /* built-in function, X is a variable, Q is a quantifier
                       and R is a list of results. */
set_of(_,_,[]).

write_list([]).
write_list([E|L]) :-
  {write(E)},
  write_list(L).

print_list(X) :-
  write_list(X),
  nl.

print_line(X) :-
  write(X),
  nl.

print_title(X) :-
  nl,
  nl,
  print_line(X).

print_text([]).
print_text([First,..Rest]) :-
  {print_line(First)},
  print_text(Rest).

print_paragraph(List) :-
  {nl},
  print_text(List).

write_token_list(X) :-
  interleave(X, ' ', R),
  write(R).

interleave([], _, []).
interleave([E|L], X, [E,X|R]) :-
  interleave(L, X, R).

/* Utility I/O routines using the input parser read_in */

continue_prompt :-
  {repeat((nl,
          read_in(Response, lowercase, 'Ready for more? ',
                  'Terminate with a period. '),
          continue_response(Response,Until,Result),
          nl, nl)
         ,Until)},
  Result.

continue_command(yes, true, true).
continue_command(., true, true).
continue_command(no, true, fail).
continue_command(quit, _, _) :- abort.
continue_command(save, fail, _) :- save_in_a_file.

continue_response([Command|_], Until, Result) :-
  continue_command(Command, Until, Result).
continue_response(_, fail, _) :-
  print_paragraph([
    'Options are:',
    'help.       prints this message.',
    'save.       to save this session for later use.',
    'quit.       to quit this session, you can continue if you did a save.',
    'yes.        to continue.',
    '.           same as yes.',
    'no.         to backup.'
    ]).


save_in_a_file :-
  print_paragraph([
    'You must enter the file name in which to save this session.',
    'For example: plhelp.',
    ' ',
    'Only uses a six letter name and only use A-Z and 0-9.'
    ]),
  read('Enter file name: ', File_name),
  print_paragraph([
    'Enter ?-restore(X). to restart from this point when you are',
    'the top level of Prolog.',
    'For example:  ?-restore(plhelp).'
    ]),
  save(File_name).


/* Utility input routines */

read(Prompt, Input) :-
  prompt(_, Prompt),
  read(Input).


/* Read in a parsed sentence(P).  Terminated by '.', '?', or '!'. */

read_in(P, Lettercase, Initial_prompt, Subsequent_prompt) :-
  init_read(L, Initial_prompt, Subsequent_prompt),
  words(P, Lettercase, L, []),
  !,
  to_nl.

read_in(P, Lettercase) :-
  init_read(L),
  words(P, Lettercase, L, []),
  !,
  to_nl.


init_read([C|Rest], Initial_prompt, Subsequent_prompt) :-
  prompt(Original_prompt, Initial_prompt),
  get(C),
  prompt(Initial_prompt, Subsequent_prompt),
  read_rest(C,Rest),
  prompt(Subsequent_prompt, Original_prompt).

init_read([C|Rest]) :-
  get(C),
  read_rest(C,Restt).

read_rest(46,[]) :- !.      /* . */
read_rest(63,[]) :- !.      /* ? */
read_rest(33,[]) :- !.      /* ! */
read_rest(K,[K1|U]) :-
  K=<32,!,get(K1),
  read_rest(K1,U).
read_rest(K1,[K2|U]) :-
  get0(K2),
  read_rest(K2,U).

words([V|U], Lettercase) -->
  word(V, Lettercase),
  !,
  blanks,
  words(U, Lettercase).
words([], Lettercase) --> [].

word(U1, lowercase) -->
  [K],
  {lowercase(K,K1)},
  !,
  alphanumericeric_string(U2),
  {name(U1,[K1|U2])}.
word(U1, Anycase) -->
  [K],
  !,
  alphanumericeric_string(U2),
  {name(U1,[K|U2])}.
word(N, Lettercase) -->
  [K],
  {digit(K)},
  !,
  digits(U),
  {name(N,[K|U])}.
word(V, Lettercase) -->
  [K],
  {name(V,[K])}.

alphanumericeric_string([K1|U]) -->
  [K],
  {alphanumeric(K,K1)},
  !,
  alphanumericeric_string(U).
alphanumericeric_string([]) --> [].

alphanumeric(95,95) :- !.
alphanumeric(K,K1):-lowercase(K,K1).
alphanumeric(K,K):-digit(K).

digits([K|U]) -->
  [K],
  {digit(K)},
  !,
  digits(U).
digits([]) --> [].

blanks-->
  [K],
  {K=<32},
  !,
  blanks.
blanks --> [].

digit(K) :-
  K>47,
  K<58.         /* 0 - 9 */

lowercase(K,K1) :-
  K>64,
  K<91,
  !,
  K1 is K\/8'40.    /* convert uppercase to lower case */
lowercase(K,K) :-
  K>96,
  K<123.            /* lower case letter */

to_nl :-
   repeat,
   get0(31), !.    /* matches any empty string of blanks (32) */