如何使用Flex实施两遍扫描仪?
作为一个宠物项目,我想尝试实现自己设计的基本语言,该语言可以用作网络脚本语言。将C ++程序作为Apache CGI运行很简单,因此真正的工作在于如何解析包含非代码(HTML / CSS标记)和服务器端代码的输入文件。
在我的本科编译器课程中,我们使用Flex和Bison生成了用于简单语言的扫描器和解析器。我们得到了语法的副本,并编写了一个解析器,将简单的语言转换为虚拟机的简单程序集。 Flex扫描器将输入标记化,然后将标记传递给Bison解析器。
那和我想做的事情之间的区别是,像PHP一样,这种语言可以具有简单的HTML标记,而脚本语言则像下面这样散布:
<p>Hello, <? echo "World ?> </p>
我是否认为按如下所述解析输入文件会比较有效,这是不正确的吗?
- 控制权返回到继续该常规模式的第一台扫描仪。
基本上,第一个扫描器仅区分标记(未经修改直接返回到浏览器)和代码,后者传递给第二个扫描器,后者依次标记代码并将标记传递给解析器。
如果这不是可靠的设计模式,那么PHP之类的语言如何有效地处理扫描输入和解析代码?
解决方案
PHP不会区分扫描和标记。在标记模式下,它仅输出到缓冲区,而在代码模式下,则切换到解析。我们不需要两遍扫描仪,只需一个flex lexer即可完成此操作。
如果我们对PHP本身的工作方式感兴趣,请下载源代码(尝试使用PHP4源代码更容易理解)。我们要查看的是Zend目录" zend_language_scanner.l"。
写过类似的文章后,我真的建议我们重新考虑走Flex和Bison路线,并选择类似Antlr的现代语言。它更容易,更容易理解(lex语法中使用的宏变得非常混乱且难以阅读),并且具有内置的调试器(AntlrWorks),因此我们不必花费数小时来查看3个Meg调试文件。它还支持多种语言(Java,c#,C,Python,Actionscript),并且有一本出色的书和一个很好的网站,应该能够立即使我们启动并运行。
我们想查看开始条件。例如:
"<?" { BEGIN (PHP); } <PHP>[a-zA-Z]* { return PHP_TOKEN; } <PHP>">?" { BEGIN (0); } [a-zA-Z]* { return HTML_TOKEN; }
我们从状态0开始,使用BEGIN宏更改状态。
要仅在处于特定状态时匹配RE,请在RE前面加上由尖括号括起来的状态名称。
在上面的示例中," PHP"为状态。 " PHP_TOKEN"和" HTML_TOKEN"是yacc文件所定义的_%token_s。