使用 Flutter 的 PetiteParser 创建 FHIRPath
Using Flutter's PetiteParser to create FHIRPath
我想寻求一些使用 petitparser 的指导(我正在更新这个问题)。有一个名为 FHIRPath 的基于 json 的语法,我正试图在 dart 中重新创建它。我对这样的语法不熟悉,所以我花了一些时间来理解我想要它做什么(或者我认为我想要它做什么)。我设法让它解析 json 路径和一般函数,它看起来像这样:
class FhirPathGrammar extends GrammarDefinition {
Parser start() => ref0(value).end();
Parser value() => (ref0(parens) | ref0(dotString) | ref0(path)).star();
Parser parens() =>
(char('(') & ref0(value) & char(')')).map((value) => value);
Parser dotString() =>
(anyOf('-_') | letter() | digit() | range(0x80, 0x10FFF))
.plus()
.flatten();
Parser path() => (char('.') & ref0(dotString)).map((value) => value);
}
如果我运行这个函数:
void main() {
var pathString = 'Patient.name.exists()';
var definition = FhirPathGrammar();
final parser = definition.build();
print(parser.parse(pathString));
}
这是结果:
[Patient, [., name], [., exists], [(, [], )]]
到目前为止一切顺利。但是现在,如果我更改我的语法 class,并添加一个相等的解析器:
class FhirPathGrammar extends GrammarDefinition {
Parser start() => ref0(value).end();
Parser value() =>
(ref0(parens) | ref0(dotString) | ref0(path) | ref0(equal)).star();
Parser equal() =>
(ref0(value) & string(' = ') & ref0(value)).map((value) => value);
Parser parens() =>
(char('(') & ref0(value) & char(')')).map((value) => value);
Parser dotString() =>
(anyOf('-_') | letter() | digit() | range(0x80, 0x10FFF))
.plus()
.flatten();
Parser path() => (char('.') & ref0(dotString)).map((value) => value);
}
我得到一个错误:
Unhandled exception:
Stack Overflow
#0 ChoiceParser.parseOn package:petitparser/…/combinator/choice.dart:71
#1 PossessiveRepeatingParser.parseOn package:petitparser/…/repeater/possessive.dart:59
#2 FlattenParser.parseOn package:petitparser/…/action/flatten.dart:31
// Then these 4 lines repeat
#3 ChoiceParser.parseOn package:petitparser/…/combinator/choice.dart:69
#4 PossessiveRepeatingParser.parseOn package:petitparser/…/repeater/possessive.dart:67
#5 SequenceParser.parseOn package:petitparser/…/combinator/sequence.dart:39
#6 MapParser.parseOn package:petitparser/…/action/map.dart:38
// until it gets here
#9491 ChoiceParser.parseOn package:petitparser/…/combinator/choice.dart:69
#9492 PossessiveRepeatingParser.parseOn package:petitparser/…/repeater/possessive.dart:67
#9493 SequenceParser.parseOn package:petitparser/…/combinator/sequence.dart:39
#9494 PickParser.parseOn package:petitparser/…/action/pick.dart:26
#9495 CastParser.parseOn package:petitparser/…/action/cast.dart:17
#9496 Parser.parse package:petitparser/…/core/parser.dart:51
#9497 main fhir_path/also_main.dart:7
#9498 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
#9499 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
正如@lukas-renggli 指出的那样,它似乎进入了无限循环。所以至少我认为这就是正在发生的事情。但我不认为我理解它是如何匹配导致无限循环的。
如果没有一个最小的、可重现的例子,很难说出发生了什么。但是,我怀疑 star-operator 中选择的解析器之一总是在不消耗任何东西的情况下成功(ref0(empty)
看起来很可疑)。这会导致无限循环。
例如,以下解析器显示此类行为:
epsilon().star().parse(''); // loops forever
更新问题的答案:你的语法在 equals
生产中是 left-recursive。一个类似的、稍微简单一些的例子是:
// Loops forever: expression is recursively called without consuming anything.
Parser expression() => (ref0(expression) & char('+') & ref0(expression))
| digit();`
// Fixes the infinite loop by forcing the grammar to consume something
// at each step.
Parser expression() => digit().separatedBy(char('+'));
有多种方法可以修复您的示例,具体取决于您想要的确切行为。最简单的选择是重写语法,将等号作为其他选项之间的分隔符:
Parser value() => (ref0(parens) | ref0(dotString) | ref0(path))
.separatedBy(string(" = "));
我建议你看看Expression Builder。它可以简化构建表达式解析器并避免常见的陷阱。
我想寻求一些使用 petitparser 的指导(我正在更新这个问题)。有一个名为 FHIRPath 的基于 json 的语法,我正试图在 dart 中重新创建它。我对这样的语法不熟悉,所以我花了一些时间来理解我想要它做什么(或者我认为我想要它做什么)。我设法让它解析 json 路径和一般函数,它看起来像这样:
class FhirPathGrammar extends GrammarDefinition {
Parser start() => ref0(value).end();
Parser value() => (ref0(parens) | ref0(dotString) | ref0(path)).star();
Parser parens() =>
(char('(') & ref0(value) & char(')')).map((value) => value);
Parser dotString() =>
(anyOf('-_') | letter() | digit() | range(0x80, 0x10FFF))
.plus()
.flatten();
Parser path() => (char('.') & ref0(dotString)).map((value) => value);
}
如果我运行这个函数:
void main() {
var pathString = 'Patient.name.exists()';
var definition = FhirPathGrammar();
final parser = definition.build();
print(parser.parse(pathString));
}
这是结果:
[Patient, [., name], [., exists], [(, [], )]]
到目前为止一切顺利。但是现在,如果我更改我的语法 class,并添加一个相等的解析器:
class FhirPathGrammar extends GrammarDefinition {
Parser start() => ref0(value).end();
Parser value() =>
(ref0(parens) | ref0(dotString) | ref0(path) | ref0(equal)).star();
Parser equal() =>
(ref0(value) & string(' = ') & ref0(value)).map((value) => value);
Parser parens() =>
(char('(') & ref0(value) & char(')')).map((value) => value);
Parser dotString() =>
(anyOf('-_') | letter() | digit() | range(0x80, 0x10FFF))
.plus()
.flatten();
Parser path() => (char('.') & ref0(dotString)).map((value) => value);
}
我得到一个错误:
Unhandled exception:
Stack Overflow
#0 ChoiceParser.parseOn package:petitparser/…/combinator/choice.dart:71
#1 PossessiveRepeatingParser.parseOn package:petitparser/…/repeater/possessive.dart:59
#2 FlattenParser.parseOn package:petitparser/…/action/flatten.dart:31
// Then these 4 lines repeat
#3 ChoiceParser.parseOn package:petitparser/…/combinator/choice.dart:69
#4 PossessiveRepeatingParser.parseOn package:petitparser/…/repeater/possessive.dart:67
#5 SequenceParser.parseOn package:petitparser/…/combinator/sequence.dart:39
#6 MapParser.parseOn package:petitparser/…/action/map.dart:38
// until it gets here
#9491 ChoiceParser.parseOn package:petitparser/…/combinator/choice.dart:69
#9492 PossessiveRepeatingParser.parseOn package:petitparser/…/repeater/possessive.dart:67
#9493 SequenceParser.parseOn package:petitparser/…/combinator/sequence.dart:39
#9494 PickParser.parseOn package:petitparser/…/action/pick.dart:26
#9495 CastParser.parseOn package:petitparser/…/action/cast.dart:17
#9496 Parser.parse package:petitparser/…/core/parser.dart:51
#9497 main fhir_path/also_main.dart:7
#9498 _delayEntrypointInvocation.<anonymous closure> (dart:isolate-patch/isolate_patch.dart:283:19)
#9499 _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
正如@lukas-renggli 指出的那样,它似乎进入了无限循环。所以至少我认为这就是正在发生的事情。但我不认为我理解它是如何匹配导致无限循环的。
如果没有一个最小的、可重现的例子,很难说出发生了什么。但是,我怀疑 star-operator 中选择的解析器之一总是在不消耗任何东西的情况下成功(ref0(empty)
看起来很可疑)。这会导致无限循环。
例如,以下解析器显示此类行为:
epsilon().star().parse(''); // loops forever
更新问题的答案:你的语法在 equals
生产中是 left-recursive。一个类似的、稍微简单一些的例子是:
// Loops forever: expression is recursively called without consuming anything.
Parser expression() => (ref0(expression) & char('+') & ref0(expression))
| digit();`
// Fixes the infinite loop by forcing the grammar to consume something
// at each step.
Parser expression() => digit().separatedBy(char('+'));
有多种方法可以修复您的示例,具体取决于您想要的确切行为。最简单的选择是重写语法,将等号作为其他选项之间的分隔符:
Parser value() => (ref0(parens) | ref0(dotString) | ref0(path))
.separatedBy(string(" = "));
我建议你看看Expression Builder。它可以简化构建表达式解析器并避免常见的陷阱。