override fun visitAdditiveExpression()

in vim-engine/src/main/kotlin/com/maddyhome/idea/vim/vimscript/parser/visitors/ExpressionVisitor.kt [129:195]


  override fun visitAdditiveExpression(ctx: AdditiveExpressionContext): Expression {
    val leftExpression = visit(ctx.expr(0))
    val rightExpression = visit(ctx.expr(1))
    val rightText: String = ctx.expr(1).text
    val operatorString = ctx.additiveOperator().text

    // Concatenation is treated like an additive expression. I.e. `foo . bar` has the same precedence as `foo + bar`
    val result =
      when {
        operatorString == "."
          && !containsSpaces(ctx)
          && evaluationResultCouldBeADictionary(leftExpression)
          && matchesLiteralDictionaryKey(rightText) -> {

          // Expr-entry: mydict.key
          val index = SimpleExpression(rightText)
          IndexedExpression(index, leftExpression)
        }

        operatorString == "-"
          && leftExpression is IndexedExpression
          && !containsSpaces(ctx)
          && matchesLiteralDictionaryKey(rightText) -> {

          // Parse dict.left-right as a key called `left-right`
          // `dict.left` is first parsed as the left expression (sublist), and `right` is the right expression (simple)
          // E.g. `let dict = {'first-key': 42, 'second-key' : {'third-key': 'oh, hi Mark'}} | echo dict.first-key`
          // TODO: Vim does not handle this case. E716: Key not present in Dictionary: "first"
          val postfix = "-$rightText"
          val newIndex = SimpleExpression((leftExpression.index as SimpleExpression).data.toVimString().value + postfix)
          IndexedExpression(newIndex, leftExpression.expression)
        }

        operatorString == "."
          && !containsSpaces(ctx)
          && evaluationResultCouldBeADictionary(leftExpression)
          && rightExpression is IndexedExpression && matchesLiteralDictionaryKey(rightExpression.expression.originalString) -> {

          // Accessing a value in a Dictionary that's an element of a Dictionary, accessed by name. Recursive
          // leftExpression is a possible Dictionary: `mydict` or `{...}`
          // rightExpression is an index
          // leftExpression.rightExpression.rightIndex or leftExpression.rightExpression[leftIndex]
          IndexedExpression(
            rightExpression.index,
            IndexedExpression(SimpleExpression(rightExpression.expression.originalString), leftExpression)
          )
        }

        operatorString == "."
          && !containsSpaces(ctx)
          && rightExpression is NamedFunctionCallExpression
          && evaluationResultCouldBeADictionary(leftExpression) -> {

          // Dictionary-function: mydict.len()
          val index = rightExpression.functionName
          FuncrefCallExpression(IndexedExpression(index, leftExpression), rightExpression.arguments)
        }

        else -> {
          // `.` as string concatenation, `-` as subtraction
          val operator = BinaryOperator.getByValue(operatorString) ?: throw RuntimeException()
          BinExpression(leftExpression, rightExpression, operator)
        }
      }
    result.originalString = ctx.text
    return result
  }