C# lexer与解析器之间的通信
每次我编写一个简单的lexer和parser时,我都会遇到同样的问题:lexer和parser应该如何通信?我看到四种不同的方法:C# lexer与解析器之间的通信,c#,c++,parsing,compiler-construction,lexer,C#,C++,Parsing,Compiler Construction,Lexer,每次我编写一个简单的lexer和parser时,我都会遇到同样的问题:lexer和parser应该如何通信?我看到四种不同的方法: lexer急切地将整个输入字符串转换为令牌向量。一旦这样做了,向量就被提供给解析器,解析器将其转换为树。这是迄今为止实现的最简单的解决方案,但由于所有令牌都存储在内存中,因此会浪费大量空间 每次lexer找到一个令牌时,它都会调用解析器上的一个函数,传递当前令牌。根据我的经验,只有当解析器可以像LALR解析器一样自然地实现为状态机时,这才有效。相比之下,我认为它对递
我的分析正确吗?还有其他我没想到的方法吗?在现实世界的编译器中使用了什么?如果像Eric Lippert这样的编译器编写者能够解释这个问题,那就太酷了。我在正在进行的toy buildsystem项目中处理它的方法是使用一个“file reader”类,带有一个函数
bool next\u标记(std::string&,const std::set&)
。此类包含一行输入(用于带有行号的错误报告目的)。函数接受一个std::string
引用以放入令牌,以及一个std::set
包含“令牌结束”字符。我的输入类是parser和lexer,但如果您需要更多的想象力,可以轻松地将其拆分。因此,解析函数只需调用next_token
,就可以完成它们的工作,包括非常详细的错误输出
如果您需要保留逐字输入,则需要将读取的每一行存储在向量中
或其他内容中,但不能单独和/或双y存储每个标记
我所说的代码位于此处:
(搜索
::next_token
和extract_nectar
函数是where is all begins)lexer-parser关系比的最一般情况更简单,因为通常通信是单向的;解析器不需要将信息发送回lexer。这就是为什么“急切生成”方法有效的原因(虽然这确实意味着可以更早地放弃输入,但会带来一些损失)
正如您所观察到的,如果lexer或parser可以以可重新命名的风格编写,那么另一个可以被视为一个简单的子例程。这始终可以作为源代码转换来实现,将局部变量转换为对象插槽
虽然C++对协同程序没有语言支持,但特别是可以使用库支持。Unixsetcontext
系列是一个选项;另一种方法是使用多线程,但使用同步队列(基本上是单线程,但在两个线程之间进行切换)。< P>也考虑到1,您不需要它的LeX令牌,例如如果存在错误,并且,您可能会在内存或I/O带宽上运行过低。我相信最好的解决方案是由Bison等工具生成的解析器使用的,解析器调用lexer来获取下一个令牌。最小化空间需求和内存带宽需求
这不值得。词法分析和解析本质上是同步的——只是没有足够的处理来证明通信成本的合理性。此外,通常您会同时解析/lex多个文件-这已经可以一次最大化所有核心。虽然我不会将上面的大部分内容归类为错误,但我确实认为有几项是误导性的
- 利益
- 在错误报告过程中可能提供更多信息
- 以允许在解析之前进行词法分析的方式编写的语言更易于指定和编写编译器
- 缺点
- 有些语言需要上下文敏感的词法分析器,在解析阶段之前根本无法操作