Saturday, April 09, 2011

dbProlog

Someone left a comment here the other day asking me to write about Prolog, Delphi and databases. I may have misunderstood his intentions, as I started thinking about how I could implement a subset of Prolog in Delphi using a database! After some cogitation, I wrote the foundations for what I call dbProlog, where 'db' is a contraction of either 'database' or 'damaged brain', depending on one's orientation. This is not some form of belated April Fool's Day joke, but on the other hand, this 'work' is not to be taken too seriously. I see it as a means of exercising my brain when there's nothing else to do.

Following is a table of Prolog statements and their db equivalents.


Prologdb
male (abraham). insert into male1 (one) values ('abraham');
male (isaac). insert into male1 (one) values ('isaac');
female (sarah). insert into female1 (one) values ('sarah');
parent (abraham, isaac). insert into parent2 (one, two) values ('abraham', 'isaac');
parent (sarah, isaac). insert into parent2 (one, two) values ('sarah', 'isaac');
?male (X).select one from male1;
father (X,Y):- parent (X, Y), male (X).???
?father (X, isaac). select male1.one from male1, parent2 where male1.one = parent2.one and parent2.two = 'isaac';


I wrote a very simple, ad hoc parser which converts a Prolog statement into a predicate and its arguments; these are then transformed into the necessary SQL statement. This parser should be replaced by a proper recursive descent parser which tokenises and checks the grammar, but at the moment I can't be bothered.

The major outstanding conceptual problem at the moment is that I can't think of a way of representing rules within an SQL database.  

By now, anyone who is still with me is probably scratching their head wondering where the 'male1', 'female1' and 'parent2' tables came from. These are created on the fly the first time the interpreter comes across the predicates. But how does the interpreter know when this is the first time? Inserting into a non-existent table is not a very good idea. I solved this by having the database contain one permanent table called 'tablenames' consisting of one field, 'name' (this table actually has more than one field, but that's an unnecessary complication at the moment). 

Each time a predicate is used, its arity (the number of arguments) is calculated and a simple SQL statement is issued: select count (*) from tablenames where name = 'male1'; (obviously 'male1' is replaced by the actual predicate and its arity). If this query returns a number greater than 0, then the predicate is known and its table exists. If the query returns 0, then the table has to be created - create table male1 (one varchar (50), primary key (one));. If the arity is two, then the statement becomes create table parent2 (one varchar (50), two varchar (50), primary key (one, two));. Within the interpreter there is a ten member array of strings whose values are 'one', 'two', 'three'...'ten'. This array is used to convert indexes into field names.

Whenever a table is created, an insert has to be performed on the 'tablenames' table to signify that the table has been created. Because SQL tables are permanent, all the predicate tables have to be cleared, either on program startup or completion. I thought it better to do this on startup, so the tables are available for inspection with standard SQL tools after the interpreter closes. This is done with two queries: one steps through 'tablenames', returning the name of each table, and the other then drops the table. At the end of this procedure, 'tablenames' is emptied.

No comments: