SOURCE

class ConditionParser {
    ops = ['>=', '<=', '>', '=', '<', 'in', 'like', '(', ')', '&&', '||']
    priority = {
        '>': 3,
        '>=': 3,
        '=': 3,
        '<': 3,
        '<=': 3,
        'like': 3,
        'in' : 3,
        '&&': 2,
        '||': 1,
        '(': 0,
        ')': 0,
    }
    getValue(str, token) {
        str = str.trim()
        if (/\{\s*([^\s]*)\s*\}/.test(str)) {
            token.push({
                type: 'ph',
                value: RegExp.$1
            })
        } else if (str) {
            token.push({
                type: 'value',
                value: str
            })
        }
    }

    parsTokens(exp) {
        let tmp = exp
        const token = []
        const esc = (val) => val.replace(/[()|]/g, '\\$&')
        const opsjoin = this.ops.map(i => esc(i)).join('|')
        const tokenReg = new RegExp(`\\s*(${opsjoin})`)
        while(tmp) {
            let idx = tmp.search(tokenReg)
            if (idx != - 1) {
                const substr = tmp.slice(0, idx)
                const nextIdx = RegExp['$&'].length + idx
               
                this.getValue(substr, token)
                const op = tmp.slice(idx, nextIdx).trim()
                token.push({
                    type: 'op',
                    value: op,
                    priority: this.priority[op]
                })
                tmp = tmp.slice(nextIdx)
            } else {
                this.getValue(tmp, token)
                tmp = ''
            }
        }
        return token
    }
    parse(exp) {
        const tokens = this.parsTokens(exp)
        const result = []
        const stack = []
        for (let i = 0; i < tokens.length; i++) {
            const token = tokens[i]
            if (/(value|ph)/.test(token.type)) {
                result.push(token)
            } else if (token.value === '(') {
                stack.push(token)
            } else if (token.value === ')') {
                let top = stack.pop()
                while(top && top.value !== '(') {
                    result.push(top)
                    top = stack.pop()
                }
                if (!top) {
                    console.log('括号不匹配')
                    stack = []
                    result = []
                    break;
                }
            } else {
                const top = stack[stack.length - 1]
                if (!top || top.priority < token.priority) {
                    stack.push(token)
                } else if (top && top.priority >= token.priority) {
                    result.push(top)
                    stack.pop()
                    stack.push(token)
                }
            }
        }
        while(stack.length) {
            const top = stack.pop()
            if (top.value === '(' || top.value === ')') {
                console.log('括号不匹配')
                stack = []
                result = []
                break
            } else {
                result.push(top)
            }
        }
        return this.buildCriteria(result)
    }
    buildCriteria(list) {
        let criterias = []
        let ops = []
        for (let i = 0; i < list.length; i++) {
            const token = list[i]
            if (token.type !== 'op') {
                ops.push(token)
            } else {
                if (!['&&', '||'].includes(token.value)) {
                    if (ops.length < 2) {
                        console.log('表达式不合法')
                        return null
                    } else {
                        const right = ops.pop()
                        const left = ops.pop()
                        if (token.value == '>') {
                            criterias.push(`Critera.where("${left.value}").gt(${Number(right.value) || 0})`)
                        } else if (token.value === '>=') {
                            criterias.push(`Critera.where("${left.value}").gte(${Number(right.value) || 0})`)
                        } else if (token.value === '<') {
                            criterias.push(`Critera.where("${left.value}").lt(${Number(right.value) || 0})`)
                        } else if (token.value === '<=') {
                            criterias.push(`Critera.where("${left.value}").lte(${Number(right.value) || 0})`)
                        } else if (token.value === '=') {
                            criterias.push(`Critera.where("${left.value}").eq(${Number(right.value) || 0})`)
                        } else if (token.value === 'in') {
                            criterias.push(`Critera.where("${left.value}").in(Arrays.asList("${right.value}".split(",")))`)
                        } else if (token.value === 'like') {
                            criterias.push(`Critera.where("${left.value}").regx()`)
                        }
                    }
                } else {
                    if (criterias.length < 2) {
                        console.log('表达式不合法')
                        return null
                    }
                    const r = criterias.pop()
                    const l = criterias.pop()
                    if (token.value === '&&') {
                        criterias.push(`new Criteria().andOperator(\n${l}, \n${r}\n)`)
                    } else if (token.value === '||') {
                        criterias.push(`new Criteria().orOperator(\n${l}, \n${r}\n)`)
                    }
                }
            }
        }
        return criterias[0]
    }
}

const exp = '{deptIds} in 1,2,3 && projectStatus in 4,5'
const d = new ConditionParser()
console.log(exp)
console.log(d.parse(exp))
console 命令行工具 X clear

                    
>
console