
com.igormaznitsa.prologparser.tokenizer.Op Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of java-prolog-parser Show documentation
Show all versions of java-prolog-parser Show documentation
It is a handwritten prolog parser, it allows to parse prolog sources written in Edinburgh Prolog style
The newest version!
/*
* Copyright (c) 2011-2018 Igor Maznitsa. All rights reserved.
*
* 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 com.igormaznitsa.prologparser.tokenizer;
import static com.igormaznitsa.prologparser.PrologParser.findBaseMetaOperator;
import static com.igormaznitsa.prologparser.tokenizer.OpAssoc.FX;
import static com.igormaznitsa.prologparser.tokenizer.OpAssoc.FY;
import static com.igormaznitsa.prologparser.tokenizer.OpAssoc.XFX;
import static com.igormaznitsa.prologparser.tokenizer.OpAssoc.XFY;
import static com.igormaznitsa.prologparser.tokenizer.OpAssoc.YFX;
import static java.util.Objects.requireNonNull;
import static java.util.stream.Stream.of;
import com.igormaznitsa.prologparser.exceptions.CriticalUnexpectedError;
import com.igormaznitsa.prologparser.terms.PrologStruct;
import com.igormaznitsa.prologparser.terms.PrologTerm;
import com.igormaznitsa.prologparser.terms.Quotation;
import com.igormaznitsa.prologparser.terms.TermType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* Prolog operator definition.
*/
@SuppressWarnings("unused")
public final class Op extends PrologTerm {
/**
* Highest allowed priority of operator.
*/
public static final int PRECEDENCE_MAX = 0;
/**
* Lowest allowed priority of operator.
*/
public static final int PRECEDENCE_MIN = 1200;
public static final Op GNU_UNARY_PLUS = make(200, FY, "+");
public static final Op ISO_BITWISE_NEGATION = make(200, FY, "\\");
public static final Op ISO_UNARY_MINUS = make(200, FY, "-");
public static final Op ISO_UNIFICATION = make(700, XFX, "=", "\\=", "=..");
public static final Op ISO_BITWISE_SHIFT = make(400, YFX, "<<", ">>");
public static final Op ISO_ORDER_TERM =
make(700, OpAssoc.XFX, "==", "\\==", "@<", "@=<", "@>", "@>=");
public static final Op ISO_ORDER_ARITH =
make(700, XFX, "<", "=<", ">", ">=", "=:=", "=\\=", "is");
public static final Op ISO_ARITH_PLUS_MINUS = make(500, YFX, "+", "-");
public static final Op ISO_ARITH_MUL_DIV = make(400, YFX, "*", "/");
public static final Op ISO_ARITH_POWER = make(200, OpAssoc.XFX, "**");
public static final Op ISO_ARITH_DIVIDE = make(400, OpAssoc.YFX, "//", "mod", "rem");
public static final Op ISO_NEGATE = make(900, OpAssoc.FY, "\\+");
public static final Op ISO_CLAUSES = make(1200, OpAssoc.XFX, ":-", "-->");
public static final Op ISO_DIRECTIVES = make(1200, OpAssoc.FX, "?-", ":-");
public static final Op ISO_BITWISE_AND_OR = make(500, OpAssoc.YFX, "/\\", "\\/");
public static final Op MODIFIERS = make(1150, OpAssoc.FX,
"public",
"dynamic",
"volatile",
"discontiguous",
"multifile",
"initialization"
);
/**
* Set of operators specific for Sictus Prolog.
*/
public static final List SICTUS_SPECIFIC =
List.of(MODIFIERS, make(1150, FX, "mode", "block", "meta_predicate"), make(1100, XFY, "do"),
make(900, FY, "spy", "nospy"), make(550, XFY, ":"), make(500, YFX, "\\"), GNU_UNARY_PLUS);
public static final Op GNU_DIV_RDIV = make(400, OpAssoc.YFX, "div", "rdiv");
public static final Op ISO_OR = make(1100, OpAssoc.XFY, ";");
public static final Op ISO_THEN = make(1050, OpAssoc.XFY, "->");
/**
* Set of operators for ISO Prolog standard.
*/
public static final List ISO =
List.of(ISO_CLAUSES, ISO_DIRECTIVES, ISO_OR, ISO_THEN, ISO_NEGATE, ISO_UNIFICATION,
ISO_ORDER_ARITH, ISO_ORDER_TERM, ISO_ARITH_PLUS_MINUS, ISO_BITWISE_AND_OR,
ISO_ARITH_MUL_DIV, ISO_BITWISE_SHIFT, ISO_ARITH_DIVIDE, ISO_ARITH_POWER,
make(200, OpAssoc.XFY, "^"), ISO_UNARY_MINUS, ISO_BITWISE_NEGATION,
make(100, OpAssoc.XFX, "@"));
public static final Op GNU_STAR_THEN = make(1050, OpAssoc.XFY, "*->");
public static final Op GNU_DOUBLE_DOT = make(600, OpAssoc.XFY, ":");
/**
* Set of operators is specific for GNU Prolog use.
*/
public static final List GNU_SPECIFIC =
List.of(GNU_STAR_THEN, GNU_DOUBLE_DOT, GNU_DIV_RDIV, GNU_UNARY_PLUS);
/**
* Set of operators for GNU Prolog.
*/
public static final List GNU = Collections.unmodifiableList(Op.join(ISO, GNU_SPECIFIC));
/**
* Set of operators is specific for SWI Prolog use.
*/
public static final List SWI_SPECIFIC = List.of(MODIFIERS,
make(1150, OpAssoc.FX, "meta_predicate", "module_transparent", "thread_local",
"thread_initialization"), GNU_STAR_THEN, make(990, OpAssoc.FY, ":="),
make(700, OpAssoc.XFX, "=@=", "\\=@=", "as", ">:<", ":<"), GNU_DOUBLE_DOT,
make(500, OpAssoc.YFX, "xor"), make(500, OpAssoc.FX, "?"), GNU_DIV_RDIV, GNU_UNARY_PLUS,
make(1, OpAssoc.FX, "$"));
/**
* Set of operators for SWI Prolog.
*/
public static final List SWI = Collections.unmodifiableList(Op.join(ISO, SWI_SPECIFIC));
/**
* Set of Finite Domain operators for GNU Prolog.
*/
public static final List GNU_FD =
List.of(make(750, XFY, "#<=>", "#\\<=>"), make(740, XFY, "#==>", "#\\==>"),
make(730, XFY, "##"), make(730, YFX, "#\\/", "#\\\\/"), make(720, YFX, "#/\\", "#\\/\\"),
make(710, FY, "#\\"),
make(700, XFX, "#=", "#\\=", "#<", "#=<", "#>", "#>=", "#=#", "#\\=#", "#<#", "#=<#",
"#>#",
"#>=#"));
/**
* Set of Constraint Logic Programming operators for SWI Prolog.
*/
public static final List SWI_CPL =
List.of(make(300, FY, "~"), make(500, YFX, "#"), make(760, YFX, "#<==>"),
make(750, XFY, "#==>"), make(750, YFX, "#<=="), make(740, YFX, "#\\/"),
make(730, YFX, "#\\"), make(720, YFX, "#/\\"), make(710, FY, "#\\"),
make(700, XFX, "#>", "#<", "#>=", "#=<", "#=", "#\\=", "in", "ins"),
make(450, XFX, ".."));
public static final Op VIRTUAL_OPERATOR_BLOCK = makeSystem(-1, OpAssoc.FX, "()");
public static final Op VIRTUAL_OPERATOR_CURLY_BLOCK = makeSystem(-1, OpAssoc.FX, "{}");
public static final Op METAOPERATOR_COMMA = makeSystem(1000, OpAssoc.XFY, ",");
public static final Op METAOPERATOR_LEFT_BRACKET = makeSystem(-1, OpAssoc.FX, "(");
public static final Op METAOPERATOR_LEFT_CURLY_BRACKET = makeSystem(-1, OpAssoc.FX, "{");
public static final Op METAOPERATOR_RIGHT_BRACKET = makeSystem(-1, OpAssoc.XF, ")");
public static final Op METAOPERATOR_RIGHT_CURLY_BRACKET = makeSystem(-1, OpAssoc.XF, "}");
public static final Op METAOPERATOR_LEFT_SQUARE_BRACKET = makeSystem(-1, OpAssoc.FX, "[");
public static final Op METAOPERATOR_RIGHT_SQUARE_BRACKET = makeSystem(-1, OpAssoc.XF, "]");
public static final Op METAOPERATOR_DOT = makeSystem(Integer.MAX_VALUE, OpAssoc.XF, ".");
public static final Op METAOPERATOR_VERTICAL_BAR = makeSystem(1105, OpAssoc.XFY, "|");
private static final long serialVersionUID = -5914313127778138548L;
private final OpAssoc opAssoc;
private final int precedence;
private final int preparedHash;
private final String[] multiNames;
private Op(final int precedence, final OpAssoc type, final String name,
final String[] multiNames) {
super(name, Quotation.NONE);
assertOpValidOpName(name);
this.multiNames = multiNames;
this.opAssoc = type;
this.precedence = precedence;
this.preparedHash = (name + '/' + this.opAssoc.name() + '/' + this.precedence).hashCode();
}
private static String[] assertOpValidOpName(final String[] names) {
Arrays.stream(names).forEach(Op::assertOpValidOpName);
return names;
}
private static String assertOpValidOpName(final String name) {
final char firstChar = assertNonEmptyString(name).charAt(0);
if (Character.isWhitespace(firstChar) || Character.isISOControl(firstChar)) {
throw new IllegalArgumentException("Space char as first one");
}
if (Character.isUpperCase(firstChar)) {
throw new IllegalArgumentException("Capital char as first one");
}
if (firstChar == '_') {
throw new IllegalArgumentException("'_' can't be firs char");
}
return name;
}
/**
* Make operator descriptor describing a bunch of operators with same
* characteristics but differently named.
*
* @param precedence the precedence
* @param type the type of operators
* @param names names of operators, must not be empty or contain null
* @return generated operator descriptor
* @see OpAssoc
*/
public static Op make(final int precedence, final OpAssoc type, final String... names) {
if (precedence < PRECEDENCE_MAX || precedence > PRECEDENCE_MIN) {
throw new IllegalArgumentException("Precedence must be in 0..1200");
}
requireNonNull(type);
requireNonNull(names);
if (names.length == 0) {
throw new IllegalArgumentException("Operator name must be defined");
}
return names.length == 1 ? new Op(precedence, type, requireNonNull(names[0]), null)
: new Op(precedence, type, ".multi.", assertOpValidOpName(names));
}
public static Op makeSystem(final int precedence, final OpAssoc type, final String... names) {
requireNonNull(type);
requireNonNull(names);
if (names.length == 0) {
throw new IllegalArgumentException("Operator name must be defined");
}
return names.length == 1 ? new Op(precedence, type, assertOpValidOpName(names[0]), null)
: new Op(precedence, type, ".system.", assertOpValidOpName(names));
}
@SafeVarargs
public static List join(final List... args) {
final List result = new ArrayList<>();
for (final List l : args) {
result.addAll(l);
}
return result;
}
public static Op[] add(final Op[] args, final Op... ops) {
final int newLen = args.length + ops.length;
final Op[] result = Arrays.copyOf(args, newLen);
int sourceIndex = 0;
for (int i = args.length; i < newLen; i++) {
result[i] = ops[sourceIndex++];
}
return result;
}
public boolean isMultiName() {
return this.multiNames != null;
}
public Stream streamOp() {
if (this.multiNames == null) {
return of(this);
} else {
return of(this.multiNames).map(x -> new Op(this.precedence, this.opAssoc, x, null));
}
}
@Override
public int getArity() {
return this.opAssoc.getArity();
}
@Override
public TermType getType() {
return TermType.OPERATOR;
}
public OpAssoc getAssoc() {
return this.opAssoc;
}
@Override
public int getPrecedence() {
return this.precedence;
}
public boolean isCompatibleWith(final PrologStruct struct) {
final boolean result;
if (struct != null) {
switch (struct.getArity()) {
case 1: {
switch (this.opAssoc) {
case XFY:
case XFX:
case YFX: {
result = false;
}
break;
case XF:
case FX: {
final PrologTerm atom = struct.getTermAt(0);
result = atom != null && atom.getPrecedence() < getPrecedence();
}
break;
case YF:
case FY: {
final PrologTerm atom = struct.getTermAt(0);
result = atom != null && atom.getPrecedence() <= getPrecedence();
}
break;
default: {
throw new CriticalUnexpectedError();
}
}
}
break;
case 2: {
switch (this.opAssoc) {
case XFY:
case XFX:
case YFX: {
final PrologTerm elementLeft = struct.getTermAt(0);
final PrologTerm elementRight = struct.getTermAt(1);
if (elementLeft == null || elementRight == null) {
result = false;
} else {
switch (this.opAssoc) {
case XFX: {
result = elementLeft.getPrecedence() < getPrecedence()
&& elementRight.getPrecedence() < getPrecedence();
}
break;
case YFX: {
result = elementLeft.getPrecedence() <= getPrecedence()
&& elementRight.getPrecedence() < getPrecedence();
}
break;
case XFY: {
result = elementLeft.getPrecedence() < getPrecedence()
&& elementRight.getPrecedence() <= getPrecedence();
}
break;
default: {
result = false;
}
break;
}
}
}
break;
case XF:
case FX: {
final PrologTerm atom = struct.getTermAt(this.opAssoc == OpAssoc.XF ? 0 : 1);
result = atom != null && atom.getPrecedence() < getPrecedence();
}
break;
case YF:
case FY: {
final PrologTerm atom = struct.getTermAt(this.opAssoc == OpAssoc.YF ? 0 : 1);
result = atom != null && atom.getPrecedence() <= getPrecedence();
}
break;
default: {
throw new CriticalUnexpectedError();
}
}
}
break;
default: {
result = false;
}
break;
}
} else {
result = false;
}
return result;
}
@Override
public int hashCode() {
return this.preparedHash;
}
@Override
public boolean equals(final Object obj) {
boolean result = false;
if (this == obj) {
result = true;
} else if (obj instanceof Op) {
final Op op = (Op) obj;
if (this.preparedHash == op.preparedHash
&& this.precedence == op.precedence
&& this.opAssoc == op.opAssoc
&& this.text.equals(op.text)) {
result = true;
}
}
return result;
}
@Override
public String toString() {
if (isMultiName()) {
return String.format("op(%d, %s, [%s]).", getPrecedence(),
getAssoc().toString().toLowerCase(Locale.ENGLISH),
of(this.multiNames).map(x -> '\'' + x + '\'').collect(Collectors.joining(",")));
} else {
return String.format("op(%d, %s, '%s').", getPrecedence(),
getAssoc().toString().toLowerCase(Locale.ENGLISH), getText());
}
}
private Object readResolve() {
final Object result = findBaseMetaOperator(this.text, this.opAssoc);
return result == null ? this : result;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy