假设我们有两个代码段:
long l = 0L;
string sqlStr = "SELECT Column1, Column2 FROM Table ORDER BY Column3 DESC";
int i = 0;
// ... C# code goes on
和
/// <summary>
/// This method does something.
/// </summary>
/// <param name="a">The a parameter.</param>
/// <param name="b">The b parameter.</param>
/// <exception cref="ArgumentNullException">
/// Thrown when <paramref name="b"/> is <c>null</c>.
/// </exception>
public static void DoSomething(int a, string b)
{
// ... etc
}
假设我已经编写了C#,SQL和XML扫描程序(我没有写,但这不是这个问题的重点)。
当此假设的C#扫描器通过内联SQL遇到sqlStr
字符串内容时,它必须调用/切换到SQL扫描器(听起来很奇怪,但请耐心;必须这样做),以SQL语言分析内容,然后转回并继续扫描C#。
同样,当在DoSomething
方法上找到XML documentation comment时,C#扫描程序必须切换到XML扫描程序并生成XML令牌,然后完成此操作,继续使用C#。
(显然,正在使用的应用程序将区分C#,SQL和XML令牌)
我的问题:
答案 0 :(得分:1)
(f)lex接口不会将缓冲区管理与词法分析分开,因此,没有机制可以在扫描过程中仅“调用另一个词法分析器”。 [注1]
但是,您可以使用start conditions轻松地使生成的扫描程序装备以处理不同的词法上下文。每个条件定义了一组完全独立的规则,因此只要可以使扫描程序使用相同的语义值类型(在C中,这将涉及扩展语义并集),则可以将任意数量的不同令牌集打包成一个生成的语法。 [注2]
棘手的部分是正确完成过渡,即使接口更加协作,也将有些棘手。由普通解析器生成器生成的LALR(1)语法依赖于提前读取标记的提前读取。执行解析器动作时,下一个输入令牌通常将已经被处理。因此,如果解析器操作调用扫描程序以更改状态,则直到先行令牌之后之前,该状态更改通常不会发生。
但是即使那样也不能保证,因为在无论是否跟随什么令牌都将执行缩减的情况下,解析器可能选择执行缩减操作而不要求先行令牌。 Bison实现了这种优化,但并不是所有的解析器生成器都执行此优化,而且并非所有解析器生成器都记录是否执行此操作。
因此,在所有可能的情况下,使分隔符字符串成为相同的标记是很有用的。如果无法做到这一点,则也可能以两种标记都具有相同效果的方式编写语法。视情况而定,还有其他各种解析器/扫描器专用的黑客工具。
除非在极少数情况下,否则将不可能在扫描器操作中触发转换,因为这将需要将大部分解析复制到扫描器中。但是在极少数情况下,当分隔符字符串特别可识别时,这是有可能的。
所有这些策略的例子都是可用的,包括flex本身的代码(需要在一个上下文中识别正则表达式,在另一个上下文中识别嵌入式C代码)。
很难批评这么久以前做出的设计决定。这是完全可以理解的。但是恕我直言,很不幸,我们仍然要忍受它。控制流倒置扫描器对于各种应用程序很有用,例如从事件循环内部的输入流进行联机解析。
使用flex,您不必使启动条件完全独立。条件可以是“排他的”(仅适用于该条件中专门标记的模式)或非排他的(也适用未标记的模式)。您采取哪种选择部分取决于令牌化的相似程度。