_EXTENDING C WITH PROLOG_ by Dennis Merritt Listing One /* Prolog program to illustrate how an expert system that resolves installation conflicts might be implemented. In this case, it resolves conficts in the IRQ table, by either selecting another IRQ for the device being installed, rearranges another device's IRQ to open a slot for the new device, or, if there is no room doubles up the com ports on a single IRQ to free up a slot. The predicate msg/1, which takes either a single term or a list as an argument, is implemented in C. The predicate get_irqs/0 is also implemented in C and asserts a number of Prolog facts in the form irq/2. */ irq_advice(Device) :- msg($IRQ Conflict Resolution Analysis$), get_irqs, % get IRQs from C program free_irq(Device), msg($ Continue normal install$). /* rules for finding a free IRQ */ free_irq(Dev) :- % fail if unknown device not(ok_irq(Dev,_,_)), msg([$ Unknown Device $, Dev]), !, fail. free_irq(Dev) :- % see if requested IRQ is free ok_irq(Dev,IRQ,Option), is_free(IRQ,Option), !. free_irq(Dev) :- % see if requested IRQ can be cleared ok_irq(Dev,IRQ,Option), clear(IRQ), !. free_irq(Dev) :- % see if an IRQ can be opened make_room, free_irq(Dev). % try again with new open slot is_free(IRQ,default) :- % if default is free, no problem irq(IRQ, open), msg($ The default IRQ is open. No action needed$). is_free(IRQ,optional) :- % use different device IRQ irq(IRQ, open), msg([$ Default IRQ not available. Set device to use $, IRQ, $ instead.$]). clear(I) :- % move another device's IRQ to free up requested IRQ irq(X, open), irq(I, D), ok_irq(D, X, _), % make sure the device can be moved msg([$ Move device $, D, $ to IRQ $, X]), retract(irq(X,open)), % update dynamic database with the switch retract(irq(I,D)), assert(irq(X,D)), assert(irq(I,open)). make_room :- % double up COM ports to make room, if possible irq(IRQ_X, com(COM_X)), irq(IRQ_Y, com(COM_Y)), IRQ_X \= IRQ_Y, msg([$ Put com ports $, COM_X, $ and $, COM_Y, $ together on IRQ $, IRQ_X]), retract(irq(IRQ_X, _)), % update the dynamic database assert(irq(IRQ_X, com(COM_X)+com(COM_Y))), retract(irq(IRQ_Y, _)), assert(irq(IRQ_Y, open)). % IRQs that can be used for different devices. We only do Sound Blasters % and Mitsumi CD-ROMs for now. ok_irq('Sound Blaster', 5, default). ok_irq('Sound Blaster', 2, optional). ok_irq('Sound Blaster', 3, optional). ok_irq('Sound Blaster', 7, optional). ok_irq('Mitsumi CD-ROM', 15, default). ok_irq('Mitsumi CD-ROM', 11, optional). ok_irq('Mitsumi CD-ROM', 12, optional). /* this IRQ information is used for relocating the mouse if necessary */ ok_irq(mouse, 2, optional). ok_irq(mouse, 3, optional). ok_irq(mouse, 4, optional). ok_irq(mouse, 5, optional). Listing Two /* Sample expert system advisor embedded in C. This particular system is used to resolve and fix conflicts for IRQ slots when installing new devices in a computer. It is set up here as a simple DOS program, but it can be used as a module in a larger program, called, for example, when a menu item is selected in a GUI interface. It illustrates the three key aspects of integrating Prolog and C. 1. The rules for deciding how to rearrange the IRQs are declarative and expressed in Prolog code. (Note that the advantage of Prolog is when there is no clear algorithm for describing how to solve a problem, but rather a collection of seemingly disconnected rules.) The rules, or logic base, is called from the C program. 2. The Prolog program calls back to C to get low level information. In this case, the C program determines the current IRQ use for the machine it's running on. For the example, the information is read from a test data file, but a real example would have code that digs around in the machine or asks for this information. The predicate is called get_irqs. 3. The Prolog program relies on C for its I/O, so that the Prolog program is independent of Ui. In this example, it simply sends output to a predicate, implemented in C, called msg. Msg is made a little interesting by the fact that it can either take a single argument, to be displayed, or a list of Prolog terms to be displayed. The I/O, for this example is just printfs, but could be any fancy GUI display. */ #include #include #include #include "cogent.h" char sTestFile[80]; /* global to hold name of test data file */ /*---- built-in predicates, callable from Prolog ----*/ /* function prototypes */ TF p_get_irqs(void); TF p_msg(void); /* Extended predicate table definitions, mapping Prolog predicate names to C functions. */ PRED_INIT irqPreds[] = { {"get_irqs", 0, p_get_irqs}, {"msg", 1, p_msg}, {NULL, 0, NULL} }; /* extended predicates */ TF p_get_irqs(void) { int i; FILE * fp; char buf[80]; /* Assert facts about the IRQs to the Prolog dynamic database */ /* Read them from the test file for now */ fp = fopen(sTestFile, "r"); for (i=0; i<16; i++) { fgets(buf, 80, fp); cpAssertzStr("irq(%i, %s)", i, buf); } fclose(fp); return(TRUE); } TF p_msg(void) { char buf[80]; TERM t, tm; pTYPE ptype; /* Get the first (and only) parameter and determine its type. If its a list, walk the list converting each term to a string and printing it. If its not a list, then just convert and print the term. */ cpGetParm(1, cTERM, &t); ptype = cpGetTermType(t); if (ptype == pLIST) { while (OK == cpPopList(&t, cTERM, &tm)) { cpTermToStr(tm, buf, 80); printf("%s", buf); } } else { cpTermToStr(t, buf, 80); printf("%s", buf); } printf("\n"); return(TRUE); } /* ----- Main ------------------------------------------------------------ */ void main() { TERM t; TF tf; char sDevice[80]; cpInit("irqxs"); cpInitPreds(irqPreds); cpLoad("irqxs"); printf("What device are you installing? "); gets(sDevice); printf("Use which test file? "); gets(sTestFile); cpCallStr(&t, "irq_advice('%s')", sDevice); cpClose(); return; } Listing Three timer keyboard com(1)+com(2) com(3) com(4) mouse disk lpt1 clock redirect open open open coprocessor disk network Figure 1: (a) void main() { char buf[20]; do { gets(buf); if (0 == strcmp(buf, open)) ... else if (0 == strcmp(buf,add)) ... else if (0 == strcmp(buf, delete)) ... } while (strcmp(buf, quit)); printf(done); } (b) main :- repeat, read(X), do(X), X == quit, write(done). do(open) :- ... do(add) :- ... do(delete) :- ... do(quit).