使用 DCG 从 EBNF 在 Prolog 中实现 DSL 的困难

Difficulties implementing DSL in Prolog from EBNF using DCG

我正在为 Prolog 中的 proto 文件实现 Google Protobuf 编译器以生成 Prolog 程序。 Prolog 是 SWI-Prolog.

我正在 运行将 EBNF 定义转化为 DCG 和 运行 解决几个问题:

  1. 我必须处理 [ ... ]{ ... } EBNF 构造 - 意思是 optional(可执行零次或一次)和 repeatative(可执行任意次数 );

  2. 我必须将 回调 插入到 DCG 代码中,以使用 DCG 的构造实现部分编译器功能(语法 switching/importing/ 等) { ... },允许在 DCG 规则中使用 Prolog 语法中的目标。

我正在申请 optionalrepeatative 元谓词:$$rep/1$$opt/1:

EBNF
decimals  = decimalDigit { decimalDigit }
exponent  = ( "e" | "E" ) [ "+" | "-" ] decimals 

DCG
decimals  --> decimalDigit, '$$rep'( decimalDigit ).
exponent  --> ( "e"; "E" ), '$$opt'( "+"; "-" ), decimals.

'$$rep'( Goal ) :- repeat, call(Goal); !, fail.

'$$opt'( Goal ) :- once(Goal) ; \+ Goal.

"Callback:"
import --> "import", opt(( "weak" ; "public", { record(public)} )), strLit,
{
     import(public, strlit )
}, ";".

对我来说看起来很尴尬(如果不是丑的话)...

问题:

我的解决方案有什么问题?

我是否应该在不使用元谓词的情况下手动将 EBNG 转换为 DCG?运行

笨拙地渗透到 DCG 规则中的替代方法是什么?

乍一看,主要问题是您不干净地混合 DCG 与常规 Prolog 谓词。

留在 DCG 中定义所有非终结符。例如:

optional(NT) --> [] | NT.

once_or_more(NT) --> NT, or_more(NT).

or_more(NT) --> [] | NT, or_more(NT).

用下面的例子定义:

a --> [a].

我们可以post:

?- phrase(optional(a), Ls).
Ls = [] ;
Ls = [a].

?- phrase(once_or_more(a), Ls).
Ls = [a] ;
Ls = [a, a] ;
Ls = [a, a, a] ;
Ls = [a, a, a, a] ;
Ls = [a, a, a, a, a] .

这似乎可以满足您的需要。

对于回调,您可以简单地传递需要调用的谓词,大纲如下:

parse_with_callback(Goal) -->
        ...,
        { Goal },
        ...

这看起来还不错。

如果此类模式频繁出现,您始终可以考虑生成此类来自不同表示的 DCG,让您更清晰地表示任务。