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

de.sayayi.lib.antlr4.AbstractAntlr4Parser Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2022 Jeroen Gremmen
 *
 * 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 de.sayayi.lib.antlr4;

import de.sayayi.lib.antlr4.syntax.GenericSyntaxErrorFormatter;
import de.sayayi.lib.antlr4.syntax.SyntaxErrorFormatter;
import de.sayayi.lib.antlr4.walker.Walker;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTreeListener;
import org.antlr.v4.runtime.tree.TerminalNode;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;

import java.util.function.Function;

import static de.sayayi.lib.antlr4.walker.Walker.WALK_FULL_RECURSIVE;
import static java.util.Objects.requireNonNull;


/**
 * @author Jeroen Gremmen
 * @since 0.1.0
 */
@SuppressWarnings({"UnstableApiUsage", "SameParameterValue", "unused"})
public abstract class AbstractAntlr4Parser
{
  private final SyntaxErrorFormatter syntaxErrorFormatter;


  protected AbstractAntlr4Parser() {
    this(new GenericSyntaxErrorFormatter(8, 0, 0, " "));
  }


  protected AbstractAntlr4Parser(@NotNull SyntaxErrorFormatter syntaxErrorFormatter) {
    this.syntaxErrorFormatter = syntaxErrorFormatter;
  }


  @Contract(mutates = "param1")
  protected 
      R parse(@NotNull L lexer, @NotNull Function parserSupplier,
              @NotNull Function ruleExecutor, @NotNull ParseTreeListener listener,
              @NotNull Function contextResultExtractor) {
    return contextResultExtractor.apply(walk(listener, parse(lexer, parserSupplier, ruleExecutor)));
  }


  @Contract(value = "_, _, _ -> new", mutates = "param1")
  protected 
      @NotNull C parse(@NotNull L lexer, @NotNull Function parserSupplier,
                       @NotNull Function ruleExecutor)
  {
    final ANTLRErrorListener errorListener = new BaseErrorListener() {
      @Override
      public void syntaxError(@NotNull Recognizer recognizer, Object offendingSymbol, int line,
                              int charPositionInLine, String msg, RecognitionException ex)
      {
        if (offendingSymbol == null && recognizer instanceof Lexer)
        {
          final Lexer lexer = (Lexer)recognizer;
          final CharStream inputStream = lexer.getInputStream();

          offendingSymbol = new PositionToken(line, charPositionInLine,
              lexer._tokenStartCharIndex, inputStream.index(), inputStream);
        }

        AbstractAntlr4Parser.this.syntaxError(
            analyseStartStopToken((Token)offendingSymbol, ex), msg, ex);
      }
    };

    if (lexer instanceof Lexer)
    {
      final Lexer antlr4Lexer = (Lexer)lexer;

      antlr4Lexer.removeErrorListener(ConsoleErrorListener.INSTANCE);  // console polluter
      antlr4Lexer.addErrorListener(errorListener);
    }

    final P parser = parserSupplier.apply(lexer);

    parser.removeErrorListener(ConsoleErrorListener.INSTANCE);  // console polluter
    parser.addErrorListener(errorListener);

    return requireNonNull(ruleExecutor.apply(parser));
  }


  @Contract(value = "_, _ -> param2", mutates = "param2")
  private 
      @NotNull C walk(@NotNull ParseTreeListener listener, @NotNull C parserRuleContext)
  {
    (listener instanceof WalkerSupplier
        ? ((WalkerSupplier)listener).getWalker()
        : WALK_FULL_RECURSIVE)
        .walk(listener, parserRuleContext);

    return parserRuleContext;
  }


