| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.jena.reasoner.rulesys.impl; |
| |
| import java.io.PrintStream; |
| import java.util.*; |
| |
| import org.apache.jena.graph.* ; |
| import org.apache.jena.reasoner.TriplePattern ; |
| import org.apache.jena.reasoner.rulesys.* ; |
| |
| /** |
| * Object used to hold the compiled bytecode stream for a single rule clause. |
| * This uses a slightly WAM-like code stream but gluing of the clauses together |
| * into disjunctions is done in the interpreter loop so a complete predicate is |
| * represented as a list of RuleClauseCode objects. |
| */ |
| public class RuleClauseCode { |
| |
| // ======================================================================= |
| // Variables |
| |
| /** The rule from which is code was derived */ |
| protected Rule rule; |
| |
| /** The byte code sequence */ |
| protected byte[] code; |
| |
| /** Any Object argements needed by the byte codes */ |
| protected Object[] args; |
| |
| /** starting byte code offset for body terms */ |
| protected int[] termStart; |
| |
| // ======================================================================= |
| // Instruction set constants |
| |
| /** fetch constant argument (const, Ai) */ |
| public static final byte GET_CONSTANT = 0x1; |
| |
| /** fetch permanent variable argument, first occurance (Yi, Ai) */ |
| public static final byte GET_VARIABLE = 0x2; |
| |
| /** fetch permanent variable argument, later occurance (Yi, Ai) */ |
| public static final byte UNIFY_VARIABLE = 0x3; |
| |
| /** fetch temporary variable argument (Ti, Ai) */ |
| public static final byte GET_TEMP = 0x4; |
| |
| /** fetch temporary variable argument, later occurance (Ti, Ai) */ |
| public static final byte UNIFY_TEMP = 0x12; |
| |
| /** put constant value into call parameter (const, Ai) */ |
| public static final byte PUT_CONSTANT = 0x5; |
| |
| /** put permanaent variable into call parameter, first occurance (Yi, Ai) */ |
| public static final byte PUT_NEW_VARIABLE = 0x6; |
| |
| /** put permanaent variable into call parameter (Yi, Ai) */ |
| public static final byte PUT_VARIABLE = 0x7; |
| |
| /** put a dereferenced permanent variable into call parameter ready for BUILTIN call (Yi, Ai) */ |
| public static final byte PUT_DEREF_VARIABLE = 0x14; |
| |
| /** put temp variable into call parameter (Ti, Ai) */ |
| public static final byte PUT_TEMP = 0x8; |
| |
| /** call a predicate code object (predicateCodeList) */ |
| public static final byte CALL_PREDICATE = 0x9; |
| |
| /** deconstruct a functor argument (functor) */ |
| public static final byte GET_FUNCTOR = 0xa; |
| |
| /** call a predicate code object with run time indexing (predicateCodeList) */ |
| public static final byte CALL_PREDICATE_INDEX = 0x17; |
| |
| /** call a pure triple match (predicate) */ |
| public static final byte CALL_TRIPLE_MATCH = 0x11; |
| |
| /** variant on CALL_PREDICATE using the last call optimization, only current used in chain rules */ |
| public static final byte LAST_CALL_PREDICATE = 0x13; |
| |
| /** call a table code object () */ |
| public static final byte CALL_TABLED = 0x18; |
| |
| /** call a table code object from a wildcard () */ |
| public static final byte CALL_WILD_TABLED = 0x19; |
| |
| /** return from a call, proceeed along AND tree */ |
| public static final byte PROCEED = 0xb; |
| |
| /** create a functor object from a rule template (templateFunctor) */ |
| public static final byte MAKE_FUNCTOR = 0xc; |
| |
| /** call a built-in operation defined by a rule clause (clauseIndex( */ |
| public static final byte CALL_BUILTIN = 0xd; |
| |
| /** reset a permanent variable to an unbound variable (Yi) */ |
| public static final byte CLEAR_VARIABLE = 0xe; |
| |
| /** reset a temp variable to an unbound variable (Ti) */ |
| public static final byte CLEAR_TEMP = 0xf; |
| |
| /** reset an argument to an unbound variable (Ai) */ |
| public static final byte CLEAR_ARG = 0x10; |
| |
| /** Allocate a new environment frame */ |
| public static final byte ALLOCATE = 0x16; |
| |
| /** Test if an argument is bound (Ai) */ |
| public static final byte TEST_BOUND = 0x20; |
| |
| /** Test if an argument is unbound (Ai) */ |
| public static final byte TEST_UNBOUND = 0x21; |
| |
| // current next = 0x22 |
| |
| /** The maximum number of permanent variables allowed in a single rule clause. |
| * Now only relevent for initial holding clause. */ |
| public static final int MAX_PERMANENT_VARS = 15; |
| |
| /** The maximum number of argument variables allowed in a single goal |
| * Future refactorings will remove this restriction. */ |
| public static final int MAX_ARGUMENT_VARS = 8; |
| |
| /** The maximum number of temporary variables allowed in a single rule clause. */ |
| public static final int MAX_TEMPORARY_VARS = 8; |
| |
| /** Dummy code block which just returns */ |
| public static RuleClauseCode returnCodeBlock; |
| |
| static { |
| returnCodeBlock = new RuleClauseCode(null); |
| returnCodeBlock.code = new byte[] {PROCEED}; |
| } |
| // ======================================================================= |
| // Methods and constructors |
| |
| /** |
| * Constructor. |
| * @param rule the rule to be compiled |
| */ |
| public RuleClauseCode(Rule rule) { |
| this.rule = rule; |
| } |
| |
| /** |
| * Return the byte code vector for this clause. |
| */ |
| public byte[] getCode() { |
| return code; |
| } |
| |
| /** |
| * Return the argument vector associated with this clauses' byte codes. |
| */ |
| public Object[] getArgs() { |
| return args; |
| } |
| |
| /** |
| * Return the rule from which this code block was compiled. |
| */ |
| public Rule getRule() { |
| return rule; |
| } |
| |
| /** |
| * Compile the rule into byte code. |
| * @param ruleStore the store of LP rules through which calls to other predicates |
| * can be resolved. |
| */ |
| public void compile(LPRuleStore ruleStore) { |
| CompileState state = new CompileState(rule); |
| |
| // Compile any early binding tests |
| int skip = 0; // state.emitBindingTests(); |
| |
| // Compile the head operations |
| ClauseEntry head = rule.getHeadElement(0); |
| if (!(head instanceof TriplePattern)) { |
| throw new LPRuleSyntaxException("Heads of backward rules must be triple patterns", rule); |
| } |
| state.emitHead((TriplePattern)head); |
| |
| // Compile the body operations |
| termStart = new int[rule.bodyLength()]; |
| for (int i = skip; i < rule.bodyLength(); i++) { |
| termStart[i] = state.p; |
| ClauseEntry entry = rule.getBodyElement(i); |
| if (entry instanceof TriplePattern) { |
| state.emitBody((TriplePattern)entry, ruleStore); |
| } else if (entry instanceof Functor) { |
| state.emitBody((Functor)entry); |
| } else { |
| throw new LPRuleSyntaxException("can't create new bRules in an bRule", rule); |
| } |
| } |
| |
| // Extract the final code |
| code = state.getFinalCode(); |
| args = state.getFinalArgs(); |
| } |
| |
| /** |
| * Translate a program counter offset to the index of the corresponding |
| * body term (or -1 if a head term or a dummy rule). |
| */ |
| public int termIndex(int pc) { |
| if (rule == null) return -1; |
| int term = 0; |
| // Trivial linear search because this is only used for logging which is |
| // horribly expensive anyway. |
| while (term < rule.bodyLength()) { |
| if (pc <= termStart[term]) { |
| return term - 1; |
| } |
| term++; |
| } |
| return term - 1; |
| } |
| |
| /** |
| * Debug helper - list the code to a stream |
| */ |
| public void print(PrintStream out) { |
| if (code == null) { |
| out.println("Code not available"); |
| } else { |
| int argi = 0; |
| for (int p = 0; p < code.length; ) { |
| byte instruction = code[p++]; |
| switch (instruction) { |
| case GET_CONSTANT: |
| out.println("GET_CONSTANT " + args[argi++] + ", A" + code[p++]); |
| break; |
| case GET_VARIABLE: |
| out.println("GET_VARIABLE " + "Y" + code[p++] + ", A" + code[p++]); |
| break; |
| case UNIFY_VARIABLE: |
| out.println("UNIFY_VARIABLE " + "Y" + code[p++] + ", A" + code[p++]); |
| break; |
| case GET_TEMP: |
| out.println("GET_TEMP " + "T" + code[p++] + ", A" + code[p++]); |
| break; |
| case UNIFY_TEMP: |
| out.println("UNIFY_TEMP " + "T" + code[p++] + ", A" + code[p++]); |
| break; |
| case PUT_CONSTANT: |
| out.println("PUT_CONSTANT " + args[argi++] + ", A" + code[p++]); |
| break; |
| case PUT_NEW_VARIABLE: |
| out.println("PUT_NEW_VARIABLE " + "Y" + code[p++] + ", A" + code[p++]); |
| break; |
| case PUT_TEMP: |
| out.println("PUT_TEMP " + "T" + code[p++] + ", A" + code[p++]); |
| break; |
| case PUT_VARIABLE: |
| out.println("PUT_VARIABLE " + "Y" + code[p++] + ", A" + code[p++]); |
| break; |
| case PUT_DEREF_VARIABLE: |
| out.println("PUT_DEREF_VARIABLE " + "Y" + code[p++] + ", A" + code[p++]); |
| break; |
| case TEST_BOUND: |
| out.println("TEST_BOUND A" + code[p++]); |
| break; |
| case TEST_UNBOUND: |
| out.println("TEST_UNBOUND A" + code[p++]); |
| break; |
| case CALL_PREDICATE: |
| out.println("CALL_PREDICATE " + args[argi++]); |
| break; |
| case CALL_TABLED: |
| out.println("CALL_TABLED "); |
| break; |
| case CALL_WILD_TABLED: |
| out.println("CALL_WILD_TABLED "); |
| break; |
| case CALL_PREDICATE_INDEX: |
| out.println("CALL_PREDICATE_INDEX " + args[argi++]); |
| break; |
| case LAST_CALL_PREDICATE: |
| out.println("LAST_CALL_PREDICATE " + args[argi++]); |
| break; |
| case CALL_TRIPLE_MATCH: |
| out.println("CALL_TRIPLE_MATCH"); |
| break; |
| case PROCEED: |
| out.println("PROCEED"); |
| break; |
| case MAKE_FUNCTOR: |
| out.println("MAKE_FUNCTOR " + args[argi++]); |
| break; |
| case GET_FUNCTOR: |
| out.println("GET_FUNCTOR " + args[argi++]); |
| break; |
| case CALL_BUILTIN: |
| out.println("CALL_BUILTIN " + ((Builtin)args[argi++]).getName() + "/" + code[p++]); |
| break; |
| case CLEAR_ARG: |
| out.println("CLEAR_ARG " + "A" + code[p++]); |
| break; |
| case ALLOCATE: |
| out.println("ALLOCATE + " + code[p++]); |
| break; |
| default: |
| out.println("Unused code: " + instruction); |
| break; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Print clause as rule for tracing. |
| */ |
| @Override |
| public String toString() { |
| if (rule == null) { |
| return "[anon]"; |
| } else { |
| return "[" + rule.toShortString() + "]"; |
| } |
| } |
| |
| /** |
| * Inner class - compiler state. |
| */ |
| static class CompileState { |
| |
| /** The temporary code vector during construction */ |
| byte[] code; |
| |
| /** The temporary arg list during construction */ |
| ArrayList<Object> args; |
| |
| /** The code pointer during construction */ |
| int p; |
| |
| /** array of lists of variables in the rule clauses, array index is 0 for head, body starts at 1 */ |
| private List<Node>[] termVarTable; |
| |
| /** Map from variables to the list of term positions in which it occurs */ |
| private Map<Node_RuleVariable, List<TermIndex>> varOccurrence = new HashMap<>(); |
| |
| /** List of all permanent variables */ |
| private List<Node_RuleVariable> permanentVars = new ArrayList<>(); |
| |
| /** List of all temporary variables */ |
| private List<Node_RuleVariable> tempVars = new ArrayList<>(); |
| |
| /** The total number of var occurrences */ |
| int totalOccurrences = 0; |
| |
| /** the set of variables processed so far during the compile phase */ |
| Set<Node_RuleVariable> seen = new HashSet<>(); |
| |
| /** The rule being parsed */ |
| Rule rule; |
| |
| |
| /** |
| * Constructor. |
| */ |
| CompileState(Rule rule) { |
| classifyVariables(rule); |
| this.rule = rule; |
| // Create a scratch area for assembling the code, use a worst-case size estimate |
| code = new byte[10 + totalOccurrences + rule.bodyLength()*10]; |
| args = new ArrayList<>(); |
| } |
| |
| /** |
| * Emit the code for any bound/unbound tests add start of body |
| * and return the number of body clauses dealt with. |
| */ |
| int emitBindingTests() { |
| int i = 0; |
| while (i < rule.bodyLength()) { |
| ClauseEntry term = rule.getBodyElement(i); |
| if (term instanceof Functor) { |
| Functor f = (Functor)term; |
| if (f.getArgLength() != 1) break; |
| int ai = aIndex(f.getArgs()[0]); |
| if (ai >= 0) { |
| if (f.getName().equals("bound")) { |
| code[p++] = TEST_BOUND; |
| code[p++] = (byte)ai; |
| } else if (f.getName().equals("unbound")) { |
| code[p++] = TEST_UNBOUND; |
| code[p++] = (byte)ai; |
| } else { |
| break; |
| } |
| } |
| } else { |
| break; |
| } |
| i++; |
| } |
| return i; |
| } |
| |
| /** |
| * Return the argument index of the given variable. |
| */ |
| int aIndex(Node n) { |
| TriplePattern tp = (TriplePattern)rule.getHeadElement(0); |
| if (tp.getSubject() == n) { |
| return 0; |
| } else if (tp.getPredicate() == n) { |
| return 1; |
| } else if (tp.getObject() == n) { |
| return 2; |
| } else { |
| return -1; |
| } |
| } |
| |
| /** |
| * emit the code for the head clause |
| */ |
| void emitHead(TriplePattern head) { |
| if (permanentVars.size() > 0) { |
| code[p++] = ALLOCATE; |
| code[p++] = (byte)permanentVars.size(); |
| } |
| emitHeadGet(head.getSubject(), 0); |
| emitHeadGet(head.getPredicate(), 1); |
| emitHeadGet(head.getObject(), 2); |
| } |
| |
| /** |
| * Emit a single head get operation |
| * @param node the node to emit |
| * @param argi the argument register to address |
| */ |
| void emitHeadGet(Node node, int argi) { |
| if (node instanceof Node_RuleVariable) { |
| Node_RuleVariable var = (Node_RuleVariable)node; |
| if (isDummy(var)) { |
| // Node code required, var not used |
| return; |
| } |
| if (isTemp(var)) { |
| List<TermIndex>occurrences = varOccurrence.get(var); |
| if (occurrences.size() == 2 && |
| occurrences.get(0).index <= 2 && |
| occurrences.get(0).index ==occurrences.get(1).index) { |
| // No movement code required, var in right place |
| } else { |
| code[p++] = seen.add(var) ? GET_TEMP : UNIFY_TEMP; |
| code[p++] = (byte)tempVars.indexOf(var); |
| code[p++] = (byte)argi; |
| } |
| } else { |
| code[p++] = seen.add(var) ? GET_VARIABLE : UNIFY_VARIABLE; |
| code[p++] = (byte)permanentVars.indexOf(var); |
| code[p++] = (byte)argi; |
| } |
| } else if (Functor.isFunctor(node)) { |
| Functor f = (Functor)node.getLiteralValue(); |
| code[p++] = GET_FUNCTOR; |
| args.add(f); |
| Node[] fargs = f.getArgs(); |
| for (int i = 0; i < fargs.length; i++) { |
| emitHeadGet(fargs[i], i+3); |
| } |
| } else { |
| code[p++] = GET_CONSTANT; |
| code[p++] = (byte)argi; |
| args.add(node); |
| } |
| } |
| |
| /** |
| * Emit code for a body clause. |
| * @param goal the triple pattern to be called |
| */ |
| void emitBody(TriplePattern goal, LPRuleStore store) { |
| emitBodyPut(goal.getSubject(), 0, false); |
| emitBodyPut(goal.getPredicate(), 1, false); |
| emitBodyPut(goal.getObject(), 2, false); |
| List<RuleClauseCode> predicateCode = store.codeFor(goal); |
| if (predicateCode == null || predicateCode.size() == 0) { |
| code[p++] = CALL_TRIPLE_MATCH; |
| } else { |
| // code[p++] = CALL_TABLED; |
| if (goal.getPredicate().isVariable()) { |
| // Experimental. Force tabling of any wildcard predicate calls |
| code[p++] = CALL_TABLED; |
| } else if (store.isTabled(goal)) { |
| // if (store.isTabled(goal)) { |
| code[p++] = goal.getPredicate().isVariable() ? CALL_WILD_TABLED : CALL_TABLED; |
| } else { |
| if (permanentVars.size() == 0) { |
| code[p++] = LAST_CALL_PREDICATE; |
| } else { |
| // Normal call, but can it be indexed further? |
| if (store.isIndexedPredicate(goal.getPredicate()) && goal.getObject().isVariable()) { |
| code[p++] = CALL_PREDICATE_INDEX; |
| } else { |
| code[p++] = CALL_PREDICATE; |
| } |
| } |
| args.add( new RuleClauseCodeList( predicateCode ) ); |
| } |
| } |
| } |
| |
| /** |
| Wrapper class with reified |
| */ |
| public static class RuleClauseCodeList |
| { |
| private final List<RuleClauseCode> list; |
| public RuleClauseCodeList( List<RuleClauseCode> list ) { this.list = list; } |
| List<RuleClauseCode> getList() { return list; } |
| } |
| |
| /** |
| * Emit code a single body put operation. |
| * @param node the node to emit |
| * @param argi the argument register to use |
| * @param deref if true force a dereference of the variable binding at this point for |
| * use in calling builtins |
| */ |
| void emitBodyPut(Node node, int argi, boolean deref) { |
| if (argi >= MAX_ARGUMENT_VARS) { |
| throw new LPRuleSyntaxException("Rule too complex for current implementation\n" |
| + "Rule clauses are limited to " + MAX_ARGUMENT_VARS + " argument variables\n", rule); |
| |
| } |
| if (node instanceof Node_RuleVariable) { |
| Node_RuleVariable var = (Node_RuleVariable)node; |
| if (isDummy(var)) { |
| code[p++] = CLEAR_ARG; |
| code[p++] = (byte)argi; |
| return; |
| } |
| if (isTemp(var)) { |
| List<TermIndex> occurrences = varOccurrence.get(var); |
| if (occurrences.size() == 2 && |
| occurrences.get(0).index ==occurrences.get(1).index) { |
| // No movement code required, var in right place |
| } else { |
| code[p++] = PUT_TEMP; |
| code[p++] = (byte)tempVars.indexOf(var); |
| code[p++] = (byte)argi; |
| } |
| } else { |
| if (! seen.add(var)) { |
| code[p++] = (deref ? PUT_DEREF_VARIABLE : PUT_VARIABLE); |
| } else { |
| code[p++] = PUT_NEW_VARIABLE; |
| } |
| code[p++] = (byte)permanentVars.indexOf(var); |
| code[p++] = (byte)argi; |
| } |
| } else if (Functor.isFunctor(node)) { |
| Functor f = (Functor)node.getLiteralValue(); |
| Node[] fargs = f.getArgs(); |
| for (int i = 0; i < fargs.length; i++) { |
| emitBodyPut(fargs[i], i+3, deref); |
| } |
| code[p++] = MAKE_FUNCTOR; |
| args.add(f); |
| } else { |
| code[p++] = PUT_CONSTANT; |
| code[p++] = (byte)argi; |
| args.add(node); |
| } |
| } |
| |
| /** |
| * Emit code for a call to a built-in predicate (functor). |
| * @param functor the built-in to be invoked. |
| */ |
| void emitBody(Functor functor) { |
| Node[] fargs = functor.getArgs(); |
| Builtin builtin = functor.getImplementor(); |
| if (builtin == null) { |
| throw new LPRuleSyntaxException("Unknown builtin operation " + functor.getName(), rule); |
| } |
| if (builtin.getArgLength() != 0 && builtin.getArgLength() != fargs.length) { |
| throw new LPRuleSyntaxException("Wrong number of arguments to functor " + functor.getName() |
| + " : got " + functor.getArgLength() |
| + " : expected " + builtin.getArgLength(), rule); |
| } |
| for (int i = 0; i < fargs.length; i++) { |
| Node node = fargs[i]; |
| // We optionally force an eager dereference of variables here. |
| // We used to force this but the current builtin implementations |
| // now robust against it (the do a deref themselves anyway). |
| emitBodyPut(node, i, true); |
| } |
| code[p++] = CALL_BUILTIN; |
| code[p++] = (byte)fargs.length; |
| args.add(builtin); |
| } |
| |
| /** |
| * Complete the byte stream with a PROCEED and return the packed version of the code. |
| * @return the byte code array |
| */ |
| byte[] getFinalCode() { |
| code[p++] = PROCEED; |
| byte[] finalCode = new byte[p]; |
| System.arraycopy(code, 0, finalCode, 0, p); |
| return finalCode; |
| } |
| |
| /** |
| * Fetch the packed array of argument objects for the byte code. |
| * @return arg array |
| */ |
| Object[] getFinalArgs() { |
| return args.toArray(); |
| } |
| |
| /** |
| * Classifies the variables now and side effects the |
| * index number of the variables so they lie in two sequences - temp |
| * and permanent separately. |
| */ |
| void classifyVariables( Rule rule ) { |
| // Build index data structure into var use in each term in the rule |
| @SuppressWarnings("unchecked") List<Node>[] termListArray = new List[rule.bodyLength() + 1]; |
| termVarTable = termListArray; |
| termVarTable[0] = termVars( rule.getHeadElement(0) ); |
| totalOccurrences += termVarTable[0].size(); |
| for (int i = 0; i < rule.bodyLength(); i++) { |
| termVarTable[i+1] = termVars( rule.getBodyElement(i) ); |
| totalOccurrences += termVarTable[i+1].size(); |
| } |
| |
| // Build the inverted data structure |
| for (int i = 0; i < rule.bodyLength() + 1; i++ ) { |
| List<Node> varEnts = termVarTable[i]; |
| for (int j = 0; j < varEnts.size(); j++) { |
| Node n = varEnts.get(j); |
| if (n.isVariable()) { |
| Node_RuleVariable var = (Node_RuleVariable)n; |
| List<TermIndex> occurrences = varOccurrence.get(var); |
| if (occurrences == null) { |
| occurrences = new ArrayList<>(); |
| varOccurrence.put(var, occurrences); |
| } |
| occurrences.add(new TermIndex(var, i, j)); |
| } |
| } |
| } |
| |
| // Classify vars as permanent unless they are just dummies (only used once at all) |
| // or only used head+first body goal (but not if used in a builtin) |
| for ( List<TermIndex> occurrences : varOccurrence.values() ) |
| { |
| Node_RuleVariable var = null; |
| boolean inFirst = false; |
| boolean inLaterBody = false; |
| for ( Iterator<TermIndex> oi = occurrences.iterator(); oi.hasNext(); ) |
| { |
| TermIndex occurence = oi.next(); |
| var = occurence.var; |
| int termNumber = occurence.termNumber; |
| if ( termNumber == 0 ) |
| { |
| inFirst = true; |
| } |
| else if ( termNumber > 1 ) |
| { |
| inLaterBody = true; |
| } |
| if ( termNumber > 0 && rule.getBodyElement( termNumber - 1 ) instanceof Functor ) |
| { |
| } |
| } |
| // Don't think we need to protected builtin's any more, so ignore that test |
| // if (inBuiltin) { |
| // permanentVars.add(var); |
| // } else { |
| if ( !isDummy( var ) ) |
| { |
| if ( inLaterBody || !inFirst ) |
| { |
| permanentVars.add( var ); |
| } |
| else |
| { |
| tempVars.add( var ); |
| } |
| } |
| // } |
| |
| } |
| |
| if (permanentVars.size() > MAX_PERMANENT_VARS) { |
| throw new LPRuleSyntaxException("Rule too complex for current implementation\n" |
| + "Rule clauses are limited to " + MAX_PERMANENT_VARS + " permanent variables\n", rule); |
| } |
| |
| if (tempVars.size() > MAX_TEMPORARY_VARS) { |
| throw new LPRuleSyntaxException("Rule too complex for current implementation\n" |
| + "Rule clauses are limited to " + MAX_TEMPORARY_VARS + " temporary variables\n", rule); |
| } |
| |
| // Builtins in the forward system use the var index to modify variable bindings. |
| // At one time I though the LP system might need to emulate this but (a) it doesn't and |
| // (b) renumber vars to make that possible wreaks rule equality which wreaks add/remove in |
| // hybrid rule sets. This code left in but commented out as a reminder to not go down |
| // that path again. |
| |
| // Renumber the vars |
| // for (int i = 0; i < permanentVars.size(); i++ ) { |
| // Node_RuleVariable var = (Node_RuleVariable)permanentVars.get(i); |
| // var.setIndex(i); |
| // } |
| // for (int i = 0; i < tempVars.size(); i++ ) { |
| // Node_RuleVariable var = (Node_RuleVariable)tempVars.get(i); |
| // var.setIndex(i); |
| // } |
| |
| } |
| |
| /** Return true if the given rule variable is a temp */ |
| boolean isTemp(Node_RuleVariable var) { |
| return !isDummy(var) && !permanentVars.contains(var); |
| } |
| |
| /** Return true if the given rule variable is a dummy */ |
| boolean isDummy(Node_RuleVariable var) { |
| List<TermIndex> occurances = varOccurrence.get(var); |
| if (occurances == null || occurances.size() <= 1) return true; |
| return false; |
| } |
| |
| /** Return an list of variables or nodes in a ClauseEntry, in flattened order */ |
| private List<Node> termVars(ClauseEntry term) { |
| List<Node> result = new ArrayList<>(); |
| if (term instanceof TriplePattern) { |
| TriplePattern goal = (TriplePattern)term; |
| result.add(goal.getSubject()); |
| result.add(goal.getPredicate()); |
| Node obj = goal.getObject(); |
| if (Functor.isFunctor(obj)) { |
| result.add(obj); |
| result.addAll(termVars((Functor)obj.getLiteralValue())); |
| } else { |
| result.add(obj); |
| } |
| } else if (term instanceof Functor) { |
| Node[] args = ((Functor)term).getArgs(); |
| for ( Node arg : args ) |
| { |
| result.add( arg ); |
| } |
| } |
| return result; |
| } |
| } |
| |
| |
| /** |
| * Inner class used to represent the occurance of a variable in a term. |
| * Not an abstract data type, just a structure directly accessed. |
| */ |
| static class TermIndex { |
| /** The term number in the rule - 0 for head, body starting from 1 */ |
| int termNumber; |
| |
| /** The variable position within the term */ |
| int index; |
| |
| /** The variable being indexed */ |
| Node_RuleVariable var; |
| |
| /** Constructor */ |
| TermIndex(Node_RuleVariable var, int termNumber, int index) { |
| this.var = var; |
| this.termNumber = termNumber; |
| this.index = index; |
| } |
| } |
| |
| /** |
| * Debug support - not unit testing. |
| */ |
| // TODO document the derivation of these strings and their intended use? |
| public static void main(String[] args) { |
| try { |
| LPRuleStore store = new LPRuleStore(); |
| String test1 = "(?a p ?y) <- (?a p2 ?z)."; |
| String test2 = "(?a p ?y) <- (?a q2 ?z) (?z q2 ?w)."; |
| String test3 = "(?a p ?a) <- (?z r2 ?w) (?z r2 ?w)."; |
| String test4 = "(?a p ?a) <- (?z r2 ?w) (?a r2 ?w)."; |
| String test5 = "(?a p ?y) <- (?a p ?z), (?z p ?y)."; |
| String test6 = "(?x p C3) <- (C1 r ?x)."; |
| String test7 = "(?x p ?y) <- (?x r ?y) (?x q ?y)."; |
| String test8 = "(?x p ?y) <- (?x p ?z) addOne(?z, ?y)."; |
| String test9 = "(?x p ?y) <- (?x p ?z) sum(?z, 2, ?y)."; |
| String test10 = "(?x p ?y) <- (?x p ?v), sum(?v 2 ?y)."; |
| String test11 = "(b p ?y) <- (a ?y ?v)."; |
| String test12 = "(?x p ?y) <- (?x p foo(?z, ?y))."; |
| String test13 = "(?x p foo(?y,?z)) <- (?x q ?y), (?x q ?z)."; |
| String test14 = "(?x p ?z) <- (?x e ?z), (?z q ?z)."; |
| String test15 = "(?x p ?y ) <- bound(?x), (?x p ?y)."; |
| String test16 = "(a p b ) <- unbound(?x)."; |
| String test17 = "(?a p ?a) <- (?a q class)."; |
| String test18 = "(?a p ?a) <- (?a s ?a)."; |
| String test19 = "(?X p ?T) <- (?X rdf:type c), noValue(?X, p), makeInstance(?X, p, ?T)."; |
| String test20 = "(a p b ) <- unbound(?x)."; |
| String testLong = "(?P p ?C) <- (?P q ?D), (?D r xsd(?B, ?S1, ?L1)),(?P p ?E), notEqual(?D, ?E) " + |
| "(?E e xsd(?B, ?S2, ?L2)),min(?S1, ?S2, ?S3),min(?L1, ?L2, ?L3), (?C r xsd(?B, ?S3, ?L3))."; |
| String test21 = "(?a p ?y) <- (?x s ?y) (?a p ?x)."; |
| String test22 = "(?C p ?D) <- (?C rb:xsdBase ?BC), (?D rb:xsdBase ?BD), notEqual(?BC, ?BD)."; |
| store.addRule(Rule.parseRule(test22)); |
| System.out.println("Code for p:"); |
| List<RuleClauseCode> codeList = store.codeFor(NodeFactory.createURI("p")); |
| RuleClauseCode code = codeList.get(0); |
| code.print(System.out); |
| } catch (Exception e) { |
| System.out.println("Problem: " + e); |
| e.printStackTrace(); |
| } |
| } |
| } |