Rewrite stdio repl to simplify secondary thread.

It should be doing solely two tasks: reading and writing. Nothing else.
This commit is contained in:
Phil Hagelberg 2021-09-08 19:41:20 -07:00
parent 0b04501302
commit 078ffc1e83

View file

@ -1,29 +1,24 @@
(require "love.event") (require "love.event")
(local {: view} (require :lib.fennel))
;; This module exists in order to expose stdio over a channel so that it ;; This module exists in order to expose stdio over a channel so that it
;; can be used in a non-blocking way from another thread. ;; can be used in a non-blocking way from another thread.
(local (event channel) ...) (fn prompt [cont?]
(io.write (if cont? ".." ">> ")) (io.flush) (.. (io.read) "\n"))
(fn prompt [next-line?] ;; This module is loaded twice: initially in the main thread where ... is nil,
(io.write (if next-line? ".." ">> ")) (io.flush) (.. (io.read) "\n")) ;; and then again in a separate thread where ... contains the channel used to
;; communicate with the main thread.
(when channel (fn looper [event channel]
((fn looper [input] (match (channel:demand)
(when input [:write vals] (do (io.write (table.concat vals "\t"))
;; This is consumed by love.handlers[event] (io.write "\n"))
(love.event.push event input) [:read cont?] (love.event.push event (prompt cont?)))
(let [output (: channel :demand) (looper event channel))
next-line (and (. output 2) (= "next-line" (. output 2)))]
;; There is probably a more efficient way of determining an error (match ...
(if next-line :ok (event channel) (looper event channel))
(and (. output 2) (= "Error:" (. output 2)))
(print (view output))
(each [_ ret (ipairs output)]
(print ret)))
(io.flush)
(looper (prompt next-line))))) (prompt)))
{:start (fn start-repl [] {:start (fn start-repl []
(let [code (love.filesystem.read "lib/stdio.fnl") (let [code (love.filesystem.read "lib/stdio.fnl")
@ -34,19 +29,17 @@
thread (love.thread.newThread luac) thread (love.thread.newThread luac)
io-channel (love.thread.newChannel) io-channel (love.thread.newChannel)
coro (coroutine.create fennel.repl) coro (coroutine.create fennel.repl)
out (fn [val] options {:readChunk (fn [{: stack-size}]
(: io-channel :push val)) (io-channel:push [:read (< 0 stack-size)])
options {:readChunk (fn [parser-state]
(when (> parser-state.stack-size 0)
(: io-channel :push ["next-line" "next-line"]))
(coroutine.yield)) (coroutine.yield))
:onValues out :onValues (fn [vals]
:onError (fn [kind ...] (out [kind "Error:" ...])) (io-channel:push [:write vals]))
:pp view :onError (fn [errtype err]
(io-channel:push [:write [err]]))
:moduleName "lib.fennel"}] :moduleName "lib.fennel"}]
;; this thread will send "eval" events for us to consume: ;; this thread will send "eval" events for us to consume:
(coroutine.resume coro options) (coroutine.resume coro options)
(: thread :start "eval" io-channel) (thread:start "eval" io-channel)
(set love.handlers.eval (set love.handlers.eval
(fn [input] (fn [input]
(coroutine.resume coro input)))))} (coroutine.resume coro input)))))}