我正在使用boost spirit lexer和grammar编写PDF文件格式的读者。
问题是,这种语法是一种上下文敏感的语法。 实际上,有一个名为Stream dictionary的对象,它具有以下结构:
<< - begin
*( - zero or more times
/NAME - being the key of dictionary
VALUE - being the value of the key in dictionary
)
>> - end
stream - keyword
DATA
endstream - keyword
因此,流中的数据具有字典中定义的大小,例如:
<</LENGTH 4>>
stream
aaaaendstream
现在问题。我需要告诉语法,跳过n个字符。 以下是解析此类对象的规则。
stream_object %=
dictionary_object
>> whitespaces
>> lexer.stream_begin
> eol
> qi::repeat(159)["a-z"]
> lexer.stream_end;
据我所知,这个语法中的每个操作都使用定义的输入词法分析器,并且'qi :: repeat(159)[“az”]'行期望一个字符完全失败,因为词法分析器不知道这样的序列。
我有多个想法,但都错了。
例如,在遇到“stream”标记后更改词法分析器状态。
this->self += stream_begin[lex::_state = "STREAM_BEGIN"];
this->self("STREAM_BEGIN") = stream_end[lex::_state = initial_state()] | character;
这在某种程度上有效,除非数据 AND 中有“endstream”标记,它还会尝试匹配数据内部每个字符序列的endstream,这会大大减慢解析速度。
接下来的方法是彻底放弃词法分析器。
stream_object %=
dictionary_object
>> whitespaces
>> lit("stream")
> lit("\r?\n")
> qi::repeat(159)["a-z"]
> lit("endstream");
这样可行,但我不喜欢仅针对单个愚蠢规则放弃词法分析器的想法。此外,由于令牌backtracking,我在阅读使用lit而不是lexer时的性能下降。
最后一种方法是忽略这样的对象,并只解析字典部分。成功解析字典对象后检查后续标记,并在没有标记化器的情况下读取其余数据,如第二种方法所示。
我真的很想看到某种“向前搜索”或“暂时忽略lexer”指令,能够跳过部分输入,而不会将解析过程分成多个位置,或者引入强迫性开销。 有这样的事吗?对每种方法的思考都表示赞赏。
Lexer Code
typedef boost::spirit::istream_iterator base_iterator_type;
typedef boost::spirit::classic::position_iterator2<base_iterator_type> pos_iterator_type;
typedef boost::spirit::lex::lexertl::token<pos_iterator_type> token_type;
typedef boost::spirit::lex::lexertl::actor_lexer<token_type> lexer_type;
class SpiritLexer : public boost::spirit::lex::lexer<lexer_type> {...}
语法代码
struct SpiritGrammar : qi::grammar<pos_iterator_type> {...}
用法
SpiritLexer lexer;
SpiritGrammar grammar(lexer);
auto result = lex::tokenize_and_parse(input_begin_pos, input_end_pos, lexer, grammar, obj);
答案 0 :(得分:1)
你能考虑通过移动寻求外部解析/ lexing的流来简化它吗?
当你正在考虑它时,重新考虑对词法分析者的需求? (啊,我明白了,你已经考虑过了。我会说,在这个部门很好,除非你知道你需要它的东西)。
暂时禁用词法分析器(显然)是不可能的。您只需从不同的点/状态重新启动lexing即可。我甚至得出结论,一些基本的指令(无证件,AFAIR)将Lexer状态转换为Parser语义动作,如果有的话,就不能有效地工作
避免使用词法分析器可以使用 spirit::repository::qi
中的指令,这些指令似乎与您所寻找的相近:
Spirit.Qi advance
is a primitive parser component 允许解析器跳过(前进)指定的迭代次数而不执行不必要的工作:
seek[]
parser-directive 会跳过所有输入,直到主题解析器匹配。
从概念上讲,对我而言,您似乎很清楚,您正在混合解析和方向解析。这表明它们应该处于不同的抽象层次。
答案 1 :(得分:1)
这是对您的任务的更多评论
使用boost spirit lexer和grammar为PDF文件格式编写阅读器。
而不是对具体问题的回答。但是,这对评论来说太大了。
如果您的任务确实要解析通用PDF文件(而不仅仅是由一组受控制的PDF生成器生成的文件,那么您的方法可能会因多种原因而注定失败,特别是间接对象的规范起始位置列在交叉引用中表格或流。主交叉引用表/流在文档的末尾引用。此外,交叉引用流可以被压缩,需要在完成解析之前进行选择性解压缩。
如果忽略这些表并尝试通过前后解析识别间接对象(这是我理解的方法),您可能会感到惊讶。特别是如果有多个具有相同ID的对象,则无法确定哪个是实际的,哪些被忽略。
此外,引用间接对象的可能性会让您陷入困境。
E.g。你的例子:
<</LENGTH 4>> stream aaaaendstream
也可能如下所示:
<</LENGTH 5 0 R>>
stream
aaaaendstream
...
5 0 obj
4
endobj
因此,您必须预先知道您的信息流有多长。