验证一个表达式

3

考虑一个包含运算符、函数和操作数的表达式,例如:

2 + sin ( max ( 2, 3 ) / 3 * 3.1415 )

如何通过编程验证表达式,使得任何函数都必须有正确数量的参数?例如abs、sin、cos必须恰好有1个参数,而sum、avg、max、min必须有2个或更多。考虑到每个参数本身可能是非常复杂的表达式,因此在程序上确定这一点似乎并不容易。我已经编写了一个词法分析器(lexer),并成功将表达式转换为后缀/RPN形式(即:2 3 max 3 / 3.1415 * sin 2 +)。但我仍然没有找到解决方案。我希望能得到一些代码或伪代码,以指导我从头开始编写。最好使用Java。以下是我的词法分析器代码:
    public static List<Token> shunt(List<Token> tokens) throws Exception {
    List<Token> rpn = new ArrayList<Token>();
    Iterator<Token> it = tokens.iterator();
    Stack<Token> stack = new Stack<Token>();
    while (it.hasNext()) {
        Token token = it.next();
        if (Type.NUMBER.equals(token.type))
            rpn.add(token);
        if (Type.FUNCTION.equals(token.type) || Type.LPAREN.equals(token.type)) 
            stack.push(token);
        if (Type.COMMA.equals(token.type)) {
            while (!stack.isEmpty() && !Type.LPAREN.equals(stack.peek().type))
                rpn.add(stack.pop());
            if (stack.isEmpty()) 
                throw new Exception("Missing left parenthesis!");
        }
        if (Type.OPERATOR.equals(token.type)) {
            while (!stack.isEmpty() && Type.OPERATOR.equals(stack.peek().type))
                rpn.add(stack.pop());
            stack.add(token);
        }
        if (Type.RPAREN.equals(token.type)) {
            while (!stack.isEmpty() && !Type.LPAREN.equals(stack.peek().type))
                rpn.add(stack.pop());
            if (stack.isEmpty()) 
                throw new Exception("Missing left parenthesis!");
            stack.pop();
            if (!stack.isEmpty() && Type.FUNCTION.equals(stack.peek().type))
                rpn.add(stack.pop());
        }
    }
    while (!stack.isEmpty()) {
        if (Type.LPAREN.equals(stack.peek().type) || Type.RPAREN.equals(stack.peek().type))
            throw new Exception("Mismatched parenthesis!");
        rpn.add(stack.pop());
    }

    return rpn;
}

你可能需要提供更多关于你希望实现什么的信息。你是想在JAVA中编写一个编译器吗? - Araymer
Weston,你是在暗示我可以使用词法分析器的输出直接确定函数是否具有正确数量的参数吗? - bitsmcgee77
如果你已经成功地转换成后缀表达式,那么你肯定已经实现了一些基本的解析器。因此,你应该已经知道每个函数有多少个参数——从 Map 中查找所需参数的数量不应该太难。 - Erwin Bolwidt
@weston 2 3 最大值 3 / 3.1415 * 正弦 2 + - bitsmcgee77
我猜你正在使用逆波兰算法?在那个点上需要找到正确的参数数量。请展示你的代码。 - weston
显示剩余6条评论
2个回答

1
你需要做的是实现一个精确的解析器,它知道你的语言的确切语法(包括“一个函数有多少运算符”)。
对于表达式来说,编写这样的解析器很容易。请参见https://dev59.com/v3E95IYBdhLWcg3wlu6z#2336769

我之前看到过那个。我很惭愧地承认我仍然觉得它很困惑。您能否在我的计算函数参数问题的上下文中给我一个例子? - bitsmcgee77
你定义一个描述表达式的语法。规则描述了表达式的各种选项。一些规则描述了内置函数,例如sin和max。例如,可以有语法规则T ='sin' '(' exp ')' ;T ='max' '(' exp ',' exp ')' ;其中,T是语法中的一个项。这些语法规则隐含地编码了函数的正确参数数量。强制执行这些规则的解析器将自动执行正确数量的参数;如果参数计数错误,则解析器将生成语法错误。 - Ira Baxter
非常感谢,我会尝试使用这些示例规则来更好地理解您的其他帖子。我很好奇,如果您可以有1 + n个参数,规则会是什么? - bitsmcgee77
1
另一种方法是定义一个语法规则 **T = IDENTIFIER '(' expressions ')' ;**,其中 **expressions = expression ( ',' expression );**。这将允许解析器接受 sin(2,3) 和 max(1),这显然是不正确的。您可以通过在 ... IDENTIFIER '(' expressions ') ... 规则的逻辑中添加一个特别检查来解决此问题,该检查在函数到参数计数表中查找标识符,在 expressions 中计算 exp 的数量,并在它们不匹配时发出警告。如果您有很多函数,则可以使用此技术。如果只有几个,请使用先前的方法。 - Ira Baxter
“n+1” 参数?你的意思是,该函数有 n 个参数属于同一类型,并且额外增加一个参数?如果 n 是一个常数,你可以将 n+1 视为常数进行检查。否则,您可以编写规则 T = nplus1args '(' expressions ',' exp ')';并验证表达式的数量为“n”。 - Ira Baxter
显示剩余2条评论

0

你需要在Shunting Yard中检测它。一个快速的想法是,在操作符堆栈上,对每个元素保持一个计数器。计算检测到的逗号数。然后,在闭合括号或末尾时,检查每个函数入口的参数数量。

另一种选择可能是将更多的信息作为您的RPN的附加值保留下来。例如,保留逗号,则可以获得:

2 , 3 max 3 / 3.1415 * sin 2 +

在处理函数时,它不仅必须从堆栈中获取值,还必须获取正确数量的,。如果太多了,以后会显示出来。

我担心这种方式可能有一些边缘情况,所以最好使用精确的解析器。

sin(1,2) * max (3)

1 , 2 sin 3 max *

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接