Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- miniforth4.lua - 2007jun13, Edrx -- For Lua-5.1 (because of string.match) -- http://angg.twu.net/miniforth/miniforth4.lua.html -- http://angg.twu.net/miniforth/miniforth3.abs.txt.html -- (find-a2ps (buffer-file-name)) 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 -- Part I: The outer interpreter. -- Global variables that hold the input: subj = "5 DUP * ." -- what we are interpreting 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 words 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 III. _F = {} _F["%L"] = function () eval(parserestofline()) end -- The "processor", as a state machine. -- Its initial behavior is to run states[state]() - i.e., -- states.interpret() - until `state' becomes "stop". state = "interpret" states = {} printstate = function () end -- stub runstate = function () printstate(); states[state]() end run = function () while state ~= "stop" do runstate() end end -- Initially the processor knows only this state, "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 PSI = function () end -- print state for "interpret" states.interpret = function () word = getwordornewline() or "" PSI() local _ = interpretprimitive() or interpretnonprimitive() or intrpretnumber() or error("Can't interpret: "..(word or "(EOT)")) end -- Our first program in MiniForth. -- First it defines a behavior for newlines (just skip them), -- for "" (change state 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 () state = "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 state = "interpret" run() -- At this point the dictionary (_F) has eight words. --- _ _ _ _ --- _ __ _ __(_)_ __ | |_ ___| |_ __ _| |_ ___ --- | '_ \| '__| | '_ \| __/ __| __/ _` | __/ _ \ --- | |_) | | | | | | | |_\__ \ || (_| | || __/ --- | .__/|_| |_|_| |_|\__|___/\__\__,_|\__\___| --- |_| vardiag = function (varname) return varname.."=".._G[varname] end varDiag = function (varname) return varname.."="..mytostring(_G[varname]) end stackdiag = function (stackname) return format("%s={ %s }", stackname, table.concat(_G[stackname])) end diags3 = function () return format("%22s%18s%22s", stackdiag("RS"), vardiag("state"), stackdiag("DS")) end diags4 = function (extravar) return diags3()..format("%12s", varDiag(extravar)) end PSI = function () print(diags4("word")) end PSC = function () print(diags4("word")) end PSF = function () print(diags4("instr")) end PSH = function () print(diags4("head")) end RS = { n = 0 } --- _ _ ____ ____ --- (_) |___ \| ___| --- | | | __) |___ \ --- | | |/ __/ ___) | --- |_|_|_____|____/ --- subj = [[ 5 DUP DUP * * . ]] DS = { n = 0 }; pos = 1; state = "interpret"; run() --- --- _ __ ___ ___ _ __ ___ ___ _ __ _ _ --- | '_ ` _ \ / _ \ '_ ` _ \ / _ \| '__| | | | --- | | | | | | __/ | | | | | (_) | | | |_| | --- |_| |_| |_|\___|_| |_| |_|\___/|_| \__, | --- |___/ 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 --- _ ___ _____ --- _ _ | | |_ _|_ _| --- (_) (_) | | | | | | --- _ _ | |___ | | | | --- (_) ( ) |_____|___| |_| --- |/ IMMEDIATE = {} _F[":"] = function () _F[parseword()] = here compile("DOCOL") state = "compile" end _F[";"] = function () compile("EXIT") state = "interpret" end IMMEDIATE[";"] = true --- _ _ _ _ --- ___| |_ __ _| |_ ___ ___ ___ ___ _ __ ___ _ __ (_) | ___ --- / __| __/ _` | __/ _ \/ __| / __/ _ \| '_ ` _ \| '_ \| | |/ _ \ --- \__ \ || (_| | || __/\__ \| (_| (_) | | | | | | |_) | | | __/ --- |___/\__\__,_|\__\___||___(_)___\___/|_| |_| |_| .__/|_|_|\___| --- |_| compileimmediateword = function () if word and _F[word] and IMMEDIATE[word] then if type(_F[word]) == "function" then -- primitive _F[word]() else push(RS, state) push(RS, _F[word]) state = "head" end end end compilenonimmediateword = function () if word and _F[word] and not IMMEDIATE[word] then compile1(_F[word]); return true end end compilenumber = function () if word and tonumber(word) then compile1("LIT"); compile1(tonumber(word)); return true end end states.compile = function () word = parseword() PSC() local _ = compileimmediateword() or compilenonimmediateword() or compilenumber() or error("Can't compile: "..(word or EOT)) end RS = { n = 0 } --- ____ _ _ ____ _____ --- _ / ___| | | | __ )| ____| _ --- (_) | | | | | | _ \| _| (_) --- _ | |___| |_| | |_) | |___ _ _ _ _ --- (_) \____|\___/|____/|_____| (_|_|_) ( ) --- |/ memory = { n = 0 }; here = 1 subj = [[ : SQUARE DUP * ; : CUBE DUP SQUARE * ; 5 CUBE . ]] DS = { n = 0 }; pos = 1; state = "interpret"; run() --- _ ___ _____ --- | | |_ _|_ _| --- | | | | | | --- | |___ | | | | --- |_____|___| |_| --- _F["LIT"] = function () push(DS, RS[RS.n]) RS[RS.n] = RS[RS.n + 1] end interpretnumber = function () if word and tonumber(word) then push(DS, tonumber(word)); return true end end --- ____ ___ _ _ ___ _ _ --- |___ \ / _ \| || | ( _ )| || | --- __) | | | | || |_ / _ \| || |_ --- / __/| |_| |__ _| (_) |__ _| --- |_____|\___/ |_| \___/ |_| --- memory = { n = 0 }; here = 1 subj = [[ : +20000 20000 + ; 22 SQUARE +20000 . ]] DS = { n = 0 }; pos = 1; state = "interpret"; run() --[=[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "miniforth4.lua" DSstate = function () return "DS={ "..table.concat(DS, " ").. " }" end printstate = function () print("state="..state.." "..DSstate()) end DS = { n = 0 } subj = [[ 5 DUP DUP * * . ]] pos = 1 state = "interpret" run() subj = [[ 5 DUP * . ]] pos = 1 state = "interpret" run = function () while state ~= "stop" do printstate(); states[state]() end end --]=] --[=====[ interpretnumber = function () if tonumber(word) then push(DS, tonumber(word)); return true end end --- ____ ____ --- | _ \/ ___| --- | | | \___ \ --- | |_| |___) | --- |____/|____/ --- 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 interpretnumber = function () if word and tonumber(word) then push(DS, tonumber(word)); return true end end (/ 55000.0 (* 14 365)) * (eepitch-lua51) -- RS={interpret 5 1 2 3} state=interpret RS = { n = 0 } memory = { n = 0 } here = 1 _F["SQUARE"] = here; compile("DOCOL", "DUP", "*", "EXIT") _F["CUBE"] = here; compile("DOCOL", "DUP", _F["SQUARE"], "*", "EXIT") _H = {} _H["DOCOL"] = function () RS[RS.n] = RS[RS.n] + 1 state = "forth" end _F["EXIT"] = function () pop(RS) if type(RS[RS.n]) == "string" then state = pop(RS) end end interpretnonprimitive = function () if type(_F[word]) == "number" then push(RS, "interpret") puhs(RS, _F[word]) state = "head" return true end states.head = function () head = memory[RS[RS.n]] head() end --[=[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile(ee_expand("~/miniforth/miniforth4.lua")) --]=] --[=[ DS = { n = 0 } subj = [[ 5 DUP DUP * * . ]] pos = 1 state = "interpret" run() -- state=interpret DS={ } -- state=interpret DS={ 5 } -- state=interpret DS={ 5 5 } -- state=interpret DS={ 5 5 5 } -- state=interpret DS={ 5 25 } -- state=interpret DS={ 125 } 125 -- state=interpret DS={ } subj = [==[ 5 . [lua print "foo" lua] 5 . ]==] DS = { n = 0 }; pos = 1; state = "interpret"; run() --]=] --[=[ PP(memory) PP(_F) RS = { n = 0 } if type(semantics) == "number" then -- "5", "22", etc push(RS, "interpret") push(RS, semantics) state = "head" return end -- what about numbers? parseluablock = function () eval(parsebypattern("^(.-)lua%]")) end readfile = function (fname) local f = assert(io.open(fname, "r")) local fcontents = f:read("*a"); f:close(); return fcontents end setsubjto = function (str) subj = str; pos = 1 end runstring = function (str) setsubjto(str); outerloop() end runfile = function (fname) runstring(readfile(fname)) end -- runfile(arg[1]) -- (find-blogmefile "") -- (find-blogmefile "blogme2-middle.lua") -- (find-luamanualw3m "#pdf-string.match") -- ee = function () runfile(ee_expand("$EEVTMPDIR/ee.mf3")) end compile = function (...) for i = 1,arg.n do if type(arg[i]) == "string" then for _,word in ipairs(split(arg[i])) do compile1(word) end else compile(arg[i]) end end end --]=] --]=====]