D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 8729 - parse!bool does not work correctly
Summary: parse!bool does not work correctly
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: phobos (show other issues)
Version: D2
Hardware: All All
: P2 normal
Assignee: monarchdodra
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-09-26 10:29 UTC by Jonathan M Davis
Modified: 2015-06-09 01:31 UTC (History)
1 user (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Jonathan M Davis 2012-09-26 10:29:46 UTC
This code

import std.conv;
import std.stdio;

void main()
{
    auto str = "123 456.7 false";

    auto i = parse!int(str);
    auto d = parse!double(str);
    auto b = parse!bool(str);

    assert(i == 123);
    assert(d == 456.7);
    assert(b == false);
}

results in this exception when you run it

std.conv.ConvException@std/conv.d(2654): Can't parse string: bool should be case-insensive 'true' or 'false'
----------------
./q(bool std.conv.parse!(bool, immutable(char)[]).parse(ref immutable(char)[])+0x183) [0x43867b]
./q(_Dmain+0x42) [0x430ec2]
./q(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x1c) [0x43b240]
./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43abba]
./q(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x3b) [0x43b287]
./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43abba]
./q(main+0xd1) [0x43ab45]
/usr/lib/libc.so.6(__libc_start_main+0xf5) [0x7fd238fc1725]
----------------

If you change it to

import std.conv;
import std.stdio;

void main()
{
    auto str = "false 123 456.7";

    auto b = parse!bool(str);
    auto i = parse!int(str);
    auto d = parse!double(str);

    assert(i == 123);
    assert(d == 456.7);
    assert(b == false);
}

you get

std.conv.ConvException@std/conv.d(1771): Unexpected ' ' when converting from type string to type int
----------------
./q(int std.conv.parse!(int, immutable(char)[]).parse(ref immutable(char)[])+0x1b8) [0x431984]
./q(_Dmain+0x33) [0x430eb3]
./q(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x1c) [0x43b240]
./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43abba]
./q(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x3b) [0x43b287]
./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void delegate())+0x2a) [0x43abba]
./q(main+0xd1) [0x43ab45]
/usr/lib/libc.so.6(__libc_start_main+0xf5) [0x7f1286cc0725]
----------------

Just parsing out bool when it's first and then parsing _nothing_ else works, but it seems like parsing it under any other circumstances fails (from what I can tell anyway).
Comment 1 monarchdodra 2012-09-26 13:48:08 UTC
(In reply to comment #0)
> This code
> 
> import std.conv;
> import std.stdio;
> 
> void main()
> {
>     auto str = "123 456.7 false";
> 
>     auto i = parse!int(str);
>     auto d = parse!double(str);
>     auto b = parse!bool(str);
> 
>     assert(i == 123);
>     assert(d == 456.7);
>     assert(b == false);
> }
> 
> results in this exception when you run it
> 
> std.conv.ConvException@std/conv.d(2654): Can't parse string: bool should be
> case-insensive 'true' or 'false'
> ----------------
> ./q(bool std.conv.parse!(bool, immutable(char)[]).parse(ref
> immutable(char)[])+0x183) [0x43867b]
> ./q(_Dmain+0x42) [0x430ec2]
> ./q(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x1c) [0x43b240]
> ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> delegate())+0x2a) [0x43abba]
> ./q(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x3b) [0x43b287]
> ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> delegate())+0x2a) [0x43abba]
> ./q(main+0xd1) [0x43ab45]
> /usr/lib/libc.so.6(__libc_start_main+0xf5) [0x7fd238fc1725]
> ----------------
> 
> If you change it to
> 
> import std.conv;
> import std.stdio;
> 
> void main()
> {
>     auto str = "false 123 456.7";
> 
>     auto b = parse!bool(str);
>     auto i = parse!int(str);
>     auto d = parse!double(str);
> 
>     assert(i == 123);
>     assert(d == 456.7);
>     assert(b == false);
> }
> 
> you get
> 
> std.conv.ConvException@std/conv.d(1771): Unexpected ' ' when converting from
> type string to type int
> ----------------
> ./q(int std.conv.parse!(int, immutable(char)[]).parse(ref
> immutable(char)[])+0x1b8) [0x431984]
> ./q(_Dmain+0x33) [0x430eb3]
> ./q(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x1c) [0x43b240]
> ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> delegate())+0x2a) [0x43abba]
> ./q(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x3b) [0x43b287]
> ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> delegate())+0x2a) [0x43abba]
> ./q(main+0xd1) [0x43ab45]
> /usr/lib/libc.so.6(__libc_start_main+0xf5) [0x7f1286cc0725]
> ----------------
> 
> Just parsing out bool when it's first and then parsing _nothing_ else works,
> but it seems like parsing it under any other circumstances fails (from what I
> can tell anyway).

