我一直在开发支持嵌入式SQL语句的COBOL语法。对于不熟悉COBOL的人来说,这是一个例子。
MOVE A TO B.
EXEC SQL
SELECT C FROM T WHERE ID=1
INTO :E
END-EXEC
MOVE F TO G
“EXEC SQL”和“END-EXEC”之间的代码使用(特别增强的)SQL语法,这是岛语法的完美示例。
我知道这可以通过ANTLR4中的Lexer模式实现。但我还有另一个要求,即SQL语法应该与COBOL语法分开,以便在嵌入其他语言(如PL1)时可以重用SQL语法,而无需复制粘贴编程。
所以我所做的是使用简单的词法分析器模式捕获“EXEC SQL”和“END-EXEC”之间的任何内容,将SQL代码解压缩为String并将其提供给单独的SQL词法分析器(和解析器)。
这样做有一个缺点 - 在SQL解析器中识别的标记的行号和char索引从提取的SQL代码字符串的开头计算,而不是从原始的COBOL程序开始计算。在追溯到源代码时,例如,如果存在解析错误,则行号变为错误引导。
所以问题是:在ANTLR 4中是否有一种更简单的方法可以单独实现孤岛语法(lexer和parser分离),但仍然保留为岛屿部分生成的标记中的正确行号和char索引?
更新:我发现ANTLR 4中有语法导入功能,我的同事告诉我,我们一直在尝试,但失败了。问题是 - 导入语法中的词法分析器模式不受支持,这会导致编译错误。正在跟踪此问题here。
答案 0 :(得分:1)
要扩展Bill的注释,在实例化SQL解析器/词法分析器时,将EXEC块开头的行偏移传递给它。实现一个自定义SQL令牌,该令牌将行号报告为偏移量加上SQL文本相对行号。让您的SQL TokenFactory将偏移量作为常量注入到生成的每个标记中。
<强>更新强>
使用模式实现惯用岛语法,无论是否使用包含(至少对我来说都很好),是最自然的方法。
除此之外,启动外部SQL块解析器进程可以来自词法分析器或解析器中的Action,通过覆盖lexer的令牌emit()方法(或相关方法),以及来自访问基本语法的解析器的访问者树。
只有你能在任何特定情况下平衡可接受,可取或必要的东西。
例如,如果解析树评估提供了一个用于动态执行SQL exec块的值,或者相反,它取决于这种执行返回的值,那么你基本上被迫使用符号表并推迟将SQL执行启动到walker。当然,您可以缓存每个不同的生成的SQL解析树,并使用特定于实例的数据重新初始化它们的符号表,以便重用,而无需重新分析SQL块。
取决于您的实际要求。