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

commonMain.org.antlr.v4.kotlinruntime.Parser.kt Maven / Gradle / Ivy

// Copyright 2017-present Strumenta and contributors, licensed under Apache 2.0.
// Copyright 2024-present Strumenta and contributors, licensed under BSD 3-Clause.
package org.antlr.v4.kotlinruntime

import com.strumenta.antlrkotlin.runtime.System
import com.strumenta.antlrkotlin.runtime.System.PrintStream
import com.strumenta.antlrkotlin.runtime.synchronized
import org.antlr.v4.kotlinruntime.atn.*
import org.antlr.v4.kotlinruntime.misc.IntegerStack
import org.antlr.v4.kotlinruntime.misc.IntervalSet
import org.antlr.v4.kotlinruntime.tree.*
import org.antlr.v4.kotlinruntime.tree.pattern.ParseTreePattern
import org.antlr.v4.kotlinruntime.tree.pattern.ParseTreePatternMatcher

/**
 * This is all the parsing support code essentially; most of it is error recovery stuff.
 */
@Suppress("PropertyName", "MemberVisibilityCanBePrivate")
public abstract class Parser(input: TokenStream) : Recognizer() {
  public inner class TraceListener : ParseTreeListener {
    override fun enterEveryRule(ctx: ParserRuleContext) {
      System.out.println("enter   ${ruleNames[ctx.ruleIndex]}, LT(1)=${_input.LT(1)!!.text}")
    }

    override fun visitTerminal(node: TerminalNode) {
      System.out.println("consume ${node.symbol} rule ${ruleNames[context!!.ruleIndex]}")
    }

    override fun visitErrorNode(node: ErrorNode) {
      // Noop
    }

    override fun exitEveryRule(ctx: ParserRuleContext) {
      System.out.println("exit    ${ruleNames[ctx.ruleIndex]}, LT(1)=${_input.LT(1)!!.text}")
    }
  }

  public object TrimToSizeListener : ParseTreeListener {
    override fun enterEveryRule(ctx: ParserRuleContext) {
      // Noop
    }

    override fun visitTerminal(node: TerminalNode) {
      // Noop
    }

    override fun visitErrorNode(node: ErrorNode) {
      // Noop
    }

    override fun exitEveryRule(ctx: ParserRuleContext) {
      val children = ctx.children

      if (children is ArrayList<*>) {
        children.trimToSize()
      }
    }
  }

  /**
   * This field holds the deserialized [ATN] with bypass alternatives, created
   * lazily upon first demand. In 4.10 I changed from `Map`
   * since we only need one per parser object, and also it complicates other targets
   * that don't use ATN strings.
   *
   * @see ATNDeserializationOptions.isGenerateRuleBypassTransitions
   */
  private var bypassAltsAtnCache: ATN? = null

  /**
   * The error handling strategy for the parser.
   *
   * The default value is a new instance of [DefaultErrorStrategy].
   */
  public var errorHandler: ANTLRErrorStrategy = DefaultErrorStrategy()

  /**
   * The input stream.
   */
  protected var _input: TokenStream = input

  protected val _precedenceStack: IntegerStack = IntegerStack().also {
    it.push(0)
  }

  /**
   * The [ParserRuleContext] object for the currently executing rule.
   *
   * This is always non-`null` during the parsing process.
   */
  public var context: ParserRuleContext? = null

  /**
   * Specifies whether the parser should construct a parse tree during
   * the parsing process.
   *
   * The default value is `true`.
   */
  public var buildParseTree: Boolean = true

  /**
   * When [isTrace]` = true` is called, a reference to the
   * [TraceListener] is stored here, so it can be easily removed in a
   * later call to [isTrace]` = false`.
   *
   * The listener itself is implemented as a parser listener
   * so this field is not directly used by other parser methods.
   */
  private var _tracer: TraceListener? = null

  /**
   * The list of [ParseTreeListener] listeners registered to receive
   * events during the parse.
   *
   * @see addParseListener
   * @see removeParseListener
   */
  protected var _parseListeners: MutableList = ArrayList()

  /**
   * The number of syntax errors reported during parsing.
   *
   * This value is incremented each time [notifyErrorListeners] is called.
   *
   * @see notifyErrorListeners
   */
  public var numberOfSyntaxErrors: Int = 0
    protected set

  /**
   * Indicates the parser has `match()`-ed the `EOF` token.
   *
   * @see exitRule
   */
  public var isMatchedEOF: Boolean = false
    protected set

  /**
   * Whether to trim the internal lists of the parse tree during parsing to conserve memory.
   *
   * This property is set to `false` by default for a newly constructed parser.
   */
  public var trimParseTree: Boolean
    get() = parseListeners.contains(TrimToSizeListener)
    set(value) {
      if (value) {
        if (trimParseTree) {
          return
        }

        addParseListener(TrimToSizeListener)
      } else {
        removeParseListener(TrimToSizeListener)
      }
    }

  public val parseListeners: List
    get() = _parseListeners

  override var tokenFactory: TokenFactory<*>
    get() = _input.tokenSource.tokenFactory
    set(value) {
      _input.tokenSource.tokenFactory = value
    }

  /**
   * The ATN with bypass alternatives is expensive to create, so we create it lazily.
   *
   * @throws UnsupportedOperationException If the current parser does not
   *   implement the [serializedATN] method
   */
  public val atnWithBypassAlts: ATN
    get() {
      // Accessing 'serializedATN' is what might throw the UnsupportedOperationException
      val serializedAtn = serializedATN
      return synchronized(this) {
        bypassAltsAtnCache ?: let {
          val deserializationOptions = ATNDeserializationOptions()
          deserializationOptions.isGenerateRuleBypassTransitions = true

          val atnDeserializer = ATNDeserializer(deserializationOptions)
          val tempBypassAltsAtnCache = atnDeserializer.deserialize(serializedAtn.toCharArray())
          bypassAltsAtnCache = tempBypassAltsAtnCache
          tempBypassAltsAtnCache
        }
      }
    }

  /**
   * Get the token stream, or set the token stream and reset the parser.
   */
  public var tokenStream: TokenStream
    get() = _input
    set(value) {
      _input = DummyTokenStream
      reset()
      _input = value
    }

  /**
   * Match needs to return the current input symbol, which gets put
   * into the label for the associated token ref, e.g., `x = ID`.
   */
  public val currentToken: Token?
    get() = _input.LT(1)

  /**
   * The precedence level for the top-most precedence rule.
   *
   * It is `-1` if the parser context is not nested within a precedence rule.
   */
  public val precedence: Int
    get() = if (_precedenceStack.isEmpty) {
      -1
    } else {
      _precedenceStack.peek()
    }

  /**
   * The set of input symbols which could follow the current parser
   * state and context, as given by [state] and [context], respectively.
   *
   * @see ATN.getExpectedTokens
   */
  public val expectedTokens: IntervalSet
    get() = atn.getExpectedTokens(state, context)

  public val expectedTokensWithinCurrentRule: IntervalSet
    get() {
      val atn = interpreter.atn
      val s = atn.states[state]
      return atn.nextTokens(s!!)
    }

  /**
   * For debugging and other purposes.
   */
  public val dfaStrings: List
    get() = synchronized(interpreter.decisionToDFA) {
      val s = ArrayList()

      for (d in 0.. 0) {
      errorHandler.reportMatch(this)
      consume()
    } else {
      t = errorHandler.recoverInline(this)

      if (buildParseTree && t.tokenIndex == -1) {
        // We must have conjured up a new token during single token insertion
        // if it's not the current symbol
        context!!.addErrorNode(createErrorNode(context, t))
      }
    }

    return t
  }

  /**
   * Registers [listener] to receive events during the parsing process.
   *
   * To support output-preserving grammar transformations (including but not
   * limited to left-recursion removal, automated left-factoring, and
   * optimized code generation), calls to listener methods during the parse
   * may differ substantially from calls made by [ParseTreeWalker.DEFAULT]
   * used after the parse is complete.
   *
   * In particular, rule entry and exit events may occur in a different order
   * during the parse than after the parser. In addition, calls to certain
   * rule entry methods may be omitted.
   *
   * With the following specific exceptions, calls to listener events are
   * *deterministic*, i.e., for identical input the calls to listener
   * methods will be the same.
   *
   * - Alterations to the grammar used to generate code may change the
   *   behavior of the listener calls
   * - Alterations to the command line options passed to ANTLR 4 when
   *   generating the parser may change the behavior of the listener calls
   * - Changing the version of the ANTLR Tool used to generate the parser
   *   may change the behavior of the listener calls
   *
   * @param listener The listener to add
   */
  public open fun addParseListener(listener: ParseTreeListener) {
    _parseListeners.add(listener)
  }

  /**
   * Remove [listener] from the list of parse listeners.
   *
   * If [listener] has not been added as a parse listener, this method does nothing.
   *
   * @param listener The listener to remove
   * @see addParseListener
   */
  public open fun removeParseListener(listener: ParseTreeListener) {
    _parseListeners.remove(listener)
  }

  /**
   * Remove all parse listeners.
   *
   * @see addParseListener
   */
  public open fun removeParseListeners() {
    _parseListeners.clear()
  }

  /**
   * Notify any parse listeners of an enter rule event.
   *
   * @see addParseListener
   */
  protected open fun triggerEnterRuleEvent() {
    for (listener in _parseListeners) {
      listener.enterEveryRule(context!!)
      context!!.enterRule(listener)
    }
  }

  /**
   * Notify any parse listeners of an exit rule event.
   *
   * @see addParseListener
   */
  protected open fun triggerExitRuleEvent() {
    // Reverse order walk of listeners
    for (listener in _parseListeners.asReversed()) {
      context!!.exitRule(listener)
      listener.exitEveryRule(context!!)
    }
  }

  /**
   * The preferred method of getting a tree pattern.
   *
   * For example, here's a sample use:
   *
   * ```
   * val t: ParseTree = parser.expr()
   * val p: ParseTreePattern = parser.compileParseTreePattern("+0", MyParser.RULE_expr)
   * val m: ParseTreeMatch = p.match(t)
   * val id = m.get("ID")
   * ```
   */
  public open fun compileParseTreePattern(pattern: String, patternRuleIndex: Int): ParseTreePattern {
    val tokenSource = tokenStream.tokenSource

    if (tokenSource is Lexer) {
      return compileParseTreePattern(pattern, patternRuleIndex, tokenSource)
    }

    throw UnsupportedOperationException("Parser can't discover a lexer to use")
  }

  /**
   * The same as [compileParseTreePattern] but specify a
   * [Lexer] rather than trying to deduce it from this parser.
   */
  public open fun compileParseTreePattern(
    pattern: String,
    patternRuleIndex: Int,
    lexer: Lexer,
  ): ParseTreePattern {
    val m = ParseTreePatternMatcher(lexer, this)
    return m.compile(pattern, patternRuleIndex)
  }

  public open fun notifyErrorListeners(msg: String): Unit =
    notifyErrorListeners(currentToken!!, msg, null)

  public open fun notifyErrorListeners(
    offendingToken: Token,
    msg: String,
    e: RecognitionException?,
  ) {
    numberOfSyntaxErrors++

    val line = offendingToken.line
    val charPositionInLine = offendingToken.charPositionInLine
    val listener = errorListenerDispatch
    listener.syntaxError(this, offendingToken, line, charPositionInLine, msg, e)
  }

  /**
   * Consume and return the [current symbol][currentToken].
   *
   * E.g., given the following input with `A` being the current
   * lookahead symbol, this function moves the cursor to `B` and returns `A`.
   *
   * ```
   * A B
   * ^
   * ```
   *
   * If the parser is not in error recovery mode, the consumed symbol is added
   * to the parse tree using [ParserRuleContext.addChild], and
   * [ParseTreeListener.visitTerminal] is called on any parse listeners.
   *
   * If the parser *is* in error recovery mode, the consumed symbol is
   * added to the parse tree using [createErrorNode], then
   * [ParserRuleContext.addErrorNode], and then [ParseTreeListener.visitErrorNode]
   * is called on any parse listeners.
   */
  public open fun consume(): Token {
    val o = currentToken

    if (o!!.type != EOF) {
      tokenStream.consume()
    }

    if (buildParseTree || _parseListeners.isNotEmpty()) {
      if (errorHandler.inErrorRecoveryMode(this)) {
        val node = context!!.addErrorNode(createErrorNode(context, o))

        for (listener in _parseListeners) {
          listener.visitErrorNode(node)
        }
      } else {
        val node = context!!.addChild(createTerminalNode(context, o))

        for (listener in _parseListeners) {
          listener.visitTerminal(node)
        }
      }
    }

    return o
  }

  /**
   * How to create a token leaf node associated with a parent.
   *
   * Typically, the terminal node to create is not a function of the parent.
   *
   * @since 4.7
   */
  public open fun createTerminalNode(parent: ParserRuleContext?, t: Token): TerminalNode =
    TerminalNodeImpl(t)

  /**
   * How to create an error node, given a token, associated with a parent.
   *
   * Typically, the error node to create is not a function of the parent.
   *
   * @since 4.7
   */
  public open fun createErrorNode(parent: ParserRuleContext?, t: Token): ErrorNode =
    ErrorNodeImpl(t)

  protected open fun addContextToParseTree() {
    val parent = context!!.getParent()

    // Add current context to parent if we have a parent
    parent?.addChild(context!!)
  }

  /**
   * Always called by generated parsers upon entry to a rule.
   *
   * Access field [context] to get the current context.
   */
  public open fun enterRule(localctx: ParserRuleContext, state: Int, ruleIndex: Int) {
    this.state = state
    context = localctx
    context!!.start = _input.LT(1)

    if (buildParseTree) {
      addContextToParseTree()
    }

    triggerEnterRuleEvent()
  }

  public fun exitRule() {
    if (isMatchedEOF) {
      // If we have matched EOF, it cannot consume past EOF so we use LT(1) here.
      // LT(1) will be end of file
      context!!.stop = _input.LT(1)
    } else {
      // Stop node is what we just matched
      context!!.stop = _input.LT(-1)
    }

    // Trigger event on context, before it reverts to parent
    triggerExitRuleEvent()
    state = context!!.invokingState
    context = context!!.getParent()
  }

  public fun enterOuterAlt(localctx: ParserRuleContext, altNum: Int) {
    localctx.altNumber = altNum

    // If we have new localctx, make sure we replace existing ctx
    // that is previous child of parse tree
    if (buildParseTree && context !== localctx) {
      val parent = context!!.getParent()

      if (parent != null) {
        parent.removeLastChild()
        parent.addChild(localctx)
      }
    }

    context = localctx
  }

  public open fun enterRecursionRule(localctx: ParserRuleContext, state: Int, ruleIndex: Int, precedence: Int) {
    this.state = state
    _precedenceStack.push(precedence)
    context = localctx
    context!!.start = _input.LT(1)

    // Simulates rule entry for left-recursive rules
    triggerEnterRuleEvent()
  }

  /**
   * Like [enterRule] but for recursive rules.
   *
   * Make the current context the child of the incoming [localctx].
   */
  public open fun pushNewRecursionContext(localctx: ParserRuleContext, state: Int, ruleIndex: Int) {
    val previous = context
    previous!!.setParent(localctx)
    previous.invokingState = state
    previous.stop = _input.LT(-1)

    context = localctx
    context!!.start = previous.start

    if (buildParseTree) {
      context!!.addChild(previous)
    }

    // Simulates rule entry for left-recursive rules
    triggerEnterRuleEvent()
  }

  public open fun unrollRecursionContexts(@Suppress("LocalVariableName") _parentctx: ParserRuleContext?) {
    _precedenceStack.pop()
    context!!.stop = _input.LT(-1)

    // Save current ctx (return value)
    val retCtx = context

    // Unroll so context is as it was before call to recursive method
    if (parseListeners.isNotEmpty()) {
      while (context !== _parentctx) {
        triggerExitRuleEvent()
        context = context!!.getParent()
      }
    } else {
      context = _parentctx
    }

    // Hook into tree
    retCtx!!.setParent(_parentctx)

    if (buildParseTree && _parentctx != null) {
      // Add return ctx into invoking rule's tree
      _parentctx.addChild(retCtx)
    }
  }

  public open fun getInvokingContext(ruleIndex: Int): ParserRuleContext? {
    var p = context

    while (p != null) {
      if (p.ruleIndex == ruleIndex) {
        return p
      }

      p = p.getParent()
    }

    return null
  }

  override fun precpred(localctx: RuleContext, precedence: Int): Boolean =
    precedence >= _precedenceStack.peek()

  public open fun inContext(context: String): Boolean {
    // TODO: useful in parser?
    return false
  }

  /**
   * Checks whether [symbol] can follow the current state in the ATN.
   *
   * The behavior of this method is equivalent to the following, but is
   * implemented such that the complete context-sensitive follow set does not
   * need to be explicitly constructed.
   *
   * ```
   * return expectedTokens.contains(symbol)
   * ```
   *
   * @param symbol The symbol type to check
   * @return `true` if [symbol] can follow the current state in the ATN, otherwise `false`
   */
  public open fun isExpectedToken(symbol: Int): Boolean {
    val atn = interpreter.atn
    var ctx = context
    val s = atn.states[state]
    var following = atn.nextTokens(s!!)

    if (following.contains(symbol)) {
      return true
    }

    if (!following.contains(Token.EPSILON)) {
      return false
    }

    while (ctx != null && ctx.invokingState >= 0 && following.contains(Token.EPSILON)) {
      val invokingState = atn.states[ctx.invokingState]
      val rt = invokingState!!.transition(0) as RuleTransition
      following = atn.nextTokens(rt.followState)

      if (following.contains(symbol)) {
        return true
      }

      ctx = ctx.getParent()
    }

    return following.contains(Token.EPSILON) && symbol == Token.EOF
  }

  /**
   * Get a rule's index (i.e., `RULE_ruleName` field) or `-1` if not found.
   */
  public open fun getRuleIndex(ruleName: String): Int {
    val ruleIndex = ruleIndexMap[ruleName]
    return ruleIndex ?: -1
  }

  /**
   * Return a list of strings of the rule names in your parser instance
   * leading up to a call to the current rule.
   *
   * You could override if you want more details such as the file/line info
   * of where in the ATN a rule is invoked.
   *
   * This is very useful for error messages.
   */
  public fun getRuleInvocationStack(ctx: RuleContext? = context): List {
    var p = ctx
    val ruleNames = ruleNames
    val stack = ArrayList()

    while (p != null) {
      // Compute what follows who invoked us
      val ruleIndex = p.ruleIndex

      if (ruleIndex < 0) {
        stack.add("n/a")
      } else {
        stack.add(ruleNames[ruleIndex])
      }

      p = p.getParent()
    }

    return stack
  }

  /**
   * For debugging and other purposes.
   */
  public fun dumpDFA(dumpStream: PrintStream = System.out) {
    synchronized(interpreter.decisionToDFA) {
      var seenOne = false

      for (d in 0..




© 2015 - 2024 Weber Informatics LLC | Privacy Policy