Sounds like the implementation is looking for a literal "true" or "false", and is forgetting to skip leading ws.

This works:

import std.conv;
import std.stdio;

void main()
{
   auto str = "123 456.7 false";

   auto i = parse!int(str);
   auto d = parse!double(str);
   str = str[1..$]; //manually skip ws.
   auto b = parse!bool(str);

   assert(i == 123);
   assert(d == 456.7);
   assert(b == false);
}

I can look into it for next week, unless somebody else solves it by then.
Comment 2 monarchdodra 2012-09-26 13:52:15 UTC
(In reply to comment #1)
> (In reply to comment #0)
> > This code
> > 
> > import std.conv;
> > import std.stdio;
> > 
> > void main()
> > {
> >     auto str = "123 456.7 false";
> > 
> >     auto i = parse!int(str);
> >     auto d = parse!double(str);
> >     auto b = parse!bool(str);
> > 
> >     assert(i == 123);
> >     assert(d == 456.7);
> >     assert(b == false);
> > }
> > 
> > results in this exception when you run it
> > 
> > std.conv.ConvException@std/conv.d(2654): Can't parse string: bool should be
> > case-insensive 'true' or 'false'
> > ----------------
> > ./q(bool std.conv.parse!(bool, immutable(char)[]).parse(ref
> > immutable(char)[])+0x183) [0x43867b]
> > ./q(_Dmain+0x42) [0x430ec2]
> > ./q(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x1c) [0x43b240]
> > ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> > delegate())+0x2a) [0x43abba]
> > ./q(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x3b) [0x43b287]
> > ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> > delegate())+0x2a) [0x43abba]
> > ./q(main+0xd1) [0x43ab45]
> > /usr/lib/libc.so.6(__libc_start_main+0xf5) [0x7fd238fc1725]
> > ----------------
> > 
> > If you change it to
> > 
> > import std.conv;
> > import std.stdio;
> > 
> > void main()
> > {
> >     auto str = "false 123 456.7";
> > 
> >     auto b = parse!bool(str);
> >     auto i = parse!int(str);
> >     auto d = parse!double(str);
> > 
> >     assert(i == 123);
> >     assert(d == 456.7);
> >     assert(b == false);
> > }
> > 
> > you get
> > 
> > std.conv.ConvException@std/conv.d(1771): Unexpected ' ' when converting from
> > type string to type int
> > ----------------
> > ./q(int std.conv.parse!(int, immutable(char)[]).parse(ref
> > immutable(char)[])+0x1b8) [0x431984]
> > ./q(_Dmain+0x33) [0x430eb3]
> > ./q(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x1c) [0x43b240]
> > ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> > delegate())+0x2a) [0x43abba]
> > ./q(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x3b) [0x43b287]
> > ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> > delegate())+0x2a) [0x43abba]
> > ./q(main+0xd1) [0x43ab45]
> > /usr/lib/libc.so.6(__libc_start_main+0xf5) [0x7f1286cc0725]
> > ----------------
> > 
> > Just parsing out bool when it's first and then parsing _nothing_ else works,
> > but it seems like parsing it under any other circumstances fails (from what I
> > can tell anyway).
> 
> Sounds like the implementation is looking for a literal "true" or "false", and
> is forgetting to skip leading ws.
> 
> [SNIP]

Bigtime!

// string to bool conversions
Target parse(Target, Source)(ref Source s)
    if (isSomeString!Source && !is(Source == enum) &&
        is(Unqual!Target == bool))
{
    if (s.length >= 4 && icmp(s[0 .. 4], "true")==0)
    {
        s = s[4 .. $];
        return true;
    }
    if (s.length >= 5 && icmp(s[0 .. 5], "false")==0)
    {
        s = s[5 .. $];
        return false;
    }
    parseError("bool should be case-insensive 'true' or 'false'");
    assert(0);
}

I got this.
Comment 3 monarchdodra 2012-09-26 14:36:53 UTC
FYI, parse!int also has the same problem. Only floating point types seem to behave correctly:

import std.conv;
import std.stdio;

void main()
{
   auto str = "456.7 123";

   auto d = parse!double(str);
   auto i = parse!int(str);

   assert(d == 456.7);
   assert(i == 123);
}

On the split side, to! seems to parse things it should actually be rejecting:

import std.conv;
import std.stdio;

void main()
{
   auto str = "456.7 123";

   auto d = to!double(" 456.7"); //Passes, but shouldn't
   auto i = to!int(" 123"); //Correctly throws
}

Just logging here the bugs I find, still on this.
Comment 4 monarchdodra 2012-09-27 13:40:52 UTC
(In reply to comment #0)
> This code
> 
> import std.conv;
> import std.stdio;
> 
> void main()
> {
>     auto str = "123 456.7 false";
> 
>     auto i = parse!int(str);
>     auto d = parse!double(str);
>     auto b = parse!bool(str);
> 
>     assert(i == 123);
>     assert(d == 456.7);
>     assert(b == false);
> }
> 
> results in this exception when you run it
> 
> std.conv.ConvException@std/conv.d(2654): Can't parse string: bool should be
> case-insensive 'true' or 'false'
> ----------------
> ./q(bool std.conv.parse!(bool, immutable(char)[]).parse(ref
> immutable(char)[])+0x183) [0x43867b]
> ./q(_Dmain+0x42) [0x430ec2]
> ./q(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x1c) [0x43b240]
> ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> delegate())+0x2a) [0x43abba]
> ./q(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x3b) [0x43b287]
> ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> delegate())+0x2a) [0x43abba]
> ./q(main+0xd1) [0x43ab45]
> /usr/lib/libc.so.6(__libc_start_main+0xf5) [0x7fd238fc1725]
> ----------------
> 
> If you change it to
> 
> import std.conv;
> import std.stdio;
> 
> void main()
> {
>     auto str = "false 123 456.7";
> 
>     auto b = parse!bool(str);
>     auto i = parse!int(str);
>     auto d = parse!double(str);
> 
>     assert(i == 123);
>     assert(d == 456.7);
>     assert(b == false);
> }
> 
> you get
> 
> std.conv.ConvException@std/conv.d(1771): Unexpected ' ' when converting from
> type string to type int
> ----------------
> ./q(int std.conv.parse!(int, immutable(char)[]).parse(ref
> immutable(char)[])+0x1b8) [0x431984]
> ./q(_Dmain+0x33) [0x430eb3]
> ./q(extern (C) int rt.dmain2.main(int, char**).void runMain()+0x1c) [0x43b240]
> ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> delegate())+0x2a) [0x43abba]
> ./q(extern (C) int rt.dmain2.main(int, char**).void runAll()+0x3b) [0x43b287]
> ./q(extern (C) int rt.dmain2.main(int, char**).void tryExec(scope void
> delegate())+0x2a) [0x43abba]
> ./q(main+0xd1) [0x43ab45]
> /usr/lib/libc.so.6(__libc_start_main+0xf5) [0x7f1286cc0725]
> ----------------
> 
> Just parsing out bool when it's first and then parsing _nothing_ else works,
> but it seems like parsing it under any other circumstances fails (from what I
> can tell anyway).

https://github.com/D-Programming-Language/phobos/pull/817
Comment 5 Kenji Hara 2012-09-27 18:29:09 UTC
(In reply to comment #3)
> FYI, parse!int also has the same problem. Only floating point types seem to
> behave correctly:

NO, this is just a bug in `T parse!T(input) if (isFloatingPoint!T)`.
The parse function family should not skip leading white spaces implicitly.

I think it is simple, no special cases, and flexible design.

See also my github comment to the pull#817:
https://github.com/D-Programming-Language/phobos/pull/817#issuecomment-8960280
Comment 6 github-bugzilla 2012-10-04 09:33:06 UTC
Commits pushed to master at https://github.com/D-Programming-Language/phobos

https://github.com/D-Programming-Language/phobos/commit/dce93b188140157732898da3d03fcfe90d32e555
fix issue 8729 do not skip leading ws

https://github.com/D-Programming-Language/phobos/commit/25c4328aef80230596c90bebb6848f5e2f228261
Merge pull request #833 from monarchdodra/bug8729

fix issue 8729 do not skip leading ws in parse (again)