如何解决使用Dart petitparser构建表达式解析器,卡在节点访问者上
我的表达式解析器工作更多了(Dart PetitParser to get at AST datastructure created with ExpressionBuilder)。它似乎正在为数字,表达式前面的浮点数,括号,幂,乘,除,加,减,一元负数生成准确的AST。 (这些节点可以是文字字符串,也可以是具有优先级的对象,该对象的优先级被走和连接的List负载)。
我现在停留在访问节点上。我可以访问顶层节点(感谢Lukas),但是我一直在决定是否添加括号。例如,在20 + 30 * 40中,我们不需要30 * 40左右的解析器,而解析树正确地使该解析器的节点更靠近根,因此在遍历期间首先将其击中。但是,在查看30 * 40节点时,我似乎没有足够的数据来确定在进入20+节点之前是否需要parens。非常相似的情况是(20 + 30)* 40,得到可以正确解析,并且离根更近20 + 30,因此再次访问20 + 30节点时,我需要在添加到* 40之前添加括号。
这必须是一个已解决的问题,但是我从来没有上过编译器学校,所以我对AST非常了解,所以很危险。我想念什么“哈”?
// rip-common.dart:
import 'package:petitparser/petitparser.dart';
// import 'package:petitparser/debug.dart';
class Node {
int precedence;
List<dynamic> args;
Node([this.precedence = 0,this.args = const []]) {
// nodeList.add(this);
}
@override
String toString() => 'Node($precedence $args)';
String visit([int fromPrecedence = -1]) {
print('=== visiting $this ===');
var buf = StringBuffer();
var parens = (precedence > 0) &&
(fromPrecedence > 0) &&
(precedence < fromPrecedence);
print('<$fromPrecedence $precedence $parens>');
// for debugging:
var curlyOpen = '';
var curlyClose = '';
buf.write(parens ? '(' : curlyOpen);
for (var arg in args) {
if (arg is Node) {
buf.write(arg.visit(precedence));
} else if (arg is String) {
buf.write(arg);
} else {
print('not Node or String: $arg');
buf.write('$arg');
}
}
buf.write(parens ? ')' : curlyClose);
print('$buf for buf');
return '$buf';
}
}
class RIPParser {
Parser _make_parser() {
final builder = ExpressionBuilder();
var number = char('-').optional() &
digit().plus() &
(char('.') & digit().plus()).optional();
// precedence 5
builder.group()
..primitive(number.flatten().map((a) => Node(0,[a])))
..wrapper(char('('),char(')'),(l,a,r) => Node(0,[a]));
// negation is a prefix operator
// precedence 4
builder.group()..prefix(char('-').trim(),(op,a) => Node(4,[op,a]));
// power is right-associative
// precedence 3
builder.group()..right(char('^').trim(),(a,op,b) => Node(3,[a,b]));
// multiplication and addition are left-associative
// precedence 2
builder.group()
..left(char('*').trim(),b) => Node(2,b]))
..left(char('/').trim(),b]));
// precedence 1
builder.group()
..left(char('+').trim(),b) => Node(1,b]))
..left(char('-').trim(),b]));
final parser = builder.build().end();
return parser;
}
Result _result(String input) {
var parser = _make_parser(); // eventually cache
var result = parser.parse(input);
return result;
}
String parse(String input) {
var result = _result(input);
if (result.isFailure) {
return result.message;
} else {
print('result.value = ${result.value}');
return '$result';
}
}
String visit(String input) {
var result = _result(input);
var top_node = result.value; // result.isFailure ...
return top_node.visit();
}
}
// rip_cmd_example.dart
import 'dart:io';
import 'package:rip_common/rip_common.dart';
void main() {
print('start');
String input;
while (true) {
input = stdin.readLinesync();
if (input.isEmpty) {
break;
}
print(RIPParser().parse(input));
print(RIPParser().visit(input));
}
;
print('done');
}
解决方法
如您所见,ExpressionBuilder
已经根据您指定的运算符组以正确的优先顺序组装了树。
对于在此处创建的包装parens节点,也将发生这种情况:..wrapper(char('('),char(')'),(l,a,r) => Node(0,[a]))
。如果我对此节点进行测试,则会返回示例表达式的输入字符串:var parens = precedence == 0 && args.length == 1 && args[0] is Node;
。
除非我丢失了某些内容,否则您应该没有理由手动跟踪优先级。我还建议您为不同的运算符创建不同的节点类:ValueNode
,ParensNode
,NegNode
,PowNode
,MulNode
,...冗长,但是如果它们每个人都可以自己访问(打印,评估,优化...),则更容易理解发生了什么。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。