/* * Copyright 2014 Igor Maznitsa (http://www.igormaznitsa.com). * * Licensed 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 com.igormaznitsa.prol.libraries; import com.igormaznitsa.prol.data.NumericTerm; import com.igormaznitsa.prol.data.Operator; import com.igormaznitsa.prol.data.Term; import com.igormaznitsa.prol.data.TermInteger; import com.igormaznitsa.prol.data.TermList; import com.igormaznitsa.prol.data.TermStruct; import com.igormaznitsa.prol.data.Var; import com.igormaznitsa.prol.exceptions.ProlCriticalError; import com.igormaznitsa.prol.exceptions.ProlDomainErrorException; import com.igormaznitsa.prol.exceptions.ProlInstantiationErrorException; import com.igormaznitsa.prol.utils.Utils; import java.lang.reflect.Field; /** * The class describes a template for a predicate argument. A term of a * predicate will be checked with the template before execution. * * @author Igor Maznitsa (igor.maznitsa@igormaznitsa.com) * @see com.igormaznitsa.prol.annotations.Predicate */ public class PredicateTemplate { /** * atom -- an atom */ public static final int TYPE_ATOM = 0; /** * atom_or_atom_list -- an atom or a list of atoms */ public static final int TYPE_ATOM_OR_ATOM_LIST = 1; /** * atomic -- an atomic term */ public static final int TYPE_ATOMIC = 2; /** * byte -- a byte */ public static final int TYPE_BYTE = 3; /** * callable_term */ public static final int TYPE_CALLABLE_TERM = 4; /** * character -- a one char atom */ public static final int TYPE_CHARACTER = 5; /** * character_code -- a character code */ public static final int TYPE_CHARACTER_CODE = 6; /** * character_code_list -- a list of character codes */ public static final int TYPE_CHARACTER_CODE_LIST = 7; /** * character_list -- a list of characters */ public static final int TYPE_CHARACTER_LIST = 8; /** * clause */ public static final int TYPE_CLAUSE = 9; /** * close_options -- a list of close options */ public static final int TYPE_CLOSE_OPTIONS = 10; /** * compound_term */ public static final int TYPE_COMPOUND_TERM = 11; /** * evaluable -- an expression */ public static final int TYPE_EVALUABLE = 12; /** * flag -- a flag */ public static final int TYPE_FLAG = 13; /** * head of a clause */ public static final int TYPE_HEAD = 14; /** * in_byte a byte or -1 */ public static final int TYPE_IN_BYTE = 15; /** * in_character a one char atom or the atom end_of_file */ public static final int TYPE_IN_CHARACTER = 16; /** * in_character_code -- a character code or -1 */ public static final int TYPE_IN_CHARACTER_CODE = 17; /** * integer */ public static final int TYPE_INTEGER = 18; /** * io_mode -- an input output mode */ public static final int TYPE_IO_MODE = 19; /** * list */ public static final int TYPE_LIST = 20; /** * nonvar -- an atomic term or a compound term */ public static final int TYPE_NONVAR = 21; /** * number */ public static final int TYPE_NUMBER = 22; /** * operator_specifier -- one of the atoms xf, yf, xfx, xfy, yfx, fx, fy */ public static final int TYPE_OPERATOR_SPECIFIER = 23; /** * predicate_indicator */ public static final int TYPE_PREDICATE_INDICATOR = 24; /** * read_options_list a read options list */ public static final int TYPE_READ_OPTIONS = 25; /** * source_sink */ public static final int TYPE_SOURCE_SINK = 26; /** * stream */ public static final int TYPE_STREAM = 27; /** * stream_options -- a list of stream_options */ public static final int TYPE_STREAM_OPTIONS = 28; /** * stream_or_alias -- a stream or an alias */ public static final int TYPE_STREAM_OR_ALIAS = 29; /** * stream_position -- a steam position */ public static final int TYPE_STREAM_POSITION = 30; /** * stream_property -- a stream property */ public static final int TYPE_STREAM_PROPERTY = 31; /** * term */ public static final int TYPE_TERM = 32; /** * write_options_list -- a write options list */ public static final int TYPE_WRITE_OPTIONS_LIST = 33; /** * not_empty_list -- a list but null-list */ public static final int TYPE_NON_EMPTY_LIST = 34; /** * triggerevent -- a trigger event describer (onassert, onretract, * onassertretract) */ public static final int TYPE_TRIGGEREVENT = 35; //------------------------------------------------------------ /** * The argument shall be instantiated. (+) */ public static final int MODIFIER_SHALL_BE_INSTANTIATED = 0; /** * The argument shall remain unaltered. Unless the argument is a compound term * this is the same as the mode +. (@) */ public static final int MODIFIER_SHALL_REMAIN_UNALTERED = 1; /** * The argument shall be a variable that will be instantiated if the goal * succeeds. (-) */ public static final int MODIFIER_SHALL_BE_VARIABLE = 2; /** * The argument shall be instantiated or a variable. (?) The ISO standard * requires that 1. if the argument is instantiated, and 2. the type of the * instantiated argument is not that required by the template of the predicate * then an error is to be raised. */ public static final int MODIFIER_SHALL_BE_INSTANTIATED_OR_VARIABLE = 3; /** * The variable contains the type of the template */ public final int Type; /** * The variable contains the modifier of the template */ public final int Modifier; /** * A constructor allows to make the tempate for predefined modifier and type * * @param modifier the modifier index * @param type the type index */ public PredicateTemplate(final int modifier, final int type) { super(); this.Modifier = modifier; this.Type = type; } /** * A constructor allows to generate a template from a string defined as in the * "<modifier><template>" format * * @param string a String object contains packed string in the right format * @throws IllegalArgumentException if the string has a wrong format */ public PredicateTemplate(final String string) { super(); if (string == null || string.length() <= 3) { throw new IllegalArgumentException("Can\'t parse template parameter \'" + string + '\''); } switch (string.charAt(0)) { case '+': this.Modifier = MODIFIER_SHALL_BE_INSTANTIATED; break; case '@': this.Modifier = MODIFIER_SHALL_REMAIN_UNALTERED; break; case '-': this.Modifier = MODIFIER_SHALL_BE_VARIABLE; break; case '?': this.Modifier = MODIFIER_SHALL_BE_INSTANTIATED_OR_VARIABLE; break; default: throw new IllegalArgumentException("Unsupported template modifier at \'" + string + '\''); } final String template = "TYPE_" + string.substring(1).toUpperCase(); try { final Field field = this.getClass().getDeclaredField(template); this.Type = field.getInt(null); } catch (IllegalAccessException ex) { throw new Error("Illegal access error", ex); } catch (NoSuchFieldException ex) { throw new ProlCriticalError("Unsupported template \'" + string + '\''); } } /** * Check an argument for the template * * @param term the term to be checked * @return true if the term must not be changed during processing and false if * it can be changed */ @SuppressWarnings("unchecked") public final boolean check(final Term term) { final int type = term.getTermType(); switch (Modifier) { case MODIFIER_SHALL_REMAIN_UNALTERED: { if (type == Term.TYPE_LIST || type == Term.TYPE_STRUCT) { checkTermForTemplate(term); return false; } } case MODIFIER_SHALL_BE_INSTANTIATED: { if (type == Term.TYPE_VAR && ((Var) term).isUndefined()) { throw new ProlInstantiationErrorException("Should be instantiated \'" + term.getSourceLikeRepresentation() + '\'', term); } checkTermForTemplate(term); } break; case MODIFIER_SHALL_BE_INSTANTIATED_OR_VARIABLE: { // any if (type == Term.TYPE_VAR && ((Var) term).isUndefined()) { return false; } checkTermForTemplate(term); } break; case MODIFIER_SHALL_BE_VARIABLE: { if (type == Term.TYPE_VAR) { if (!((Var) term).isUndefined()) { throw new ProlInstantiationErrorException("Should not be instantiated \'" + term.getSourceLikeRepresentation() + '\'', term); } } else { throw new ProlInstantiationErrorException("Should be noninstantiated variable \'" + term.getSourceLikeRepresentation() + '\'', term); } return true; } default: throw new ProlCriticalError("Unknown template modifier"); } return false; } /** * Auxiliary function to check a term for compatibility with the template * * @param term a term to be checked, must not be null */ private void checkTermForTemplate(final Term term) { final Term checkAtom = Utils.getTermFromElement(term); switch (Type) { case TYPE_ATOM: { boolean error = true; switch (checkAtom.getTermType()) { case Term.TYPE_LIST: { error = !((TermList) checkAtom).isNullList(); } break; case Term.TYPE_ATOM: { error = checkAtom instanceof NumericTerm; } break; } if (error) { throw new ProlInstantiationErrorException("Should be atom \'" + term + '\'', term); } } break; case TYPE_ATOM_OR_ATOM_LIST: { if (checkAtom != null) { boolean error = true; switch (checkAtom.getTermType()) { case Term.TYPE_ATOM: { if (!(checkAtom instanceof NumericTerm)) { error = false; } } break; case Term.TYPE_LIST: { TermList lst = (TermList) checkAtom; error = false; if (lst == TermList.NULLLIST) { break; } while (true) { Term head = lst.getHead(); if (head.getTermType() == Term.TYPE_VAR) { head = ((Var) head).getValue(); if (head == null) { error = true; break; } } if (head.getTermType() != Term.TYPE_ATOM) { error = true; break; } final Term tail = lst.getTail(); if (tail == TermList.NULLLIST) { break; } if (tail.getTermType() == Term.TYPE_LIST) { lst = (TermList) tail; } else { error = true; break; } } } break; } if (error) { throw new ProlInstantiationErrorException("Should be atom or atom list \'" + term + '\'', term); } } } break; case TYPE_ATOMIC: { if (checkAtom != null) { boolean errorresult = false; switch (checkAtom.getTermType()) { case Term.TYPE_LIST: { errorresult = !((TermList) checkAtom).isNullList(); } break; case Term.TYPE_ATOM: { } break; default: { errorresult = true; } break; } if (errorresult) { throw new ProlInstantiationErrorException("Should be atomic \'" + term + '\'', term); } } } break; case TYPE_BYTE: { if (checkAtom != null) { boolean error = true; if (checkAtom instanceof TermInteger) { final int value = ((TermInteger) checkAtom).getNumericValue().intValue(); if ((value & 0xFF) == 0) { error = false; } } if (error) { throw new ProlInstantiationErrorException("Should be byte \'" + term + '\'', term); } } } break; case TYPE_CALLABLE_TERM: { if (checkAtom != null) { boolean error = true; final int typeAtom = checkAtom.getTermType(); if (typeAtom == Term.TYPE_ATOM) { if (!(checkAtom instanceof NumericTerm)) { error = false; } } else if (typeAtom == Term.TYPE_STRUCT) { error = false; } if (error) { throw new ProlInstantiationErrorException("Should be callable term \'" + term + '\'', term); } } } break; case TYPE_CHARACTER: { if (checkAtom != null) { boolean error = true; if (checkAtom.getTermType() == Term.TYPE_ATOM && checkAtom.getText().length() == 1) { error = false; } if (error) { throw new ProlInstantiationErrorException("Should be character \'" + term + '\'', term); } } } break; case TYPE_CHARACTER_CODE: { if (checkAtom != null) { boolean error = true; if (checkAtom instanceof TermInteger) { final int value = ((TermInteger) checkAtom).getNumericValue().intValue(); if ((value & 0xFFFF0000) == 0) { error = false; } } if (error) { throw new ProlInstantiationErrorException("Should be character code \'" + term + '\'', term); } } } break; case TYPE_CHARACTER_CODE_LIST: { boolean error = false; if (checkAtom != null) { if (checkAtom.getTermType() == Term.TYPE_LIST) { TermList lst = (TermList) checkAtom; error = false; if (lst == TermList.NULLLIST) { break; } while (true) { Term head = lst.getHead(); if (head.getTermType() == Term.TYPE_VAR) { head = ((Var) head).getValue(); if (head == null) { error = true; break; } } if (head.getTermType() == Term.TYPE_ATOM) { if (head instanceof TermInteger) { if ((((TermInteger) head).getNumericValue().intValue() & 0xFFFF0000) != 0) { error = true; break; } } else { error = true; break; } } else { error = true; break; } final Term tail = lst.getTail(); if (tail == TermList.NULLLIST) { break; } if (tail.getTermType() == Term.TYPE_LIST) { lst = (TermList) tail; } else { error = true; break; } } } else { error = true; } } if (error) { throw new ProlInstantiationErrorException("Should be character code list \'" + term + '\'', term); } } break; case TYPE_CHARACTER_LIST: { boolean error = false; if (checkAtom != null) { if (checkAtom.getTermType() == Term.TYPE_LIST) { TermList lst = (TermList) checkAtom; error = false; if (lst == TermList.NULLLIST) { break; } while (true) { Term head = lst.getHead(); if (head.getTermType() == Term.TYPE_VAR) { head = ((Var) head).getValue(); if (head == null) { error = true; break; } } if (head.getTermType() == Term.TYPE_ATOM) { if (head.getText().length() != 1) { error = true; break; } } else { error = true; break; } final Term tail = lst.getTail(); if (tail == TermList.NULLLIST) { break; } if (tail.getTermType() == Term.TYPE_LIST) { lst = (TermList) tail; } else { error = true; break; } } } else { error = true; } } if (error) { throw new ProlInstantiationErrorException("Should be character code list \'" + term + '\'', term); } } break; case TYPE_CLAUSE: { if (checkAtom != null) { boolean error = false; switch (checkAtom.getTermType()) { case Term.TYPE_ATOM: { if (checkAtom instanceof NumericTerm) { error = true; } } break; case Term.TYPE_STRUCT: { final TermStruct struct = (TermStruct) checkAtom; final Term functor = struct.getFunctor(); final boolean rule = struct.isFunctorLikeRuleDefinition(); final int functorType = functor.getTermType(); // check left part if (rule) { final Term left = struct.getElement(0); switch (left.getTermType()) { case Term.TYPE_ATOM: { if (left instanceof NumericTerm) { error = true; } } break; case Term.TYPE_LIST: { error = true; } break; case Term.TYPE_VAR: { error = true; } break; } } else { switch (functorType) { case Term.TYPE_ATOM: { if (functor instanceof NumericTerm) { error = true; } } break; case Term.TYPE_LIST: { error = true; } break; case Term.TYPE_VAR: { error = true; } break; } } } break; default: { error = true; } break; } if (error) { throw new ProlInstantiationErrorException("Should be clause or atom \'" + term + '\'', term); } } } break; case TYPE_COMPOUND_TERM: { if (checkAtom != null) { switch (checkAtom.getTermType()) { case Term.TYPE_LIST: case Term.TYPE_STRUCT: { } break; default: throw new ProlInstantiationErrorException("Should be compound term \'" + term + '\'', term); } } } break; case TYPE_EVALUABLE: { if (checkAtom != null) { boolean error = true; if (checkAtom instanceof NumericTerm) { error = false; } else { if (checkAtom.getTermType() == Term.TYPE_STRUCT) { final TermStruct struct = (TermStruct) checkAtom; final PredicateProcessor processor = struct.getPredicateProcessor(); if (processor.isEvaluable()) { error = false; } } } if (error) { throw new ProlInstantiationErrorException("Should be evaluable \'" + term + '\'', term); } } } break; case TYPE_HEAD: { if (checkAtom != null) { boolean error = true; switch (checkAtom.getTermType()) { case Term.TYPE_ATOM: { if (!(checkAtom instanceof NumericTerm)) { error = false; } } break; case Term.TYPE_STRUCT: { final Term functor = ((TermStruct) checkAtom).getFunctor(); if (functor.getTermType() == Term.TYPE_ATOM) { error = false; } } break; } if (error) { throw new ProlInstantiationErrorException("Imcompatible clause head", term); } } } break; case TYPE_IN_BYTE: { boolean error = false; if (checkAtom != null) { if (checkAtom instanceof TermInteger) { final int val = ((TermInteger) checkAtom).getNumericValue().intValue(); if ((val & 0xFF) != 0 && val == -1) { error = true; } } else { error = true; } } if (error) { throw new ProlInstantiationErrorException("Should be byte or -1 \'" + term + '\'', term); } } break; case TYPE_IN_CHARACTER: { boolean error = false; if (checkAtom != null) { if (checkAtom.getTermType() == Term.TYPE_ATOM) { final String text = checkAtom.getText(); if (text.length() != 1 && !"end_of_file".equals(text)) { error = true; } } else { error = true; } } if (error) { throw new ProlInstantiationErrorException("Should be character code or -1 \'" + term + '\'', term); } } break; case TYPE_IN_CHARACTER_CODE: { boolean error = false; if (checkAtom != null) { if (checkAtom instanceof TermInteger) { final int val = ((TermInteger) checkAtom).getNumericValue().intValue(); if ((val & 0xFFFF0000) != 0 && val != -1) { error = true; } } else { error = true; } } if (error) { throw new ProlInstantiationErrorException("Should be character code or -1 \'" + term + '\'', term); } } break; case TYPE_INTEGER: { if (checkAtom != null && !(checkAtom instanceof TermInteger)) { throw new ProlInstantiationErrorException("Should be integer \'" + term + '\'', term); } } break; case TYPE_IO_MODE: { if (checkAtom != null) { boolean error = true; if (checkAtom.getTermType() == Term.TYPE_ATOM) { final String text = checkAtom.getText(); if (text.equals("read") || text.equals("write") || text.equals("append")) { error = false; } } if (error) { throw new ProlInstantiationErrorException("Should be 'read', 'write' or 'append' [" + term + ']', term); } } } break; case TYPE_LIST: { if (checkAtom != null) { if (checkAtom.getTermType() != Term.TYPE_LIST) { throw new ProlInstantiationErrorException("Should be list \'" + term + '\'', term); } } } break; case TYPE_NON_EMPTY_LIST: { if (checkAtom != null) { if (checkAtom.getTermType() != Term.TYPE_LIST) { throw new ProlInstantiationErrorException("Should be list \'" + term + '\'', term); } else { if (checkAtom == TermList.NULLLIST) { throw new ProlInstantiationErrorException("Should not be empty list \'" + term + '\'', term); } } } } break; case TYPE_TRIGGEREVENT: { if (checkAtom != null) { if (checkAtom.getTermType() != Term.TYPE_ATOM) { throw new ProlInstantiationErrorException("Should be an atom \'" + term + '\'', term); } else { final String value = checkAtom.getText(); if (!"onassert".equals(value) && !"onretract".equals(value) && !"onassertretract".equals(value)) { throw new ProlDomainErrorException("Should be a value from the list [onassert, onretract, onassertretract] \'" + term + '\'', term); } } } } break; case TYPE_NONVAR: { if (checkAtom == null) { throw new ProlInstantiationErrorException("Should be nonvar \'" + term + '\'', term); } } break; case TYPE_NUMBER: { if (checkAtom != null) { if (!(checkAtom instanceof NumericTerm)) { throw new ProlInstantiationErrorException("Should be number \'" + term + '\'', term); } } } break; case TYPE_OPERATOR_SPECIFIER: { boolean error; if (checkAtom != null) { if (checkAtom.getTermType() == Term.TYPE_ATOM && !(checkAtom instanceof NumericTerm)) { final String text = checkAtom.getText(); error = true; if (Operator.getTypeFromString(text) >= 0) { error = false; } } else { error = true; } } else { error = true; } if (error) { throw new ProlDomainErrorException("Should be only [xfx,yfx,xfy,xf,fx,yf,fy] but \'" + term + '\'', term); } } break; case TYPE_PREDICATE_INDICATOR: { if (checkAtom != null) { boolean error = true; switch (checkAtom.getTermType()) { case Term.TYPE_STRUCT: { error = Utils.extractPredicateSignatureFromStructure((TermStruct) checkAtom) == null; } break; } if (error) { throw new ProlInstantiationErrorException("Should be predicate indicator \'" + term + '\'', term); } } } break; case TYPE_TERM: { // any term is term } break; case TYPE_FLAG: case TYPE_CLOSE_OPTIONS: case TYPE_READ_OPTIONS: case TYPE_SOURCE_SINK: case TYPE_STREAM: case TYPE_STREAM_OPTIONS: case TYPE_STREAM_OR_ALIAS: case TYPE_STREAM_POSITION: case TYPE_STREAM_PROPERTY: case TYPE_WRITE_OPTIONS_LIST: default: throw new ProlCriticalError("Unknown or nonimplemented template type"); } } }