JplEngine.java
/*
* #%L
* prolobjectlink-jpi-jpl
* %%
* Copyright (C) 2019 Prolobjectlink Project
* %%
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 2.1 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Lesser Public License for more details.
*
* You should have received a copy of the GNU General Lesser Public
* License along with this program. If not, see
* <http://www.gnu.org/licenses/lgpl-2.1.html>.
* #L%
*/
package io.github.prolobjectlink.prolog.jpl;
import static io.github.prolobjectlink.prolog.PrologLogger.IO;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import io.github.prolobjectlink.prolog.AbstractEngine;
import io.github.prolobjectlink.prolog.ArrayIterator;
import io.github.prolobjectlink.prolog.PrologClause;
import io.github.prolobjectlink.prolog.PrologEngine;
import io.github.prolobjectlink.prolog.PrologIndicator;
import io.github.prolobjectlink.prolog.PrologOperator;
import io.github.prolobjectlink.prolog.PrologProgram;
import io.github.prolobjectlink.prolog.PrologProvider;
import io.github.prolobjectlink.prolog.PrologQuery;
import io.github.prolobjectlink.prolog.PrologTerm;
import io.github.prolobjectlink.prolog.PrologTermType;
import jpl.Atom;
import jpl.Query;
import jpl.Term;
import jpl.Util;
/**
*
* @author Jose Zalacain
* @since 1.0
*/
public abstract class JplEngine extends AbstractEngine implements PrologEngine {
// used only for findall list result
private static final String KEY = "X";
// JPL use for fact clauses true prolog term
private static final Term BODY = new Atom("true");
// cache file in OS temporal directory
private static String cache = null;
// parser to obtain terms from text
private final JplParser parser = new JplParser();
// main memory prolog program
private JplProgram program = new JplProgram();
// formulated string for < consult(cache), >
private static String consultCacheComma;
static {
try {
File f = File.createTempFile("prolobjectlink-jpi-jpl-cache-", ".pl");
cache = f.getCanonicalPath().replace(File.separatorChar, '/');
consultCacheComma = "consult('" + cache + "'),";
} catch (IOException e) {
JplProvider.logger.error(JplEngine.class, IO, e);
}
}
protected JplEngine(PrologProvider provider) {
super(provider);
}
protected JplEngine(PrologProvider provider, String path) {
super(provider);
consult(path);
}
public final void consult(String path) {
program = parser.parseProgram(path);
persist(cache);
}
public final void consult(Reader reader) {
program = parser.parseProgram(reader);
persist(cache);
}
public final void include(String path) {
program.add(parser.parseProgram(path));
persist(cache);
}
public final void include(Reader reader) {
program.add(parser.parseProgram(reader));
persist(cache);
}
public final void persist(String path) {
PrintWriter writer = null;
try {
writer = new PrintWriter(new FileOutputStream(path, false));
writer.print(program);
} catch (FileNotFoundException e) {
getLogger().error(getClass(), IO + cache, e);
} finally {
if (writer != null) {
writer.close();
}
}
}
public final void abolish(String functor, int arity) {
program.removeAll(functor, arity);
persist(cache);
}
public final void asserta(String stringClause) {
asserta(Util.textToTerm(stringClause));
}
public final void asserta(PrologTerm term) {
asserta(fromTerm(term, Term.class));
}
public final void asserta(PrologTerm head, PrologTerm... body) {
asserta(fromTerm(head, body, Term.class));
}
private void asserta(Term t) {
program.push(t);
persist(cache);
}
public final void assertz(String stringClause) {
assertz(Util.textToTerm(stringClause));
}
public final void assertz(PrologTerm term) {
assertz(fromTerm(term, Term.class));
}
public final void assertz(PrologTerm head, PrologTerm... body) {
assertz(fromTerm(head, body, Term.class));
}
private void assertz(Term t) {
program.add(t);
persist(cache);
}
public final boolean clause(String stringClause) {
return clause(Util.textToTerm(stringClause));
}
public final boolean clause(PrologTerm term) {
return clause(fromTerm(term, Term.class));
}
public final boolean clause(PrologTerm head, PrologTerm... body) {
return clause(fromTerm(head, body, Term.class));
}
private boolean clause(Term t) {
Term h = t;
Term b = BODY;
if (t.hasFunctor(":-", 2)) {
h = t.arg(1);
b = t.arg(2);
}
return new JplQuery(
this, cache, "clause(" + h + "," + b + ")"
).hasSolution();
}
public final void retract(String stringClause) {
retract(Util.textToTerm(stringClause));
}
public final void retract(PrologTerm term) {
retract(fromTerm(term, Term.class));
}
public final void retract(PrologTerm head, PrologTerm... body) {
retract(provider.fromTerm(head, body, Term.class));
}
private void retract(Term t) {
program.remove(t);
persist(cache);
}
public final PrologQuery query(String stringQuery) {
return new JplQuery(this, cache, stringQuery);
}
public final PrologQuery query(PrologTerm term) {
StringBuilder buffer = new StringBuilder();
buffer.append("" + term + ".");
return query("" + buffer + "");
}
public final PrologQuery query(PrologTerm[] terms) {
Iterator<PrologTerm> i = new ArrayIterator<PrologTerm>(terms);
StringBuilder buffer = new StringBuilder();
while (i.hasNext()) {
buffer.append(i.next());
if (i.hasNext()) {
buffer.append(',');
}
}
buffer.append(".");
return query("" + buffer + "");
}
public final PrologQuery query(PrologTerm term, PrologTerm... terms) {
Iterator<PrologTerm> i = new ArrayIterator<PrologTerm>(terms);
StringBuilder buffer = new StringBuilder();
buffer.append("" + term + "");
while (i.hasNext()) {
buffer.append(',');
buffer.append(i.next());
}
buffer.append(".");
return query("" + buffer + "");
}
public final void operator(int priority, String specifier, String operator) {
new Query(consultCacheComma + "op(" + priority + "," + specifier + ", " + operator + ")").hasSolution();
}
public final boolean currentPredicate(String functor, int arity) {
String x = functor;
if (x.startsWith("'") && x.endsWith("'")) {
x = x.substring(1, x.length() - 1);
}
return getPredicates().contains(new JplIndicator(x, arity));
}
public final boolean currentOperator(int priority, String specifier, String operator) {
return new Query(consultCacheComma + "current_op(" + priority + "," + specifier + ", " + operator + ")")
.hasSolution();
}
public final Set<PrologOperator> currentOperators() {
Set<PrologOperator> operators = new HashSet<PrologOperator>();
String opQuery = consultCacheComma + "findall(P/S/O,current_op(P,S,O)," + KEY + ")";
Query query = new Query(opQuery);
if (query.hasSolution()) {
Term term = (Term) query.oneSolution().get(KEY);
Term[] termArray = term.toTermArray();
for (Term t : termArray) {
Term prio = t.arg(1).arg(1);
Term pos = t.arg(1).arg(2);
Term op = t.arg(2);
int p = prio.intValue();
String s = pos.name();
String n = op.name();
PrologOperator o = new JplOperator(p, s, n);
operators.add(o);
}
}
query.close();
return operators;
}
public final int getProgramSize() {
return program.size();
}
public final PrologProgram getProgram() {
return new JplScript(this);
}
public final Set<PrologIndicator> getPredicates() {
Set<PrologIndicator> indicators = new HashSet<PrologIndicator>();
String opQuery = consultCacheComma + "findall(X/Y,current_predicate(X/Y)," + KEY + ")";
Query query = new Query(opQuery);
if (query.hasSolution()) {
Term term = (Term) query.oneSolution().get(KEY);
Term[] termArray = term.toTermArray();
for (Term t : termArray) {
Term f = t.arg(1);
Term a = t.arg(2);
int arity = a.intValue();
String functor = f.name();
JplIndicator pi = new JplIndicator(functor, arity);
indicators.add(pi);
}
}
return indicators;
}
public final Set<PrologIndicator> getBuiltIns() {
Set<PrologIndicator> pis = predicates();
Set<PrologClause> clauses = getProgramClauses();
for (PrologClause prologClause : clauses) {
PrologIndicator pi = prologClause.getPrologIndicator();
if (pis.contains(pi)) {
pis.remove(pi);
}
}
return pis;
}
private Set<PrologIndicator> predicates() {
Set<PrologIndicator> indicators = new HashSet<PrologIndicator>();
String stringQuery = "consult('" + cache + "')," + "findall(X/Y,current_predicate(X/Y)," + KEY + ")";
PrologQuery query = new JplQuery(this, cache, stringQuery);
if (query.hasSolution()) {
Map<String, PrologTerm>[] s = query.allVariablesSolutions();
for (Map<String, PrologTerm> map : s) {
for (PrologTerm term : map.values()) {
if (term.isCompound()) {
int arity = term.getArity();
String functor = term.getFunctor();
JplIndicator pi = new JplIndicator(functor, arity);
indicators.add(pi);
}
}
}
}
return indicators;
}
public final Iterator<PrologClause> iterator() {
List<PrologClause> cls = new ArrayList<PrologClause>();
for (List<Term> family : program.getClauses().values()) {
for (Term clause : family) {
if (clause.hasFunctor(":-", 2)) {
PrologTerm head = toTerm(clause.arg(1), PrologTerm.class);
PrologTerm body = toTerm(clause.arg(2), PrologTerm.class);
if (body.getType() != PrologTermType.TRUE_TYPE) {
cls.add(new JplClause(provider, head, body, false, false, false));
} else {
cls.add(new JplClause(provider, head, false, false, false));
}
} else {
cls.add(new JplClause(provider, toTerm(clause, PrologTerm.class), false, false, false));
}
}
}
return new PrologProgramIterator(cls);
}
public final void dispose() {
File c = new File(cache);
PrintWriter writer = null;
try {
writer = new PrintWriter(new FileOutputStream(cache, false));
writer.print("");
} catch (FileNotFoundException e) {
getLogger().error(getClass(), IO + cache, e);
} finally {
if (writer != null) {
writer.close();
}
}
c.deleteOnExit();
program.clear();
}
public final String getCache() {
return cache;
}
@Override
public int hashCode() {
final int prime = 31;
int result = super.hashCode();
result = prime * result + ((program == null) ? 0 : program.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (!super.equals(obj))
return false;
if (getClass() != obj.getClass())
return false;
JplEngine other = (JplEngine) obj;
if (program == null) {
if (other.program != null)
return false;
} else if (!program.equals(other.program)) {
return false;
}
return true;
}
}