Warning: this is an htmlized version!
The original is here, and the conversion rules are here. |
-- This file: -- http://angg.twu.net/SRF/srfa.lua.html -- http://angg.twu.net/SRF/srfa.lua -- (find-angg "SRF/srfa.lua") -- Author: Eduardo Ochs <eduardoochs@gmail.com> -- -- [A]nother attempt to reimplement Marc's srfish.lua. -- This is just the core of my reimplementation; only a few of Marc's -- primitives and colon definitions were ported and tested, and I'm -- currently rewriting the Terp class again. My code looks big, but -- that's because it has lots of printing functions that are used in -- the test blocks. Practically all its documentation is in test -- blocks - see: -- -- http://angg.twu.net/LATEX/2021emacsconf.pdf -- http://angg.twu.net/eev-videos/emacsconf2021.mp4 -- http://angg.twu.net/emacsconf2021.html -- -- Version: 2022feb09 -- -- (defun a () (interactive) (find-angg "SRF/srfa.lua")) -- (defun b () (interactive) (find-angg "SRF/srfb.lua")) -- (defun c () (interactive) (find-angg "SRF/srfc.lua")) -- -- To test this, run: (find-angg "SRF/srfa.lua" "download") -- then run its test block. -- «.download» (to "download") -- -- «.Throw» (to "Throw") -- «.utils» (to "utils") -- «.Stack» (to "Stack") -- «.Stack-tests» (to "Stack-tests") -- «.Toknz» (to "Toknz") -- «.Toknz-tests» (to "Toknz-tests") -- «.Code» (to "Code") -- «.Code-tests» (to "Code-tests") -- «.Vocab» (to "Vocab") -- «.Vocab-test» (to "Vocab-test") -- «.marcsprims» (to "marcsprims") -- «.marcspreamble» (to "marcspreamble") -- «.aux» (to "aux") -- «.Terp» (to "Terp") -- «.Terp-tests» (to "Terp-tests") -- «.Terp-loop-test» (to "Terp-loop-test") -- «.Terp-break-test» (to "Terp-break-test") -- «.Terp-prim:-test» (to "Terp-prim:-test") -- «download» (to ".download") --[[ * (eepitch-shell) * (eepitch-kill) * (eepitch-shell) rm -Rv /tmp/srfa/ mkdir /tmp/srfa/ cd /tmp/srfa/ wget -O edrxlib.lua http://angg.twu.net/LUA/lua50init.lua wget http://angg.twu.net/SRF/srfa.lua * (setenv "LUA_INIT" "@/tmp/srfa/edrxlib.lua") * (find-anchor "/tmp/srfa/srfa.lua" "Terp-tests") --]] require "edrxlib" -- (find-angg "LUA/lua50init.lua" "edrxlib") -- «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, eol = function(w) error('eol', 0) end, }, } throw = Throw {} -- «utils» (to ".utils") -- 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 trim = bitrim 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 spaces = function (n) return (" "):rep(n) end -- «Stack» (to ".Stack") -- Marc's version. -- Compare with: (find-dn6 "stacks.lua" "Stack") -- Stack = Class { type = "Stack", new = function () return Stack {} end, __tostring = function(s) return s:image() end, -- marc __tostring = function(s) return mapconcat(mytostring, s, " ") end, -- edrx -- __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() -- «Stack-tests» (to ".Stack-tests") --[[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "srfa.lua" s = Stack.new() = s s:push(10) s:push(20) s:push("30") s:push("a b c") = s = tonumber(30) = tonumber("30") --]] -- _____ _ -- |_ _|__ | | ___ __ ____ -- | |/ _ \| |/ / '_ \|_ / -- | | (_) | <| | | |/ / -- |_|\___/|_|\_\_| |_/___| -- -- «Toknz» (to ".Toknz") -- A tokenizer class that can use both Lua patterns and Lpeg. -- Half of its code is made of printing functions. Toknz = Class { type = "Toknz", new = function (line, pos) return Toknz {subj=line, pos=pos or 1} end, __tostring = function (tknz) return tknz:tostring() end, __index = { -- -- Low-level parsers. setlasttoken = function (tknz, o) tknz.lasttoken = o tknz.pos = o.e return o end, parsepat0 = function (tknz, pat, kind) local b,body,e = tknz.subj:match(pat, tknz.pos) local o = {b=b, kind=kind, body=body, e=e} if b then return o end end, parsepat = function (tknz, pat, kind) local b,body,e = tknz.subj:match(pat, tknz.pos) local o = {b=b, kind=kind, body=body, e=e} if b then return tknz:setlasttoken(o) end end, parselpegpat = function (tknz, lpegpat) local o = lpegpat:match(tknz.subj, tknz.pos) if o then return tknz:setlasttoken(o) end end, -- -- Simple parsers with predefined patterns. -- Parsers with more complex patterns, like one that parses either -- a word in the vocabulary or a number, can be added later. word = function (tknz) return tknz:parsepat("^%s*()(%S+)()", "word") end, rest = function (tknz) -- rest of the line return tknz:parsepat("^%s()(.*)()$", "rest") -- note the initial "%s"! end, dqstring = function (tknz) -- doubly-quoted string return tknz:parsepat('^%s*()"([^"]*)"()', "strlit") end, sqstring = function (tknz) -- singly-quoted string return tknz:parsepat("^%s*()'([^']*)'()", "strlit") end, wordsuchthat = function (tknz, validator, kind) local o = tknz:parsepat0("^%s*()(%S+)()", "word") if o and validator(o.body) then o = {b=o.b, kind=kind, body=o.body, e=o.e} return tknz:setlasttoken(o) end end, -- -- Several ways to convert a tknz to a string. tostring = function (tknz) local last = "" local bigstr = tknz.subj .. "\n" .. tknz:caretunderpos() if tknz.lasttoken then bigstr = bigstr.."\n"..tknz:last() end return bigstr end, caretunderpos = function (tknz, pos) pos = pos or tknz.pos return (" "):rep(#tknz.subj):replace(pos-1, "^") end, last = function (tknz) return tknz:carets("usecopy")..tknz:shortfields() end, carets = function (tknz, usecopy, b, e) b = b or tknz.lasttoken.b e = e or tknz.lasttoken.e local cars = usecopy and tknz.subj:sub(b, e-1) or ("^"):rep(e-b) return (" "):rep(#tknz.subj):replace(b-1, cars) end, shortfields = function (tknz, o) o = o or tknz.lasttoken local f = function (field, q) if not o[field] then return "" end return " "..field..":"..(q or "")..o[field]..(q or "") end return f("kind")..f("body", '"')..f("base") end, -- -- Run method several times and print tknz after each one. test = function (tknz, verbose, method) method = method or "word" print(tknz.subj) -- print subj only once local lt = tknz[method](tknz) while lt do print(tknz:last()) if verbose then PP(lt); print() end lt = tknz[method](tknz) end end, }, } -- «Toknz-tests» (to ".Toknz-tests") -- --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "srfa.lua" line = [=[ : 5* 5 * ; 'foo bar' + 0x23 ]=] tk = Toknz.new(line) = tk PP(tk:word()) = tk PP(tk:word()) = tk Toknz.new(line):test() Toknz.new(line):test(nil, "rest") Toknz.new(line, 12):test(nil, "rest") -- Toknz.new(line):test("verbose") line = [=[ 'foo "" bar' 'plic bletch' "qux" ]=] Toknz.new(line):test(nil, "sqstring") line = [=[ "foo '' bar" "plic bletch" 'qux' ]=] Toknz.new(line):test(nil, "dqstring") Toknz.__index.num = function (tknz) local isnum = function (str) return str:match("^[0-9]+$") end return tknz:wordsuchthat(isnum, "num") end Toknz.__index.complextoken = function (tknz) return tknz:dqstring() or tknz:sqstring() or tknz:num() or tknz:word() end line = [=[ : 5* 5 * ; "foo bar" 'plic bletch' ]=] Toknz.new(line):test(nil, "complextoken") --]==] -- ____ _ -- / ___|___ __| | ___ -- | | / _ \ / _` |/ _ \ -- | |__| (_) | (_| | __/ -- \____\___/ \__,_|\___| -- -- «Code» (to ".Code") -- See: (find-es "lua5" "lambda-with-Code") -- An object of class Code contains source code in Lua both in an -- abbreviated form (in the ".src" field) and as standard Lua code -- (the ".code" field). -- -- TA-DA: an object of the class Code does NOT contain a compiled -- version of its .code field! 8-O Code = Class { type = "Code", parse2 = function (src) local vars,rest = src:match("^%s*([%w_,]+)%s*=>(.*)$") if not vars then error("Code.parse2 can't parse: "..src) end return vars, rest end, format2 = function (fmt, src) return format(fmt, Code.parse2(src)) end, ve = function (src) -- src is "vars => expression" local fmt = "local %s=...; return %s" return Code {src=src, code=Code.format2(fmt, src)} end, vc = function (src) -- src is "vars => code" local fmt = "local %s=...; %s" return Code {src=src, code=Code.format2(fmt, src)} end, __tostring = function (c) return c.src end, __call = function (c, ...) return assert(loadstring(c.code))(...) end, __index = { }, } ve = Code.ve vc = Code.vc -- «Code-tests» (to ".Code-tests") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "srfa.lua" = ve [[ a,b => a*b ]] = ve [[ a,b => a*b ]] .src = ve [[ a,b => a*b ]] .code = ve [[ a,b => a*b ]] (2, 3) = vc [[ a,b => print(a*b) ]] (2, 3) = vc [[ a,b => print('hi'); return a*b ]] (2, 3) = vc [[ a,b => print('hi'); return a*b ]] .src = vc [[ a,b => print('hi'); return a*b ]] .code = ve [[ a,b => a*b ]] .code --]==] -- __ __ _ -- \ \ / /__ ___ __ _| |__ -- \ \ / / _ \ / __/ _` | '_ \ -- \ V / (_) | (_| (_| | |_) | -- \_/ \___/ \___\__,_|_.__/ -- -- «Vocab» (to ".Vocab") -- Both the set of primitives and the set of colon definitions are -- implemented as objects of the class Vocab. Vocab = Class { type = "Vocab", newprims = function (width) return Vocab {_ = {}, width = width or 11} end, newcolons = function (width) return Vocab { _ = {}, width = width or 12, tostring1 = function (p, name) return p:colontostring(name) end, } end, -- __tostring = function (p) return p:tostring() end, __index = { width = 8, primtostring = function (p, name, src) return name:replace(p.width, src or p._[name].src) end, colontostring = function (p, name, def) return (": "..name):replace(p.width, def or p._[name]) end, -- or: tostring1 = function (p, name) return p:colontostring(name) end, tostring1 = function (p, name) return p:primtostring(name) end, tostring = function (p) local f = function (name) return p:tostring1(name) end return mapconcat(f, sorted(keys(p._)), "\n") end, -- toset = function (p) return Set.from(keys(p._)) end, -- add = function (p, name, o) p._[name] = o; return p; end, addvc = function (p, name, src) p._[name] = vc(src); return p; end, addtovc = function (p, name, src, prefix) p._[name] = vc(p._[name].src .. p:vcprefix(prefix) .. src) return p end, vcprefix = function (p, prefix) if type(prefix) == "string" then return prefix end if type(prefix) == "number" then return "\n"..spaces(prefix) end return "\n"..spaces(p.vcnspaces) end, vcnspaces = 15, -- addprims = function (p, bigstr) for _,line in ipairs(splitlines(bigstr)) do local name,src = line:match "^%s*(%S+)%s*(.*)$" if name then p:add(name, vc(src)) end end return p end, addcolons = function (p, bigstr) for _,line in ipairs(splitlines(bigstr)) do local name,def = line:match "^%s*:%s+(%S+)%s*(.*)$" if name then p:add(name, def) end end return p end, }, } prims = Vocab.newprims() vocab = Vocab.newcolons() -- «Vocab-test» (to ".Vocab-test") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "srfa.lua" prims = Vocab.newprims():addprims [=[ abc a,b,c => return 100*a + 10*b + c ab a,b => return 10*a + b ]=] = prims = prims._["abc"] = prims._["abc"] .src = prims._["abc"] .code = prims._["abc"] (2, 3, 4) = prims:addvc ("hello", "o => print 'Hello'") = prims:addtovc("hello", " print 'there!'") vocab = Vocab.newcolons():addcolons [=[ : square dup * : cube dup square * ]=] = vocab = vocab._["cube"] = prims:toset() = vocab:toset() = (prims:toset() + vocab:toset()) = (prims:toset() + vocab:toset()):ksc(" ") --]==] -- __ __ _ _ -- | \/ | __ _ _ __ ___( )___ _ __ _ __(_)_ __ ___ ___ -- | |\/| |/ _` | '__/ __|// __| | '_ \| '__| | '_ ` _ \/ __| -- | | | | (_| | | | (__ \__ \ | |_) | | | | | | | | \__ \ -- |_| |_|\__,_|_| \___| |___/ | .__/|_| |_|_| |_| |_|___/ -- |_| -- -- (find-angg "SRF/srfx-interpreter.lua" "interpreter_primitives") -- «marcsprims» (to ".marcsprims") marcsprims = [=[ * o => o:push(o:pop() * o:pop()) ** o => o.stack:swap(); o:push(o:pop() ^ o:pop()) + o => o:push(o:pop() + o:pop()) - o => o.stack:swap(); o:push(o:pop() - o:pop()) / o => o.stack:swap(); o:push(o:pop() / o:pop()) // o => o.stack:swap(); o:push(o:pop() % o:pop()) . o => print(o:pop()) .s o => print(o.stack) .vocab o => print(o.vocab) -- edrx < o => o:push(fromBool(o:pop() > o:pop())) <= o => o:push(fromBool(o:pop() >= o:pop())) <> o => o:push(fromBool(o:pop() ~= o:pop())) = o => o:push(fromBool(o:pop() == o:pop())) > o => o:push(fromBool(o:pop() < o:pop())) >= o => o:push(fromBool(o:pop() <= o:pop())) and o => o:push(fromBool(toBool(o:pop()) and toBool(o:pop()))) break o => throw.brk() clear o => o.vocab._[o:pop()]=nil concat o => o.stack:swap(); o:push(o:pop() .. o:pop()) defined? o => o:push(fromBool(o.vocab._[o:pop()] ~= nil)) do o => o:dophrase(o:pop()) drop o => o.stack:pop() dup o => o.stack:dup() either o => if not toBool(o:pop()) then o.stack:swap() end; o:pop() fetch o => o:push(o.vocab._[o:pop()] or '') not o => o:push(fromBool(not toBool(o:pop()))) or o => o:push(fromBool(toBool(o:pop()) or toBool(o:pop()))) over o => o.stack:over() quote o => o:push(quote(o:pop())) repeat o => o.aux_loop(o) repeat# o => o.aux_loop(o, true) reset o => o.stack:reset() -- edrx reverse o => o:push(string.reverse(o:pop())) rot o => o.stack:rot() sentence o => o:push((o. tknz:rest() or throw.eol()).body) -- edrx sentencep o => o:push((o.p.tknz:rest() or throw.eol()).body) -- edrx store o => o.vocab._[o:pop()] = o:pop() -- edrx swap o => o.stack:swap() trim o => o:push(trim(o:pop())) version o => o:push(_VERSION) word o => o:push((o. tknz:word() or throw.eol()).body) -- edrx? wordp o => o:push((o.p.tknz:word() or throw.eol()).body) -- edrx? words o => print((o.prims:toset() + o.vocab:toset()):ksc(" ")) -- edrx words o => print(( prims:toset() + o.vocab:toset()):ksc(" ")) -- edrx ]=] --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "srfa.lua" = prims prims = Vocab.newprims():addprims(marcsprims) = print((prims:toset() + vocab:toset()):ksc(" ")) = prims._["words"] = prims._["words"]() --]==] -- TODO: reimplement this one.. -- -- ['host'] = function(o) -- local env = copyTable(_ENV or _G); env.self = o -- local fn, err = loadstr(o.stack:pop(), env) -- if not fn then throw.host(err) end -- fn() -- end, -- __ __ _ _ _ -- | \/ | __ _ _ __ ___( )___ _ __ _ __ ___ __ _ _ __ ___ | |__ | | ___ -- | |\/| |/ _` | '__/ __|// __| | '_ \| '__/ _ \/ _` | '_ ` _ \| '_ \| |/ _ \ -- | | | | (_| | | | (__ \__ \ | |_) | | | __/ (_| | | | | | | |_) | | __/ -- |_| |_|\__,_|_| \___| |___/ | .__/|_| \___|\__,_|_| |_| |_|_.__/|_|\___| -- |_| -- -- (find-angg "SRF/srfx-interpreter.lua" "interpreter_preamble") -- «marcspreamble» (to ".marcspreamble") -- marcspreamble = [[ -- 'word sentence swap store' ':' store : : wordp sentencep swap store : if either do : when '' swap if : unless not when : until 'break' swap when : while 'break' swap unless : nip swap drop : tuck swap over : shell 'os.execute(self:pop())' host : value swap quote swap store : drops 'drop' swap repeat : inc 1 + : dec 1 - ]] --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "srfa.lua" vocab = Vocab.newcolons():addcolons(marcspreamble) = vocab --]==] -- _____ -- |_ _|__ _ __ _ __ -- | |/ _ \ '__| '_ \ -- | | __/ | | |_) | -- |_|\___|_| | .__/ -- |_| -- -- «Terp» (to ".Terp") -- A class for defining interpreters that can work like the standard -- outer interpreter of Forth, like the interpreter of %D lines in -- dednat6, like Marc's srf, etc etc. Terp = Class { type = "Terp", newsrf = function (line, pos, parent) return Terp {tknz = Toknz.new(line or "", pos or 1), stack = stack, vocab = vocab, aux = aux, p = parent} end, __index = { push = function (terp, o) return terp.stack:push(o) end, pop = function (terp) return terp.stack:pop() end, -- declit = function (terp) -- decimal literals; no floats yet local pat = "^[0-9]+$" local v = function (str) return str:match(pat) end return terp.tknz:wordsuchthat(v, "declit") end, prim = function (terp) local v = function (str) return prims._[str] end return terp.tknz:wordsuchthat(v, "prim") end, nonprim = function (terp) local v = function (str) return vocab._[str] end return terp.tknz:wordsuchthat(v, "nonprim") end, unknownword = function (terp) local v = function (str) return true end return terp.tknz:wordsuchthat(v, "unknown") end, -- token = function (terp) terp.lasttoken = terp:nonprim() or terp:prim() or terp:declit() or terp.tknz:dqstring() or terp.tknz:sqstring() or terp:unknownword() return terp.lasttoken end, -- doprim = function (terp, primname) prims._[primname](terp) end, dophrase = function (terp, line) Terp.newsrf(line, nil, terp):dotokens() end, dononprim = function (terp, name) terp:dophrase(terp.vocab._[name]) end, dolines = function (terp, bigstr) for _,line in ipairs(splitlines(bigstr)) do terp:dophrase(line) end end, -- dotoken = function (terp) -- for srf local t = terp.lasttoken local k = terp.lasttoken.kind if k == "declit" then terp:push(tonumber(t.body)) elseif k == "strlit" then terp:push(t.body) elseif k == "prim" then terp:doprim(t.body) elseif k == "nonprim" then terp:dononprim(t.body) elseif k == "unknown" then throw.unknown(t.body) else PP("Error in dotoken:", t); error() end end, dotokens = function (terp, verbose) if verbose then print(terp.tknz.subj) print(terp.tknz:caretunderpos()) end while terp:token() do terp:dotoken() if verbose then print(terp.tknz:carets("usecopy").." stack: "..tostring(stack)) end end end, -- aux_loop = function (o, dopush) local n,str = o:pop(), o:pop() for i=1,n do if dopush then o:push(i) end o:dophrase(str) end end, aux_word = function (o) return (o.tknz:word() or throw.eol()).body end, aux_sentence = function (o) return (o.tknz:rest() or throw.eol()).body end, }, } -- «Terp-tests» (to ".Terp-tests") -- --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "srfa.lua" stack = Stack.new() tp = Terp.newsrf [=[ 'boo' 42 99 'foo "" bar' "plic ' bletch" ]=] tp = Terp.newsrf [=[ 20 50 * . ]=] --> unknown word * tp:dotokens("verbose") = stack = stack stack = Stack.new() prims = Vocab.newprims():addprims(marcsprims) vocab = Vocab.newcolons():addcolons(marcspreamble) dosrf_v = function (line) Terp.newsrf(line):dotokens("verbose") end dosrf = function (bigstr) Terp.newsrf(""):dolines(bigstr) end stack = Stack.new() dosrf_v [=[ 20 50 * . ]=] dosrf [=[ 20 50 * . ]=] dosrf [=[ word ploft . ]=] dosrf [=[ sentence foo bar ]=] dosrf [=[ . ]=] dosrf [=[ .vocab ]=] dosrf [=[ words ]=] dosrf [=[ : ab swap 10 * + 3 4 ab . ]=] dosrf [=[ : :0bad word sentence .s reset : :0 wordp sentencep .s reset :0bad :0 foo bar plic ]=] --]==] -- «Terp-loop-test» (to ".Terp-loop-test") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "srfa.lua" stack = Stack.new() prims = Vocab.newprims():addprims(marcsprims) vocab = Vocab.newcolons():addcolons(marcspreamble) dosrf_v = function (line) Terp.newsrf(line):dotokens("verbose") end dosrf = function (bigstr) Terp.newsrf(""):dolines(bigstr) end = prims = otype(prims) prims:addprims [[ HELLO o => print 'hello' ]] prims:addprims [[ HELLO o => print(otype(o)) ]] = prims dosrf [=[ HELLO ]=] prims:addprims [[ HELLO o => o:aux_loop() ]] dosrf [=[ '42 .' 4 HELLO ]=] dosrf [=[ '42 .' 4 repeat ]=] dosrf [=[ '.' 4 repeat# ]=] --]==] -- «Terp-break-test» (to ".Terp-break-test") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "srfa.lua" stack = Stack.new() prims = Vocab.newprims():addprims(marcsprims) vocab = Vocab.newcolons():addcolons(marcspreamble) dosrf_v = function (line) Terp.newsrf(line):dotokens("verbose") end dosrf = function (bigstr) Terp.newsrf(""):dolines(bigstr) end PP(keys(prims)) PP(keys(prims._)) PPPV(prims._.drop) = prims._.drop prims:add("4times", vc [[o => local str=o:pop(); for i=1,4 do o:dophrase(str) end ]]) = prims dosrf [=[ '2 3 * .' 4times ]=] dosrf [=[ '2 3 * .' do ]=] dosrf [=[ 1 . 'break' do 2 .]=] --]==] -- «Terp-prim:-test» (to ".Terp-prim:-test") --[==[ * (eepitch-lua51) * (eepitch-kill) * (eepitch-lua51) dofile "srfa.lua" stack = Stack.new() prims = Vocab.newprims():addprims(marcsprims) vocab = Vocab.newcolons():addcolons(marcspreamble) dosrf_v = function (line) Terp.newsrf(line):dotokens("verbose") end dosrf = function (bigstr) Terp.newsrf(""):dolines(bigstr) end prims:add("Test0", vc [[o => PP(o:aux_word(), o:aux_sentence()) ]]) dosrf [=[ Test0 foo bar plic ]=] prims:addvc ("Test1", [[o => print("Line1") ]]) prims:addtovc("Test1", [[ print("Line2") ]]) prims:addtovc("Test1", [[ print("Line3") ]], "") prims:addtovc("Test1", [[ print("Line4") ]], 10) = prims prims:addvc("prim:", [[o => prim_name = o:aux_word() prim_rest = o:aux_sentence() prims:addvc (prim_name, prim_rest) ]]) prims:addvc("prim\\", [[o => prim_rest = o:aux_sentence() prims:addtovc(prim_name, prim_rest) ]]) dosrf [=[ prim: Test2 o => print("foo") prim\ print("bar") prim\ print("plic") ]=] = prims dosrf [=[ prim: host: o => eval(o:aux_sentence()) prim: .prims o => print(prims) .prims host: print("Foo") ]=] dosrf [=[ prim: host/ o => host_code = o:aux_sentence() prim: host| o => host_code = host_code.."\n"..o:aux_sentence() prim: host\ o => host_code = host_code.."\n"..o:aux_sentence(); eval(host_code) .prims host/ print([[ Line 1 host| Line 2 host| Line 3 host\ Line 4 ]]) ]=] --]==] -- Local Variables: -- coding: utf-8-unix -- End: