Package dtool.parser.common

Source Code of dtool.parser.common.AbstractParser$ParseRuleDescription

/*******************************************************************************
* Copyright (c) 2013, 2013 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
*     Bruno Medeiros - initial API and implementation
*******************************************************************************/
package dtool.parser.common;

import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;

import melnorme.lang.tooling.ast.SourceRange;
import melnorme.utilbox.core.CoreUtil;
import melnorme.utilbox.misc.ArrayUtil;
import dtool.ast.ASTNode;
import dtool.ast.IASTNode;
import dtool.ast.NodeListView;
import dtool.ast.definitions.DefUnit.ProtoDefSymbol;
import dtool.parser.DeeTokens;
import dtool.parser.ParserError;
import dtool.parser.ParserError.ParserErrorTypes;
import dtool.parser.common.LexElement.MissingLexElement;
import dtool.util.ArrayView;

/**
* Basic parsing functionality.
*/
public abstract class AbstractParser {
 
  protected String source;
  protected LexElementSource lexSource;
  protected boolean enabled;
 
  public AbstractParser() {
    this.enabled = true;
  }
 
  /* ---- Core functionality ---- */

  public final String getSource() {
    return source;
  }
 
  public final LexElementSource getEnabledLexSource() {
    assertTrue(enabled);
    return lexSource;
  }
 
  protected final LexElementSource getLexSource() {
    return lexSource;
  }
 
  public final void setEnabled(boolean enabled) {
    assertTrue(this.enabled == !enabled);
    this.enabled = enabled;
  }
 
  // There should be no reason to use this other than for contract checks only
  public final boolean isEnabled() {
    return enabled;
  }
 
  public final int getSourcePosition() {
    return getLexSource().getSourcePosition();
  }
 
  public final LexElement lookAheadElement(int laIndex) {
    return getEnabledLexSource().lookAheadElement(laIndex);
  }
 
  public final LexElement lastLexElement() {
    return getLexSource().lastLexElement();
  }
 
  public final LexElement consumeLookAhead() {
    return getEnabledLexSource().consumeInput();
  }
 
  public ParserState saveParserState() {
    LexElementSource lexSource = getEnabledLexSource().saveState();
    return new ParserState(lexSource, enabled);
  }
 
  public void restoreOriginalState(ParserState savedState) {
    this.lexSource.resetState(savedState.lexSource);
    this.enabled = savedState.enabled;
  }
 
  public class ParserState {
   
    public final LexElementSource lexSource;
    public final boolean enabled;
   
    public ParserState(LexElementSource lexSource, boolean enabled) {
      this.lexSource = lexSource;
      this.enabled = enabled;
    }
   
  }
 
  /* ---- Lookahead and consume helpers ---- */
 
  public final LexElement lookAheadElement() {
    return lookAheadElement(0);
  }
 
  public final DeeTokens lookAhead() {
    return lookAheadElement(0).type;
  }
 
  public final DeeTokens lookAhead(int laIndex) {
    return lookAheadElement(laIndex).type;
  }
 
  public final LexElement consumeLookAhead(DeeTokens tokenType) {
    assertTrue(lookAhead() == tokenType);
    return consumeLookAhead();
  }
 
  protected final LexElement consumeIf(DeeTokens tokenType) {
    return lookAhead() == tokenType ? consumeLookAhead() : null;
  }
 
  protected final boolean tryConsume(DeeTokens tokenType) {
    if(lookAhead() == tokenType) {
      consumeLookAhead();
      return true;
    }
    return false;
  }
  protected final boolean tryConsume(DeeTokens tokenType, DeeTokens tokenType2) {
    if(lookAhead() == tokenType && lookAhead(1) == tokenType2) {
      consumeLookAhead();
      consumeLookAhead();
      return true;
    }
    return false;
  }
  protected final boolean tryConsume(DeeTokens tokenType, DeeTokens tokenType2, DeeTokens tokenType3) {
    if(lookAhead() == tokenType && lookAhead(1) == tokenType2 && lookAhead(2) == tokenType3) {
      consumeLookAhead();
      consumeLookAhead();
      consumeLookAhead();
      return true;
    }
    return false;
  }
 
  /* ----  ---- */
 
  protected static final HashMap<String, ParseRuleDescription> parseRules = new HashMap<>();
 
  // TODO: maybe this should be an enum
  public static class ParseRuleDescription {
    public final String id;
    public final String description;
   
    public ParseRuleDescription(String id, String description) {
      this.id = id;
      this.description = description;
      assertTrue(parseRules.get(id) == null);
      parseRules.put(id, this);
    }
  }
 
  public static ParseRuleDescription getRule(String key) {
    return parseRules.get(key);
  }
 
  /* ---- error helpers ---- */
 
  protected ParserError createError(ParserErrorTypes errorType, SourceRange sr, Object msgData) {
    return new ParserError(errorType, sr, sr.getRangeSubString(getSource()), msgData);
  }
 
  protected ParserError createError(ParserErrorTypes errorType, IToken errorToken, Object msgData) {
    return createError(errorType, errorToken.getSourceRange(), msgData);
  }
 
  protected ParserError createErrorOnLastToken(ParserErrorTypes parserError, Object msgData) {
    return createError(parserError, lastLexElement().getSourceRange(), msgData);
  }
 
  protected ParserError createExpectedTokenError(DeeTokens expected) {
    return createErrorOnLastToken(ParserErrorTypes.EXPECTED_TOKEN, expected);
  }
 
  protected ParserError createErrorExpectedRule(ParseRuleDescription expectedRule) {
    return createErrorOnLastToken(ParserErrorTypes.EXPECTED_RULE, expectedRule.description);
  }
 
  protected ParserError createErrorExpectedRule(ParseRuleDescription expectedRule, SourceRange sourceRange) {
    return createError(ParserErrorTypes.EXPECTED_RULE, sourceRange, expectedRule.description);
  }
 
  protected ParserError createSyntaxError(ParseRuleDescription expectedRule) {
    return createErrorOnLastToken(ParserErrorTypes.SYNTAX_ERROR, expectedRule.description);
  }
 
  /* ---- Result helpers ---- */
 
  public static abstract class CommonRuleResult {
   
    public final boolean ruleBroken; // Indicates if rule was terminated properly
   
    public CommonRuleResult(boolean ruleBroken) {
      this.ruleBroken = ruleBroken;
    }
   
  }
 
  public static class NodeResult<T extends ASTNode> extends CommonRuleResult {
   
    public final T node;
   
    public NodeResult(boolean ruleBroken, T result) {
      super(ruleBroken);
      this.node = result;
      assertTrue(!(ruleBroken && result == null));
    }
   
    @SuppressWarnings("unchecked")
    public final <SUPER_OF_T extends ASTNode> NodeResult<SUPER_OF_T> upcastTypeParam() {
      return (NodeResult<SUPER_OF_T>) this;
    }
   
    public T getNode() {
      return node;
    }
   
  }
 
  public static <T extends ASTNode> NodeResult<T> nullResult() {
    return new NodeResult<T>(false, null);
  }
  public static <T extends ASTNode> NodeResult<T> result(boolean ruleBroken, T node) {
    return new NodeResult<T>(ruleBroken, node);
  }
 
  public static boolean isNull(NodeResult<?> result) {
    return result == null || result.node == null;
  }
 
  /* ---- Additional input consume helpers ---- */
 
  public final void advanceSubChannelTokens() {
    getEnabledLexSource().advanceSubChannelTokens();
  }
 
  public final MissingLexElement consumeSubChannelTokensNoError() {
    return consumeSubChannelTokens(null);
  }
 
  public MissingLexElement consumeSubChannelTokens(ParserError error) {
    // Missing element will consume whitetokens ahead
    LexElement la = lookAheadElement();
    MissingLexElement missingLexElement = new MissingLexElement(la.getStartPos(), error,
      la.getFullRangeStartPos(), la.relevantPrecedingSubChannelTokens);
    getEnabledLexSource().advanceSubChannelTokens();
    return missingLexElement;
  }
 
  protected final BaseLexElement consumeExpectedContentToken(DeeTokens expectedTokenType) {
    if(lookAhead() == expectedTokenType) {
      return consumeLookAhead();
    } else {
      ParserError error = createExpectedTokenError(expectedTokenType);
      BaseLexElement missingToken = consumeSubChannelTokens(error);
      return missingToken;
    }
  }
 
  /* ------------  Node finalization  ------------ */
 
  protected final <T extends ASTNode> NodeResult<T> resultConclude(boolean ruleBroken, T node) {
    return result(ruleBroken, conclude(node));
  }
 
  protected final <T extends ASTNode> T conclude(SourceRange sr, T node) {
    node.setSourceRange(sr);
    return concludeDo(null, null, node);
  }
 
  protected final <T extends ASTNode> T concludeNode(T node) {
    return concludeDo(null, null, node);
  }
  protected final <T extends ASTNode> T conclude(T node) {
    return concludeDo(null, null, node);
  }
  protected final <T extends ASTNode> T conclude(ParserError error, final T node) {
    return concludeDo(error, null, node);
  }
  protected final <T extends ASTNode> T concludeDo(ParserError error1, ParserError error2, final T node) {
    if(error1 == null) {
      assertTrue(error2 == null);
      node.setParsedStatus();
    } else if(error2 == null) {
      node.setParsedStatusWithErrors(error1);
    } else {
      node.setParsedStatusWithErrors(error1, error2);
    }
    nodeConcluded(node);
    return node;
  }
 
  @SuppressWarnings("unused")
  protected void nodeConcluded(final ASTNode node) {
  }
 
  /** Temporary node parsing helper class. Designed to be used once per node about to parsed.
   * Also intended to have its allocation elided by means of escape analysis optimization,
   * therefore (in most circumstances) instances of this class should only be assigned to local variables.
   */
  public class ParseHelper {
   
    public int nodeStart;
    protected ParserError error1 = null;
    protected ParserError error2 = null;
    public boolean ruleBroken = false;
   
    public ParseHelper(int nodeStart) {
      this.nodeStart = nodeStart;
    }
   
    public ParseHelper(LexElement token) {
      this(token.getStartPos());
    }
   
    public ParseHelper() {
      this(lastLexElement());
    }
   
    public ParseHelper(ASTNode startNode) {
      this(startNode.getStartPos());
    }
   
    public void setStartPosition(int nodeStart) {
      assertTrue(this.nodeStart == -1);
      this.nodeStart = nodeStart;
    }
   
    public <T extends ASTNode> T initRange(T node) {
      assertTrue(!node.hasSourceRangeInfo());
      return srToPosition(nodeStart, node);
    }
   
    public final ParseHelper consumeRequired(DeeTokens expectedTokenType) {
      consume(expectedTokenType, false, true);
      return this;
    }
   
    public final boolean consumeExpected(DeeTokens expectedTokenType) {
      return consume(expectedTokenType, false, false);
    }
   
    public final boolean consumeOptional(DeeTokens tokenType) {
      assertTrue(!ruleBroken);
      return tryConsume(tokenType);
    }
   
    public final boolean consume(DeeTokens expectedTokenType, boolean isOptional, boolean breaksRule) {
      assertTrue(!ruleBroken);
      if(lookAhead() == expectedTokenType) {
        consumeLookAhead();
        return true;
      }
      if(isOptional == false) {
        storeError(createExpectedTokenError(expectedTokenType));
        parseBrokenRule(breaksRule);
      }
      return false;
    }
   
    public BaseLexElement consumeExpectedIdentifier() {
      BaseLexElement token = consumeExpectedContentToken(DeeTokens.IDENTIFIER);
      if(token.getMissingError() != null) {
        storeError(token.getMissingError());
      }
      return token;
    }
   
    public void setRuleBroken(boolean ruleBroken) {
      this.ruleBroken = ruleBroken;
    }
   
    public void parseBrokenRule(boolean ruleBroken) {
      if(ruleBroken) {
        advanceSubChannelTokens();
        setRuleBroken(true);
      }
    }
   
    public final ParseHelper clearRuleBroken() {
      if(ruleBroken) {
        ruleBroken = false;
      }
      return this;
    }
   
    /** Disable parsing until ruleBroken is explicitly checked. (safety feature) */
    public void requireBrokenCheck() {
      setEnabled(false);
    }
   
    public boolean checkRuleBroken() {
      setEnabled(true);
      return ruleBroken;
    }
   
    public final ProtoDefSymbol checkResult(ProtoDefSymbol defId) {
      setRuleBroken(defId.isMissing());
      return defId;
    }
   
    public final <T extends ASTNode> T checkResult(NodeResult<T> nodeResult) {
      if(nodeResult == null)
        return null;
      setRuleBroken(nodeResult.ruleBroken);
      return nodeResult.node;
    }
   
    /**
     * Parse a required rule.
     * Note that the parsing of the required rule is not actually performed by this function,
     * but instead it must be called *immediately* before this function is called,
     * and the result placed in give nodeResult.
     * (This is so that lambdas/function-delegate don't have to be used)
     */
    public <T extends ASTNode> T parseRequiredRule(NodeResult<T> nodeResult, ParseRuleDescription expectedRule) {
      if(nodeResult.node == null) {
        storeError(createErrorExpectedRule(expectedRule));
        parseBrokenRule(true);
        return null;
      }
      parseBrokenRule(nodeResult.ruleBroken);
      return nodeResult.node;
    }
   
    public final ParserError storeError(ParserError error) {
      assertTrue(error2 == null);
      if(error1 == null) {
        error1 = error;
      } else {
        error2 = error;
      }
      return error;
    }
   
    public final <T extends ASTNode> T conclude(T node) {
      initRange(node);
      nodeStart = -nodeStart; // invalidate
      return concludeDo(error1, error2, node);
    }
   
    public final <T extends ASTNode> NodeResult<T> resultConclude(T node) {
      return result(ruleBroken, conclude(node));
    }
   
  }
 
  /* ---- Source range helpers ---- */
 
  public static SourceRange srAt(int offset) {
    return new SourceRange(offset, 0);
  }
 
  public static <T extends ASTNode> T srBounds(int startPos, int endPos, T node) {
    node.setSourceRange(startPos, endPos - startPos);
    return node;
  }
 
  public static <T extends ASTNode> T srBounds(ASTNode left, ASTNode right, T node) {
    int startPos = left.getStartPos();
    int endPos = right.getEndPos();
    return srBounds(startPos, endPos, node);
  }
 
  public static <T extends ASTNode> T srOf(BaseLexElement lexElement, T node) {
    node.setSourceRange(lexElement.getStartPos(), lexElement.getEndPos() - lexElement.getStartPos());
    return node;
  }
 
  public static <T extends ASTNode> T srOf(ASTNode rangeNode, T node) {
    node.setSourceRange(rangeNode.getStartPos(), rangeNode.getLength());
    return node;
  }
 
  public static <T extends ASTNode> T srEffective(BaseLexElement lexElement, T node) {
    int startPos = lexElement.getEffectiveStartPos();
    node.setSourceRange(startPos, lexElement.getEndPos() - startPos);
    return node;
  }
 
  public final <T extends ASTNode> T srToPosition(int nodeStart, T node) {
    node.setSourceRange(nodeStart, getSourcePosition() - nodeStart);
    return node;
  }
 
  public final <T extends ASTNode> T srToPosition(BaseLexElement start, T node) {
    int declStart = start.getStartPos();
    node.setSourceRange(declStart, getSourcePosition() - declStart);
    return node;
  }
 
  public final <T extends ASTNode> T srToPosition(ASTNode left, T node) {
    assertTrue(left.hasSourceRangeInfo() && !node.hasSourceRangeInfo());
    assertTrue(!node.isParsedStatus());
    return srToPosition(left.getStartPos(), node);
  }
 
  /* ---- Collection creation helpers ---- */
 
  // TODO: optimize some of this arrayView creation
 
  public static <T extends IASTNode> ArrayView<T> arrayView(Collection<T> list) {
    if(list == null)
      return null;
    T[] array = ArrayUtil.createFrom(list, CoreUtil.<Class<T>>blindCast(ASTNode.class));
    return ArrayView.create(array);
  }
 
  public static <T> ArrayView<T> arrayViewG(Collection<? extends T> list) {
    if(list == null)
      return null;
    @SuppressWarnings("unchecked")
    T[] array = (T[]) ArrayUtil.createFrom(list, Object.class);
    return ArrayView.create(array);
  }
 
  public static <T extends IASTNode> NodeListView<T> nodeListView(ArrayList<T> list) {
    if(list == null)
      return null;
    boolean hasEndingSeparator = false;
    if(list.size() > 0 && list.get(list.size()-1) == null) {
      list.remove(list.size()-1);
      hasEndingSeparator = true;
    }
    return nodeListView(list, hasEndingSeparator);
  }
 
  public static <T extends IASTNode> NodeListView<T> nodeListView(ArrayList<T> list, boolean hasEndingSeparator) {
    T[] array = ArrayUtil.createFrom(list, CoreUtil.<Class<T>>blindCast(ASTNode.class));
    return new NodeListView<>(array, hasEndingSeparator);
  }
 
  public static boolean lazyInitIsEmpty(ArrayView<?> arrayView) {
    if(arrayView != null) {
      assertTrue(!arrayView.isEmpty());
    }
    return arrayView == null;
  }
 
}
TOP

Related Classes of dtool.parser.common.AbstractParser$ParseRuleDescription

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.