In this assignment, you will extend your code from the previous lab assignment to perform resolution and semantic evaluation of Horn clauses based on the logic programming, syntax, and semantic techniques we have discussed this semester.
Specifically, you will modify and extend your program from the previous lab assignment, so that it operates
in an interactive mode receiving and executing commands repeatedly from the user, including a process
command
that will load well-formed Horn clauses from a file and display them to the user in a numbered list,
an assert
command that will read and parse a single Horn clause from the command line itself, and if it is well formed add it to the
numbered list, up
and down
commands to allow the user to scroll through the numbered list of Horn
clauses if it is longer than can be displayed on a single screen, a resolve
command that
takes the numbers of two Horn clauses in the list
and (if it can) resolves them to produce one or more new Horn clauses and adds them to the numbered list,
along with commands for setting and generating values for bound variables.
For this assignment, the program will run either without any command line arguments (e.g., simply as in
lab3.exe
) or with a single command line argument giving the number of
Horn clauses to display at a time (e.g., as in lab3.exe
10), will store the number
that was passed in the command line argument (or a default value suitable for the number of lines that can be displayed
in a maximized console terminal window in the Windows lab environment while leaving room for messages and an input prompt,
if no command line argument was given) and then will repeatedly read in one line of user input at a time, to execute
each of the following commands as they are given:
process <filename>
-- loads and parses a file of Horn clauses, adds well
formed Horn clauses from that file to the symbol table, and re-displays the numbered list of Horn clauses, starting from
the top of the list;
assert <Horn clause> {<Horn clause>}
-- parses the rest of the line
following the assert
keyword to identify one or more well formed Horn clauses, adds
the bound variables, numeric constants, and predicates of any of them that are well formed to the symbol table, and
re-displays the numbered list of Horn clauses, starting from the top of the list;
up [lines]
-- scrolls up a full screen if no number is given following the command,
or scrolls up the number of lines given if a number was given (scrolling should stop at the top of the list if it is reached);
down [lines]
-- scrolls down a full screen if no number is given following the command,
or scrolls down the number of lines given if a number was given (scrolling should stop at the bottom of the list if it is
reached);
resolve <Horn_clause_number>
<Horn_clause_number>
-- resolves the Horn
clauses whose numbers (in the list) are given in the command: if the same number is
given twice, or if the Horn clauses involved are identical, or if the
head of the first clause (i.e., the clause whose number was given
first) cannot be unified with a predicate in the body of the second
clause (i.e., the clause whose number was given second), the program
should simply output a message to that effect after re-displaying the
same screen of Horn clauses -- otherwise the program should (1) make a
copy of the two Horn clauses, (2) manipulate the copies to do any
substitutions needed to unify the head of the first clause with a
matching predicate in the body of the second clause (please remember
to apply all substitutions throughout both Horn clause copies), and
then (3) replace the matched predicate in the body of the copy of the second Horn
clause with the (possibly updated due to substitutions and/or renaming) body of the
copy of the first Horn clause (if the body of the copy of the first Horn clause is empty,
i.e., that entire first clause is a fact, then the
replacement will simply remove the matched predicate from the copy of the second Horn clause), and
then (4) assert the resulting resolved Horn clause into the deductive database
and re-display the numbered list of Horn clauses
(note that if the head of the first clause can be unified with more than one predicate in the
body of the second clause, then steps (1) through (4) should be performed for each such match)
;
randomize <variable> [<max>]
-- generates a randomized value for a
bound variable;
set <variable> <value>
-- sets a bound variable to the given value; and
print
-- prints out the contents of the symbol table including both predicates
and bound variables (with their values, for the latter).
.h
) file and source (.cpp
) file.As in the previous lab assignments, it also is possible that further semantic checking (e.g., whether or not a particular file exists) will need to be performed as part of executing some commands.
process <filename>
command
should cause the program to check that the file given by <filename>
can be opened for reading, and if not should display a message to that effect and
then simply re-display the current list of Horn Clauses and prompt the user for the
next command; otherwise the program should read
in the contents of file <filename>
as a sequence of whitespace delimited string
tokens and process that sequence according to the following input grammar (an updated version
of the input grammar from the previous assignment, with modifications to accomodate bound
vs. unbound variables):
hornclause -> LEFTPAREN head [body] RIGHTPAREN
head -> predicate
body -> LEFTPAREN predicate {predicate} RIGHTPAREN
predicate -> LEFTPAREN name {symbol} RIGHTPAREN
name -> LABEL
symbol -> UNBOUND | NUMBER | BOUND
where the UNBOUND
token is any string containing only a single uppercase alphabetic character ('A'
through 'Z'
); the BOUND
token is any string containing only a single lowercase alphabetic character ('a'
through 'z'
);
and all other tokens are defined as they were in the previous assignment.
Hint: you may want to consider extending your token hierarchy,
e.g., with BOUND and UNBOUND tokens being subtypes of LABEL.
As it processes the file, your program should again parse out the different
elements of each well formed Horn clause expression according to the grammar above.
In this assignment the program should fill in symbol table entries for bound variables
(indicated by BOUND
tokens) and predicates (you can either simply keep all
unbound variables and numeric constants within the predicates, or if you would like you
can keep -- and potentially print out -- separate collections of them; similarly
you can keep the list of Horn clauses -- which should alias the predicates within the
symbol table -- either within the symbol table or separately within a diferent distinct
entity like a deductive database object).
When a bound variable is introduced into the symbol table for the first time, the semantic attribute for its value should be set to 0.
assert
command should scan and parse the rest of the line
following the assert
keyword as a sequence of whitespace delimited
tokens, identifying any well formed Horn clauses and adding the predicates and bound variables
from them to the symbol table, and then re-displaying the numbered list of
Horn clauses (including those newly recognized Horn clauses), starting from the top of the list;
assert
command,
the program should instead treat its
collection of Horn clauses as a deductive database, attempting unification only as part
of resolution and only for the appropriate predicates (i.e., the head of a clause and
a matching predicate in the body of another clause) within copies of the two Horn clauses
whose numbers are indicated when the user issues a resolve
command
(described below).
up
command should scroll up a full screen worth of Horn clauses if no
number is given following
the up
keyword, or if a value is given should scroll upward that number of lines, but should
stop at the top of the list of Horn clauses if it is reached in either case;
down
command should scroll down a full screen worth
of Horn clauses if
no number is given following the down
keyword, or if a value
is given should scroll downward that number of lines, but should
stop at the bottom of the list of Horn clauses if it is reached in either case;
resolve
command,
or if the Horn clauses involved
are identical, or if two numbers were not provided or if the head of the first clause
cannot be unified with a predicate in the body of the second clause,
the program should simply output a message to that effect after re-displaying the same
screen of Horn clauses; otherwise the program should make a copy of the two Horn clauses,
perform any substitutions needed to unify the head of the first clause with a predicate in
the body of the second clause, and then replace
the matched predicate in the copy of the second Horn clause with the (possibly updated after
unification) body of the copy of the first clause, and then assert the resulting Horn
clause into the deductive database (as as new Horn clause) and re-display the numbered
list of Horn clauses (if the head of the first clause can be unified with more
than one predicate in the body of the second clause, then the program should perform
a separate resolution attempt, starting fresh with unmodified copies of the two original
Horn clauses, for each such case);
In addition, modify the unification semantics from the previous assignment so that
for this assignment:
NUMBER
token) can be
substituted for any bound variable (indicated by a BOUND
token)
whose value is the same as the constant's value;
NUMBER
token) can be
substituted for any unbound variable (indicated by an UNBOUND
token)
that either is not subject to any previous substitutions during that resolution attempt,
or for which the cumulative
substitutions so far produce the same value as the constant's value;
For example, given the following numbered list of Horn clauses:
1: ( ( ascending 1 2 ) )
2: ( ( ascending 2 3 ) )
3: ( ( ascending x Z ) ( ( ascending x Y ) ( ascending Y Z ) ) )
then if the command resolve 1 3
were given, if (and only if)
bound variable x has value 1, the head of first Horn clause would unify successfully with both
(1) the first predicate in the body of third Horn clause -- allowing that resolution attempt
to produce a new fourth Horn clause, and (2) the second predicate in the body of third Horn
clause -- allowing that resolution attempt to produce a new fifth Horn clause, giving (after
both of the new clauses have been added to the deductive database):
1: ( ( ascending 1 2 ) )
2: ( ( ascending 2 3 ) )
3: ( ( ascending x Z ) ( ( ascending x Y ) ( ascending Y Z ) ) )
4: ( ( ascending 1 Z ) ( ( ascending 2 Z ) ) )
5: ( ( ascending x 2 ) ( ( ascending x 1 ) ) )
then if the command resolve 2 4
were given, the head of second
Horn clause would unify with the (sole) predicate in the body of the fourth Horn clause,
allowing resolution to produce a new (sixth) Horn clause (which is a
fact, since it has only a head and no body) within the
deductive database:
1: ( ( ascending 1 2 ) )
2: ( ( ascending 2 3 ) )
3: ( ( ascending x Z ) ( ( ascending x Y ) ( ascending Y Z ) ) )
4: ( ( ascending 1 Z ) ( ( ascending 2 Z ) ) )
5: ( ( ascending x 2 ) ( ( ascending x 1 ) ) )
6: ( ( ascending 1 3 ) )
randomize <variable> [<max>]
command, it should first check whether <variable>
is a syntactically
valid bound variable (and if <max>
is given whether it is a
syntactically valid numeric (unsigned integer) constant corresponding to
a NUMBER
token): if so it should look up that variable in the symbol table
and if it is not there should add it and then should set the bound variable's value to an
unsigned integer (from the range 0
to <max>
if
<max>
was given, or from the entire range of non-negative integers otherwise),
obtained from a pseudorandom number sequence (e.g., seeded when the program starts by
calling the C srand
function with the result of calling
the C time
function with the value 0, and then calling the
C rand
function each time a new pseudorandom number is needed, or
using the new C++11 library features available for generating pseudorandom number sequences).
set <variable> <value>
command, it should first check whether <variable>
is a syntactically
valid bound variable: if so it should look up that variable in the symbol table
and if it is not there should add it. If <variable>
is a syntactically valid
bound variable and <value>
is a syntactically valid numeric (unsigned integer)
constant
(corresponding to a NUMBER
token), the program then should set the variable's value
to be that of the constant.
print
command, it should print
out a legible representation of the contents of the symbol table, including the predicates that
are contained within the Horn clauses in the deductive
database, and the names and values of the bound variables that appear within those predicates.
In your project report please document which test cases you ran, and summarize what your program did and whether what you saw was or was not correct behavior (and why or why not) in each case.
IMPORTANT:please make sure that no .exe file is included in any .zip file you send, as the WUSTL e-mail servers will block delivery of email with a .zip attachment that includes a .exe file. Please also make sure to send a .zip file (not a .7z file or other zip format) when you send your solutions.
Please add an extra credit section to your project report documenting your design and implementation of that additional capability. In that same section, please show examples of input and output from the different cases that you tested in order to validate that it is working correctly.
Please submit both the required and extra credit portions within the same code base, along with your project report.