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
}