% crochet open crochet.wrapper.node.io; open crochet.debug; effect repl with quit(); abort(); depth(); end effect repl-eval with syntax-error(repl, error); success(repl, value); end local singleton module; type repl(environment is environment); command #repl spawn do handle terminal write-line: "Pocket Lisp interactive shell"; terminal write-line: "Use `Ctrl + D` to exit."; terminal write-line: ""; module repl: new repl(pocket-lisp global-environment spawn) with on repl.quit() => nothing; on repl.depth() => continue with 1; on repl-eval.syntax-error(Repl, Error) do module show-syntax-error: Error; module repl: Repl; end on repl-eval.success(Repl, Result) do module show-result: Result; module repl: Repl; end end end command module depth = perform repl.depth(); command module repl: Repl do let Program = module read-program; condition when Program is error => perform repl-eval.syntax-error(Repl, Program reason); when Program is ok do handle module repl: Repl with-interactive-error-recovery: { let Result = pocket-lisp run: Program value with-environment: Repl.environment; perform repl-eval.success(Repl, Result); }; with on repl.abort() => module repl: Repl; end end end; end command module read-program do let Line = terminal prompt: "[module depth to-text]> "; let Tree = pocket-lisp try-parse: Line; condition when Tree is ok => Tree; when Tree is error => module read-program-with: Line; end end command module read-program-with: Previous do let Line = terminal prompt: ".. "; let Source = (Previous ++ "\n" ++ Line) flatten-into-plain-text; let Tree = pocket-lisp try-parse: Source; condition when Line is-empty and (Tree is error) => Tree; when Tree is error => module read-program-with: Source; when Tree is ok => Tree; end end command module show-syntax-error: Error do terminal write-line: Error; terminal write-line: ""; end command module show-result: p-nil = nothing; command module show-result: Value = terminal write-line: "([Value type-name]) [Value print-format]\n"; command module repl: Repl with-interactive-error-recovery: (Block is (() -> A)) do handle Block(); with on pocket-lisp.raise(Frame, Form, Error) do terminal write-line: (pocket-lisp format-error: Error); terminal write-line: ""; let Choice = module read-interactive-choice; let Depth = perform repl.depth(); condition when Choice =:= "Q" => perform repl.quit(); when Choice =:= "A" => perform repl.abort(); when Choice =:= "R" do handle let Result = module repl: (new repl(pocket-lisp global-environment spawn)); continue with Result; with on repl-eval.success(_, Value) do continue with Value; end on repl-eval.syntax-error(_, Error) do module show-syntax-error: Error; module repl: Repl; end on repl.abort() => perform repl.abort(); on repl.depth() => continue with Depth + 1; end end end end end end command module read-interactive-choice do terminal write-line: "(Q)uit -- Exit this interactive shell"; terminal write-line: "(A)bort -- Stops the evaluation, continues with the previous state"; terminal write-line: "(R)ecover -- Provide a recover value and continue the evaluation"; terminal write-line: ""; let Choice = (terminal prompt: "Choice> ") trim; let Valid = #set from: ["Q", "A", "R"]; condition when Valid contains: Choice => Choice; always => module read-interactive-choice; end end