[m-users.] Inst error on !IO mode in DCG rule
Sean Charles (emacstheviking)
objitsu at gmail.com
Mon May 17 17:51:06 AEST 2021
Premature send from mail.app, apologies list!
> Excellent explanation as usual Zoltan. I get it now, and the printer paper analogy really really made the light come on about that.
> As it turned it, the logic didn’t feel right
…didn’t feel right and so I changed the parse to be deterministic and then implemented execute:
:- pred execute(string::in, io::di, io::uo) is det.
execute(Input, !IO) :-
lexer.on_string(chomp(Input), Lx, !IO),
% dump_lexer(Lx, !IO),
( if lex_error(Lx) = yes(ok_eob) then
parse_cmd(CmdRes, tokens(Lx), _),
(
CmdRes = no,
(if list.length(tokens(Lx)) > 0
then io.format("Try ?, h or help.\n", [], !IO)
else true)
;
CmdRes = yes(help),
show_help_for("help", !IO)
;
CmdRes = yes(help_for(Topic)),
show_help_for(Topic, !IO)
)
else
io.format("%sERROR! %s\n",
[s(prompt), s(string(lex_error(Lx)))], !IO)
).
It’s all working fine and the parsing of commands is now as clean as I hoped it should be now that my tokeniser works.
:- type ltok == list(token).
:- type cmd ---> help; help_for(string).
:- pred parse_cmd(maybe(cmd)::out, ltok::in, ltok::out) is det.
parse_cmd(Cmd) -->
( if help_cmd, [tk(_, Subject)] then
{Cmd=yes(help_for(Subject))}
else if help_cmd then
{Cmd = yes(help)}
else
{Cmd=no}
).
:- pred help_cmd(ltok::in, ltok::out) is semidet.
help_cmd --> [tk(_, "help")] ; [tk(_, "?")] ; [tk(_, "h")].
Thanks again for your help it is always very welcome and much appreciated.
Sean.
> On 17 May 2021, at 08:47, Sean Charles (emacstheviking) <objitsu at gmail.com> wrote:
>
> Excellent explanation as usual Zoltan. I get it now, and the printer paper analogy really really made the light come on about that.
> As it turned it, the logic didn’t feel right
>
>> On 17 May 2021, at 02:45, Zoltan Somogyi <zoltan.somogyi at runbox.com> wrote:
>>
>>
>> 2021-05-17 06:36 GMT+10:00 "Sean Charles (emacstheviking)" <objitsu at gmail.com>:
>>> execute(Input, !IO) :-
>>> lexer.on_string(chomp(Input), Lx, !IO),
>>> % dump_lexer(Lx, !IO),
>>> ( if lex_error(Lx) = yes(ok_eob) then
>>> io.format("DCG this: %s\n", [s(string(tokens(Lx)))], !IO),
>>> 96 ===> ( if parse_cmd(!IO, tokens(Lx), _) % do I care what's left?
>>> then io.format("DID IT\n", [], !IO)
>>> else io.format("PARSE FAILED\n", [], !IO)
>>> )
>>> else
>>> io.format("%sERROR! %s\n”,
>>> [s(prompt), s(string(lex_error(Lx)))], !IO)
>>> ).
>>
>> The condition of that if-then-else does two things.
>>
>> 1: It calls parse_cmd, which destroys the old state of the world (call it IO_0)
>> and creates a new state of the world (call it IO_1). This code is det,
>> i.e. it cannot fail.
>>
>> 2: It checks whether the third arg returned by parse_cmd is unifiable
>> with tokens(Lx). This code is semidet, i.e. it can fail.
>>
>> You are getting the error because the compiler knows that if the
>> unification with tokens(Lx) fails, then the condition as a whole fails,
>> and execution would have to go on to the else case, *with the current
>> state of the world being IO_0*. (This is because the semantics of
>> an if-then-else is "either execute the conjunction of the condition
>> and then then-part, or execute the else-part, depending on whether
>> the condition succeeds.) But the compiler also knows that the
>> call to parse_cmd destroyed IO_0, and there is no way to get it back.
>> It's like you tell a printer to print something, then later tell it
>> never mind, give me back the blank paper you started with.
>> In other words, you cannot do I/O in places that can be
>> backtracked over.
>>
>> The fix is simple. Move the call to parse_cmd to *before* the if-then-else,
>> and leave only the test of whether the third arg it returns is tokens(Lx)
>> in the condition.
>>
>> Zoltan.
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.mercurylang.org/archives/users/attachments/20210517/5bd0ba31/attachment.html>
More information about the users
mailing list