<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Premature send from mail.app, apologies list!<div class=""><br class=""></div><div class=""><blockquote type="cite" class="">Excellent explanation as usual Zoltan. I get it now, and the printer paper analogy really really made the light come on about that.<br class="">As it turned it, the logic didn’t feel right<br class=""></blockquote><div class="">…didn’t feel right and so I changed the parse to be deterministic and then implemented execute:</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><font face="Courier" class="">:- pred execute(string::in, io::di, io::uo) is det.</font></div><div class=""><font face="Courier" class="">execute(Input, !IO) :-<br class=""> lexer.on_string(chomp(Input), Lx, !IO),<br class=""> % dump_lexer(Lx, !IO),<br class=""> ( if lex_error(Lx) = yes(ok_eob) then<br class=""> parse_cmd(CmdRes, tokens(Lx), _),<br class=""> (<br class=""> CmdRes = no,<br class=""> (if list.length(tokens(Lx)) > 0<br class=""> then io.format("Try ?, h or help.\n", [], !IO)<br class=""> else true)<br class=""> ;<br class=""> CmdRes = yes(help),<br class=""> show_help_for("help", !IO)<br class=""> ;<br class=""> CmdRes = yes(help_for(Topic)),<br class=""> show_help_for(Topic, !IO)<br class=""> )<br class=""> else<br class=""> io.format("%sERROR! %s\n",<br class=""> [s(prompt), s(string(lex_error(Lx)))], !IO)<br class=""> ).</font><br class=""><br class=""></div><div class="">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.</div><div class=""><font face="Courier" class=""><br class=""></font></div><div class=""><font face="Courier" class="">:- type ltok == list(token).<br class="">:- type cmd ---> help; help_for(string).<br class=""><br class="">:- pred parse_cmd(maybe(cmd)::out, ltok::in, ltok::out) is det.<br class="">parse_cmd(Cmd) --><br class=""> ( if help_cmd, [tk(_, Subject)] then<br class=""> {Cmd=yes(help_for(Subject))}<br class=""> else if help_cmd then<br class=""> {Cmd = yes(help)}<br class=""> else<br class=""> {Cmd=no}<br class=""> ).<br class=""><br class="">:- pred help_cmd(ltok::in, ltok::out) is semidet.<br class="">help_cmd --> [tk(_, "help")] ; [tk(_, "?")] ; [tk(_, "h")].<br class=""></font><br class=""></div><div class="">Thanks again for your help it is always very welcome and much appreciated.</div><div class="">Sean.</div><div class=""><br class=""></div><div class=""><br class=""></div><br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On 17 May 2021, at 08:47, Sean Charles (emacstheviking) <<a href="mailto:objitsu@gmail.com" class="">objitsu@gmail.com</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">Excellent explanation as usual Zoltan. I get it now, and the printer paper analogy really really made the light come on about that.<br class="">As it turned it, the logic didn’t feel right<br class=""><br class=""><blockquote type="cite" class="">On 17 May 2021, at 02:45, Zoltan Somogyi <<a href="mailto:zoltan.somogyi@runbox.com" class="">zoltan.somogyi@runbox.com</a>> wrote:<br class=""><br class=""><br class="">2021-05-17 06:36 GMT+10:00 "Sean Charles (emacstheviking)" <<a href="mailto:objitsu@gmail.com" class="">objitsu@gmail.com</a>>:<br class=""><blockquote type="cite" class=""> execute(Input, !IO) :-<br class=""> lexer.on_string(chomp(Input), Lx, !IO),<br class=""> % dump_lexer(Lx, !IO),<br class=""> ( if lex_error(Lx) = yes(ok_eob) then<br class=""> io.format("DCG this: %s\n", [s(string(tokens(Lx)))], !IO),<br class="">96 ===> ( if parse_cmd(!IO, tokens(Lx), _) % do I care what's left?<br class=""> then io.format("DID IT\n", [], !IO)<br class=""> else io.format("PARSE FAILED\n", [], !IO)<br class=""> )<br class=""> else<br class=""> io.format("%sERROR! %s\n”,<br class=""> [s(prompt), s(string(lex_error(Lx)))], !IO)<br class=""> ).<br class=""></blockquote><br class="">The condition of that if-then-else does two things.<br class=""><br class="">1: It calls parse_cmd, which destroys the old state of the world (call it IO_0)<br class=""> and creates a new state of the world (call it IO_1). This code is det,<br class=""> i.e. it cannot fail.<br class=""><br class="">2: It checks whether the third arg returned by parse_cmd is unifiable<br class=""> with tokens(Lx). This code is semidet, i.e. it can fail.<br class=""><br class="">You are getting the error because the compiler knows that if the<br class="">unification with tokens(Lx) fails, then the condition as a whole fails,<br class="">and execution would have to go on to the else case, *with the current<br class="">state of the world being IO_0*. (This is because the semantics of<br class="">an if-then-else is "either execute the conjunction of the condition<br class="">and then then-part, or execute the else-part, depending on whether<br class="">the condition succeeds.) But the compiler also knows that the<br class="">call to parse_cmd destroyed IO_0, and there is no way to get it back.<br class="">It's like you tell a printer to print something, then later tell it<br class="">never mind, give me back the blank paper you started with.<br class="">In other words, you cannot do I/O in places that can be<br class="">backtracked over.<br class=""><br class="">The fix is simple. Move the call to parse_cmd to *before* the if-then-else,<br class="">and leave only the test of whether the third arg it returns is tokens(Lx)<br class="">in the condition.<br class=""><br class="">Zoltan.<br class=""></blockquote><br class=""></div></div></blockquote></div><br class=""></div></body></html>