-- We tricked the Fennel repl into running inside a browser using Fengari and
-- init.lua+repl.fnl. Now we must trick our trick into running back inside
-- a normal shell, because debugging in the browser is kinda miserable.

-- Helper functions
local p = print
local noop = function() return {} end

local createElement = function()
   return {
      style={},
      appendChild=table.insert,
      getAttribute=noop,
      setAttribute=noop,
      dispatchEvent=noop,
   }
end

-- io.open gets replaced with XHR stuff, so create a fake XHR that knows how
-- to secretly io.open:
local open = io.open
local open = function(x, _, filename)
   local f = assert(open(filename))
   x.response = f:read("*all")
   f:close()
end

-- Prepopulate the js module with pretend versions of every function the web
-- repl uses to interact with the DOM, but redirect them to the shell.
package.loaded.js = {
   new = function(x) return x end,
   global = {
      XMLHttpRequest = {open=open, send=noop, status=200, statusText=""},
      document = {
         createEvent=function() return {initEvent=noop} end,
         createElement=createElement,
         createTextNode=function(_, x) return x end,

         -- Most elements we just don't care about, but we treat fengari-console
         -- specially because that's where our print output gets sent!
         getElementById=function(_, id)
            if id == "fengari-console" then
               local console = createElement()
               -- print gets replaced with some DOM stuff, so make
               -- fake DOM stuff that actually prints instead of
               -- inserting child elements into a div:
               function console.appendChild(_, line)
                  p(table.concat(line, " "))
               end
               return console
            else
               return createElement()
            end
         end,
      },
   }
}

-- This file thinks it's running in the browser! Nobody spill the beans.
local coro = require("init")

-- The web repl contains an interactive tutorial. In the future, this could
-- allow us to test the interactive tutorial, but for now let's just feed it
-- some values and see what it does with them.

-- The init.lua file launches the normal Fennel repl inside a
-- coroutine, and instead of using io.read to get input from stdin, it
-- replaces that function with coroutine.yield. This means that by
-- resuming the coroutine, it simulates someone pressing enter in the
-- shell. In the browser, this coroutine is resumed when someone
-- presses enter in the web repl's <input> field, passing the contents
-- of that field in when it resumes. Since we don't have that here, we
-- just resume it manually with the "input" strings we want.

coroutine.resume(coro, "(print :abc)")
coroutine.resume(coro, "(var x 1)")
coroutine.resume(coro, "(set x 2)")
coroutine.resume(coro, "(print x :better-equal 2)")

-- Output:
-- Welcome to Fennel 0.4.0, running on Fengari (Lua 5.2)
-- You can run any Fennel code here; try this: (print "Hello world!")
-- > (print :abc)
-- abc
--
-- Well, not exactly what I had in mind, but close enough.
-- How about some math; do you like math? Try this: (+ 1 1)
-- > (var x 1)
--
-- > (set x 2)
--
-- > (print x :better-equal 2)
-- 2 better-equal 2

Generated by Phil Hagelberg using scpaste at Wed May 13 19:16:48 2020. PDT. (original)