I'm trying to write a DCG for a command interface. The idea is to read a string of input, split it on spaces, and hand the resulting list of tokens to a DCG to parse it into a command and arguments. The result of parsing should be a list of terms which I can use with =..
to construct a goal to call. However, I've become really confused by the string type situation in SWI-Prolog (ver. 7.2.3). SWI-Prolog includes a library of basic DCG functionality, including a goal integer//1
which is supposed to parse an integer. It fails due to a type error, but the bigger problem is that I can't figure out how to make a DCG work nicely in SWI-Prolog with "lists of tokens".
Here's what I'm trying to do:
:- use_module(library(dcg/basics)).
% integer//1 is from the dcg/basics lib
amount(X) --> integer(X), { X > 0 }.
cmd([show,all]) --> ["show"],["all"].
cmd([show,false]) --> ["show"].
cmd([skip,X]) --> ["skip"], amount(X).
% now in the interpreter:
?- phrase(cmd(L), ["show","all"]).
L = [show, all].
% what is the problem with this next query?
?- phrase(cmd(L), ["skip", "50"]).
ERROR: code_type/2: Type error: `character' expected, found `"50"' (a string)
I have read Section 5.2 of the SWI manual, but it didn't quite answer my questions:
- What type is expected by
integer//1
in thedcg/basics
library? The error message says "character", but I can't find any useful reference as to what exactly this means and how to provide it with "proper" input. - How do I pass a list of strings (tokens) to
phrase/2
such that I can useinteger//1
to parse a token as an integer? - If there's no way to use the
integer//1
primitive to parse a string of digits into an integer, how should I accomplish this?
I did quite a bit of expermenting with using different values for the double_quote
flag in SWI-Prolog, plus different input formats, such as using a list of atoms, using a single string as the input, i.e. "skip 50"
rather than ["skip", "50"]
, and so on, but I feel like there are assumptions about how DCGs work that I don't understand.
I have studied these three pages as well, which have lots of examples but none quite address my issues (some links omitted since I don't have enough reputation to post all of them):
- The tutorial "Using Definite Clause Grammars in SWI-Prolog" by Anne Ogborn
- A tutorial from Amzi! Prolog about writing command interfaces as DCGs.
- Section 7.3 of J. R. Fisher's Prolog tutorial
A third, more broad question is how to generate an error message if an integer is expected but cannot be parsed as one, something like this:
% the user types:
> skip 50x
I didn't understand that number.
One approach is to set the variable X
in the DCG above to some kind of error value and then check for that later (like in the hypothetical skip/1
goal that is supposed to get called by the command), but perhaps there's a better/more idiomatic way? Most of my experience in writing parsers comes from using Haskell's Parsec and Attoparsec libraries, which are fairly declarative but work somewhat differently, especially as regards error handling.
integer
defined in your DCG? – lurkerinteger(X)
) requires an integer argument, according to the documentation, but you are trying to parse an atom ('50'
) with it (I assume your SWI prolog is configured to interpret"50"
as the atom'50'
). – lurker