Tuesday, November 18, 2025

Implementing CP Prolog - 8, an epiphany

 On Sunday, I wrote: During the week, I had been thinking about how I had become addicted to programming this interpreter; yesterday was certainly addiction, but it wasn't much fun. At the moment we're at the stage of clutching at straws with no fresh ideas of how to solve this problem that is down to memory management, something that Delphi - and expecially the very stable Delphi 7 version - is supposed to handle for the user.

Last night when I got into bed, I started thinking about the parser - not a recommended way of falling asleep, I note - when I had an epiphany. At some stage on Saturday, CoPilot had suggested saving the value that the parser was returning in a global variable or similar. This isn't a very good idea in terms of computer science and we ignored it. But last night, I realised that pointers to all the terms were being saved in the program's heap so that they could be disposed of during garbage collection. All I needed to do after parsing a clause in the rule's body was to walk down this heap, looking for the first (actually, the last) term of a specific type - this is the value that should be saved. CoPilot would never have 'thought' of this.

What does this mean in simple terms (pun not intended)? Let's say that there is the clause female (X). The parser will read the token female, and because of the bracket, it will know that this token is the functor of the clause. The token X will be then be read and a new term allocated (and stored on the heap) for this variable. Then a term will be allocated (and again, stored on the heap) for a compound item, consisting of the functor (female) and its arguments (X). So if I want to get the value of this term later on, it will found at the top (or close to the top) of the heap.

This solution, whilst quite brilliant, is sidestepping the actual problem that is preventing the proper execution of the code - but I'm prepared to accept the pragmatic solution if it keeps me moving forward. Incidentally, after using the value provided by walking the heap, the entire body was displayed correctly, which is a big step forward.

Next is checking that this rule can be resolved, just to ensure that everything is as it should be. Then will come the task of parsing a fact that has nested clauses such as owns (noam, cd (unhalfbricking, 'fairport convention')). I'm fairly sure that all I will have to do is change one statement in order to achieve this. I wonder whether the result of parsing these nested clauses will also display the same bug that I've just fixed (or at least, worked around). 

But I'm going to beat my addiction and not touch this material for a few days.

Internal links
[1] 2037



This day in blog history:

Blog #Date TitleTags
14518/11/2008Lonely at the topMIDI, Randy Newman
30518/11/2010How to save money when ordering books from abroadBook depository
30618/11/2010Alesis Q49MIDI, King Crimson, Antibes
52118/11/2012Warming up for DBA examDBA
77718/11/2014Enron's spreadsheetsDBA, Excel
155218/11/2022Fairport newsFairport Convention
168718/11/2023The musical group reconvenesMusical group
186218/11/2024Shel Talmy, Peter Sinfield RIPObituary, King Crimson, Van der Graaf Generator

Sunday, November 16, 2025

Implementing CP Prolog - 8, a problematic start

What, I hear you say, a weekend without an update on the status of CP Prolog? Well, yes, that's because I spent many frustrating hours over the weekend trying to finish the first part of a new version - and not succeeding.

The impetus for the new version was a set of clauses that I saw in a book, along these lines 

owns (noam, cd (unhalfbricking, 'Fairport Convention')). owns (sarit, cd ('best of', 'Dire Straits')); owns (noam, book ('nightwings', 'Robert Silverberg')).

In other words, I want to add the possibility of nested clauses, and also have quoted names. This will enable me to get a list of all the items that I own, or a list of cds, or even a list of cds by Fairport Convention.

To this end, CoPilot built a recursive descent parser along with a tokeniser that worked perfectly with prefix operators. I was initially pleased as this parser would replace the rather ad hoc parsing that existed so far. Unfortunately the pure model of prefix Prolog is ruined by those pesky infix mathematical operators, so first I had to add some code to the tokeniser to recognise these, and then some code to the parser. This last part was quite difficult; there was something not quite right with one of the functions which of course was the one that dealt with the infix operators. I added a patch which seemed to fix the parsing of some expressions but not all. 

I ran out of time on Friday afternoon so I had to continue on Saturday morning with a fresh brain. I soon found the correct implementation of the fix (if you really want to know, the parser was confusing the comma between clauses with the comma between operands), and later found that this code caused problems right at the very end of the list of clauses so another condition had to be added. This was my work, not CP's; I had to 'argue' with it until it conceeded that I was correct.

The parser now reads a list of clauses well, although we haven't trained it on those nested clauses at the beginning of the blog - these are on the 'head' side of a rule and not the 'body' side. I am hoping that one simple change will allow the program to read those nested head clauses.

After I finished with the parser, I idly thought to display the clauses that had been parsed, just to check that everything was correct. And not only was the output incorrect, it had somehow missed the first two clauses out of five, the third was repeated as the fourth and only the fifth was correct! So CoPilot and I started a debugging marathon of several hours that has yet to be completed. It's completely weird that for every clause except for the final clause in a list, one function can be seen to return a certain value, but that value gets mangled by the time that it arrives at its destination. Very strange. CoPilot was convinced for some time that the problem was caused by something overwriting something else, but I told CP that it was wrong, for if that were the case, then all the output would be identical. Again, it eventually conceeded that my explanation was correct

There wasn't much else to do yesterday as it was pouring with rain (30.2 mm) so we continued and continued. During the week, I had been thinking about how I had become addicted to programming this interpreter; yesterday was certainly addiction, but it wasn't much fun. At the moment we're at the stage of clutching at straws with no fresh ideas of how to solve this problem that is down to memory management, something that Delphi - and expecially the very stable Delphi 7 version - is supposed to handle for the user.

Once the parsing and memory management is fixed, then I will check whether displaying the knowledge base needs improving. And only then will it be possible to start on improving the goal solver.



This day in blog history:

Blog #Date TitleTags
64716/11/2013Song festivalMIDI, Kibbutz, Song writing
135816/11/2020Vinyl log #32Vinyl log, Heron
168616/11/2023Counting beats with Van der Graaf Generator (4) - Whatever would Robert have said?Van der Graaf Generator
186016/11/2024New CPAP mask (2)CPAP