View Javadoc

1   /*
2    * #%L
3    * prolobjectlink-jpi-jtrolog
4    * %%
5    * Copyright (C) 2012 - 2018 WorkLogic Project
6    * %%
7    * This program is free software: you can redistribute it and/or modify
8    * it under the terms of the GNU Lesser General Public License as
9    * published by the Free Software Foundation, either version 2.1 of the
10   * License, or (at your option) any later version.
11   * 
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Lesser Public License for more details.
16   * 
17   * You should have received a copy of the GNU General Lesser Public
18   * License along with this program.  If not, see
19   * <http://www.gnu.org/licenses/lgpl-2.1.html>.
20   * #L%
21   */
22  /*
23   * tuProlog - Copyright (C) 2001-2007 aliCE team at deis.unibo.it
24   *
25   * This library is free software; you can redistribute it and/or
26   * modify it under the terms of the GNU Lesser General Public
27   * License as published by the Free Software Foundation; either
28   * version 2.1 of the License, or (at your option) any later version.
29   *
30   * This library is distributed in the hope that it will be useful,
31   * but WITHOUT ANY WARRANTY; without even the implied warranty of
32   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
33   * Lesser General Public License for more details.
34   *
35   * You should have received a copy of the GNU Lesser General Public
36   * License along with this library; if not, write to the Free Software
37   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
38   */
39  package jTrolog.lib;
40  
41  import jTrolog.engine.BindingsTable;
42  import jTrolog.engine.Prolog;
43  import jTrolog.errors.InvalidLibraryException;
44  import jTrolog.errors.PrologException;
45  import jTrolog.parser.Parser;
46  import jTrolog.terms.Clause;
47  import jTrolog.terms.Flag;
48  import jTrolog.terms.Int;
49  import jTrolog.terms.Number;
50  import jTrolog.terms.Struct;
51  import jTrolog.terms.StructAtom;
52  import jTrolog.terms.Term;
53  import jTrolog.terms.Var;
54  import jTrolog.terms.WrapStruct;
55  
56  import java.io.File;
57  import java.io.FileInputStream;
58  import java.io.FileNotFoundException;
59  import java.io.IOException;
60  import java.util.Iterator;
61  import java.util.LinkedList;
62  import java.util.List;
63  
64  /**
65   * Library of built-in predicates
66   * 
67   * @author Alex Benini
68   * @author janerist@stud.ntnu.no
69   * @author ivar.orstavik@hist.no
70   */
71  @SuppressWarnings({ "rawtypes", "unchecked","serial" })
72  public class BuiltIn extends Library {
73  
74  	private Prolog engine;
75  
76  	public BuiltIn(Prolog mediator) {
77  		engine = mediator;
78  	}
79  
80  	/**
81  	 * Defines a map for synonyms for primitives. String primitive name =
82  	 * String[]{synonym name, another synonym name, ..}.
83  	 */
84  	public String[] getSynonym(String primitive) {
85  		if (primitive.equals("cut"))
86  			return new String[] { "!" };
87  		if (primitive.equals("unify"))
88  			return new String[] { "=" };
89  		if (primitive.equals("deunify"))
90  			return new String[] { "\\=" };
91  		if (primitive.equals("comma"))
92  			return new String[] { "," };
93  		if (primitive.equals("solve"))
94  			return new String[] { "initialization" };
95  		if (primitive.equals("assertz"))
96  			return new String[] { "assert" };
97  		if (primitive.equals("copy"))
98  			return new String[] { "copy_term" };
99  		return null;
100 	}
101 
102 	/**
103 	 * Primitives
104 	 */
105 	public boolean comma_2(BindingsTable bt, Term arg0, Term arg1) {
106 		throw new RuntimeException("','/2 method is hardcoded in the Engine, do not use Buitlin.comma_2!");
107 	}
108 
109 	public static boolean cut_0(BindingsTable bt) {
110 		throw new RuntimeException("!/0 method is hardcoded in engine, do not use Buitlin.cut_0!");
111 	}
112 
113 	public static boolean fail_0(BindingsTable bt) {
114 		return false;
115 	}
116 
117 	public static boolean true_0(BindingsTable bt) {
118 		return true;
119 	}
120 
121 	public static boolean call_1(BindingsTable bt, Term arg1) {
122 		throw new RuntimeException("call/1 method is hardcoded in engine, do not use Buitlin.call_1!");
123 	}
124 
125 	public static boolean halt_0(BindingsTable bt) throws PrologException {
126 		throw new PrologException("halt()");
127 	}
128 
129 	public boolean halt_1(BindingsTable bt, jTrolog.terms.Number arg0) throws PrologException {
130 		throw new PrologException("halt(" + arg0.intValue() + ")");
131 	}
132 
133 	public boolean $debug_1(BindingsTable bt, Term arg0) {
134 		System.out.println("debug=== " + bt.variant(arg0));
135 		return true;
136 	}
137 
138 	public boolean $debug_3(BindingsTable bt, Term arg0, Term arg2, Term arg3) {
139 		System.out.println("debug0=== " + bt.variant(arg0));
140 		System.out.println("debug1=== " + bt.variant(arg2));
141 		System.out.println("debug2=== " + bt.variant(arg3));
142 		return true;
143 	}
144 
145 	public boolean asserta_1(BindingsTable bt, Term arg0) throws PrologException {
146 		arg0 = bt.variant(arg0);
147 		engine.assertA(convertTermToClause(arg0));
148 		return true;
149 	}
150 
151 	public boolean assertz_1(BindingsTable bt, Term arg0) throws PrologException {
152 		arg0 = bt.variant(arg0);
153 		engine.assertZ(convertTermToClause(arg0));
154 		return true;
155 	}
156 
157 	public boolean $retract_1(BindingsTable bt, Struct arg0) throws PrologException {
158 		arg0 = (Struct) bt.variant(arg0);
159 		arg0 = convertTermToClause(arg0).original;
160 		Struct sarg0 = (Struct) bt.wrapWithID(arg0);
161 		Struct retractedClause = engine.retract(sarg0);
162 		// if clause to retract found -> retract + true
163 		if (retractedClause != null) {
164 			bt.unify(retractedClause, arg0);
165 			return true;
166 		}
167 		return false;
168 	}
169 
170 	/**
171 	 * see 8.9.4 p.81 in ISO standard
172 	 * 
173 	 * @param predicateIndicator
174 	 * @return true
175 	 * @throws PrologException
176 	 *             in case of errors
177 	 */
178 	public boolean abolish_1(BindingsTable bt, Struct predicateIndicator) throws PrologException {
179 		engine.abolish(isPredicateIndicator(predicateIndicator, bt));
180 		return true;
181 	}
182 
183 	/**
184 	 * Registers the existence predicate indicator in the dynamic data base. If
185 	 * such a predicate is later requested, but none exists, the search will not
186 	 * throw an error, but return false. see ISO spec on abolish for details on
187 	 * errors.
188 	 * 
189 	 * @param predicateIndicator
190 	 * @return true
191 	 * @throws PrologException
192 	 *             in case of bad input
193 	 */
194 	public boolean dynamic_1(BindingsTable bt, Struct predicateIndicator) throws PrologException {
195 		engine.setDynamicPredicateIndicator(isPredicateIndicator(predicateIndicator, bt));
196 		return true;
197 	}
198 
199 	/*
200 	 * loads a engine library, given its java class name
201 	 */
202 	public boolean load_library_1(BindingsTable bt, Struct arg0) throws InvalidLibraryException {
203 		if (!BasicLibrary.atom_1(bt, arg0))
204 			return false;
205 		engine.loadLibrary(arg0.name);
206 		return true;
207 	}
208 
209 	/**
210 	 * unloads a engine library, given its java class name
211 	 */
212 	public boolean unload_library_1(BindingsTable bt, Struct arg0) throws InvalidLibraryException {
213 		if (!BasicLibrary.atom_1(bt, arg0))
214 			return false;
215 		engine.unloadLibrary(arg0.name);
216 		return true;
217 	}
218 
219 	/*
220 	 * get flag list: flag_list(-List)
221 	 */
222 	public boolean flag_list_1(BindingsTable bt, Term arg0) {
223 		return bt.unify(arg0, bt.wrapWithID(engine.getPrologFlagList()));
224 	}
225 
226 	public boolean unify_2(BindingsTable bt, Term arg0, Term arg1) {
227 		return bt.unify(arg0, arg1);
228 	}
229 
230 	// \=
231 	public boolean deunify_2(BindingsTable bt, Term arg0, Term arg1) {
232 		return !bt.unify(arg0, arg1);
233 	}
234 
235 	// unifies a new Struct list of the given length with Any vars as elements
236 	// with the Var out
237 	public boolean newlist_2(BindingsTable bt, Int length, Var out) throws PrologException {
238 		int lengthInt = length.intValue();
239 		if (lengthInt < 0)
240 			throw new PrologException("domain_error(not_less_than_zero, " + length + ")");
241 		if (lengthInt == 0)
242 			return bt.unify(out, Term.emptyList);
243 		return bt.unify(out, (WrapStruct) bt.wrapWithID(Parser.createListContainingAnyVars(lengthInt)));
244 	}
245 
246 	// $copy
247 	public boolean copy_2(BindingsTable bt, Term arg0, Term arg1) {
248 		return bt.unify(bt.wrapWithID(bt.variant(arg0)), arg1);
249 	}
250 
251 	// $find
252 	// look for clauses whose head unifies whith arg0 and unify the list of them
253 	// with arg1
254 	public boolean $find_2(BindingsTable bt, Struct arg0, Term arg1) throws PrologException, CloneNotSupportedException {
255 		String key = arg0.predicateIndicator;
256 		if (engine.hasPrimitive(key))
257 			throw new PrologException("permission_error(access, static_procedure, " + key + ")");
258 		LinkedList res = new LinkedList();
259 		List clauses = engine.find(key);
260 		if (clauses.isEmpty())
261 			return bt.unify(arg1, Term.emptyList);
262 		for (Iterator it = clauses.iterator(); it.hasNext();)
263 			res.add(((Clause) it.next()).original);
264 		res.add(Term.emptyList);
265 		return bt.unify(arg1, bt.wrapWithID(Parser.createStructList(res)));
266 	}
267 
268 	// set_prolog_flag(+Name,@Value)
269 	public boolean set_prolog_flag_2(BindingsTable bt, Term arg0, Term arg1) {
270 		if (!BasicLibrary.atom_1(bt, arg0) && !(arg0 instanceof Struct) || !BasicLibrary.ground_1(bt, arg1))
271 			return false;
272 
273 		String name = arg0.toString();
274 		Flag flag = engine.getFlag(name);// Flag) it.next();
275 		if (flag != null) {
276 			try {
277 				for (Iterator it2 = bt.structListIterator(flag.getValueList(), true); it2.hasNext();) {
278 					Term t = (Term) it2.next();
279 					if (Prolog.match(arg1, t)) {
280 						flag.setValue(arg1);
281 						return true;
282 					}
283 				}
284 			} catch (PrologException e) {
285 				throw new RuntimeException("error in iterating a Struct list");
286 			}
287 		}
288 		return false;
289 	}
290 
291 	// get_prolog_flag(@Name,?Value)
292 	public boolean get_prolog_flag_2(BindingsTable bt, Term arg0, Term arg1) {
293 		if (!BasicLibrary.atom_1(bt, arg0) && !(arg0 instanceof Struct))
294 			return false;
295 
296 		String name = arg0.toString();
297 		Term value = engine.getFlagValue(name);
298 		if (value == null)
299 			return false;
300 		return bt.unify(value, arg1);
301 	}
302 
303 	/*
304 	 * DIRECTIVES
305 	 */
306 
307 	/**
308 	 * op(+Precedence, +Type, +Name) defines a new operator if precedence not in
309 	 * 0..1200 = delete currently present op
310 	 */
311 	public void op_3(BindingsTable bt, Number arg0, StructAtom arg1, StructAtom arg2) {
312 		String st = ((Struct) arg1).name;
313 		if (st.matches("[xy]?f[xy]?")) // a tad simplified
314 			engine.opNew(arg2.toString(), st, arg0.intValue());
315 	}
316 
317 	public void flag_4(BindingsTable bt, Term flagName, Struct flagSet, Term flagDefault, Term flagModifiable) {
318 		if (flagModifiable.equals(Term.TRUE) || flagModifiable.equals(Term.FALSE))
319 			engine.defineFlag(flagName.toString(), flagSet, flagDefault, flagModifiable.equals(Term.TRUE));
320 	}
321 
322 	// todo test that this works properly.
323 	// todo Can alternatively use a temporary list to be run after the rest of
324 	// the theories in consults have been added.
325 	public void solve_1(BindingsTable bt, Struct goal) throws Throwable {
326 		engine.solve(bt.wrapWithID(bt.variant(goal)).toString());
327 	}
328 
329 	public void load_library_1(BindingsTable bt, Term lib) throws InvalidLibraryException {
330 		if (BasicLibrary.atom_1(bt, lib))
331 			engine.loadLibrary(((Struct) lib).name);
332 	}
333 
334 	public void consult_1(BindingsTable bt, Term theory) throws FileNotFoundException, PrologException, IOException {
335 		FileInputStream is;
336 		try {
337 			is = new FileInputStream(Parser.removeApices(theory.toString()));
338 		} catch (FileNotFoundException e) {
339 			File dir = new File(System.getProperty("user.dir"));
340 			String filename = dir + File.separator + Parser.removeApices(theory.toString());
341 			is = new FileInputStream(filename);
342 		}
343 		engine.addTheory(IOLibrary.readStream(is));
344 	}
345 
346 	public boolean $instantiation_error_0(BindingsTable bt) throws PrologException {
347 		throw new PrologException("instantiation_error");
348 	}
349 
350 	public boolean $type_error_2(BindingsTable bt, Term typeName, Term term) throws PrologException {
351 		throw new PrologException("type_error(" + typeName + ", " + term + ")");
352 	}
353 
354 	public boolean $representation_error_1(BindingsTable bt, Term maxArity) throws PrologException {
355 		throw new PrologException("representation_error(" + maxArity + ")");
356 	}
357 
358 	public boolean $domain_error_zero_1(BindingsTable bt, Term term) throws Exception {
359 		throw new PrologException("domain_error(not_less_than_zero, " + term + ")");
360 	}
361 
362 	/**
363 	 * see ISO 7.6.1 and 7.6.2
364 	 * 
365 	 * @param arg0
366 	 * @return
367 	 * @throws PrologException
368 	 */
369 	public static Clause convertTermToClause(Term arg0) throws PrologException {
370 		if (arg0 instanceof Var)
371 			throw new PrologException("instantiation_error");
372 
373 		if (arg0 instanceof Number)
374 			throw new PrologException("type_error(callable, " + arg0 + ")");
375 
376 		Term head, body;
377 		Struct cl = (Struct) arg0;
378 		if (cl.predicateIndicator != Parser.doubleClauseSignature) {
379 			head = arg0;
380 			body = Term.TRUE;
381 		} else {
382 			head = cl.getArg(0);
383 			body = cl.getArg(1);
384 		}
385 		if (head instanceof Number)
386 			throw new PrologException("type_error(callable, " + head + ")");
387 		if (head instanceof Var)
388 			throw new PrologException("instantiation_error");
389 
390 		if (body instanceof Number)
391 			throw new PrologException("type_error(callable, " + body + ")");
392 		body = convertTermToClauseBody(body);
393 		Struct[] body2 = convertTermToClauseBody2(body);
394 
395 		return new Clause(body2, (Struct) head, new Struct(":-", new Term[] { head, body }));
396 	}
397 
398 	// todo see 7.6.2 and table 9 in ISO
399 	public static Struct[] convertTermToClauseBody2(Term body) throws PrologException {
400 		LinkedList tmp = new LinkedList();
401 		while (body instanceof Struct && ((Struct) body).predicateIndicator == Parser.commaSignature) {
402 			tmp.add(convertTermToClauseBody(((Struct) body).getArg(0))); // todo,
403 																			// this
404 																			// might
405 																			// turn
406 																			// out
407 																			// to
408 																			// be
409 																			// a
410 																			// problem
411 																			// if
412 																			// the
413 																			// left
414 																			// child
415 																			// is
416 																			// a
417 																			// comma...
418 			body = ((Struct) body).getArg(1);
419 		}
420 		tmp.add(convertTermToClauseBody(body));
421 		return (Struct[]) tmp.toArray(new Struct[tmp.size()]);
422 	}
423 
424 	public static Struct convertTermToClauseBody(Term body) throws PrologException {
425 		if (body instanceof Var)
426 			return new Struct("call", new Term[] { body });
427 		if (body instanceof Number)
428 			throw new PrologException("type_error(callable, " + body + ")");
429 		Struct s = (Struct) body;
430 		String sPredIndic = s.predicateIndicator;
431 		if (sPredIndic == Parser.ifSignature || sPredIndic == Parser.commaSignature || sPredIndic == Parser.semiColonSignature || sPredIndic == Parser.throwSignature
432 				|| sPredIndic == Parser.catchSignature) {
433 			Term[] args = new Term[s.arity];
434 			for (int i = 0; i < s.arity; i++)
435 				args[i] = convertTermToClauseBody(s.getArg(i));
436 			return new Struct(s.name, args, s.getOperatorType());
437 		}
438 		return s;
439 	}
440 
441 	public static String isPredicateIndicator(Struct predicateIndicator, BindingsTable bt) throws PrologException {
442 		if (predicateIndicator.predicateIndicator != "'/'/2")
443 			throw new PrologException("type_error(predicate_indicator, " + predicateIndicator + ")");
444 		Term name = bt.resolve(predicateIndicator.getArg(0));
445 		Term arity = bt.resolve(predicateIndicator.getArg(1));
446 		if (name instanceof Var || arity instanceof Var)
447 			throw new PrologException("instantiation_error");
448 		if (!(arity instanceof Int))
449 			throw new PrologException("type_error(integer, " + arity + ")");
450 		if (!(name instanceof StructAtom))
451 			throw new PrologException("type_error(atom, " + name + ")");
452 		Int arityInt = (Int) arity;
453 		if (arityInt.intValue() < 0)
454 			throw new PrologException("domain_error(not_less_than_zero, " + arityInt + ")");
455 		if (arityInt.longValue() > Integer.MAX_VALUE)
456 			throw new PrologException("representation_error(max_arity)");
457 		return Parser.wrapAtom(name.toString()) + "/" + arity;
458 	}
459 }