-- 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. _F = {} _F["%L"] = function () assert(load(parserestofline(), '=L', nil, _ENV))() 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 -- ( Program 3a: the core of miniforth ) -- Our first program in MiniForth. -- It defines a meaning for newlines (they are no-ops; go ahead), -- for "" ("at end-of-text, change the mode to 'stop'"), -- and for "[L" - read everything until a "L]", and interpret -- what is between the "[L" and the "L] as Lua code, with eval. -- 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 } %L push = function (stack, x) stack.n = stack.n + 1; stack[stack.n] = x end %L pop = function (stack) local x = stack[stack.n]; stack[stack.n] = nil; stack.n = stack.n - 1; return x end %L _F["5"] = function () push(DS, 5) end %L _F["DUP"] = function () push(DS, DS[DS.n]) end %L _F["*"] = function () push(DS, pop(DS) * pop(DS)) end %L _F["."] = function () io.write(" "..pop(DS)) end 5 DUP * . ]=] -- Now run it. There's no visible output. pos = 1 mode = "interpret" run() -- At this point the dictionary (_F) has eight words. -- ( Program 3b: a first program in miniforth )