Paste: Lua Stack Language Bootstrapping

Author: Capital
Mode: lua
Date: Wed, 27 Dec 2023 08:09:19
Plain Text |
--
-- Much of this implementation is following "Bootstrapping a Forth in 40 lines 
-- of Lua code" by Eduardo Ochs
--
-- http://angg.twu.net/miniforth-article.html
--
_F = {}
DS, POS = { n = 0 }, 1

LOAD = function (code) return assert(load(code)) end
EVAL = function (code) (LOAD(code))() end
TOS  = function (s) return s[s.n] end
POP  = function (s) local x = TOS(s) s[s.n], s.n = nil, s.n - 1 return x end
PSH  = function (s, x) s.n = s.n + 1 s[s.n] = x end

local __tostring = function (self) return self:compile() end
LUA_QUOTE = function (source)
    return setmetatable(
           { source = source
           , func   = assert(load(source))
           , interpret
                = function (self) self.func() end
           , compile
                = function (self) return self.source end
           }, {__tostring = __tostring})
end

LUPA_QUOTE = function (body)
    return setmetatable(
           { body  = body
           , interpret
                = function (self)
                    for _, item in self.body do
                        item:interpret()
                    end
                end
           , compile
                = function (self)
                    local code = {}
                    for _, item in self.body do
                        code[#code+1] = item:compile()
                    end
                    return table.concat(code, "\n")
                end
           }, {__tostring = __tostring})
end

---@param args { name: string, def: table, parsing: boolean }
---@return table
WORD = function (args)
    return setmetatable(
           { name = args.name
           , def = args.def
           , parsing = (args.parsing ~= nil) and args.parsing or false
           , interpret
                = function (self)
                    self.def:interpret()
                end
           , compile
                = function (self)
                    return self.def:compile()
                end
           }, {__tostring = __tostring})
end

NOP = WORD{ name = "NOP", def = LUA_QUOTE("") }

function parse_pattern(pat)
    local capture, newpos = PROG:match(pat, POS)
    if newpos then POS = newpos return capture end
end

-- Allow quotes to be parsed on their own
function parse_squote () return parse_pattern("^(')()")         end
function parse_dquote () return parse_pattern("^(\")()")        end

-- A word can `"`, `'`, or any space seperated token
function parse_word   () return parse_squote() or
                                      parse_dquote() or
                                      parse_pattern("^([^ \t\n]+)()") end

-- Parsing words to handle whitespace 
function parse_spaces () return parse_pattern("^([ \t]*)()")    end
function parse_nl     () return parse_pattern("^(\n)()")        end
function parse_to_nl  () return parse_pattern("^([^\n]*)()")    end

function parse_word_or_nl () return parse_word() or parse_nl()        end
function get_word         () parse_spaces() return  parse_word()      end
function get_word_or_nl   () parse_spaces() return parse_word_or_nl() end


_F["%L"] = WORD{name = "%L", def = LUA_QUOTE([[ parse_spaces() EVAL(parse_to_nl()) ]]), parsing = true}

RUNNING = true MODES = {}
RUN = function() while RUNNING do INTERPRET() end end

INTERPRET_WORD = function()
    if _F[word] then _F[word]:interpret() return true end
end

INTERPRET_NUMBER = function()
    local n = tonumber(word) if n then PSH(DS, n) return true end
end

INTERPRET = function()
    word = get_word_or_nl() or ""
    local _ = INTERPRET_WORD() or INTERPRET_NUMBER() or error("Can't interpret: " .. word)
end


PROG = [=[
%L _F["\n"] = NOP -- Enable us to handle new lines

%L -- Enable use to handle the of the input stream
%L _F[""] = WORD{ name = "HALT",   def = LUA_QUOTE([[ RUNNING = false ]]) }

%L -- Define the core primitives: Lua Quotation, Symbols, and DEFINE.
%L -- These two need to execute at parse time, otherwise we can't build 
%L -- future literals containing them.
%L _F["L["]     = WORD{ name = "L[", def = LUA_QUOTE([[ PSH(DS, LUA_QUOTE(parse_pattern("^(.-)%s]()"))) ]]), parsing = true }
%L _F["'"]      = WORD{ name = "'",  def = LUA_QUOTE([[ PSH(DS, parse_word()) ]]), parsing = true }

%L -- Finally, create the definition for DEFINE. With all three primitives
%L -- we can drop down into Lupa and define new words directly on the stack.
%L _F["DEFINE"] = WORD{ name = "DEFINE",   def = LUA_QUOTE([[ local name = POP(DS) _F[name] = WORD{ name = name, def = POP(DS) } ]]) }

%L -- Implement CALL, DIP, and ? primitives
L[ POP(DS):interpret() ] 'CALL DEFINE
L[ local x, q = POP2(DS) q:interpret() PSH(DS, x) ] 'DIP DEFINE
L[ local b, f, t = POP3(DS) if b then PSH(DS, t) else PSH(DS, f) end ] '? DEFINE

%L -- This is enough to write a primitive hello, world
L[ print("Hello, World!") ] CALL
]=]

POS = 1
MODE = "interpret"
RUN()

New Annotation

Summary:
Author:
Mode:
Body: