I’m using: SWI-Prolog version 8.4.1
I’m writing a parser with DCGs that exhibits weird behavior, which I’m inclined to believe is a bug on tabling and/or compilation. I can reliably turn the bug on or off by exporting the tabled predicate.
Repro steps
My code is at swifun/kilanone.pl at 6494a5ba24fba328f1510cd0f602c58e537ebe8a · brunokim/swifun · GitHub (sorry I didn’t make it shorter). If you execute tests with
$ swipl -g '(load_test_files([]), run_tests)' -t halt kilanone.pl
it passes all tests. If you remove the operation//3
export in the first line, some tests fail.
% PL-Unit: kilanone ................................
ERROR: /home/bkim/Projects/swifun/kilanone.plt:92:
test parse prefix operation (forall bindings = [[43,45,97],operation(op(_1010,_1012,"+"),operation(op(_1030,_1032,"-"),id("a")))]): assertion failed
Assertion: operation(op(500,yfx,"-"),id("+"),id("a"))=operation(op(_196,_198,"+"),operation(op(_216,_218,"-"),id("a")))
A
ERROR: /home/bkim/Projects/swifun/kilanone.plt:92:
test parse prefix operation (forall bindings = [[45,43,97],operation(op(_13310,_13312,"-"),operation(op(_13330,_13332,"+"),id("a")))]): assertion failed
Assertion: operation(op(500,yfx,"+"),id("-"),id("a"))=operation(op(_11332,_11334,"-"),operation(op(_11352,_11354,"+"),id("a")))
A. done
% 2 assertions failed
% 2 tests failed
% 33 tests passed
A bit more explanation
I’m writing a parser that, like Prolog, accepts a dynamic list of operators. An operator must be an identifier, and the same symbol may be used as a prefix, suffix or infix position.
These failed cases start in kilanone.plt:82. In essence, I want that a string like “- + a” to be parsed as -(+(a))
, but it’s parsed as +(-,a)
when operation//3
is not exported.
The operation//3
predicate works on a list of exprs like [id("-"), id("+"), id("a")]
and is organized as follows (I’ve coalesced some less important logic into gen_*_op
here):
:- table(operation//3).
% Terminal branch
operation(_, _, Expr) --> [Expr].
% Prefix branch
operation(OpSet, MaxPrec, operation(Op, Expr)) -->
{gen_prefix_op(OpSet, MaxPrec, prefix, Op, Symb, Prec)},
[id(Symb)],
operation(OpSet, Prec, Expr).
% Suffix branch
operation(OpSet, MaxPrec, operation(Op, Expr)) -->
{gen_suffix_op(OpSet, MaxPrec, suffix, Op, Symb, Prec)},
operation(OpSet, Prec, Expr),
[id(Symb)].
% Infix branch
operation(OpSet, MaxPrec, operation(Op, Left, Right)) -->
{gen_infix_op(OpSet, MaxPrec, Op, Symb, Left, LeftPrec, Right, RightPrec)},
operation(OpSet, LeftPrec, Left),
[id(Symb)],
operation(OpSet, RightPrec, Right).
By stepping the execution with the graphical tracer, I see that it correctly picks the prefix branch for “-” and “+”, but when reaching the terminal branch for “a”, it redoes the previous prefix branch instead of accepting the terminal.
I’ve used the following commands:
$ swipl kilanone.pl
?- gtrace.
[trace] ?- phrase(expression(Tree), `-+a`).
Thanks for your attention.
PS.: I do call abolish_all_tables
before every attempt!