Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- This file:
--   http://angg.twu.net/SRF/srfx.lua.html
--   http://angg.twu.net/SRF/srfx.lua
--           (find-angg "SRF/srfx.lua")
-- Author: Eduardo Ochs <eduardoochs@gmail.com>
-- Version: 2021aug24
-- Based on Marc's srf.lua.
--
-- (defun e  () (interactive) (find-angg "SRF/srfx.lua"))
-- (defun e1 () (interactive) (find-angg "SRF/srfx-interpreter.lua"))
-- (defun eb () (interactive) (find-angg "SRF/srfx-basics.lua"))
-- (defun e0 () (interactive) (find-srffile "ports/lua/srfish.lua"))
-- (defun ee () (interactive) (find-es "srf"))
--

-- «.throw»		(to "throw")
-- «.loadstr»		(to "loadstr")
-- «.Stack»		(to "Stack")
-- «.tokenisation»	(to "tokenisation")
-- «.interpreter»	(to "interpreter")
-- «.entry»		(to "entry")




-- «throw»  (to ".throw")
--
Throw = Class {
  type    = "Throw",
  __index = {
    bool      = function(_)   error('logical value must be 0 or 1', 0) end,
    brk       = function()    error('break', 0) end, -- todo
    host      = function(err) error('host interpretation failed: ' .. err, 0) end,
    underflow = function()    error('stack underflow', 0) end,
    unknown   = function(w)   error('unknown word: ' .. w, 0) end,
  },
}
throw = Throw {}



-- «loadstr»  (to ".loadstr")
loadstr = -- load with environment
  setfenv and function(s, env)
                local fn, err = loadstring(s)
                if fn then setfenv(fn, env) end
                return fn, err
              end
          or  function(s, env) return load(s, nil, nil, env) end

toBool = function(i)
  if     i == 0 then return false
  elseif i == 1 then return true
  else               throw.bool(i) end
end

fromBool = function(b) return b and 1 or 0 end

copyTable = function(tbl)
  local t = {}
  for k, v in pairs(tbl) do t[k]=v end
  return t
end

-- sorted = function(list)
--   local l = copyTable(list)
--   table.sort(l)
--   return l
-- end

trim = function(s)
  return string.gsub(string.gsub(s, '^%s*', ''), '%s*$', '')
end

image = function(v)
  if     v == nil    then return '(nil)'
  elseif tonumber(v) then return v
  else                    return quote(v) end
end

quote = function(s)
  return "'" .. string.gsub(s, "'", "''") .. "'"
end




-- «Stack»  (to ".Stack")
--
Stack = Class {
  type = "Stack",
  new  = function () return Stack {} end,
  __tostring = function(s) return s:image() end,
  --
  __index = { -- s: a stack object
    depth = function(s) return #s end,
    dup   = function(s) s:push(s:peek(#s)) end,
    over  = function(s) s:push(s:peek(#s-1)) end,
    push  = table.insert,
    peek  = function(s, n)
              if n <= 0 then throw.underflow() end
              return s[n]
            end,
    pop   = function(s)
              local v = table.remove(s)
              if v == nil then throw.underflow() end
              return v
            end,
    reset = function(s) for i, v in ipairs(s) do s[i] = nil end end,
    rot   = function(s)
              if #s < 3 then throw.underflow() end
              s[#s], s[#s-1], s[#s-2] = s[#s-2], s[#s], s[#s-1]
            end,
    swap  = function(s) s[#s], s[#s-1] = s:peek(#s-1), s:peek(#s) end,
    image = function(s)
              local img = '<' .. #s .. '> '
              for _, v in ipairs(s) do img = img .. image(v) .. ' ' end
              return img
            end,
  },
}

stack = Stack.new()


-- «tokenisation»  (to ".tokenisation")
--
nextToken = function(phrase)
  local stripped = string.gsub(phrase, '^%s+', '')
  local first = string.sub(stripped, 0, 1)
  if first == "'" or first == '"' then return parseString(first, stripped)
  else                                 return parseWord(stripped) end
end

parseString = function(quote, phrase)
  local pattern = string.format('%s([^%s]*)%s(.*)', quote, quote, quote)
  local i, word, w = 0, '', ''
  while string.sub(phrase, 0, 1) == quote do
    w, phrase = string.match(phrase, pattern)
    if i > 0 then w = quote .. w end
    word = word .. w
    i = i + 1
  end
  return {kind='literal', word=word, rest=phrase}
end

parseWord = function(line)
  local word, rest = string.match(line, "(%S+)(.*)")
  local n = tonumber(word) -- todo: srf recognises binary, hex and octal
  if n == nil then return {kind='term',    word=word, rest=rest}
  else             return {kind='literal', word=n,    rest=rest} end
end







-- «interpreter»  (to ".interpreter")
-- (find-angg "SRF/srfx-interpreter.lua")
dofile            "srfx-interpreter.lua"

-- (find-lua51manual "#pdf-setmetatable")

make_interpreter = function ()
    return {
      vocab = {},
      stack = Stack.new(),
      -- prims = interpreter.primitives,
      -- aux   = interpreter.auxiliary,
      prims = interpreter_primitives,
      aux   = interpreter_auxiliary,
    }
  end

interpreter = {
  new = function()
    local mt = { __index = interpreter__index }
    local t = setmetatable(make_interpreter(), mt)
    -- local t = setmetatable(make_interpreter(), interpreter)
    -- interpret the preamble (final line must end in a newline)
    for line in string.gmatch(interpreter.preamble, "(.-)\n") do
      t:doline(line)
    end
    return t
  end,

  auxiliary  = interpreter_auxiliary, -- functions used by a subset of primitives
  primitives = interpreter_primitives,
  preamble   = interpreter_preamble,
  __index    = interpreter__index,

}



-- «entry»  (to ".entry")

terp = interpreter.new()

repl = function()
  for line in io.lines() do
    terp:doline(line)
  end
end

-- arg = arg or {}
-- 
-- if   arg[1]
-- then terp:doline(arg[1])
-- else repl()
-- end



--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "srfx.lua"
PPV(terp.prims)
PPV(terp.vocab)

PPV(myterp.vocab)
myterp:dopreamble()
PPV(myterp.vocab)

myterp:repl()
  'hello' 'world'
  10 20
  .s
  : square dup *
  5 square .
  'myterp.STOP = 1' host


see 2swap
'2swap' expand .

'print "HELLO"' host
'PPV(interpreter_primitives)' host
'os.exit()'     host


repl()


--]]



--[[
* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
./srfish.lua ''
./srfish.lua '1 2 + .'
./srfish.lua
  'print "HELLO"' host
  'os.exit()'     host

* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
arg = {""}
dofile "srfish.lua"
terp:doline '1 2 + .'

--]]








-- Local Variables:
-- coding:               utf-8-unix
-- End: