使用 DCG 从 EBNF 在 Prolog 中实现 DSL 的困难
Difficulties implementing DSL in Prolog from EBNF using DCG
我正在为 Prolog 中的 proto 文件实现 Google Protobuf 编译器以生成 Prolog 程序。 Prolog 是 SWI-Prolog.
我正在 运行将 EBNF 定义转化为 DCG 和 运行 解决几个问题:
我必须处理 [ ... ]
和 { ... }
EBNF 构造 - 意思是 optional
(可执行零次或一次)和 repeatative
(可执行任意次数 );
我必须将 回调 插入到 DCG 代码中,以使用 DCG 的构造实现部分编译器功能(语法 switching/importing/ 等) { ... }
,允许在 DCG 规则中使用 Prolog 语法中的目标。
我正在申请 optional
和 repeatative
元谓词:$$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,让您更清晰地表示任务。
我正在为 Prolog 中的 proto 文件实现 Google Protobuf 编译器以生成 Prolog 程序。 Prolog 是 SWI-Prolog.
我正在 运行将 EBNF 定义转化为 DCG 和 运行 解决几个问题:
我必须处理
[ ... ]
和{ ... }
EBNF 构造 - 意思是optional
(可执行零次或一次)和repeatative
(可执行任意次数 );我必须将 回调 插入到 DCG 代码中,以使用 DCG 的构造实现部分编译器功能(语法 switching/importing/ 等)
{ ... }
,允许在 DCG 规则中使用 Prolog 语法中的目标。
我正在申请 optional
和 repeatative
元谓词:$$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,让您更清晰地表示任务。