Warning: this is an htmlized version!
The original is here, and
the conversion rules are here.
-- About "Boostrapping a Forth in 40 lines of Lua code"...
-- In 2008 I published an article with the title above in the "Lua
-- Gems" book. Here are links to the article and the book:
--
--    http://angg.twu.net/miniforth-article.html
--    http://angg.twu.net/miniforth/miniforth-article.pdf
--    http://www.lua.org/gems/
--
-- The core of Miniforth became the basis for the language for 2D
-- diagrams in Dednat6. Here are the links to the home page of Dednat6
-- and to my article on TUGboat about it, called "Dednat6: an
-- extensible (semi-)preprocessor for LuaLaTeX that understands
-- diagrams in ASCII art":
--
--    http://angg.twu.net/dednat6.html
--    https://tug.org/TUGboat/tb39-3/tb123ochs-dednat.pdf
--
-- This file contains an updated version of the source code for
-- Miniforth, in a format that should be very easy to run
-- interactively by anyone who knows a bit of Emacs and who is willing
-- to try eev. Eev is an extension for Emacs that I usually describe
-- as "a tool for recording executable notes and playing them back".
-- If you access the htmlized version of this file, at:
--
--   http://angg.twu.net/miniforth/miniforth6.lua.html
--
-- then most of the elisp hyperlinks in this file will point to
-- sections in the eev tutorials that explain them. The main eev
-- features that are used here are explained at:
--
--   (find-eev-quick-intro "3. Elisp hyperlinks")
--   (find-eev-quick-intro "3.1. Non-elisp hyperlinks")
--   (find-eev-quick-intro "6. Controlling shell-like programs")
--   (find-eev-quick-intro "6.2. Other targets")
--   (find-eev-quick-intro "8. Anchors")
--   (find-eev-quick-intro "8.1. Introduction: `to'")
--   (find-refining-intro "2. Refining hyperlinks")
--   (find-pdf-like-intro "2. Preparation")
--   (find-pdf-like-intro "4. Hyperlinks to pages of PDF files")
--   (find-pdf-like-intro "7. Shorter hyperlinks to PDF files")
--
-- Author, version, etc:
-- Eduardo Ochs <eduardoochs@gmail.com>, 2019sep15, GPL3.
-- Feedback VERY welcome!



-- «.downloads»			(to "downloads")
-- «.PP»			(to "PP")
-- «.bootstrapping»		(to "bootstrapping")
-- «.outer-interpreter»		(to "outer-interpreter")
-- «.printstate»		(to "printstate")
-- «.modes»			(to "modes")
-- «.inner-interpreter»		(to "inner-interpreter")
-- «.numbers»			(to "numbers")
-- «.virtual-modes»		(to "virtual-modes")
-- «.polynomials»		(to "polynomials")
-- «.propositional-calculus»	(to "propositional-calculus")



--[[
--  ____                      _                 _ 
-- |  _ \  _____      ___ __ | | ___   __ _  __| |
-- | | | |/ _ \ \ /\ / / '_ \| |/ _ \ / _` |/ _` |
-- | |_| | (_) \ V  V /| | | | | (_) | (_| | (_| |
-- |____/ \___/ \_/\_/ |_| |_|_|\___/ \__,_|\__,_|
--                                                
-- «downloads»  (to ".downloads")
-- The shell commands below downloads a local copy of the PDF of the
-- article; the `code-pdf-{page,text}' sexps define the functions
-- `find-miniforth{page,text}'. See:
--    (find-eev-quick-intro "6. Controlling shell-like programs")
--    (find-eev-quick-intro "6.2. Other targets")
--    (find-pdf-like-intro "2. Preparation")
--    (find-pdf-like-intro "7. Shorter hyperlinks to PDF files")

* (eepitch-shell)
* (eepitch-kill)
* (eepitch-shell)
rm -Rfv /tmp/miniforth/
mkdir   /tmp/miniforth/
cd      /tmp/miniforth/
wget http://angg.twu.net/miniforth/miniforth-article.pdf
wget http://angg.twu.net/miniforth/miniforth6.lua

# (find-fline "/tmp/miniforth/")
# (code-pdf-page "miniforth" "/tmp/miniforth/miniforth-article.pdf")
# (code-pdf-text "miniforth" "/tmp/miniforth/miniforth-article.pdf")
# (find-miniforthpage)
# (find-miniforthtext)
# (find-miniforthpage 2 "Introduction")
# (find-miniforthtext 2 "Introduction")
# (find-miniforthpage 2 "Forth via examples")
# (find-miniforthtext 2 "Forth via examples")
# (find-miniforthpage 5 "Bootstrapping miniforth")
# (find-miniforthtext 5 "Bootstrapping miniforth")
# (find-miniforthpage 7 "Modes")
# (find-miniforthtext 7 "Modes")
# (find-miniforthpage 8 "Virtual modes")
# (find-miniforthtext 8 "Virtual modes")
# (find-miniforthpage 10 "A bytecode for polynomials")
# (find-miniforthtext 10 "A bytecode for polynomials")
# (find-miniforthpage 11 "A bytecode language for propositional calculus")
# (find-miniforthtext 11 "A bytecode language for propositional calculus")

--]]




-- «PP»  (to ".PP")
-- A simple pretty-printer that can print tables.
-- From: (find-es "lua5" "tos-standalone")
--       (find-angg "LUA/lua50init.lua" "compat")
--       (find-angg "LUA/lua50init.lua" "split")
write  = io.write
format = string.format
pack   = table.pack or function (...) return {n=select("#", ...), ...} end
split = function (str, pat)
    local A = {}
    local f = function (word) table.insert(A, word) end
    string.gsub(str, pat or "([^%s]+)", f)
    return A
  end
eval = function (str) return assert(loadstring(str))() end
printf = function (...) write(format(...)) end

map = function (f, A)
    local B = {}
    for i=1,#A do table.insert(B, (f(A[i]))) end
    return B
  end
mapconcat = function (f, A, sep)
    return table.concat(map(f, A), sep)
  end

sorted = function (tbl, lt) table.sort(tbl, lt); return tbl end
keys = function (tbl)
    local ks = {}
    for k,_ in pairs(tbl) do table.insert(ks, k) end
    return ks
  end

tos_compare_pairs = function (pair1, pair2)
    local key1,  key2  = pair1.key,  pair2.key
    local type1, type2 = type(key1), type(key2)
    if type1 == type2 then
      if type1 == "number" then return key1 < key2 end
      if type1 == "string" then return key1 < key2 end
      return tostring(key1) < tostring(key2)  -- fast
    else
      return type1 < type2   -- numbers before strings before tables, etc
    end
  end
tos_sorted_pairs = function (T)
    local Tpairs = {}
    for key,val in pairs(T) do
      table.insert(Tpairs, {key=key, val=val})
    end
    table.sort(Tpairs, tos_compare_pairs)
    return Tpairs
  end
tos_pair = function (pair)
    return tos(pair.key).."="..tos(pair.val)
  end
tos_table = function (T, sep)
    return "{"..mapconcat(tos_pair, tos_sorted_pairs(T), sep or ", ").."}"
  end
tos = function (o)
    local t = type(o)
    if t=="number" then return tostring(o) end
    if t=="string" then return format("%q", o) end
    if t=="table"  then return tos_table(o) end
    return "<"..tostring(o)..">"
  end

PP = function (...)
    local arg = pack(...)
    for i=1,arg.n do printf(" %s", tos(arg[i])) end
    print()
  end

--[[
-- Some tests from: (find-es "lua-intro" "intro:table-constructors")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"

a = {10, 20, 30}
print(a[2])                      --> 20
print(200, "some string", a)     --> 200  some string  table: 0x8e2eee0
PP   (200, "some string", a)     --> 200 "some string" {1=10, 2=20, 3=30}
b = {11, a, "foo", print}
PP(b)   --> {1=11, 2={1=10, 2=20, 3=30}, 3="foo", 4=<function: 0x8e1e020>}
function foo() return 30, 40, 50 end
c = {10, 20, foo()}              --> {1=10, 2=20, 3=30, 4=40, 5=50}
PP(c)

--]]






--  ____              _       _                         _             
-- | __ )  ___   ___ | |_ ___| |_ _ __ __ _ _ __  _ __ (_)_ __   __ _ 
-- |  _ \ / _ \ / _ \| __/ __| __| '__/ _` | '_ \| '_ \| | '_ \ / _` |
-- | |_) | (_) | (_) | |_\__ \ |_| | | (_| | |_) | |_) | | | | | (_| |
-- |____/ \___/ \___/ \__|___/\__|_|  \__,_| .__/| .__/|_|_| |_|\__, |
--                                         |_|   |_|            |___/ 
--
-- «bootstrapping»  (to ".bootstrapping")
-- «outer-interpreter»  (to ".outer-interpreter")
-- From: (find-miniforthpage 5 "Bootstrapping miniforth")
--       (find-miniforthtext 5 "Bootstrapping miniforth")
-- Here we define the outer interpreter.

-- Global variables that hold the input:
subj = "5 DUP * ."      -- what we are interpreting (example)
pos  = 1                -- where are are (1 = "at the beginning")

-- Low-level functions to read things from "pos" and advance "pos".
-- Note: the "pat" argument in "parsebypattern" is a pattern with
-- one "real" capture and then an empty capture.
parsebypattern = function (pat)
    local capture, newpos = string.match(subj, pat, pos)
    if newpos then pos = newpos; return capture end
  end
parsespaces     = function () return parsebypattern("^([ \t]*)()") end
parseword       = function () return parsebypattern("^([^ \t\n]+)()") end
parsenewline    = function () return parsebypattern("^(\n)()") end
parserestofline = function () return parsebypattern("^([^\n]*)()") end
parsewordornewline = function () return parseword() or parsenewline() end

-- A "word" is a sequence of one or more non-whitespace characters.
-- The outer interpreter reads one word at a time and executes it.
-- Note that `getwordornewline() or ""' returns a word, or a newline, or "".
getword          = function () parsespaces(); return parseword() end
getwordornewline = function () parsespaces(); return parsewordornewline() end

-- The dictionary.
-- Entries whose values are functions are primitives.
-- We will only introduce non-primitives in part II.
_F = {}
_F["%L"] = function () eval(parserestofline()) end

-- The "processor". It can be in any of several "modes".
-- Its initial behavior is to run modes[mode]() - i.e.,
-- modes.interpret() - until `mode' becomes "stop".
mode  = "interpret"
modes = {}
run = function () while mode ~= "stop" do modes[mode]() end end

-- Initially the processor knows only this mode, "interpret"...
-- Note that "word" is a global variable.
interpretprimitive = function ()
    if type(_F[word]) == "function" then _F[word](); return true end
  end
interpretnonprimitive = function () return false end   -- stub
interpretnumber       = function () return false end   -- stub
p_s_i = function () end  -- print state, for "interpret" (stub)
modes.interpret = function ()
    word = getwordornewline() or ""
    p_s_i()
    local _ = interpretprimitive() or
              interpretnonprimitive() or
              interpretnumber() or
              error("Can't interpret: "..word)
  end

-- Our first program in MiniForth.
-- First it defines a behavior for newlines (just skip them),
-- for "" (change mode to "stop"; note that `word' becomes "" on
-- end of text), and for "[L ___ L]" blocks (eval "___" as Lua code).
-- Then it creates a data stack - DS - and four words - "5", "DUP",
-- "*", "." - that operate on it.
--
subj = [==[
%L _F["\n"] = function () end
%L _F[""]   = function () mode = "stop" end
%L _F["[L"] = function () eval(parsebypattern("^(.-)%sL]()")) end
[L
  DS = { n = 0 }
  push = function (stack, x) stack.n = stack.n + 1; stack[stack.n] = x end
  pop  = function (stack) local x = stack[stack.n]; stack[stack.n] = nil;
                          stack.n = stack.n - 1; return x end
  _F["5"]   = function () push(DS, 5) end
  _F["DUP"] = function () push(DS, DS[DS.n]) end
  _F["*"]   = function () push(DS, pop(DS) * pop(DS)) end
  _F["."]   = function () io.write(" "..pop(DS)) end
L]
]==]

-- Now run it. There's no visible output.
pos = 1
mode = "interpret"
run()

-- At this point the dictionary (_F) has eight words.


--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
= subj
PP(_F)

--]]





---  ____            _     ___ ___ 
--- |  _ \ __ _ _ __| |_  |_ _|_ _|
--- | |_) / _` | '__| __|  | | | | 
--- |  __/ (_| | |  | |_   | | | | 
--- |_|   \__,_|_|   \__| |___|___|
---                                
-- Part II: add to miniforth several features of a real Forth.

---             _       _       _        _       
---  _ __  _ __(_)_ __ | |_ ___| |_ __ _| |_ ___ 
--- | '_ \| '__| | '_ \| __/ __| __/ _` | __/ _ \
--- | |_) | |  | | | | | |_\__ \ || (_| | ||  __/
--- | .__/|_|  |_|_| |_|\__|___/\__\__,_|\__\___|
--- |_|                                          
--
-- «printstate»  (to ".printstate")
-- In this block "d" is as a shorthand for "dump"...

format = string.format
d = {}
d.q = function (obj)
    if type(obj) == "string" then return format("%q", obj) end
    if type(obj) == "number" then return format("%s", obj) end
  end
d.qw = function (obj, w) return format("%-"..w.."s", d.q(obj)) end
d.o  = function (obj)    return string.gsub(d.q(obj),     "\\\n", "\\n") end
d.ow = function (obj, w) return string.gsub(d.qw(obj, w), "\\\n", "\\n") end
d.arr = function (array) return "{ "..table.concat(array, " ").." }" end
d.RS = function (w) return format("RS=%-"..w.."s", d.arr(RS)) end
d.DS = function (w) return format("DS=%-"..w.."s", d.arr(DS)) end
d.PS = function (w) return format("PS=%-"..w.."s", d.arr(PS)) end
d.mode = function (w) return format("mode=%-"..w.."s", mode) end
d.v = function (varname) return varname.."="..d.o(_G[varname]) end

d.subj   = function () print((string.gsub(subj, "\n$", ""))) end
d.memory = function () print(" memory ="); PP(memory) end

d.base = function () return d.RS(9)..d.mode(11)..d.DS(11) end

p_s_i = function () print(d.base()..d.v("word")) end
p_s_c = function () print(d.base()..d.v("here").." "..d.v("word")) end
p_s_f = function () print(d.base()..d.v("instr")) end
p_s_h = function () print(d.base()..d.v("head")) end
p_s_lit   = function () print(d.base()..d.v("data")) end
p_s_pcell = function () print(d.base()..d.v("pdata")) end

t = 0
d.t = function (w) return format("t=%-"..w.."d", t) end
d.tick = function () t = t + 1; return "" end

_F["."] = function () io.write(" "..pop(DS)) end  -- original
_F["."] = function () print(" "..pop(DS)) end     -- better for when we're always printing the mode

-- d._F(): print the dictionary.
-- TODO: make it print the primitives and non-primitives separately.
d._F = function ()
    local tos1 = function (s) return (format("%q", s):gsub("\n", "n")) end
    local f = function (k) return format("%-10s %s", tos1(k), tos(_F[k])) end
    return " _F =\n"..mapconcat(f, sorted(keys(_F)), "\n")
  end

test1 = function ()
    d.base = function () return d.mode(11)..d.DS(11) end
    print()
    print("First test:")
    subj = [[  5 DUP DUP * * .  ]]
    d.subj()
    DS = { n = 0 }; pos = 1; mode = "interpret"; run()
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test1()
= d._F()

--]]





---                                            
---  _ __ ___   ___ _ __ ___   ___  _ __ _   _ 
--- | '_ ` _ \ / _ \ '_ ` _ \ / _ \| '__| | | |
--- | | | | | |  __/ | | | | | (_) | |  | |_| |
--- |_| |_| |_|\___|_| |_| |_|\___/|_|   \__, |
---                                      |___/ 
--
-- «modes»  (to ".modes")
-- The return stack, the memory, and "here".
-- (find-miniforthpage 7 "Modes")
-- (find-miniforthtext 7 "Modes")

RS     = { n = 0 }
memory = { n = 0 }
here = 1

compile  = function (...) for i = 1,arg.n do compile1(arg[i]) end end
compile1 = function (x)
    memory[here] = x; here = here + 1
    memory.n = math.max(memory.n, here)
  end


---  _                         _       _                  
--- (_)_ __  _ __   ___ _ __  (_)_ __ | |_ ___ _ __ _ __  
--- | | '_ \| '_ \ / _ \ '__| | | '_ \| __/ _ \ '__| '_ \ 
--- | | | | | | | |  __/ |    | | | | | ||  __/ |  | |_) |
--- |_|_| |_|_| |_|\___|_|    |_|_| |_|\__\___|_|  | .__/ 
---                                                |_|    
--
-- «inner-interpreter»  (to ".inner-interpreter")
-- The bytecode of a Forth word starts with the head
-- "DOCOL" and ends with the Forth instruction "EXIT".
-- We store heads in the memory as strings, and the
-- table _H converts the name of a head into its code.

_H = {}
_H["DOCOL"] = function ()
    -- RS[RS.n] = RS[RS.n] + 1
    mode = "forth"
  end
_F["EXIT"] = function ()
    pop(RS)
    if type(RS[RS.n]) == "string" then mode = pop(RS) end
    -- if mode == nil then mode = "stop" end    -- hack
  end

-- Modes for the inner interpreter.
-- Remember that heads are always strings,
-- Forth instructions that are strings are primitives (-> "_F[str]()"), and
-- Forth instructions that are numbers are calls to non-primitives.
modes.head = function ()
    head = memory[RS[RS.n]]
    p_s_h()
    RS[RS.n] = RS[RS.n] + 1
    _H[head]()
  end
modes.forth = function ()
    instr = memory[RS[RS.n]]
    p_s_f()
    RS[RS.n] = RS[RS.n] + 1
    if type(instr) == "number" then push(RS, instr); mode = "head"; return end
    if type(instr) == "string" then _F[instr](); return end
    error("Can't run forth instr: "..mytostring(instr))
  end

-- This was a stub. Now that we know how to execute non-primitives,
-- replace it with the real definition.
interpretnonprimitive = function ()
    if type(_F[word]) == "number" then
      push(RS, "interpret")
      push(RS, _F[word])
      mode = "head"
      return true
    end
  end

-- ":" starts a definition, and switches from "interpret" to "compile".
-- ";" closes a definition, and switches from "compile" to "interpret".
_F[":"] = function ()
    _F[getword()] = here
    compile("DOCOL")
    mode = "compile"
  end
_F[";"] = function ()
    compile("EXIT")
    mode = "interpret"
  end
IMMEDIATE = {}
IMMEDIATE[";"] = true

-- Define a new mode: "compile".
compileimmediateword = function ()
    if word and _F[word] and IMMEDIATE[word] then
      if type(_F[word]) == "function" then   -- primitive
        _F[word]()
      else
        push(RS, mode)
        push(RS, _F[word])
        mode = "head"
      end
      return true
    end
  end
compilenonimmediateword = function ()
    if word and _F[word] and not IMMEDIATE[word] then
      if type(_F[word]) == "function" then
        compile1(word)	    -- primitive: compile its name (string)
      else
        compile1(_F[word])  -- non-primitive: compile its address (a number)
      end
      return true
    end
  end
compilenumber = function ()
    if word and tonumber(word) then
      compile1("LIT"); compile1(tonumber(word)); return true
    end
  end
modes.compile = function ()
    word = getword()
    p_s_c()
    local _ = compileimmediateword() or
              compilenonimmediateword() or
              compilenumber() or
              error("Can't compile: "..(word or EOT))
  end





test2 = function ()
    d.base = function () return d.RS(9)..d.mode(8)..d.DS(11) end
    print()
    print('Run "5 CUBE" (from the inner interpreter):')
    memory = { n = 0 }
    here = 1
    _F["SQUARE"] = here; compile("DOCOL", "DUP", "*", "EXIT")
    _F["CUBE"]   = here; compile("DOCOL", "DUP", _F["SQUARE"], "*", "EXIT")
    RS = { [0]="stop", _F["CUBE"]; n = 1 }; mode = "head"; DS = { 5; n = 1 }
    run()
    d.memory()
  end

--[[
-- (find-miniforthpage 3 "when we run CUBE")
-- (find-miniforthtext 3 "when we run CUBE")
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test2()
= d._F()

--]]

test3 = function ()
    d.base = function () return d.RS(19)..d.mode(11)..d.DS(11) end
    print()
    print('Compile SQUARE and CUBE; run "5 CUBE ." (from the outer interpreter):')
    memory = { n = 0 }; here = 1; RS = { n = 0 }
    subj = [[
      : SQUARE DUP * ;
      : CUBE DUP SQUARE * ;
      5 CUBE .
    ]]
    d.subj()
    DS = { n = 0 }; pos = 1; mode = "interpret"; run()
    d.memory()
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test3()
= d._F()

--]]





---                        _                   
---  _ __  _   _ _ __ ___ | |__   ___ _ __ ___ 
--- | '_ \| | | | '_ ` _ \| '_ \ / _ \ '__/ __|
--- | | | | |_| | | | | | | |_) |  __/ |  \__ \
--- |_| |_|\__,_|_| |_| |_|_.__/ \___|_|  |___/
---                                            
-- «numbers»  (to ".numbers")
-- How to interpret arbritrary numbers.
-- In our simplest examples the "5" worked because it was in the dictionary.
-- As the bootstrap code didn't define a data stack (DS), and
-- "interpretnumber" uses DS, it was cleaner to define it there as a stub.
-- Now we replace the stub by the real definition.
interpretnumber = function ()
    if word and tonumber(word) then push(DS, tonumber(word)); return true end
  end

-- "compilenumber", above, defines the behavior of numbers in compile mode.
-- It compiles first a "LIT" - a Forth primitive that eats bytecode - and
-- then the value of the number. Now we define how "LIT" works.
_F["LIT"] = function ()
    push(DS, memory[RS[RS.n]])
    RS[RS.n] = RS[RS.n] + 1
  end

_F["LIT"] = function () mode = "lit" end
modes.lit = function ()
    data = memory[RS[RS.n]]
    p_s_lit()
    push(DS, memory[RS[RS.n]])
    RS[RS.n] = RS[RS.n] + 1
    mode = "forth"
  end

_F["+"] = function () push(DS, pop(DS) + pop(DS)) end

-- (find-miniforthpage 7 "Modes")
-- (find-miniforthtext 7 "Modes")
-- (find-miniforthpage 8 "LIT")
-- (find-miniforthtext 8 "LIT")

test4 = function ()
    d.base = function () return d.RS(17)..d.mode(11)..d.DS(14) end
    print()
    print("Test the support for arbitrary numbers:")
    memory = { n = 0 }; here = 1
    subj = [[
      : SQUARE DUP * ;
      : +20000 20000 + ;
      22 SQUARE +20000 .
    ]]
    d.subj()
    DS = { n = 0 }; RS = { n = 0 }; pos = 1; mode = "interpret"; run()
    d.memory()
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test4()
= d._F()

--]]







---                  _                                              
---  _ __  _   _ ___| |__      _ __      _ __   ___  _ __     _ __  
--- | '_ \| | | / __| '_ \    | '_ \    | '_ \ / _ \| '_ \   | '_ \ 
--- | |_) | |_| \__ \ | | |   | | | |_  | |_) | (_) | |_) |  | | | |
--- | .__/ \__,_|___/_| |_|___|_| |_( ) | .__/ \___/| .__/___|_| |_|
--- |_|                  |_____|    |/  |_|         |_| |_____|     

push_1 = function (S, a)
    S[S.n], S[S.n+1], S.n =
    a,      S[S.n],   S.n+1
  end
push_2 = function (S, a)
    S[S.n-1], S[S.n],   S[S.n+1], S.n =
    a,        S[S.n-1], S[S.n],   S.n+1
  end
pop_1 = function (S)
    local a = S[S.n-1]; S[S.n-1], S[S.n], S.n =
                        S[S.n],   nil,    S.n-1
    return a
  end
pop_2 = function (S)
    local a = S[S.n-2]; S[S.n-2], S[S.n-1], S[S.n], S.n =
                        S[S.n-1], S[S.n],   nil,    S.n-1
    return a
  end



---                 _ _ 
---  _ __   ___ ___| | |
--- | '_ \ / __/ _ \ | |
--- | |_) | (_|  __/ | |
--- | .__/ \___\___|_|_|
--- |_|                 
--
-- «virtual-modes»  (to ".virtual-modes")
-- (find-miniforthpage 8 "Virtual modes")
-- (find-miniforthtext 8 "Virtual modes")
-- (find-miniforthpage 9 "PCELL")
-- (find-miniforthtext 9 "PCELL")

pcellread = function () return memory[PS[PS.n]] end
pcell     = function ()
    local p = memory[PS[PS.n]]
    PS[PS.n] = PS[PS.n] + 1
    return p
  end
_F["R>P"] = function () push(PS, pop_1(RS)) end
_F["P>R"] = function () push_1(RS, pop(PS)) end
_F["PCELL"] = function () mode = "pcell" end
modes.pcell = function ()
    pdata = memory[PS[PS.n]]
    p_s_pcell()
    push(DS, memory[PS[PS.n]])
    PS[PS.n] = PS[PS.n] + 1
    mode = "forth"
  end


test6 = function ()
    d.base = function () return d.RS(17)..d.mode(11)..d.DS(14) end
    d.base = function () return d.t(3)..d.tick()..d.RS(10)..d.mode(8)..d.PS(8)..d.DS(13) end
    print()
    print("Virtual modes (LIT vs. VLIT):")
    memory = { n = 0 }; here = 1
    _F["VLIT"] = here
    compile("DOCOL", "R>P", "PCELL", "P>R", "EXIT")
    _F["TESTLITS"] = here
    compile("DOCOL", "LIT", 123, _F["VLIT"], 234, "EXIT")
    t = 0
    PS = { n = 0 }
    DS = { n = 0 }; RS = { [0] = "stop", _F["TESTLITS"], n = 1 }; mode = "head"; run()
    d.memory()
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test6()
= d._F()

--]]






---                    _       
---  _ __  _ __   ___ | |_   _ 
--- | '_ \| '_ \ / _ \| | | | |
--- | |_) | |_) | (_) | | |_| |
--- | .__/| .__/ \___/|_|\__, |
--- |_|   |_|            |___/ 
--
-- «polynomials»  (to ".polynomials")
-- (find-miniforthpage 10 "A bytecode for polynomials")
-- (find-miniforthtext 10 "A bytecode for polynomials")
-- (find-miniforthpage 10 "PPOLY")
-- (find-miniforthtext 10 "PPOLY")

p_s_polyn = function () print(d.base().."n="..polyncoefs) end
p_s_polyc = function ()
    print(d.base().."n="..polyncoefs.." acc="..polyacc.." coef="..polycoef)
  end
p_s_polye = function () print(d.base().."n="..polyncoefs) end
p_s_polye = function () print(d.base().."acc="..polyacc) end

-- There's a new trick here. When we run "ppoly" we pass by several
-- modes that only occur here; instead of implementing this as several
-- modes in the "modes" table, and letting the main loop call these
-- modes, we run everything in a single function.

ppoly = function ()
    polyacc = 0
    polyncoefs = pcellread()
    mode = "ppolyn"
    p_s_polyn()
    pcell()
    mode = "ppolyc"
    while polyncoefs > 0 do
      polycoef = pcellread()
      p_s_polyc()
      pcell()
      polyacc = polyacc * DS[DS.n] + polycoef
      polyncoefs = polyncoefs - 1
    end
    mode = "ppolye"
    p_s_polye()
    DS[DS.n] = polyacc
    mode = pop(RS)
    return polyacc
  end
-- (find-sh "lua51 ~/miniforth/miniforth5.lua")

---  ____   ___  ____   ___  _  __   __
--- |  _ \ / _ \|  _ \ / _ \| | \ \ / /
--- | | | | | | | |_) | | | | |  \ V / 
--- | |_| | |_| |  __/| |_| | |___| |  
--- |____/ \___/|_|    \___/|_____|_|  
---                                    
-- To do: write down a honest explanation for this... I created these
-- words half by writing down traces (by hand!) and then trying to
-- create functions that did the transitions that were happening
-- there, and half by luck...

_F["PPOLY"] = function () push(RS, mode); DS[DS.n] = ppoly() end
_H["DOPOLY"] = function ()
    push(PS, pop(RS) + 1)
    push(RS, "forth")
    return ppoly()    -- tail call
  end

test7 = function ()
    print()
    print("A bytecode for polynomials, 1: PPOLY")
    d.base = function () return d.RS(10)..d.mode(8)..d.PS(8)..d.DS(13) end
    memory = { n = 0 }; here = 1
    -- _F["P1"]     = here; compile("DOPOL")
    _F["P1ADDR"]    = here; compile("DOADDR", 4, 2, 3, 4, 5.5 )
    -- _F["P1TEST"] = here; compile("DOCOL", "LIT", 10, _F["P1"], "EXIT")
    PS = { _F["P1ADDR"] + 1, n = 1 }
    DS = { 10, n = 1 }
    RS = { [0] = "stop", n = 0 }
    d.memory()
    ppoly()
    print(polyacc)
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test7()

--]]

test8 = function ()
    print()
    print("A bytecode for polynomials, 2: POLY")
    d.base = function () return d.RS(13)..d.mode(8)..d.PS(8)..d.DS(12) end
    memory = { n = 0 }; here = 1
    _F["POLY"] = here; compile("DOCOL", "R>P", "PPOLY", "P>R", "EXIT")
    _F["POLYTEST"]  = here; compile("DOCOL", _F["POLY"], 4, 2, 3, 4, 5.5, "EXIT")
    d.memory()
    PS = { n = 0 }
    DS = { 10, n = 1 }
    RS = { [0] = "stop", _F["POLYTEST"], n = 1 }
    mode = "head"
    run()
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test8()

--]]

test9 = function ()
    print()
    print("A bytecode for polynomials 3: DOPOLY")
    d.base = function () return d.RS(14)..d.mode(8)..d.PS(7)..d.DS(12) end
    memory = { n = 0 }; here = 1
    _F["P1(X)"]  = here; compile("DOPOLY")
    _F["P1ADDR"] = here; compile("DOADDR", 4, 2, 3, 4, 5.5 )
    _F["DOPOLYTEST"] = here; compile("DOCOL", "LIT", 10, _F["P1(X)"], "EXIT")
    PS = { n = 0 }
    DS = { n = 0 }
    RS = { [0] = "stop", _F["DOPOLYTEST"], n = 1 }
    mode = "head"
    d.memory()
    xxcall(run)
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test9()

--]]





---  ____                     ____      _      
--- |  _ \ _ __ ___  _ __    / ___|__ _| | ___ 
--- | |_) | '__/ _ \| '_ \  | |   / _` | |/ __|
--- |  __/| | | (_) | |_) | | |__| (_| | | (__ 
--- |_|   |_|  \___/| .__/   \____\__,_|_|\___|
---                 |_|                        
--
-- «propositional-calculus»  (to ".propositional-calculus")
-- (find-miniforthpage 11 "A bytecode language for propositional calculus")
-- (find-miniforthtext 11 "A bytecode language for propositional calculus")

atomicformulas = { ["P"]=true, ["Q"]=true, ["R"]=true }
isatomicformula = function (name) return atomicformulas[name] end

d.ff  = function (field)    return field.."="..d.o (formula[field])    end
d.ffw = function (field, w) return field.."="..d.ow(formula[field], w) end
d.formulafields = function ()
    if formula.cc then return table.concat({
        d.ff("addr"), d.ff("cc"), d.ff("l"), d.ff("r"), d.ff("next"), d.ff("name")
      }, ", ")
    end
    if not formula.cc then return table.concat({
        d.ff("addr"), d.ff("next"), d.ff("name")
      }, ", ")
    end
  end
d.formula = function (n)
    -- formula = formulas[n]
    formula = props[n]
    -- print(format("formulas["..n.."] = { "..d.formulafields().." }"))
    print(format(n..": { "..d.formulafields().." }"))
  end

formula  = {}
formulas = {}
-- formulas[100] = { addr=100, cc="&", l=101, r=102, name="(P&Q)" }
-- formulas[101] = { addr=101, atomic="P" }
-- for i=100,101 do d.formula(i) end

atomicprops = { ["P"] = true, ["Q"] = true, ["R"] = true }
isatomicprop = function (propname) return atomicprops[propname] end

-- pprop: parse a proposition (in Proposition Calculus, in Polish Notation).
-- This function memoizes already-parsed propositions using the table "props".
-- 
pprop = function ()
    local addr = PS[PS.n]
    if props[addr] then
      PS[PS.n] = props[addr].next
    else
      local propword = pcell()
      if isatomicprop(propword) then
        local next = PS[PS.n]
        props[addr] = { addr=addr, next=next, name=propword }
      else
        local cc, l, r = propword, pprop(), pprop()
        local next = PS[PS.n]
        local name = "("..props[l].name..cc..props[r].name..")"
        props[addr] = { addr=addr, next=next, cc=cc, l=l, r=r, name=name }
      end
    end
    return addr
  end

test10 = function ()
    print()
    print("Recursive immediate data: propositional calculus")
    memory = { n = 0 }; here = 1
    compile("=>", "=>", "Q", "R", "=>", "&", "P", "Q", "&", "P", "R")
    d.memory()
    props  = {}
    PS = { 1, n = 1 }
    pprop()
    print(" props =")
    for i=1,11 do d.formula(i) end
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test10()
= d._F()

--]]





---  _____         _       
--- |_   _|__  ___| |_ ___ 
---   | |/ _ \/ __| __/ __|
---   | |  __/\__ \ |_\__ \
---   |_|\___||___/\__|___/
---                        




test5 = function ()
    d.base = function () return d.RS(18)..d.mode(11)..d.DS(11) end
    print()
    print("A simple test for LIT: MINUTES")
    subj = [[
      : MINUTES 60 * ;
      20 MINUTES .
    ]]
    d.subj()
    -- memory = { n = 0 }; here = 1; RS = { n = 0 }
    -- DS = { n = 0 }; pos = 1; mode = "interpret"; run()
    --
    d.base = function () return d.RS(7)..d.mode(7)..d.DS(11) end
    memory = {"DOCOL", "LIT", 60, "*", "EXIT"}
    RS = { [0] = "stop", 1, n = 1 }; mode = "head"; DS = { 20, n = 1 }; run()
    d.memory()
  end

--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test5()
= d._F()

--]]







-- PP("args:", ...)
if ({...})[1] then eval(({...})[1]) end



--[[
* (eepitch-lua51)
* (eepitch-kill)
* (eepitch-lua51)
dofile "miniforth6.lua"
test1()
test2()
test3()
test4()
test5()
test6()
test7()
test8()
test9()
test10()

--]]


-- Local Variables:
-- coding: raw-text-unix
-- End: