-- c lib / bindings for libuv local uv = require 'luv' -- tcp local tcp = require 'async.tcp' -- handle local handle = require 'async.handle' -- fiber local fiber = require 'async.fiber' -- repl: local repl = {} -- bind io: local bindok = false local stdin,stdout,stderr local function bindio() if not bindok then -- auto bind stdin/stdout: io.stdin = uv.new_tty(0,1) io.stdout = uv.new_tty(1) io.stderr = uv.new_tty(2) io.stdout.write = uv.write io.stderr.write = uv.write -- handlify stds: stdin = handle(io.stdin) stdout = handle(io.stdout) stderr = handle(io.stderr) -- flush local ioflush = io.flush io.flush = function() while not ioflush() do end end -- io.write io.write = function(...) io.stdout:write(...) end -- done bindok = true -- export repl.stdin = stdin repl.stdout = stdout repl.stderr = stderr end end repl.bindio = bindio -- Eval: local function eval(str,usefiber) -- capture: local function captureResults(success, ...) local n = select('#', ...) return success, { n = n, ... } end -- eval: local f,err = loadstring('return ' .. str, 'REPL') if not f then f,err = loadstring(str, 'REPL') end if f then if usefiber then fiber(function() local ok,results = captureResults(xpcall(f, debug.traceback)) if ok then print(unpack(results)) else print(results[1]) end end) else local ok,results = captureResults(xpcall(f, debug.traceback)) if ok then print(unpack(results)) else print(results[1]) end end else print(err) end end -- repl: local function lrepl(self,opts) -- options? local prompt,fiber opts = opts or {} if type(opts) == 'string' then prompt = opts else fiber = opts.fiber prompt = opts.prompt end -- bind io bindio() -- prompt: prompt = prompt or _PROMPT or '> ' stdout.write(prompt) -- capture stdin: stdin.ondata(function(line) local res = eval(line,fiber) stdout.write(prompt) end) -- terminate: stdin.onend(function() os.exit() end) end -- remote repl: setmetatable(repl, { __call = lrepl }) -- repl server: function repl.listen(domain, callback) -- prompt local prompt if type(domain) == 'table' and domain.prompt then prompt = domain.prompt end -- list of connected clients, to mirror: local clients = {} local iowrite = io.write io.write = function(...) for client in pairs(clients) do client.write(...) end iowrite(...) io.flush() end -- listen: tcp.listen(domain, function(client) -- prompt: local s = client.sockname prompt = prompt or (s.address..':'..s.port..'> ') -- verbose: io.write(prompt .. 'remote client @ ' .. client.peername.address..':'..client.peername.port..'\n') -- wait for remote commands: client.onsplitdata('\n', function(data) eval(data) client.write(prompt) end) -- on end, disconnect mirroring: client.onend(function() clients[client] = nil end) -- connect: clients[client] = true -- prompt: client.write(prompt) -- user callback if callback then callback(client) end end) end -- repl client: function repl.connect(domain, callback) -- bind io bindio() -- connect tcp.connect(domain, function(client) -- receive results from server client.ondata(function(data) stdout.write(data) end) -- capture stdin: stdin.ondata(function(line) client.write(' '..line) end) -- terminate: stdin.onend(function() os.exit() end) -- user callback if callback then callback(client) end end) end -- parallel repl client: function repl.connectn(domains, opts, callback) -- bind io bindio() -- opts? opts = opts or {} if opts and type(opts) == 'function' then callback = opts end -- connect local clients = {} local N = #domains local done = 0 for i = 1,N do tcp.connect(domains[i], function(client) -- store client: clients[i] = client done = done + 1 -- receive results from server client.ondata(function(data) if not opts.mute then -- TODO: cleanup these \n hacks. The current solution -- is approximate (distorts the original stream), but -- minimizes edge effects. if data ~= '\n' then stdout.write(data:gsub('\n$',''):gsub('^\n','')..'\n') end end end) -- user callback if done == N and callback then callback(client) end end) end -- capture stdin: stdin.ondata(function(line) for i = 1,N do if clients[i] then clients[i].write(' '..line) end end end) end -- useful: colors repl.colors = { none = '\27[0m', black = '\27[0;30m', red = '\27[0;31m', green = '\27[0;32m', yellow = '\27[0;33m', blue = '\27[0;34m', magenta = '\27[0;35m', cyan = '\27[0;36m', white = '\27[0;37m', Black = '\27[1;30m', Red = '\27[1;31m', Green = '\27[1;32m', Yellow = '\27[1;33m', Blue = '\27[1;34m', Magenta = '\27[1;35m', Cyan = '\27[1;36m', White = '\27[1;37m', _black = '\27[40m', _red = '\27[41m', _green = '\27[42m', _yellow = '\27[43m', _blue = '\27[44m', _magenta = '\27[45m', _cyan = '\27[46m', _white = '\27[47m' } local function colorize(str,color) return repl.colors[color] .. str .. repl.colors.none end local short = {} for color in pairs(repl.colors) do short[color] = function(str) return colorize(str,color) end end repl.colorize = short -- return return repl