0
votes

I'm trying to learn prolog DCGs by creating a program that will recognize a valid floating point number. For example:

123.234   {true}
123..124  {false}
+123.123  {true}
-123.1    {true}
+-123.42  {false}

My code looks like this:

isFloat(Num) :- float(Num).

sign --> "", "+", "-".

float --> sign, number, ".", number.

number --> digit.
number --> digit, number.

digit --> [0,1,2,3,4,5,6,7,8,9].

For the most part it works, but sometimes I get an error output rather than false whenever there is something that doesn't fit the grammar.

output:

?- ['practice.pro'].
true.

?- isFloat(+12.234).
true.

?- isFloat(+12).
false.

?- isFloat(++12).
ERROR: Syntax error: Operator expected
ERROR: isFloat(+
ERROR: ** here **
ERROR: +12) . 

?- isFloat(123.345..).
ERROR: Syntax error: Operator expected
ERROR: isFloat(123.34
ERROR: ** here **
ERROR: 5..) . 

?- isFloat(+12.567).
true.

I assumed that if it doesn't fit the grammar correctly, the DCG would return false, but instead I get a syntax error if the passed in number doesn't fit the grammar. Could anyone explain what I'm doing wrong? Thanks.

Edit: Format question better.

1
Because it is a syntax error? - Scott Hunter
Really, your DCG is doing nothing. You should convert input to codes list, and use phrase/2. Look for it in docs - CapelliC

1 Answers

0
votes

I'm afraid you've not actually managed to use the DCG you have defined. float/1 is a built-in that you are calling, and without quoting anything you haven't actually passed a string into anything to be parsed. Prolog is parsing your input as a literal; if that fails due to a syntax issue, nothing else is going to happen. Also, to engage the DCG system, you have to use phrase/2, and you aren't, so even when you pass things to isFloat/2 it's going to just hand them directly to float/1.

Also, be aware that DCGs don't magically return what they've parsed. If your DCG rule has nothing before the arrow, nothing is going to come back from phrase/2 for you to use. So the code you have here is accepting floats (or does with the changes below) but it does not really parse the floats from a string into an actual float representation.

To actually invoke your code would look something like this:

phrase(float, `+12.234`).

This won't succeed for a few reasons, the plainest being that your digit//0 rule does not do what you think it does. It actually expects exactly the list of digits from 0 to 9. What you want to do there is probably something more like this:

digit --> "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".

Once you have that, you may be able to parse "number". But you make a similar mistake in sign//0, because comma in Prolog means and, not or. The fixed code looks like this:

sign --> "" | "+" | "-".
float --> sign, number, ".", number.
number --> digit.
number --> digit, number.

digit --> "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9".

And using it looks like this:

?- phrase(float, `-923.23`).
true

Note that now you get the parse failures you're expecting:

?- phrase(float, `-923..23`).
false.

Note that this does not give you back the float. You'll have to do more work to achieve that.