You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Kotlin开发自定义编程语言:高效处理运算符的方法求助

自定义编程语言运算符处理优化方案

问题背景

用Kotlin开发自定义编程语言时,手动处理运算符(如加法)的代码臃肿且扩展性差,无法支持PEMDAS运算优先级,也难以处理字符串与数字混合的表达式(例如var1 = "The sum is: " + someIntegerVariable - (5 + 10) / 5 * 20 + " !")。

当前加法处理的冗余代码示例:

// inside the lexer
val additionPattern = Regex("""(\w+)\s*=\s*((?:\"[^\"]*\"|\d+|\w+)(?:\s*\+\s*(?:\"[^\"]*\"|\d+|\w+))*)""")

additionPattern.matchEntire(code)?.let {
        val varName = it.groups[1]!!.value
        val parts = it.groups[2]!!.value.split("+").map { it.trim() }
        return listOf("multi_addition", varName) + parts
    }
// inside the interpreter function
"multi_addition" -> {
            val varName = tokens[1]
            val parts = tokens.drop(2)
            if (parts[0].startsWith("\"")) {
                var result = parts[0].removeSurrounding("\"")
                var sum = 0
                var addingNumbers = false

                for (part in parts.drop(1)) {
                    when {
                        part.toDoubleOrNull() != null -> {
                            if (!addingNumbers) {
                                addingNumbers = true
                                sum = part.toInt()
                            } else {
                                sum += part.toInt()
                            }
                        }
                        variables.containsKey(part) -> {
                            when (val value = variables[part]) {
                                is InterpreterValue.NumberValue -> {
                                    if (!addingNumbers) {
                                        addingNumbers = true
                                        sum = value.value.toInt()
                                    } else {
                                        sum += value.value.toInt()
                                    }
                                }
                                is InterpreterValue.StringValue -> {
                                    if (addingNumbers) {
                                        result += sum.toString()
                                        addingNumbers = false
                                        sum = 0
                                    }
                                    result += value.value
                                }
                                else -> throw IllegalArgumentException("Invalid token in addition: '$part'")
                            }
                        }
                        part.startsWith("\"") -> {
                            if (addingNumbers) {
                                result += sum.toString()
                                addingNumbers = false
                                sum = 0
                            }
                            result += part.removeSurrounding("\"")
                        }
                        else -> throw IllegalArgumentException("Invalid token in addition: '$part'")
                    }
                }

                if (addingNumbers) {
                    result += sum.toString()
                }

                variables[varName] = InterpreterValue.StringValue(result)
            } else {
                var sum = 0.0
                for (part in parts) {
                    when {
                        part.toDoubleOrNull() != null -> sum += part.toDouble()
                        variables.containsKey(part) -> {
                            when (val value = variables[part]) {
                                is InterpreterValue.NumberValue -> sum += value.value
                                else -> throw IllegalArgumentException("Invalid token in numeric addition: '$part'")
                            }
                        }
                        else -> throw IllegalArgumentException("Invalid token in numeric addition: '$part'")
                    }
                }
                variables[varName] = InterpreterValue.NumberValue(sum)
            }
        }

优化思路

采用编译器标准流程替代手动正则匹配,实现可扩展的运算符处理:

  • 词法分析器(Lexer):将代码转换为标准化Token序列
  • 语法分析器(Parser):基于Token构建抽象语法树(AST),天然支持PEMDAS优先级
  • 求值器(Evaluator):递归遍历AST,统一处理不同类型的混合运算

具体实现

1. 定义Token类型

枚举所有需要的语法单元:

enum class TokenType {
    NUMBER, STRING, IDENTIFIER,
    PLUS, MINUS, MULTIPLY, DIVIDE,
    LPAREN, RPAREN, ASSIGN, EOF
}

data class Token(val type: TokenType, val value: String)

2. 实现词法分析器(Lexer)

将输入代码拆解为Token序列:

class Lexer(private val input: String) {
    private var pos = 0
    private var currentChar: Char? = input.getOrNull(pos)

    fun nextToken(): Token {
        while (currentChar?.isWhitespace() == true) advance()

        return when (currentChar) {
            '+' -> advanceAndReturn(TokenType.PLUS)
            '-' -> advanceAndReturn(TokenType.MINUS)
            '*' -> advanceAndReturn(TokenType.MULTIPLY)
            '/' -> advanceAndReturn(TokenType.DIVIDE)
            '(' -> advanceAndReturn(TokenType.LPAREN)
            ')' -> advanceAndReturn(TokenType.RPAREN)
            '=' -> advanceAndReturn(TokenType.ASSIGN)
            '"' -> readString()
            in '0'..'9' -> readNumber()
            in 'a'..'z', in 'A'..'Z', '_' -> readIdentifier()
            null -> Token(TokenType.EOF, "")
            else -> throw IllegalArgumentException("Unexpected character: $currentChar")
        }
    }

    private fun advance() {
        pos++
        currentChar = input.getOrNull(pos)
    }

    private fun advanceAndReturn(type: TokenType): Token {
        val char = currentChar!!
        advance()
        return Token(type, char.toString())
    }

    private fun readString(): Token {
        advance() // 跳过左引号
        val startPos = pos
        while (currentChar != '"' && currentChar != null) advance()
        val value = input.substring(startPos, pos)
        advance() // 跳过右引号
        return Token(TokenType.STRING, value)
    }

    private fun readNumber(): Token {
        val startPos = pos
        while (currentChar in '0'..'9' || currentChar == '.') advance()
        val value = input.substring(startPos, pos)
        return Token(TokenType.NUMBER, value)
    }

    private fun readIdentifier(): Token {
        val startPos = pos
        while (currentChar in 'a'..'z' || currentChar in 'A'..'Z' || currentChar == '_' || currentChar in '0'..'9') advance()
        val value = input.substring(startPos, pos)
        return Token(TokenType.IDENTIFIER, value)
    }
}

3. 定义抽象语法树(AST)节点

用密封类表示不同类型的表达式:

sealed class Expr {
    data class Assign(val name: String, val value: Expr) : Expr()
    data class Binary(val left: Expr, val op: TokenType, val right: Expr) : Expr()
    data class NumberLiteral(val value: Double) : Expr()
    data class StringLiteral(val value: String) : Expr()
    data class Identifier(val name: String) : Expr()
}

4. 实现语法分析器(Parser)

构建AST并处理PEMDAS优先级:

class Parser(private val lexer: Lexer) {
    private var currentToken = lexer.nextToken()

    fun parse(): Expr {
        val expr = parseAssignment()
        if (currentToken.type != TokenType.EOF) throw IllegalArgumentException("Unexpected token after expression")
        return expr
    }

    private fun parseAssignment(): Expr {
        val expr = parseExpression()
        if (currentToken.type == TokenType.ASSIGN) {
            eat(TokenType.ASSIGN)
            val right = parseExpression()
            if (expr !is Expr.Identifier) throw IllegalArgumentException("赋值左侧必须是标识符")
            return Expr.Assign(expr.name, right)
        }
        return expr
    }

    private fun parseExpression(): Expr {
        var expr = parseTerm()
        while (currentToken.type in listOf(TokenType.PLUS, TokenType.MINUS)) {
            val op = currentToken.type
            eat(currentToken.type)
            val term = parseTerm()
            expr = Expr.Binary(expr, op, term)
        }
        return expr
    }

    private fun parseTerm(): Expr {
        var expr = parseFactor()
        while (currentToken.type in listOf(TokenType.MULTIPLY, TokenType.DIVIDE)) {
            val op = currentToken.type
            eat(currentToken.type)
            val factor = parseFactor()
            expr = Expr.Binary(expr, op, factor)
        }
        return expr
    }

    private fun parseFactor(): Expr {
        return when (currentToken.type) {
            TokenType.NUMBER -> {
                val num = Expr.NumberLiteral(currentToken.value.toDouble())
                eat(TokenType.NUMBER)
                num
            }
            TokenType.STRING -> {
                val str = Expr.StringLiteral(currentToken.value)
                eat(TokenType.STRING)
                str
            }
            TokenType.IDENTIFIER -> {
                val id = Expr.Identifier(currentToken.value)
                eat(TokenType.IDENTIFIER)
                id
            }
            TokenType.LPAREN -> {
                eat(TokenType.LPAREN)
                val expr = parseExpression()
                eat(TokenType.RPAREN)
                expr
            }
            else -> throw IllegalArgumentException("Unexpected token: ${currentToken.type}")
        }
    }

    private fun eat(type: TokenType) {
        if (currentToken.type == type) {
            currentToken = lexer.nextToken()
        } else {
            throw IllegalArgumentException("Expected token $type but got ${currentToken.type}")
        }
    }
}

5. 实现求值器(Evaluator)

递归求值AST,处理类型混合运算:

sealed class InterpreterValue {
    data class NumberValue(val value: Double) : InterpreterValue()
    data class StringValue(val value: String) : InterpreterValue()
}

class Evaluator(private val variables: MutableMap<String, InterpreterValue>) {
    fun evaluate(expr: Expr): InterpreterValue {
        return when (expr) {
            is Expr.Assign -> {
                val value = evaluate(expr.value)
                variables[expr.name] = value
                value
            }
            is Expr.Binary -> {
                val left = evaluate(expr.left)
                val right = evaluate(expr.right)
                when (expr.op) {
                    TokenType.PLUS -> handleAddition(left, right)
                    TokenType.MINUS -> handleSubtraction(left, right)
                    TokenType.MULTIPLY -> handleMultiplication(left, right)
                    TokenType.DIVIDE -> handleDivision(left, right)
                    else -> throw IllegalArgumentException("Unsupported binary operator: ${expr.op}")
                }
            }
            is Expr.NumberLiteral -> InterpreterValue.NumberValue(expr.value)
            is Expr.StringLiteral -> InterpreterValue.StringValue(expr.value)
            is Expr.Identifier -> variables[expr.name] ?: throw IllegalArgumentException("未定义变量: ${expr.name}")
        }
    }

    private fun handleAddition(left: InterpreterValue, right: InterpreterValue): InterpreterValue {
        return when (left) {
            is InterpreterValue.StringValue -> {
                val rightStr = when (right) {
                    is InterpreterValue.StringValue -> right.value
                    is InterpreterValue.NumberValue -> right.value.toString()
                }
                InterpreterValue.StringValue(left.value + rightStr)
            }
            is InterpreterValue.NumberValue -> {
                when (right) {
                    is InterpreterValue.NumberValue -> InterpreterValue.NumberValue(left.value + right.value)
                    is InterpreterValue.StringValue -> InterpreterValue.StringValue(left.value.toString() + right.value)
                }
            }
        }
    }

    private fun handleSubtraction(left: InterpreterValue, right: InterpreterValue): InterpreterValue {
        if (left is InterpreterValue.NumberValue && right is InterpreterValue.NumberValue) {
            return InterpreterValue.NumberValue(left.value - right.value)
        }
        throw IllegalArgumentException("减法仅支持数字类型")
    }

    private fun handleMultiplication(left: InterpreterValue, right: InterpreterValue): InterpreterValue {
        if (left is InterpreterValue.NumberValue && right is InterpreterValue.NumberValue) {
            return InterpreterValue.NumberValue(left.value * right.value)
        }
        throw IllegalArgumentException("乘法仅支持数字类型")
    }

    private fun handleDivision(left: InterpreterValue, right: InterpreterValue): InterpreterValue {
        if (left is InterpreterValue.NumberValue && right is InterpreterValue.NumberValue) {
            if (right.value == 0.0) throw ArithmeticException("除以零")
            return InterpreterValue.NumberValue(left.value / right.value)
        }
        throw IllegalArgumentException("除法仅支持数字类型")
    }
}

6. 使用示例

fun main() {
    val code = """var1 = "The sum is: " + someIntegerVariable - (5 + 10) / 5 * 20 + " !""""
    val lexer = Lexer(code)
    val parser = Parser(lexer)
    val ast = parser.parse()
    
    val variables = mutableMapOf("someIntegerVariable" to InterpreterValue.NumberValue(100.0))
    val evaluator = Evaluator(variables)
    val result = evaluator.evaluate(ast)
    
    println(result) // 输出 StringValue(value="The sum is: 100-60 !")
}

内容的提问来源于stack exchange,提问作者EndoBotM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.16 14:54:52