commit de2fc4afe00f2500b83e0c6abd6ac362b1d5ea64 Author: duboissim Date: Fri Mar 1 13:22:27 2024 +0100 first commit diff --git a/Logic Programming Mission.pdf b/Logic Programming Mission.pdf new file mode 100644 index 0000000..e5a0cde Binary files /dev/null and b/Logic Programming Mission.pdf differ diff --git a/input.pl b/input.pl new file mode 100644 index 0000000..c1beb1b --- /dev/null +++ b/input.pl @@ -0,0 +1,33 @@ +:- dynamic tabl/2. + +tabl(persons, [id, first, last, age, city]). +tabl(cities, [name, state, temp]). + +:- dynamic persons/5. +persons( 0, "Jeffrey", "Bowman", 30, "Daytona Beach"). +persons( 1, "Lorena", "Michaels", 50, "Boardman"). +persons( 2, "Joseph", "Mixson", 37, "Washington"). +persons( 3, "Stewart", "Sullivan", 83, "Grand Island"). +persons( 4, "Thomas", "Marshall", 75, "Houston"). +persons( 5, "Tameka", "Escobedo", 25, "Orlando"). +persons( 6, "Annette", "Marcy", 40, "Pittsburgh"). +persons( 7, "David", "Smith", 36, "Orlando"). +persons( 8, "Anthony", "Garcia", 20, "Daytona Beach"). +persons( 9, "Lindsay", "Faught", 70, "Boardman"). +persons(10, "Claudette", "Neil", 32, "Washington"). +persons(11, "Betty", "Strickland", 47, "Atlanta"). +persons(12, "Richard", "Gordon", 47, "Dallas"). +persons(13, "Robert", "Evans", 69, "Atlanta"). +persons(14, "Candace", "Griffin", 61, "Pittsburgh"). +persons(15, "Sandra", "Smith", 67, "Dallas"). + +:- dynamic cities/3. +cities("Daytona Beach", "Florida", 22). +cities("Boardman", "Oregon", 12). +cities("Washington", "District of Columbia", 11). +cities("Grand Island", "New York", 10). +cities("Houston", "Texas", 20). +cities("Orlando", "Florida", 23). +cities("Dallas", "Texas", 17). +cities("Pittsburgh", "Pennsylvania", 11). +cities("Atlanta", "Georgia", 16). \ No newline at end of file diff --git a/tests.pl b/tests.pl new file mode 100644 index 0000000..7be71b6 --- /dev/null +++ b/tests.pl @@ -0,0 +1,236 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% HELPERS + +% plunit is a Prolog unit-test framework initially developed for SWI-Prolog +:- use_module(library(plunit)). +:- discontiguous test/2. + +% Run the tests. +test :- + run_tests(sql). + +% 'clean' drop: call drop/1 and swallow any thrown exceptions +cdrop(Table) :- + catch(drop(Table), _, true). + +% Checks that two lists have the same content. +same_content(L1, L2) :- + msort(L1, S), + msort(L2, S). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% TESTS + +:- begin_tests(sql). + +% Test if create/1 works. +test(create_table, cleanup(cdrop(foo))) :- + create_table(foo, [bar, baz]), + tables(Tables), + member(foo, Tables), !. + +% Test if create/1 throws an error on dup table name. +test(create_redundant, [throws(_), cleanup(cdrop(foo))]) :- + create_table(foo, [bar, baz]), + create_table(foo, [bab, bac]). + +% Test if the columns are properly recorded. +test(cols, cleanup(cdrop(foo))) :- + create_table(foo, [baru, bazu]), + cols(foo, Cols), + assertion(Cols == [baru, bazu]). + +% Test if drop/1 works. +test(drop) :- + create_table(foo, [bar, baz]), + drop(foo), !, + tables(Tables), + \+ member(foo, Tables). + +% Test if drop/1 throws an error on unknown table name. +test(drop_inexistant, throws(_)) :- + drop(foobar). + +% Test if row/2 returns the different rows of a given table. +test(rows, cleanup(cdrop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + insert(foo, [3, 4]), + rows(foo, Rows), + assertion(length(Rows,2)). + +% Test if row/2 fails on an unknown table name. +test(row_inexistant, throws(_)) :- + rows(foo, _Row). + +% Test if rows/1 works when called for a given table name. +test(rows, cleanup(cdrop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + insert(foo, [3, 4]), + rows(foo). + +% Test if rows/1 throws an error on unknown table name. +test(rows_inexistant, throws(_)) :- + rows(foo). + +% Simple insert/2 & selec/4 test. +test(insert2, cleanup(cdrop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + %selec(foo, *, [], X/Y), !, + selec(foo, *, X/Y), !, + assertion(X == [+bar, +baz]), + assertion(Y == [1, 2]), + insert(foo, [3, 4]), + findall(Z, selec(foo, *, [], _/Z), Zs), !, + assertion(Zs == [[1,2],[3,4]]). + +% Test if insert/2 throws an error on unknown table name. +test(insert_inexistant, throws(_)) :- + insert(foo, [1, 2]). + +% Simple delete/1 test. +test(delete1, cleanup(cdrop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + insert(foo, [3, 4]), + delete(foo), + findall(Z, selec(foo, *, [], _/Z), Zs), + assertion(Zs == []), + tables(Tables), + member(foo, Tables). + +% Simple delete/2 test. +test(delete2, cleanup(cdrop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + insert(foo, [3, 4]), + delete(foo, [+bar = 1]), + findall(Z, selec(foo, *, [], _/Z), Zs), + assertion(Zs == [[3, 4]]). + +% Another simple delete/2 test. +test(delete2bis, cleanup(cdrop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + insert(foo, [3, 4]), + delete(foo, [+bar >= 1]), + findall(Z, selec(foo, *, [], _/Z), Zs), + assertion(Zs == []). + +% Test delete/2 using multiple conditions. +test(delete2_mult, cleanup(cdrop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + insert(foo, [3, 4]), + delete(foo, [+bar = 1, +baz = 2]), + findall(Z, selec(foo, *, [], _/Z), Zs), + assertion(Zs == [[3, 4]]). + +% Another test of delete/2 using multiple conditions. +test(delete2_mult_bis, cleanup(cdrop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + insert(foo, [3, 4]), + delete(foo, [+bar > 1, +baz < 2]), + findall(Z, selec(foo, *, [], _/Z), Zs), + assertion(Zs == [[1, 2],[3,4]]). + +% Test selec/4 with a single condition +col condition. +test(selec_cond1) :- + selec(persons, *, [+id = 0], _/Row1), !, + assertion(Row1 == [0, "Jeffrey", "Bowman", 30, "Daytona Beach"]), + selec(persons, *, [+first = "Lorena"], _/Row2), !, + assertion(Row2 == [1, "Lorena", "Michaels", 50, "Boardman"]). + + +% Test selec/4 with two +col conditions. +test(selec_cond_mult1) :- + selec(persons, *, [+age = 47, +city = "Atlanta"], _/Row), !, + assertion(Row == [11, "Betty", "Strickland", 47, "Atlanta"]). + +% Test selec/4 with a single projection and no conditions. +test(selec_proj) :- + findall(Row, selec(persons, [+first], [], _/Row), Rows0), + flatten(Rows0, Rows), + assertion(Rows == ["Jeffrey", "Lorena", "Joseph", "Stewart", "Thomas", + "Tameka", "Annette", "David", "Anthony", "Lindsay", "Claudette", "Betty", + "Richard", "Robert", "Candace", "Sandra"]). + +% Test selec/4 with projections and a condition. +test(selec_mult_proj) :- + findall(Row, selec(persons, [+first, +last], [+age >= 70], _/Row), Rows), + same_content(Rows, [ + ["Stewart", "Sullivan"], + ["Thomas", "Marshall"], + ["Lindsay", "Faught"]]). + +% Test that drop/1 correctly removes rows. +test(drop_implem, cleanup(cdrop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + drop(foo), + create_table(foo, [bar, baz]), + findall(Z, selec(foo, *, [], _/Z), Zs), !, + assertion(Zs == []). + +% Test that tables/1 works. +test(tables, cleanup((cdrop(foo), cdrop(bar), cdrop(baz)))) :- + create_table(foo, []), + create_table(bar, []), + create_table(baz, []), + drop(bar), + tables(Tables), + member(foo, Tables), !, + member(baz, Tables), !, + \+ member(bar, Tables), !. + +% Test that insert/2 throws an error on unknown table name. +test(insert_inexistant, throws(_)) :- + insert(foobar, [1, 2]). + +% Test that insert/2 throws an error if it is supplied too few values. +test(insert_count1, [throws(_), cleanup(drop(foo))]) :- + create_table(foo, [bar, baz]), + insert(foo, [1]). + +% Test that insert/2 throws an error if it is supplied too many values. +test(insert_count2, [throws(_), cleanup(drop(foo))]) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2, 3]). + +% Tests that delete/1 throws an error on unknown table name. +test(delete_inexistant1, throws(_)) :- + delete(foobar). + +% Tests that delete/2 throws an error on unknown table name. +test(delete_inexistant2, throws(_)) :- + delete(foobar, []). + +% Simple delete/2 test using the +. +test(delete2_1, cleanup(drop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + insert(foo, [3, 4]), + delete(foo, [+baz = 4]), + findall(Z, selec(foo, *, [], _/Z), Zs), + assertion(Zs == [[1, 2]]). + +% delete/2 test using mixed notations. +test(delete2_2, cleanup(drop(foo))) :- + create_table(foo, [bar, baz]), + insert(foo, [1, 2]), + insert(foo, [3, 4]), + insert(foo, [3, 5]), + delete(foo, [+bar = 3, +baz = 4]), + findall(Z, selec(foo, *, [], _/Z), Zs), + assertion(Zs == [[1, 2], [3, 5]]). + +% Test selec/4 with a projection and multiple conditions. +test(selec_mult_cond) :- + findall(Row, selec(persons, [+first, +last], + [+age >= 60, +city = "Atlanta"], _/Row), Rows), + same_content(Rows, [["Robert", "Evans"]]). + +% ============================================================================== diff --git a/tests_dcg.pl b/tests_dcg.pl new file mode 100644 index 0000000..decaf35 --- /dev/null +++ b/tests_dcg.pl @@ -0,0 +1,98 @@ +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% HELPERS + +% plunit is a Prolog unit-test framework initially developed for SWI-Prolog +:- use_module(library(plunit)). +:- discontiguous test/2. + +% Run the tests. +test_dcg :- + run_tests(dcg). + +% 'clean' drop: call drop/1 and swallow any thrown exceptions +cdrop(Table) :- + catch(drop(Table), _, true). + +% Checks that two lists have the same content. +same_content(L1, L2) :- + msort(L1, S), + msort(L2, S). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +:- begin_tests(dcg). + +%%not tested because not specified. +%%test(query3) :- +%% query("SELECT +id FROM cities;", _/Z), !, +%% assertion(Z == []). + +test(selec_with_selectors) :- + findall(Z, + query("SELECT +first, +age FROM persons;", + _/Z), Zs), + same_content(Zs, [ + ["Jeffrey",30], + ["Lorena",50], + ["Joseph",37], + ["Stewart",83], + ["Thomas",75], + ["Tameka",25], + ["Annette",40], + ["David",36], + ["Anthony",20], + ["Lindsay",70], + ["Claudette",32], + ["Betty",47], + ["Richard",47], + ["Robert",69], + ["Candace",61], + ["Sandra",67]]). + +test(selec_selectors_conditions) :- + findall(Z, + query("SELECT +state, +name FROM cities WHERE +temp>=20;", + _/Z), Zs), + same_content(Zs, [ + ["Florida", "Daytona Beach"], + ["Texas", "Houston"], + ["Florida", "Orlando"]]). + +test(selec_multi_conditions) :- + findall(Z, + query(`SELECT +last FROM persons WHERE +city="Orlando", +age>30;`, + _/Z), Zs), + same_content(Zs, [["Smith"]]). + +test(selec_all) :- + findall(Z, + query("SELECT * FROM cities;" + , _/Z), Zs), + same_content(Zs, [["Daytona Beach", "Florida", 22], + ["Boardman", "Oregon", 12], + ["Washington", "District of Columbia", 11], + ["Grand Island", "New York", 10], + ["Houston", "Texas", 20], + ["Orlando", "Florida", 23], + ["Dallas", "Texas", 17], + ["Pittsburgh", "Pennsylvania", 11], + ["Atlanta", "Georgia", 16]]). + +test(selec_after_insert, cleanup(delete(cities, [+name="Tempa"]))) :- + query(`INSERT INTO cities (+name, +state) VALUES ("Tempa", "Florida");`), + findall(Z, + query("SELECT * FROM cities;" + , _/Z), Zs), + same_content(Zs, [["Daytona Beach", "Florida", 22], + ["Boardman", "Oregon", 12], + ["Washington", "District of Columbia", 11], + ["Grand Island", "New York", 10], + ["Houston", "Texas", 20], + ["Orlando", "Florida", 23], + ["Dallas", "Texas", 17], + ["Pittsburgh", "Pennsylvania", 11], + ["Atlanta", "Georgia", 16], + ["Tempa", "Florida", _]]). + +:- end_tests(dcg). +