  @Contract(value = "null, null -> fail", pure = true)
  protected @NotNull Token[] analyseStartStopToken(Token offendingSymbol, RecognitionException ex)
  {
    if (ex != null)
    {
      final Token offendingToken = ex.getOffendingToken();
      if (offendingToken != null)
        return new Token[] { offendingToken, offendingToken };

      final RuleContext ctx = ex.getCtx();
      if (ctx instanceof ParserRuleContext)
      {
        final ParserRuleContext parserRuleContext = (ParserRuleContext)ctx;
        return new Token[] { parserRuleContext.getStart(), parserRuleContext.getStop() };
      }
    }

    return new Token[] { requireNonNull(offendingSymbol), offendingSymbol };
  }


  @Contract("_, _ -> fail")
  protected void syntaxError(@NotNull ParserRuleContext ctx, @NotNull String errorMsg) {
    syntaxError(ctx, errorMsg, null);
  }


  @Contract("_, _, _ -> fail")
  protected void syntaxError(@NotNull ParserRuleContext ctx, @NotNull String errorMsg,
                             Exception cause) {
    syntaxError(new Token[] { ctx.getStart(), ctx.getStop() }, errorMsg, cause);
  }


  @Contract("_, _ -> fail")
  protected void syntaxError(@NotNull TerminalNode terminalNode, @NotNull String errorMsg) {
    syntaxError(terminalNode, errorMsg, null);
  }


  @Contract("_, _, _ -> fail")
  protected void syntaxError(@NotNull TerminalNode terminalNode, @NotNull String errorMsg,
                             Exception cause) {
    syntaxError(terminalNode.getSymbol(), errorMsg, cause);
  }


  @Contract("_, _ -> fail")
  protected void syntaxError(@NotNull Token token, @NotNull String errorMsg) {
    syntaxError(token, errorMsg, null);
  }


  @Contract("_, _, _ -> fail")
  protected void syntaxError(@NotNull Token token, @NotNull String errorMsg, Exception cause) {
    syntaxError(new Token[] { token, token }, errorMsg, cause);
  }


  @Contract("_, _, _ -> fail")
  private void syntaxError(@NotNull Token[] startStopToken, @NotNull String errorMsg,
                           Exception cause)
  {
    final Token startToken = startStopToken[0];
    final Token stopToken = startStopToken[1];

    throw createException(startToken, stopToken,
        syntaxErrorFormatter.format(startToken, stopToken, cause), errorMsg, cause);
  }


  @Contract("_, _, _, _, _ -> new")
  protected abstract @NotNull RuntimeException createException(
      @NotNull Token startToken, @NotNull Token stopToken, @NotNull String formattedMessage,
      @NotNull String errorMsg, Exception cause);




  public interface WalkerSupplier extends ParseTreeListener
  {
    @Contract(pure = true)
    default @NotNull Walker getWalker() {
      return WALK_FULL_RECURSIVE;
    }
  }




  private static final class PositionToken implements Token
  {
    private final int line;
    private final int charPositionInLine;
    private final int startIndex;
    private final int stopIndex;
    private final CharStream inputStream;


    public PositionToken(int line, int charPositionInLine, int startIndex, int stopIndex,
                         @NotNull CharStream inputStream)
    {
      this.line = line;
      this.charPositionInLine = charPositionInLine;
      this.startIndex = startIndex;
      this.stopIndex = stopIndex;
      this.inputStream = inputStream;
    }


    @Override
    public int getLine() {
      return line;
    }


    @Override
    public int getCharPositionInLine() {
      return charPositionInLine;
    }


    @Override
    public int getStartIndex() {
      return startIndex;
    }


    @Override
    public int getStopIndex() {
      return stopIndex;
    }


    @Override
    public CharStream getInputStream() {
      return inputStream;
    }


    @Override
    public String getText() {
      return inputStream.getText(new Interval(startIndex, stopIndex));
    }


    @Override
    public int getType() {
      return 0;
    }


    @Override
    public int getChannel() {
      return DEFAULT_CHANNEL;
    }


    @Override
    public int getTokenIndex() {
      return -1;
    }


    @Override
    public TokenSource getTokenSource() {
      return null;
    }
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy