1 | var fs = require('fs');
|
2 | var log = console.log;
|
3 |
|
4 | var kb = function() {
|
5 | this.rules = [];
|
6 | this.facts = {};
|
7 | this.dict = {};
|
8 | }
|
9 |
|
10 | var kbp = kb.prototype;
|
11 |
|
12 | kbp.load = function(code) {
|
13 | var lines = code.split(/[\.]+ ?/);
|
14 | log("%j", lines);
|
15 | for (var i in lines) {
|
16 | if (lines[i].trim().length > 0)
|
17 | this.addRule(lines[i]);
|
18 | }
|
19 | return this;
|
20 | }
|
21 |
|
22 | kbp.loadFile = function(file) {
|
23 | var code = fs.readFileSync(process.cwd()+"/"+file, "utf8").replace(/\n/gi, "");
|
24 | this.load(code);
|
25 | return this;
|
26 | }
|
27 |
|
28 | kbp.isFact=function(term) {
|
29 | if (term.length == 0)
|
30 | return true;
|
31 | return this.facts[term];
|
32 | }
|
33 |
|
34 | kbp.check = function(rule) {
|
35 | for (var i in rule.terms) {
|
36 | var term = rule.terms[i].trim();
|
37 | if (this.isFact(term))
|
38 | continue;
|
39 | else
|
40 | return false;
|
41 | }
|
42 | return true;
|
43 | }
|
44 |
|
45 | kbp.addFact = function(term) {
|
46 | this.facts[term] = true;
|
47 | log("addFact(%s)", term);
|
48 | }
|
49 |
|
50 | kbp.addRule = function(line) {
|
51 | var m = line.match(/^([^<=]*)(<=(.*))?$/);
|
52 | var head = (m[1]==null)?"":m[1].trim();
|
53 | var terms= (m[3]==null)?"":m[3].trim().split(/&+/);
|
54 | log("rule:head=%s terms=%j", head, terms);
|
55 | var rule = { head:head, terms:terms, satisfy:false };
|
56 | this.rules.push(rule);
|
57 | this.dict[head] = { headHits: [rule], bodyHits:[] };
|
58 | }
|
59 |
|
60 | kbp.forwardChaining = function() {
|
61 | do {
|
62 | var anySatisfy = false;
|
63 | for (var i in this.rules) {
|
64 | var rule = this.rules[i];
|
65 | if (!rule.satisfy) {
|
66 | if (this.check(rule)) {
|
67 | this.addFact(rule.head);
|
68 | rule.satisfy = true;
|
69 | anySatisfy = true;
|
70 | }
|
71 | }
|
72 | }
|
73 | } while (anySatisfy);
|
74 | log("facts=%j", Object.keys(this.facts));
|
75 | return this;
|
76 | }
|
77 |
|
78 | kbp.trySatisfy = function(goal) {
|
79 | log("trySatisfy(%s)", goal);
|
80 | var word = this.dict[goal];
|
81 | if (word == null) return false;
|
82 | var headHits = word.headHits;
|
83 | for (var i in headHits) {
|
84 | var rule = headHits[i];
|
85 | if (rule.satisfy) {
|
86 | this.addFact(goal);
|
87 | return true;
|
88 | } else {
|
89 | var isSatisfy = true;
|
90 | for (var ti in rule.terms) {
|
91 | var term = rule.terms[ti];
|
92 | var satisfy = this.trySatisfy(term);
|
93 | if (!satisfy) isSatisfy = false;
|
94 | }
|
95 | rule.satisfy = isSatisfy;
|
96 | if (isSatisfy) {
|
97 | this.addFact(goal);
|
98 | return true;
|
99 | }
|
100 | }
|
101 | }
|
102 | return false;
|
103 | }
|
104 |
|
105 | kbp.backwardChaining = function(goal) {
|
106 | this.trySatisfy(goal);
|
107 | log("facts=%j", Object.keys(this.facts));
|
108 | return this;
|
109 | }
|
110 |
|
111 | kbp.query = function() {
|
112 | var r = require('readline').createInterface(process.stdin, process.stdout);
|
113 | r.setPrompt('?- ');
|
114 | r.prompt();
|
115 | var self = this;
|
116 | r.on('line', function(line) {
|
117 | var term = line.trim();
|
118 | if (line === "exit") process.exit();
|
119 | self.addFact(term);
|
120 | self.forwardChaining();
|
121 | r.prompt();
|
122 | }).on('close', function() {
|
123 | process.exit(0);
|
124 | });
|
125 | return this;
|
126 | }
|
127 |
|
128 | module.exports = kb; |
\ | No newline at end of file |