All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.igormaznitsa.prologparser.PrologParser Maven / Gradle / Ivy

Go to download

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;

import static com.igormaznitsa.prologparser.ParserContext.FLAG_DOT2_AS_LIST;
import static com.igormaznitsa.prologparser.ParserContext.FLAG_NONE;
import static com.igormaznitsa.prologparser.ParserContext.FLAG_VAR_AS_FUNCTOR;
import static com.igormaznitsa.prologparser.ParserContext.FLAG_ZERO_STRUCT;
import static com.igormaznitsa.prologparser.tokenizer.Op.METAOPERATOR_COMMA;
import static com.igormaznitsa.prologparser.tokenizer.Op.METAOPERATOR_VERTICAL_BAR;
import static com.igormaznitsa.prologparser.utils.Koi7CharOpMap.ofOps;
import static java.util.Objects.requireNonNull;

import com.igormaznitsa.prologparser.exceptions.CriticalUnexpectedError;
import com.igormaznitsa.prologparser.exceptions.PrologParserException;
import com.igormaznitsa.prologparser.terms.OpContainer;
import com.igormaznitsa.prologparser.terms.PrologAtom;
import com.igormaznitsa.prologparser.terms.PrologList;
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 com.igormaznitsa.prologparser.tokenizer.Op;
import com.igormaznitsa.prologparser.tokenizer.OpAssoc;
import com.igormaznitsa.prologparser.tokenizer.Tokenizer;
import com.igormaznitsa.prologparser.tokenizer.TokenizerResult;
import com.igormaznitsa.prologparser.utils.Koi7CharOpMap;
import java.io.IOException;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;

/**
 * Abstract base Prolog parser.
 */
@SuppressWarnings({"GrazieInspection", "unused"})
public abstract class PrologParser implements Iterable, AutoCloseable {

  public static final PrologTerm[] EMPTY_TERM_ARRAY = new PrologTerm[0];
  protected static final Koi7CharOpMap META_OP_MAP;
  private static final OpContainer OPERATOR_COMMA;
  private static final OpContainer OPERATOR_LEFTBRACKET;
  private static final OpContainer OPERATOR_LEFTCURLYBRACKET;
  private static final OpContainer OPERATOR_RIGHTBRACKET;
  private static final OpContainer OPERATOR_RIGHTCURLYBRACKET;
  private static final OpContainer OPERATOR_RIGHTSQUAREBRACKET;
  private static final OpContainer OPERATOR_DOT;
  private static final OpContainer OPERATOR_VERTICALBAR;
  private static final Koi7CharOpMap OPERATORS_PHRASE;
  private static final Koi7CharOpMap OPERATORS_INSIDE_LIST;
  private static final Koi7CharOpMap OPERATORS_END_LIST;
  private static final Koi7CharOpMap OPERATORS_INSIDE_STRUCT;
  private static final Koi7CharOpMap OPERATORS_SUBBLOCK;
  private static final Koi7CharOpMap OPERATORS_SUBBLOCK_CURLY;

  static {
    META_OP_MAP = ofOps();

    OPERATOR_DOT = META_OP_MAP.add(Op.METAOPERATOR_DOT);
    OPERATOR_LEFTBRACKET = META_OP_MAP.add(Op.METAOPERATOR_LEFT_BRACKET);
    OPERATOR_LEFTCURLYBRACKET = META_OP_MAP.add(Op.METAOPERATOR_LEFT_CURLY_BRACKET);
    OPERATOR_RIGHTBRACKET = META_OP_MAP.add(Op.METAOPERATOR_RIGHT_BRACKET);
    OPERATOR_RIGHTCURLYBRACKET = META_OP_MAP.add(Op.METAOPERATOR_RIGHT_CURLY_BRACKET);
    META_OP_MAP.add(Op.METAOPERATOR_LEFT_SQUARE_BRACKET);
    OPERATOR_RIGHTSQUAREBRACKET = META_OP_MAP.add(Op.METAOPERATOR_RIGHT_SQUARE_BRACKET);
    OPERATOR_VERTICALBAR = META_OP_MAP.add(Op.METAOPERATOR_VERTICAL_BAR);
    OPERATOR_COMMA = META_OP_MAP.add(METAOPERATOR_COMMA);

    OPERATORS_PHRASE = ofOps(OPERATOR_DOT);
    OPERATORS_INSIDE_LIST =
        ofOps(OPERATOR_COMMA, OPERATOR_RIGHTSQUAREBRACKET, OPERATOR_VERTICALBAR);
    OPERATORS_END_LIST = ofOps(OPERATOR_RIGHTSQUAREBRACKET);
    OPERATORS_INSIDE_STRUCT = ofOps(OPERATOR_COMMA, OPERATOR_RIGHTBRACKET);
    OPERATORS_SUBBLOCK = ofOps(OPERATOR_RIGHTBRACKET);
    OPERATORS_SUBBLOCK_CURLY = ofOps(OPERATOR_RIGHTCURLYBRACKET);
  }

  protected final ParserContext context;
  protected final int parserFlags;
  private final Tokenizer tokenizer;
  private final List commentTokenListeners;
  private PrologTermReadResult deferredReadTerm;

  protected PrologParser(
      final Reader source,
      final ParserContext context,
      final List tokenizedCommentListeners
  ) {
    this.context = context == null ? DefaultParserContext.of(ParserContext.FLAG_NONE) : context;
    this.parserFlags = context == null ? FLAG_NONE : context.getFlags();
    this.tokenizer = new Tokenizer(this, META_OP_MAP, requireNonNull(source));
    this.commentTokenListeners = List.copyOf(tokenizedCommentListeners);
  }

  public static Op findBaseMetaOperator(final String text, final OpAssoc type) {
    if (text.length() != 1) {
      return null;
    }

    OpContainer container = META_OP_MAP.get(text);

    if (container == null) {
      container = META_OP_MAP.get(text);
    }

    Op result = null;

    if (container != null) {
      result = container.findForType(type);
    }

    return result;
  }

  private static int getOnlyCharCode(final String text) {
    if (text == null || text.length() != 1) {
      return -1;
    } else {
      return text.charAt(0);
    }
  }

  public static Koi7CharOpMap findMetaOps() {
    return Koi7CharOpMap.copyOf(META_OP_MAP);
  }

  private static boolean isComment(final TokenizerResult tokenizerResult) {
    return tokenizerResult != null
        && tokenizerResult.getResult().getType() == TermType.ATOM
        && (tokenizerResult.getResult().getQuotation() == Quotation.COMMENT_LINE
        || tokenizerResult.getResult().getQuotation() == Quotation.COMMENT_BLOCK);
  }

  /**
   * Access to the internal tokenizer, it is mainly for test purposes because parser makes reading into internal buffers.
   *
   * @return the internal tokenizer in use by the parser
   */
  public Tokenizer getInternalTokenizer() {
    return this.tokenizer;
  }

  private boolean isEndOperator(final PrologTerm operator, final Koi7CharOpMap endOperators) {
    if (operator == null) {
      return true;
    }

    if (endOperators == null) {
      return false;
    }

    return operator.getType() == TermType.OPERATOR && endOperators.contains(operator.getText());
  }

  public ParserContext getContext() {
    return context;
  }

  private void notifyCommentTokenListeners(final TokenizerResult commentToken) {
    for (final TokenizedCommentListener listener : this.commentTokenListeners) {
      listener.onCommentToken(this, commentToken);
    }
  }

  private TokenizerResult extractNextTokenCommentAware() {
    TokenizerResult result;
    while (true) {
      result = this.tokenizer.readNextToken();
      if (isComment(result)) {
        this.notifyCommentTokenListeners(result);
      } else {
        break;
      }
    }
    return result;
  }

  private PrologTermReadResult extractNextBlockAndWrapError() {
    try {
      final PrologTerm found = this.readBlock(OPERATORS_PHRASE);
      if (found == null) {
        throw new NoSuchElementException("No terms in source");
      } else {
        final TokenizerResult endAtom = this.extractNextTokenCommentAware();
        if (endAtom == null || !endAtom.getResult().getText().equals(OPERATOR_DOT.getText())) {
          throw new PrologParserException("End operator is not found",
              this.tokenizer.getLine(),
              this.tokenizer.getPos());
        }
      }
      return new PrologTermReadResult(found, null);
    } catch (RuntimeException ex) {
      return new PrologTermReadResult(null, ex);
    }
  }

  public boolean hasNext() {
    if (this.deferredReadTerm == null) {
      this.deferredReadTerm = this.extractNextBlockAndWrapError();
    }
    return this.deferredReadTerm.isPresented();
  }

  public PrologTerm next() {
    if (this.deferredReadTerm == null) {
      this.deferredReadTerm = this.extractNextBlockAndWrapError();
    }
    final PrologTerm result;
    try {
      result = this.deferredReadTerm.getResult();
    } finally {
      this.deferredReadTerm = null;
    }
    return result;
  }

  private PrologStruct readStruct(final PrologTerm functor) {
    final List listOfAtoms = new ArrayList<>();
    PrologStruct result;
    boolean active = true;
    while (active) {
      final PrologTerm block = this.readBlock(OPERATORS_INSIDE_STRUCT);
      if (block == null) {
        return null;
      }

      final TokenizerResult nextAtom = this.extractNextTokenCommentAware();
      if (nextAtom == null) {
        throw new PrologParserException("Can't read next token in block", this.tokenizer.getLine(),
            this.tokenizer.getPos());
      }

      final String nextText = nextAtom.getResult().getText();

      switch (getOnlyCharCode(nextText)) {
        case ',': {
          listOfAtoms.add(block);
        }
        break;
        case ')': {
          listOfAtoms.add(block);
          active = false;
        }
        break;
        default:
          throw new PrologParserException("Unexpected term in structure: " + nextText,
              nextAtom.getLine(), nextAtom.getPos());
      }
    }

    result = new PrologStruct(functor, listOfAtoms.toArray(EMPTY_TERM_ARRAY));
    return result;
  }

  private PrologTerm readList(final TokenizerResult openingBracket) {
    PrologList leftPart = new PrologList();
    PrologList leftPartFirst = leftPart;
    PrologTerm rightPart = null;

    boolean hasSeparator = false;
    boolean continueReading = true;

    while (continueReading) {
      final PrologTerm block = readBlock(OPERATORS_INSIDE_LIST);

      final TokenizerResult nextAtom = this.extractNextTokenCommentAware();
      if (nextAtom == null) {
        throw new PrologParserException("Can't read next token in list", this.tokenizer.getLine(),
            this.tokenizer.getPos());
      }

      final String text = nextAtom.getResult().getText();

      switch (getOnlyCharCode(text)) {
        case ']': {
          continueReading = false;
          if (block == null) {
            continue;
          }
        }
        break;
        case '|': {
          // we have found the list tail, so we need read it as one block
          // until the ']' atom
          checkForNull(block, "There is not any list element", openingBracket);
          if (leftPartFirst.isEmpty()) {
            leftPartFirst = PrologList.setTermAsNewListTail(leftPart, block);
          } else {
            PrologList.setTermAsNewListTail(leftPart, block);
          }

          hasSeparator = true;

          rightPart = readBlock(OPERATORS_END_LIST);

          if (rightPart != null
              && rightPart.getType() == TermType.STRUCT
              && rightPart.getFunctor().getText().equals(OPERATOR_VERTICALBAR.getText())) {
            throw new PrologParserException(
                "Duplicated list tail definition",
                tokenizer.getLastTokenLine(),
                tokenizer.getLastTokenPos(), null);
          }

          final TokenizerResult nextAtomTwo = this.extractNextTokenCommentAware();
          if (nextAtomTwo == null) {
            throw new PrologParserException("Can't find expected token in list",
                this.tokenizer.getLine(), this.tokenizer.getPos());
          }
          if (!nextAtomTwo.getResult().getText().equals(OPERATOR_RIGHTSQUAREBRACKET.getText())) {
            throw new PrologParserException("Wrong end of the list tail",
                this.tokenizer.getLastTokenLine(), this.tokenizer.getLastTokenPos());
          }
          continueReading = false;
          continue;
        }
        case ',': {
          // all good and we read next block
          checkForNull(block, "List element not found", nextAtom);
        }
        break;
        default: {
          throw new CriticalUnexpectedError();
        }
      }

      if (leftPartFirst.isEmpty()) {
        leftPartFirst = PrologList.setTermAsNewListTail(leftPart, block);
        leftPart = leftPartFirst;
      } else {
        leftPart = PrologList.setTermAsNewListTail(leftPart, block);
      }
    }

    if (hasSeparator) {
      // '|' separator was found at the list
      if (rightPart == null) {
        throw new PrologParserException("There is not any term as the tail at the list",
            this.tokenizer.getLastTokenLine(), this.tokenizer.getLastTokenPos());
      }

      if (rightPart.getType() == TermType.STRUCT
          && (rightPart.getFunctor() == METAOPERATOR_COMMA
          || rightPart.getFunctor() == METAOPERATOR_VERTICAL_BAR)
      ) {
        throw new PrologParserException("Unexpected comma or bar in rest of list",
            this.tokenizer.getLastTokenLine(), this.tokenizer.getLastTokenPos());
      }

      if (rightPart.getType() == TermType.ATOM
          && rightPart.getQuotation() == Quotation.NONE
          && ",".equals(rightPart.getText())
      ) {
        throw new PrologParserException("Comma operator in list tail",
            this.tokenizer.getLastTokenLine(), this.tokenizer.getLastTokenPos());
      }

      leftPartFirst.replaceEndListElement(rightPart);
    }
    return leftPartFirst;
  }

  private PrologTerm readBlock(final Koi7CharOpMap endOperators) {
    // the variable will contain last processed tree item contains either
    // atom or operator
    AstItem currentTreeItem = null;

    while (true) {
      // read next atom from tokenizer
      TokenizerResult readAtomContainer = this.extractNextTokenCommentAware();

      if (readAtomContainer == null) {
        if (currentTreeItem == null) {
          // end_of_file
          return null;
        } else {
          // non closed something
          throw new PrologParserException("Non-ended clause", this.tokenizer.getLastTokenLine(),
              this.tokenizer.getLastTokenPos());
        }
      }

      PrologTerm readAtom = readAtomContainer.getResult();

      // check the atom to be the end atom
      if (isEndOperator(readAtom, endOperators)) {
        // it's an end atom so we push it back and end the cycle
        this.tokenizer.push(readAtomContainer);
        break;
      }

      // the variable contains calculated item precedence (it can be not the
      // same as the natural precedence)
      int readAtomPrecedence = 0; // we make it as highest precedence

      if (readAtom instanceof OpContainer) {
        // it is operator list
        // try to get the single operator from the list if the list
        // contains only one
        final Op readOperator = ((OpContainer) readAtom).getIfSingle();

        // check that the operator is single
        if (readOperator == null) {

          // there are a few operators in the list so we need to
          // select one
          final OpContainer readOperators = (OpContainer) readAtom;

          boolean leftPresented = false;

          if (currentTreeItem != null) {
            if (currentTreeItem.getType() == TermType.OPERATOR) {
              if (currentTreeItem.getRightBranch() != null) {
                leftPresented = true;
              }
            } else {
              leftPresented = true;
            }
          }

          final TokenizerResult peekResult = this.tokenizer.peek();
          final boolean rightPresented =
              peekResult != null && !isEndOperator(peekResult.getResult(), endOperators);

          readAtom = readOperators.findSimilar(leftPresented, rightPresented);

          if (readAtom == null) {
            if (currentTreeItem == null && !rightPresented) {
              // alone operator, it is an atom
              return new PrologAtom(readOperators.getText(), Quotation.SINGLE,
                  readOperators.getLine(), readOperators.getPos());
            }
            // we didn't get any operator for our criteria, so throw
            // an exception
            throw new PrologParserException(
                "Operator clash detected [" + readAtomContainer.getResult().getText() + ']',
                readAtomContainer.getLine(), readAtomContainer.getPos());
          }
          // we have found needed operator so get its precedence
          readAtomPrecedence = readAtom.getPrecedence();
        } else {
          readAtom = readOperator;
          final String operatorText = readOperator.getText();

          if (operatorText.length() == 1) {
            final int onlyCharCode = getOnlyCharCode(operatorText);
            switch (onlyCharCode) {
              case '[': {
                // it's a list
                readAtom = readList(readAtomContainer);
                readAtom.setPos(readAtomContainer.getPos());
                readAtom.setLine(readAtomContainer.getLine());
              }
              break;
              case '{':
              case '(': {
                boolean processReadAtom = true;
                if (onlyCharCode == '(') {
                  readAtom = readBlock(OPERATORS_SUBBLOCK);
                } else {
                  if ((this.parserFlags & ParserContext.FLAG_CURLY_BRACKETS) == 0) {
                    readAtomPrecedence = readOperator.getPrecedence();
                    processReadAtom = false;
                  } else {
                    readAtom = readBlock(OPERATORS_SUBBLOCK_CURLY);
                  }
                }

                if (processReadAtom) {
                  if (readAtom == null) {
                    if (onlyCharCode == '{') {
                      readAtom = new PrologStruct(Op.VIRTUAL_OPERATOR_CURLY_BLOCK, EMPTY_TERM_ARRAY,
                          readAtomContainer.getLine(), readAtomContainer.getPos());
                    } else {
                      throw new PrologParserException("Illegal start of term",
                          readAtomContainer.getLine(), readAtomContainer.getPos());
                    }
                  } else {
                    readAtom.setLine(readAtomContainer.getLine());
                    readAtom.setPos(readAtomContainer.getPos());
                    readAtom = new PrologStruct(
                        onlyCharCode == '{' ? Op.VIRTUAL_OPERATOR_CURLY_BLOCK :
                            Op.VIRTUAL_OPERATOR_BLOCK, new PrologTerm[] {readAtom},
                        readAtomContainer.getLine(), readAtomContainer.getPos());
                  }

                  final TokenizerResult token = this.extractNextTokenCommentAware();

                  final PrologTerm closingAtom;
                  if (token == null) {
                    closingAtom = null;
                  } else {
                    closingAtom = token.getResult();
                  }

                  if (closingAtom == null || !closingAtom.getText().equals(
                      (onlyCharCode == '{' ? OPERATOR_RIGHTCURLYBRACKET :
                          OPERATOR_RIGHTBRACKET).getText())) {
                    throw new PrologParserException("Non-closed brackets: " + onlyCharCode,
                        this.tokenizer.getLine(), this.tokenizer.getPos());
                  }
                }
              }
              break;
              default: {
                readAtomPrecedence = readOperator.getPrecedence();
              }
              break;
            }
          } else {
            readAtomPrecedence = readOperator.getPrecedence();
          }
        }
      } else {
        if (readAtom.getType() != TermType.VAR || (this.parserFlags & FLAG_VAR_AS_FUNCTOR) != 0) {
          TokenizerResult nextToken = this.extractNextTokenCommentAware();

          if (nextToken == null) {
            throw new PrologParserException("Non-closed clause", this.tokenizer.getLastTokenLine(),
                this.tokenizer.getLastTokenPos());
          }

          if (nextToken.getResult().getText().equals(OPERATOR_LEFTBRACKET.getText())) {
            final int nextTokenLineNumber = nextToken.getLine();
            final int nextTokenStrPosition = nextToken.getPos();

            // it is a structure
            if (readAtom.getType() == TermType.ATOM
                || (readAtom.getType() == TermType.VAR
                && (this.parserFlags & FLAG_VAR_AS_FUNCTOR) != 0)) {

              final PrologTerm prev = readAtom;
              readAtom = readStruct(readAtom);
              if (readAtom == null) {
                // we have met the empty brackets
                if ((this.parserFlags & FLAG_ZERO_STRUCT) == 0) {
                  throw new PrologParserException("Empty structure is not allowed",
                      nextTokenLineNumber, nextTokenStrPosition);
                } else {
                  final TokenizerResult pushed = this.tokenizer.pop();
                  if (pushed.getResult() == OPERATOR_RIGHTBRACKET) {
                    readAtom = new PrologStruct(prev);
                  } else {
                    throw new CriticalUnexpectedError();
                  }
                }
              }
            } else {
              tokenizer.push(nextToken);
              throw new PrologParserException("You must have an atom as the structure functor",
                  nextTokenLineNumber, nextTokenStrPosition);
            }
          } else {
            // push back the next atom
            tokenizer.push(nextToken);
          }
        }
      }

      final AstItem readAtomTreeItem = new AstItem(readAtom,
          readAtomContainer.getLine(),
          readAtomContainer.getPos());

      if (currentTreeItem == null) {
        // it's first
        currentTreeItem = readAtomTreeItem;
      } else {
        // not first
        if (currentTreeItem.getType() == TermType.OPERATOR) {
          // it's not first operator
          if (currentTreeItem.getPrecedence() <= readAtomPrecedence) {
            if (readAtom.getType() == TermType.OPERATOR && ((Op) readAtom).getAssoc().isPrefix()) {
              // it is a prefix operator so that it can be there
              currentTreeItem = currentTreeItem.makeAsRightBranch(readAtomTreeItem);
            } else {
              // new has lower or equal precedence
              // make it as ascendant one
              final AstItem foundItem =
                  currentTreeItem.findFirstNodeWithSuchOrLowerPrecedence(readAtomPrecedence);
              if (foundItem.getPrecedence() < readAtomPrecedence) {
                // make as parent
                currentTreeItem = foundItem.makeAsOwnerWithLeftBranch(readAtomTreeItem);
              } else if (foundItem.getPrecedence() > readAtomPrecedence) {
                // make new as right sub-branch
                currentTreeItem = foundItem.makeAsRightBranch(readAtomTreeItem);
              } else {
                // equal precedence
                switch (foundItem.getOpAssoc()) {
                  case XF:
                  case YF:
                  case FX:
                  case XFX:
                  case YFX:
                    currentTreeItem = foundItem.makeAsOwnerWithLeftBranch(readAtomTreeItem);
                    break;
                  case FY:
                  case XFY:
                    currentTreeItem = foundItem.makeAsRightBranch(readAtomTreeItem);
                    break;
                  default:
                    throw new CriticalUnexpectedError();
                }
              }
            }
          } else if (currentTreeItem.getPrecedence() > readAtomPrecedence) {
            // new has greater precedence
            if (readAtomTreeItem.getType() != TermType.OPERATOR
                && currentTreeItem.getRightBranch() != null) {
              // it's a ground atom and its right branch is not empty
              throw new PrologParserException(
                  "There is no any operator before the atom",
                  readAtomContainer.getLine(),
                  readAtomContainer.getPos());
            }
            // make it as right
            currentTreeItem = currentTreeItem.makeAsRightBranch(readAtomTreeItem);
          }
        } else {
          // check that it is an operator
          if (currentTreeItem.getType() != TermType.OPERATOR
              && readAtomTreeItem.getType() != TermType.OPERATOR) {
            throw new PrologParserException(
                "There must be an operator between atoms or structures",
                readAtomContainer.getLine(),
                readAtomContainer.getPos());
          }

          // make it as left branch
          currentTreeItem = currentTreeItem.makeAsOwnerWithLeftBranch(readAtomTreeItem);
        }
      }
    }
    if (currentTreeItem == null) {
      return null;
    } else {
      PrologTerm result = currentTreeItem.findRoot().convertToTermAndRelease(this);
      if ((this.parserFlags & FLAG_DOT2_AS_LIST) != 0
          && result.getType() == TermType.STRUCT
          && result.getText().equals(".")
          && result.getArity() == 2) {
        final PrologStruct asStruct = (PrologStruct) result;
        result = new PrologList(asStruct.getTermAt(0), asStruct.getTermAt(1));
      }
      return result;
    }
  }

  private void checkForNull(final Object obj, final String message,
                            final TokenizerResult startTerm) {
    if (obj == null) {
      throw new PrologParserException(message, startTerm.getLine(), startTerm.getPos());
    }
  }

  @Override
  public void close() throws IOException {
    this.deferredReadTerm = null;
    this.tokenizer.close(this.isCloseReader());
  }

  /**
   * Returns flag to close the base reader. By default the reader will be closed.
   *
   * @return true if close base reader during its close, false to not close reader
   * @see PrologParser#close()
   * @since 2.2.0
   */
  protected boolean isCloseReader() {
    return true;
  }

  @Override
  public Iterator iterator() {
    return new Iterator<>() {
      @Override
      public boolean hasNext() {
        return PrologParser.this.hasNext();
      }

      @Override
      public PrologTerm next() {
        return PrologParser.this.next();
      }
    };
  }

  public Stream stream() {
    return StreamSupport.stream(
        Spliterators.spliteratorUnknownSize(
            this.iterator(),
            Spliterator.ORDERED | Spliterator.IMMUTABLE | Spliterator.NONNULL),
        false
    );
  }

  private static final class PrologTermReadResult {
    private final PrologTerm result;
    private final RuntimeException exception;

    private PrologTermReadResult(final PrologTerm result, final RuntimeException error) {
      this.result = result;
      this.exception = error;
    }

    boolean isPresented() {
      return this.exception == null || !(this.exception instanceof NoSuchElementException);
    }

    PrologTerm getResult() {
      if (this.exception == null) {
        return this.result;
      } else {
        throw this.exception;
      }
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy