<!--

// jan grant of ILRT, University of Bristol wrote this. 
// see http://rdf.desire.org/~cmjg/test/prolog.html
//
// this version amended by daniel.brickley@bristol.ac.uk slightly
// to allow for API experiments in rdfquery.js. 

        function cls() {
                document.output.output.value = "";
        }

        function print (str) {
                document.output.output.value += str;
        }


        function freeform() {
                cls();

                var rules = document.rules.rules.value;
                var query = document.input.query.value;

                print ("Parsing rulesets.\n");
                rules = rules.split("\n");
                var outr = [], outi=0;
                for (var r=0; r < rules.length; r++) {
                        var rule = rules[r];
                        if (rule.substring(0, 1) == "#" || rule == "") continue;
                        var or = ParseRule(new Tokeniser(rule));
                        if (or == null) continue;
                        outr[outi++] = or;
                        // print ("Rule "+outi+" is : ");
//                      or.print()
                }

                print ("\nParsing query.\n");
                var q = ParseBody(new Tokeniser(query));
                if (q == null) {
                        print ("An error occurred parsing the query.\n");
                        return;
                }
                q = new Body(q);
                print ("Query is: ");
                q.print();
                print ("\n\n");

                var vs = varNames(q.list);


                // Prove the query.


                results = new ResultSet("resultset_001");//somewhere to store results
                prove(renameVariables(q.list, 0), [], outr, 1, vs); // do the thinky thing...
                print ("Getting report from RDF ResultSet: \n"+ results.report() );

                print ("Getting a second HTMLised report from RDF ResultSet: \n"+ results.htmlreport() );

                htmlWindow(results.htmlreport());

        }




        // Some auxiliary bits and pieces... environment-related.

        // Print out an environment's contents.
        function printEnv(env) {
                if (env == null) {
                        print ("null\n");
                        return;
                }
                var k=false;
                for (var i in env) {
                        k=true;
                        print (" " + i + " = ");
                        env[i].print();
                        print ("\n");
                }
                if (!k) print("true\n");
        }

        // The value of x in a given environment
        function value(x, env) {
                if (x.type == "Term") {
                        var l = [];
                        for (var i=0; i<x.partlist.list.length; i++) {
                                l[i] = value(x.partlist.list[i], env);
                        }
                        return new Term(x.name, l);
                }
                if (x.type != "Variable") return x;             // We only need to check the values of variables...
                var binding = env[x.name];
                if (binding == null) return x;          // Just the variable, no binding.
                return value(binding, env);
        }

        // Give a new environment from the old with "n" (a string variable name) bound to "z" (a part)
        // Part is Atom|Term|Variable
        function newEnv(n, z, e) {
                // We assume that n has been 'unwound' or 'followed' as far aspossible
                // in the environment. If this is not the case, we could get an alias loop.
                var ne = [];
                ne[n] = z;
                for (var i in e)
                        if (i != n) ne[i] = e[i];

                return ne;
        }

        // More substantial utility functions.

        // Unify two terms in the current environment. Returns a new environment.
        // On failure, returns null.
        function unify(x, y, env) {
                x = value(x, env);
                y = value(y, env);
                if (x.type == "Variable") return newEnv(x.name, y, env);
                if (y.type == "Variable") return newEnv(y.name, x, env);
                if (x.type == "Atom" || y.type == "Atom")
                        if (x.type == y.type && x.name == y.name)
                                return env;
                        else
                                return null;

                // x.type == y.type == Term...
                if (x.name != y.name) return null;      // Ooh, so first-order.
                if (x.partlist.list.length != y.partlist.list.length) return null;

                for (var i=0; i<x.partlist.list.length; i++) {
                        env = unify(x.partlist.list[i], y.partlist.list[i], env);
                        if (env == null) return null;
                }
                        
                return env;
        }

        // Go through a list of terms (ie, a Body or Partlist's list) renamingvariables
        // by appending 'level' to each variable name.
        // How non-graph-theoretical can this get?!?
        function renameVariables(list, level) {
                var out = [];

                for (var i = 0; i < list.length; i++) {
                        if (list[i].type == "Atom") {
                                out[i] = list[i];
                        } else if (list[i].type == "Variable") {
                                out[i] = new Variable(list[i].name + level);
                        } else if (list[i].type == "Term") {
                                out[i] = new Term(list[i].name, renameVariables(list[i].partlist.list, level));
                        }
                }

                return out;
        }

        // Return a list of all variables mentioned in a list of Terms.
        function varNames(list) {
                var out = [];

                main: for (var i=0; i < list.length; i++) {
                        if (list[i].type == "Variable") {
                                for (var j=0; j < out.length; j++)
                                        if (out[j].name == list[i].name) continue main;
                                out[out.length] = list[i];
                        } else if (list[i].type == "Term") {
                                var o2 = varNames(list[i].partlist.list);
                                inner: for (var j=0; j<o2.length; j++) {
                                        for (var k=0; k<out.length; k++)
                                                if (o2[j].name == out[k].name)continue inner;
                                        out[out.length] = o2[j];
                                }
                        }
                }
                return out;
        }

        // The meat of this thing... js-tinyProlog.
        // Don't expect built-ins at present. To come:
        //      unification of term heads, cut, fail, call, bagof
        //      (in that order, probably).

        // The main proving engine. Returns: null (keep going), other (drop out)
        function prove(goalList, environment, db, level, origVars) {
                if (goalList.length == 0) {
                        // Print bindings.
                        if (origVars.length == 0) {
                                print ("true\n");
                        } else {

                        //danbri addons


var tmp_res="";
var result = new RDFResult( 'result' );

                                for (var i=0; i<origVars.length; i++) {

                                        n1= (origVars[i].name);
                                        print (n1);

                                        print (" = ");
                                v1=     value(new Variable(origVars[i].name + 0), environment);
                                        v1.print();
                                        print ("\n");
                                tmp_res = tmp_res+ "   "+ n1 +" = "+ v1.toString();// should be rdfitem (danbri)

                                var item = new RDFItem( n1, v1.toString());
                                result.addItem( item );
                                }

                                res1 = new RDFResult("res"+i , tmp_res+"\n" );
                                //results.addResult( res1 );
                                results.addResult ( result );
                }
                        print ("\n");

                        //if (!more) return "done";
                        return null;
                }

                // Prove the first term in the goallist. We do this by trying to
                // unify that term with the rules in our database. For each
                // matching rule, replace the term with the body of the matching
                // rule, with appropriate substitutions.
                // Then prove the new goallist. (recursive call)
                
                var thisTerm = goalList[0];

                for (var i = 0; i < db.length; i++) {
                        var rule = db[i];

                        // We'll need better unification to allow the 2nd-order
                        // rule matching ... later.
                        if (rule.head.name != thisTerm.name) continue;

                        // Rename the variables in the head and body
                        var renamedHead = new Term(rule.head.name, renameVariables(rule.head.partlist.list, level));

                        var env2 = unify(thisTerm, renamedHead, environment);
                        if (env2 == null) continue;

                        var body = rule.body;
                        if (body != null) {
                                var newFirstGoals = renameVariables(rule.body.list, level);
                                // Stick the new body list
                                var newGoals = [];
                                var j, k;
                                for (j=0; j<newFirstGoals.length; j++) newGoals[j] = newFirstGoals[j];
                                for (k=1; k<goalList.length; k++) newGoals[j++] = goalList[k];
                                var ret = prove(newGoals, env2, db, level+1, origVars);
                                if (ret != null) return ret;
                        } else {
                                // Just prove the rest of the goallist, recursively.
                                var newGoals = [];
                                var j;
                                for (j=1; j<goalList.length; j++) newGoals[j-1] = goalList[j];
                                var ret = prove(newGoals, env2, db, level+1, origVars);
                                if (ret != null) return ret;
                        }
                }

                return null;
        }


        // Object (of a style...) definitions:
        // Rule = (Head, Body)
        // Head = Term
        // Body = [Term]
        // Term = (id, Parameters)
        // Parameters {Partlist} = [Part]
        // Part = Variable | Atom | Term

        function Variable(head) {
                this.name = head;
                // I had a sneaking suspicion that the (rather nice, I reckon)
                // idiom below returned a closure-like reference rather than an
                // anonymous function reference. It does; but I'm not overly
                // concerned with efficiency here. This is is Prolog interpreter
                // written in JS, for goodness' sake!
                this.print = function () { print (this.name); };
                this.toString = function () { return (this.name); };//danbri addition
                this.type = "Variable";
        }

        function Atom(head) {
                this.name = head;
                this.print = function () { print (this.name); };
                this.toString = function () { return (this.name); };//danbri3 addition
                this.type = "Atom";
        }

        function Term(head, list) {
                this.name = head;
                this.partlist = new Partlist(list);
                this.print =
                        function () {
                                if (this.name == "cons") {
                                        var x = this;
                                        while (x.type == "Term" && x.name == "cons" && x.partlist.list.length == 2) {
                                                x = x.partlist.list[1];
                                        }
                                        if ((x.type == "Atom" && x.name == "nil") || x.type == "Variable") {
                                                x = this;
                                                print ("[");
                                                var com = false;
                                                while (x.type == "Term" && x.name == "cons" && x.partlist.list.length == 2) {
                                                        if (com) print (", ");
                                                        x.partlist.list[0].print();
                                                        com = true;
                                                        x = x.partlist.list[1];
                                                }
                                                if (x.type == "Variable") {
                                                        print (" | ");
                                                        x.print();
                                                }
                                                print ("]");
                                                return;
                                        }
                                }
                                print ("" + this.name + "(");
                                this.partlist.print();
                                print (")");
                        };
                this.type = "Term";
        }

        function Partlist(list) {
                this.list = list;
                this.print =
                        function () {
                                for (var i = 0; i < this.list.length; i++) {
                                        this.list[i].print();
                                        if (i < this.list.length - 1)
                                                print (", ");
                                }
                        };
        }

        function Body(list) {
                this.list = list;
                this.print =
                        function () {
                                for (var i = 0; i < this.list.length; i++) {
                                        this.list[i].print();
                                        if (i < this.list.length - 1)
                                                print (", ");
                                }
                        };

                this.toString = function ()
                                {
                                tmp = "";
                                for (var i = 0; i < this.list.length; i++) {
                                        tmp += this.list[i];
                                        if (i < this.list.length - 1)
                                                tmp += (", ");
                                        }
                                return (tmp);
                                }                       //danbri addition


        }

        function Rule(head) { return new Rule(head, null); }
        function Rule(head, bodylist) {
                this.head = head;
                if (bodylist != null)
                        this.body = new Body(bodylist);
                else
                        this.body = null;

                this.print =
                        function () {
                                if (this.body == null) {
                                        this.head.print();
                                        print (".\n");
                                } else {
                                        this.head.print();
                                        print (" :- ");
                                        this.body.print();
                                        print (".\n");
                                }
                        };
        }


        // The Tiny-Prolog parser goes here.
        function Tokeniser(string) {
                this.remainder = string;
                this.current = null;
                this.type = null;       // "eof", "id", "var", "punc" etc.
                this.consume =
                        function () {
                                if (this.type == "eof") return;
                                // Eat any leading WS
                                var r = this.remainder.match(/^\s*(.*)$/);
                                if (r) {
                                        this.remainder = r[1];
                                }

                                if (this.remainder == "") {
                                        this.current = null;
                                        this.type = "eof";
                                        return;
                                }

                                r = this.remainder.match(/^([\(\)\.,\[\]\|]|\:\-)(.*)$/);
                                if (r) {
                                        this.remainder = r[2];
                                        this.current = r[1];
                                        this.type = "punc";
                                        return;
                                }

                                r = this.remainder.match(/^([A-Z][a-zA-Z0-9]*)(.*)$/);
                                if (r) {
                                        this.remainder = r[2];
                                        this.current = r[1];
                                        this.type = "var";
                                        return;
                                }

                                // URLs in angle-bracket pairs
                                // danbri edit to allow { }
                                r = this.remainder.match(/^(\{[^\}]*\})(.*)$/);
//                              r = this.remainder.match(/^(\<[^\>]*\>)(.*)$/);
                                if (r) {
                                        this.remainder = r[2];
                                        this.current = r[1];
                                        this.type = "id";
                                        return;
                                }

                                // Quoted strings
                                r = this.remainder.match(/^("[^"]*")(.*)$/);
                                if (r) {
                                        this.remainder = r[2];
                                        this.current = r[1];
                                        this.type = "id";
                                        return;
                                }

                                r = this.remainder.match(/^([a-zA-Z0-9]*)(.*)$/);
                                if (r) {
                                        this.remainder = r[2];
                                        this.current = r[1];
                                        this.type = "id";
                                        return;
                                }

                                this.current = null;
                                this.type = "eof";

                        };
                this.consume(); // Load up the first token.
        }

        var tokenstring;
        var currenttoken;

        function ParseRule(tk) {
                // A rule is a Head followed by . or by :- Body

                var h = ParseHead(tk);
                if (!h) return null;

                if (tk.current == ".") {
                        // A simple rule.
                        return new Rule(h);
                }

                if (tk.current != ":-") return null;
                tk.consume();
                var b = ParseBody(tk);

                if (tk.current != ".") return null;

                return new Rule(h, b);
        }

        function ParseHead(tk) {
                // A head is simply a term. (errors cascade back up)
                return ParseTerm(tk);
        }

        function ParseTerm(tk) {
                // Term -> id ( optParamList )

                if (tk.type != "id") return null;
                var name = tk.current;
                tk.consume();

                if (tk.current != "(") return null;
                tk.consume();

                var p = [];
                var i = 0;
                while (tk.current != ")") {
                        if (tk.type == "eof") return null;

                        var part = ParsePart(tk);
                        if (part == null) return null;

                        if (tk.current == ",") tk.consume();
                        else if (tk.current != ")") return null;

                        // Add the current Part onto the list...
                        p[i++] = part;
                }
                tk.consume();

                return new Term(name, p);
        }

        // This was a beautiful piece of code. It got kludged to add [a,b,c|Z]sugar.
        function ParsePart(tk) {
                // Part -> var | id | id(optParamList)
                // Part -> [ listBit ] ::-> cons(...)
                if (tk.type == "var") {
                        var n = tk.current;
                        tk.consume();
                        return new Variable(n);
                }

                if (tk.type != "id") {
                        if (tk.type != "punc" || tk.current != "[") return null;
                        // Parse a list (syntactic sugar goes here)
                        tk.consume();
                        // Special case: [] = new atom(nil).
                        if (tk.type == "punc" && tk.current == "]") {
                                tk.consume();
                                return new Atom("nil");
                        }

                        // Get a list of parts into l
                        var l = [], i=0;

                        while (true) {
                                var t = ParsePart(tk);
                                if (t == null) return null;

                                l[i++] = t;
                                if (tk.current != ",") break;
                                tk.consume();
                        }

                        // Find the end of the list ... "| Var ]" or "]".
                        var append;
                        if (tk.current == "|") {
                                tk.consume();
                                if (tk.type != "var") return null;
                                append = new Variable(tk.current);
                                tk.consume();
                        } else {
                                append = new Atom("nil");
                        }
                        if (tk.current != "]") return null;
                        tk.consume();
                        // Return the new cons.... of all this rubbish.
                        for (i--; i>=0; i--) append = new Term("cons", [l[i], append]);
                        return append;
                }
                        
                var name = tk.current;
                tk.consume();

                if (tk.current != "(") return new Atom(name);
                tk.consume();

                var p = [];
                var i = 0;
                while (tk.current != ")") {
                        if (tk.type == "eof") return null;

                        var part = ParsePart(tk);
                        if (part == null) return null;

                        if (tk.current == ",") tk.consume();
                        else if (tk.current != ")") return null;

                        // Add the current Part onto the list...
                        p[i++] = part;
                }
                tk.consume();

                return new Term(name, p);
        }

        function ParseBody(tk) {
                // Body -> Term {, Term...}

                var p = [];
                var i = 0;

                var t;
                while ((t = ParseTerm(tk)) != null) {
                        p[i++] = t;
                        if (tk.current != ",") break;
                        tk.consume();
                }

                if (i == 0) return null;
                return p;
        }


// -->

