From f1129626606384a7a55a21a83531f51f8b5dee25 Mon Sep 17 00:00:00 2001 From: Hans Hagen Date: Tue, 14 Jul 2020 00:25:53 +0200 Subject: 2020-07-13 23:52:00 --- tex/context/base/mkiv/mlib-lua.lua | 1412 +----------------------------------- 1 file changed, 29 insertions(+), 1383 deletions(-) (limited to 'tex/context/base/mkiv/mlib-lua.lua') diff --git a/tex/context/base/mkiv/mlib-lua.lua b/tex/context/base/mkiv/mlib-lua.lua index 9b4ec44dd..b38dbdcf6 100644 --- a/tex/context/base/mkiv/mlib-lua.lua +++ b/tex/context/base/mkiv/mlib-lua.lua @@ -6,1399 +6,45 @@ if not modules then modules = { } end modules ['mlib-lua'] = { license = "see context related readme files", } --- This is very preliminary code! +local type = type +local insert, remove = table.insert, table.remove --- maybe we need mplib.model, but how with instances +local set = mp.set +local get = mp.get -local type, tostring, tonumber, select, loadstring = type, tostring, tonumber, select, loadstring -local find, match, gsub, gmatch = string.find, string.match, string.gsub, string.gmatch -local concat, insert, remove = table.concat, table.insert, table.remove +local currentmpx = nil +local stack = { } -local formatters = string.formatters -local lpegmatch = lpeg.match -local lpegpatterns = lpeg.patterns +local get_numeric = mplib.get_numeric +local get_integer = mplib.get_integer +local get_string = mplib.get_string +local get_boolean = mplib.get_boolean +local get_path = mplib.get_path +local set_path = mplib.set_path -local P, S, Ct, Cs, Cc, C = lpeg.P, lpeg.S, lpeg.Ct, lpeg.Cs, lpeg.Cc, lpeg.C - -local report_luarun = logs.reporter("metapost","lua") -local report_script = logs.reporter("metapost","script") -local report_message = logs.reporter("metapost") - -local trace_luarun = false trackers.register("metapost.lua",function(v) trace_luarun = v end) - -local be_tolerant = true directives.register("metapost.lua.tolerant", function(v) be_tolerant = v end) - -local get, set, aux, scan, inject = { }, { }, { }, { }, { } - -mp = mp or { -- system namespace - set = set, - get = get, - aux = aux, - scan = scan, - inject = inject, -} - -MP = MP or { -- user namespace -} - --- We had this: --- --- table.setmetatablecall(mp,function(t,k) mpprint(k) end) --- --- but the next one is more interesting because we cannot use calls like: --- --- lua.mp.somedefdname("foo") --- --- which is due to expansion of somedefdname during suffix creation. So: --- --- lua.mp("somedefdname","foo") - -table.setmetatablecall(mp,function(t,k,...) return t[k](...) end) -table.setmetatablecall(MP,function(t,k,...) return t[k](...) end) - -do - - local currentmpx = nil - local stack = { } - - if CONTEXTLMTXMODE > 0 then - - local scan_next = mplib.scan_next - local scan_expression = mplib.scan_expression - local scan_token = mplib.scan_token - local scan_symbol = mplib.scan_symbol - local scan_numeric = mplib.scan_numeric - local scan_integer = mplib.scan_integer - local scan_boolean = mplib.scan_boolean - local scan_string = mplib.scan_string - local scan_pair = mplib.scan_pair - local scan_color = mplib.scan_color - local scan_cmykcolor = mplib.scan_cmykcolor - local scan_transform = mplib.scan_transform - local scan_path = mplib.scan_path - local scan_pen = mplib.scan_pen - - scan.next = function(k) return scan_next (currentmpx,k) end - scan.expression = function(k) return scan_expression(currentmpx,k) end - scan.token = function(k) return scan_token (currentmpx,k) end - scan.symbol = function(k,e) return scan_symbol (currentmpx,k,e) end - scan.numeric = function() return scan_numeric (currentmpx) end - scan.integer = function() return scan_integer (currentmpx) end - scan.boolean = function() return scan_boolean (currentmpx) end - scan.string = function() return scan_string (currentmpx) end - scan.pair = function(t) return scan_pair (currentmpx,t) end - scan.color = function(t) return scan_color (currentmpx,t) end - scan.cmykcolor = function(t) return scan_cmykcolor (currentmpx,t) end - scan.transform = function(t) return scan_transform (currentmpx,t) end - scan.path = function(t) return scan_path (currentmpx,t) end - scan.pen = function(t) return scan_pen (currentmpx,t) end - - local inject_path = mplib.inject_path - local inject_numeric = mplib.inject_numeric - local inject_pair = mplib.inject_pair - local inject_boolean = mplib.inject_boolean - local inject_integer = mplib.inject_integer - local inject_string = mplib.inject_string - local inject_color = mplib.inject_color - local inject_cmykcolor = mplib.inject_cmykcolor - local inject_transform = mplib.inject_transform - local inject_whatever = mplib.inject_whatever - - inject.path = function(t,cycle,curled) return inject_path (currentmpx,t,cycle,curled) end - inject.numeric = function(n) return inject_numeric (currentmpx,n) end - inject.pair = function(x,y) return inject_pair (currentmpx,x,y) end - inject.boolean = function(b) return inject_boolean (currentmpx,b) end - inject.integer = function(i) return inject_integer (currentmpx,i) end - inject.string = function(s) return inject_string (currentmpx,s) end - inject.color = function(r,g,b) return inject_color (currentmpx,r,g,b) end - inject.cmykcolor = function(c,m,y,k) return inject_cmykcolor(currentmpx,c,m,y,k) end - inject.transform = function(x,y,xx,xy,yx,yy) return inject_transform(currentmpx,x,y,xx,xy,yx,yy) end - inject.whatever = function(...) return inject_whatever (currentmpx,...) end - - -- bonus: - - scan .number = scan .numeric - inject.number = inject.numeric - - table.setmetatablecall(inject,function(t,...) - inject_whatever(currentmpx,...) - end) - - -- experiment - - function mp.autoinject(m) - local t = type(m) - if t == "table" then - local n = #t - if n == 2 then - inject_pair(currentmpx,m) - elseif n == 3 then - inject_color(currentmpx,m) - elseif n == 4 then - inject_cmykcolor(currentmpx,m) - elseif n == 6 then - inject_transform(currentmpx,m) - end - elseif t == "number" then - inject_numeric(currentmpx,m) - elseif t == "string" then - inject_string(currentmpx,m) - elseif t == "boolean" then - inject_boolean(currentmpx,m) - end - end - - else - - local get_numeric = mplib.get_numeric - local get_integer = mplib.get_integer - local get_string = mplib.get_string - local get_boolean = mplib.get_boolean - local get_path = mplib.get_path - local set_path = mplib.set_path - - get.numeric = function(s) return get_numeric(currentmpx,s) end - get.number = function(s) return get_numeric(currentmpx,s) end - get.integer = function(s) return get_integer(currentmpx,s) end - get.string = function(s) return get_string (currentmpx,s) end - get.boolean = function(s) return get_boolean(currentmpx,s) end - get.path = function(s) return get_path (currentmpx,s) end - set.path = function(s,t) return set_path (currentmpx,s,t) end -- not working yet - - end - - function metapost.pushscriptrunner(mpx) - insert(stack,mpx) - currentmpx = mpx - end - - function metapost.popscriptrunner() - currentmpx = remove(stack,mpx) - end - - function metapost.currentmpx() - return currentmpx - end - - local status = mplib.status - - function metapost.currentmpxstatus() - return status and status(currentmpx) or 0 - end - -end - --- unless we adapt the old mp library too, in the end we will have two --- variants - -do - - local lmtxmode = CONTEXTLMTXMODE > 0 - - -- serializers - - local f_integer = formatters["%i"] - local f_numeric = formatters["%F"] - - -- no %n as that can produce -e notation and that is not so nice for scaled butmaybe we - -- should then switch between ... i.e. make a push/pop for the formatters here ... not now. - - local f_integer = formatters["%i"] - local f_numeric = formatters["%F"] - local f_pair = formatters["(%F,%F)"] - local f_ctrl = formatters["(%F,%F) .. controls (%F,%F) and (%F,%F)"] - local f_triplet = formatters["(%F,%F,%F)"] - local f_quadruple = formatters["(%F,%F,%F,%F)"] - local f_transform = formatters["totransform(%F,%F,%F,%F,%F,%F)"] - local f_pen = formatters["(pencircle transformed totransform(%F,%F,%F,%F,%F,%F))"] - - local f_points = formatters["%p"] - local f_pair_pt = formatters["(%p,%p)"] - local f_ctrl_pt = formatters["(%p,%p) .. controls (%p,%p) and (%p,%p)"] - local f_triplet_pt = formatters["(%p,%p,%p)"] - local f_quadruple_pt = formatters["(%p,%p,%p,%p)"] - - local r = P('%') / "percent" - + P('"') / "dquote" - + P('\n') / "crlf" - -- + P(' ') / "space" - local a = Cc("&") - local q = Cc('"') - local p = Cs(q * (r * a)^-1 * (a * r * (P(-1) + a) + P(1))^0 * q) - - mp.cleaned = function(s) return lpegmatch(p,s) or s end - - -- management - - -- sometimes we gain (e.g. .5 sec on the sync test) - - local cache = table.makeweak() - - local runscripts = { } - local runnames = { } - local nofscripts = 0 - - function metapost.registerscript(name,f) - nofscripts = nofscripts + 1 - if f then - runscripts[nofscripts] = f - runnames[name] = nofscripts - else - runscripts[nofscripts] = name - end - return nofscripts - end - - function metapost.scriptindex(name) - return runnames[name] or 0 - end - - -- The gbuffer sharing and such is not really needed now but make a dent when - -- we have a high volume of simpel calls (loops) so we keep it around for a - -- while. - - local nesting = 0 - local runs = 0 - local gbuffer = { } - local buffer = gbuffer - local n = 0 - - local function mpdirect1(a) - n = n + 1 buffer[n] = a - end - local function mpdirect2(a,b) - n = n + 1 buffer[n] = a - n = n + 1 buffer[n] = b - end - local function mpdirect3(a,b,c) - n = n + 1 buffer[n] = a - n = n + 1 buffer[n] = b - n = n + 1 buffer[n] = c - end - local function mpdirect4(a,b,c,d) - n = n + 1 buffer[n] = a - n = n + 1 buffer[n] = b - n = n + 1 buffer[n] = c - n = n + 1 buffer[n] = d - end - local function mpdirect5(a,b,c,d,e) - n = n + 1 buffer[n] = a - n = n + 1 buffer[n] = b - n = n + 1 buffer[n] = c - n = n + 1 buffer[n] = d - n = n + 1 buffer[n] = e - end - - local function mpflush(separator) - buffer[1] = concat(buffer,separator or "",1,n) - n = 1 - end - - function metapost.runscript(code) - nesting = nesting + 1 - runs = runs + 1 - - local index = type(code) == "number" - local f - local result - - if index then - f = runscripts[code] - if not f then - report_luarun("%i: bad index: %s",nesting,code) - elseif trace_luarun then - report_luarun("%i: index: %i",nesting,code) - end - else - if trace_luarun then - report_luarun("%i: code: %s",nesting,code) - end - f = cache[code] - if not f then - f = loadstring("return " .. code) - if f then - cache[code] = f - elseif be_tolerant then - f = loadstring(code) - if f then - cache[code] = f - end - end - end - end - - -- returning nil is more efficient and a signal not to scan in mp - - if f then - - local lbuffer, ln - - if nesting == 1 then - buffer = gbuffer - n = 0 - else - lbuffer = buffer - ln = n - buffer = { } - n = 0 - end - - result = f() - if result then - local t = type(result) - if lmtxmode then - -- we can consider to use the injector for tables but then we need to - -- check of concatination is expected so best keep this! - if t == "number" or t == "boolean" then - -- native types - elseif t == "string" or t == "table" then - -- (concatenated) passed to scantokens - else - -- scantokens - result = tostring(result) - end - else - if t == "number" then - result = f_numeric(result) - elseif t == "table" then - result = concat(result) -- no spaces here - else - result = tostring(result) - end - end - if trace_luarun then - report_luarun("%i: %s result: %s",nesting,t,result) - end - elseif n == 0 then --- result = "" -result = nil -- no scantokens done then - if trace_luarun then - report_luarun("%i: no buffered result",nesting) - end - elseif n == 1 then - result = buffer[1] - if trace_luarun then - report_luarun("%i: 1 buffered result: %s",nesting,result) - end - else - -- the space is why we sometimes have collectors - if nesting == 1 then - -- if we had no space we could pass result directly in lmtx - result = concat(buffer," ",1,n) - if n > 500 or #result > 10000 then - gbuffer = { } -- newtable(20,0) - lbuffer = gbuffer - end - else - -- if we had no space we could pass result directly in lmtx - result = concat(buffer," ") - end - if trace_luarun then - report_luarun("%i: %i buffered results: %s",nesting,n,result) - end - end - - if nesting == 1 then - n = 0 - else - buffer = lbuffer - n = ln - end - - else - report_luarun("%i: no result, invalid code: %s",nesting,code) - result = "" - end - - nesting = nesting - 1 - - return result - end - - function metapost.nofscriptruns() - return runs - end - - -- writers - - local function mpp(value) - n = n + 1 - local t = type(value) - if t == "number" then - buffer[n] = f_numeric(value) - elseif t == "string" then - buffer[n] = value - elseif t == "table" then - if #t == 6 then - buffer[n] = "totransform(" .. concat(value,",") .. ")" - else - buffer[n] = "(" .. concat(value,",") .. ")" - end - else -- boolean or whatever - buffer[n] = tostring(value) - end - end - - local function mpprint(first,second,...) - if second == nil then - if first ~= nil then - mpp(first) - end - else - for i=1,select("#",first,second,...) do - local value = (select(i,first,second,...)) - if value ~= nil then - mpp(value) - end - end - end - end - - local function mpp(value) - n = n + 1 - local t = type(value) - if t == "number" then - buffer[n] = f_numeric(value) - elseif t == "string" then - buffer[n] = lpegmatch(p,value) - elseif t == "table" then - if #t > 4 then - buffer[n] = "" - else - buffer[n] = "(" .. concat(value,",") .. ")" - end - else -- boolean or whatever - buffer[n] = tostring(value) - end - end - - local function mpvprint(first,second,...) -- variable print - if second == nil then - if first ~= nil then - mpp(first) - end - else - for i=1,select("#",first,second,...) do - local value = (select(i,first,second,...)) - if value ~= nil then - mpp(value) - end - end - end - end - - local function mpstring(value) - n = n + 1 - buffer[n] = lpegmatch(p,value) - end - - local function mpboolean(b) - n = n + 1 - buffer[n] = b and "true" or "false" - end - - local function mpnumeric(f) - n = n + 1 - if not f or f == 0 then - buffer[n] = "0" - else - buffer[n] = f_numeric(f) - end - end - - local function mpinteger(i) - n = n + 1 - -- buffer[n] = i and f_integer(i) or "0" - buffer[n] = i or "0" - end - - local function mppoints(i) - n = n + 1 - if not i or i == 0 then - buffer[n] = "0pt" - else - buffer[n] = f_points(i) - end - end - - local function mppair(x,y) - n = n + 1 - if type(x) == "table" then - buffer[n] = f_pair(x[1],x[2]) - else - buffer[n] = f_pair(x,y) - end - end - - local function mppairpoints(x,y) - n = n + 1 - if type(x) == "table" then - buffer[n] = f_pair_pt(x[1],x[2]) - else - buffer[n] = f_pair_pt(x,y) - end - end - - local function mptriplet(x,y,z) - n = n + 1 - if type(x) == "table" then - buffer[n] = f_triplet(x[1],x[2],x[3]) - else - buffer[n] = f_triplet(x,y,z) - end - end - - local function mptripletpoints(x,y,z) - n = n + 1 - if type(x) == "table" then - buffer[n] = f_triplet_pt(x[1],x[2],x[3]) - else - buffer[n] = f_triplet_pt(x,y,z) - end - end - - local function mpquadruple(w,x,y,z) - n = n + 1 - if type(w) == "table" then - buffer[n] = f_quadruple(w[1],w[2],w[3],w[4]) - else - buffer[n] = f_quadruple(w,x,y,z) - end - end - - local function mpquadruplepoints(w,x,y,z) - n = n + 1 - if type(w) == "table" then - buffer[n] = f_quadruple_pt(w[1],w[2],w[3],w[4]) - else - buffer[n] = f_quadruple_pt(w,x,y,z) - end - end - - local function mptransform(x,y,xx,xy,yx,yy) - n = n + 1 - if type(x) == "table" then - buffer[n] = f_transform(x[1],x[2],x[3],x[4],x[5],x[6]) - else - buffer[n] = f_transform(x,y,xx,xy,yx,yy) - end - end - - local function mpcolor(c,m,y,k) - n = n + 1 - if type(c) == "table" then - local l = #c - if l == 4 then - buffer[n] = f_quadruple(c[1],c[2],c[3],c[4]) - elseif l == 3 then - buffer[n] = f_triplet(c[1],c[2],c[3]) - else - buffer[n] = f_numeric(c[1]) - end - else - if k then - buffer[n] = f_quadruple(c,m,y,k) - elseif y then - buffer[n] = f_triplet(c,m,y) - else - buffer[n] = f_numeric(c) - end - end - end - - -- we have three kind of connectors: - -- - -- .. ... -- (true) - - local function mp_path(f2,f6,t,connector,cycle) - if type(t) == "table" then - local tn = #t - if tn == 1 then - local t1 = t[1] - n = n + 1 - if t.pen then - buffer[n] = f_pen(unpack(t1)) - else - buffer[n] = f2(t1[1],t1[2]) - end - elseif tn > 0 then - if connector == true or connector == nil then - connector = ".." - elseif connector == false then - connector = "--" - end - if cycle == nil then - cycle = t.cycle - if cycle == nil then - cycle = true - end - end - local six = connector == ".." -- otherwise we use whatever gets asked for - local controls = connector -- whatever - local a = t[1] - local b = t[2] - n = n + 1 - buffer[n] = "(" - n = n + 1 - if six and #a == 6 and #b == 6 then - buffer[n] = f6(a[1],a[2],a[5],a[6],b[3],b[4]) - controls = ".." - else - buffer[n] = f2(a[1],a[2]) - controls = connector - end - for i=2,tn-1 do - a = b - b = t[i+1] - n = n + 1 - buffer[n] = connector - n = n + 1 - if six and #a == 6 and #b == 6 then - buffer[n] = f6(a[1],a[2],a[5],a[6],b[3],b[4]) - controls = ".." - else - buffer[n] = f2(a[1],a[2]) - controls = connector - end - end - n = n + 1 - buffer[n] = connector - a = b - b = t[1] - n = n + 1 - if cycle then - if six and #a == 6 and #b == 6 then - buffer[n] = f6(a[1],a[2],a[5],a[6],b[3],b[4]) - controls = ".." - else - buffer[n] = f2(a[1],a[2]) - controls = connector - end - n = n + 1 - buffer[n] = connector - n = n + 1 - buffer[n] = "cycle" - else - buffer[n] = f2(a[1],a[2]) - end - n = n + 1 - buffer[n] = ")" - end - end - end - - local function mppath(...) - mp_path(f_pair,f_ctrl,...) - end - - local function mppathpoints(...) - mp_path(f_pair_pt,f_ctrl_pt,...) - end - - local function mpsize(t) - n = n + 1 - buffer[n] = type(t) == "table" and f_numeric(#t) or "0" - end - - local replacer = lpeg.replacer("@","%%") - - local function mpfprint(fmt,...) - n = n + 1 - if not find(fmt,"%",1,true) then - fmt = lpegmatch(replacer,fmt) - end - buffer[n] = formatters[fmt](...) - end - - local function mpquoted(fmt,s,...) - if s then - n = n + 1 - if not find(fmt,"%",1,true) then - fmt = lpegmatch(replacer,fmt) - end - -- buffer[n] = '"' .. formatters[fmt](s,...) .. '"' - buffer[n] = lpegmatch(p,formatters[fmt](s,...)) - elseif fmt then - n = n + 1 - -- buffer[n] = '"' .. fmt .. '"' - buffer[n] = lpegmatch(p,fmt) - else - -- something is wrong - end - end - - aux.direct = mpdirect1 - aux.direct1 = mpdirect1 - aux.direct2 = mpdirect2 - aux.direct3 = mpdirect3 - aux.direct4 = mpdirect4 - aux.flush = mpflush - - aux.print = mpprint - aux.vprint = mpvprint - aux.boolean = mpboolean - aux.string = mpstring - aux.numeric = mpnumeric - aux.number = mpnumeric - aux.integer = mpinteger - aux.points = mppoints - aux.pair = mppair - aux.pairpoints = mppairpoints - aux.triplet = mptriplet - aux.tripletpoints = mptripletpoints - aux.quadruple = mpquadruple - aux.quadruplepoints = mpquadruplepoints - aux.path = mppath - aux.pathpoints = mppathpoints - aux.size = mpsize - aux.fprint = mpfprint - aux.quoted = mpquoted - aux.transform = mptransform - aux.color = mpcolor - - -- for the moment - - local function mpdraw(lines,list) -- n * 4 - if list then - local c = #lines - for i=1,c do - local ci = lines[i] - local ni = #ci - n = n + 1 buffer[n] = i < c and "d(" or "D(" - for j=1,ni,2 do - local l = j + 1 - n = n + 1 buffer[n] = ci[j] - n = n + 1 buffer[n] = "," - n = n + 1 buffer[n] = ci[l] - n = n + 1 buffer[n] = l < ni and ")--(" or ");" - end - end - else - local l = #lines - local m = l - 4 - for i=1,l,4 do - n = n + 1 buffer[n] = i < m and "d(" or "D(" - n = n + 1 buffer[n] = lines[i] - n = n + 1 buffer[n] = "," - n = n + 1 buffer[n] = lines[i+1] - n = n + 1 buffer[n] = ")--(" - n = n + 1 buffer[n] = lines[i+2] - n = n + 1 buffer[n] = "," - n = n + 1 buffer[n] = lines[i+3] - n = n + 1 buffer[n] = ");" - end - end - end - - local function mpfill(lines,list) - if list then - local c = #lines - for i=1,c do - local ci = lines[i] - local ni = #ci - n = n + 1 buffer[n] = i < c and "f(" or "F(" - for j=1,ni,2 do - local l = j + 1 - n = n + 1 buffer[n] = ci[j] - n = n + 1 buffer[n] = "," - n = n + 1 buffer[n] = ci[l] - n = n + 1 buffer[n] = l < ni and ")--(" or ")--C;" - end - end - else - local l = #lines - local m = l - 4 - for i=1,l,4 do - n = n + 1 buffer[n] = i < m and "f(" or "F(" - n = n + 1 buffer[n] = lines[i] - n = n + 1 buffer[n] = "," - n = n + 1 buffer[n] = lines[i+1] - n = n + 1 buffer[n] = ")--(" - n = n + 1 buffer[n] = lines[i+2] - n = n + 1 buffer[n] = "," - n = n + 1 buffer[n] = lines[i+3] - n = n + 1 buffer[n] = ")--C;" - end - end - end - - aux.draw = mpdraw - aux.fill = mpfill - - for k, v in next, aux do mp[k] = v end - -end - -do - - -- Another experimental feature: - - local mpnumeric = mp.numeric - local scanstring = scan.string - local scriptindex = metapost.scriptindex - - function mp.mf_script_index(name) - local index = scriptindex(name) - -- report_script("method %i, name %a, index %i",1,name,index) - mpnumeric(index) - end - - -- once bootstrapped ... (needs pushed mpx instances) - - metapost.registerscript("scriptindex",function() - local name = scanstring() - local index = scriptindex(name) - -- report_script("method %i, name %a, index %i",2,name,index) - mpnumeric(index) - end) - -end - --- the next will move to mlib-lmp.lua - -do - - local mpnamedcolor = attributes.colors.mpnamedcolor - local mpprint = aux.print - local scanstring = scan.string - - mp.mf_named_color = function(str) - mpprint(mpnamedcolor(str)) - end - - -- todo: we can inject but currently we always get a string back so then - -- we need to deal with it upstream in the color module ... not now - - metapost.registerscript("namedcolor",function() - mpprint(mpnamedcolor(scanstring())) - end) +get.numeric = function(s) return get_numeric(currentmpx,s) end +get.number = function(s) return get_numeric(currentmpx,s) end +get.integer = function(s) return get_integer(currentmpx,s) end +get.string = function(s) return get_string (currentmpx,s) end +get.boolean = function(s) return get_boolean(currentmpx,s) end +get.path = function(s) return get_path (currentmpx,s) end +set.path = function(s,t) return set_path (currentmpx,s,t) end -- not working yet +function metapost.pushscriptrunner(mpx) + insert(stack,mpx) + currentmpx = mpx end -function mp.n(t) -- used ? - return type(t) == "table" and #t or 0 +function metapost.popscriptrunner() + currentmpx = remove(stack,mpx) end -do - - -- experiment: names can change - - local mppath = aux.path - local mpsize = aux.size - - local whitespace = lpegpatterns.whitespace - local newline = lpegpatterns.newline - local setsep = newline^2 - local comment = (S("#%") + P("--")) * (1-newline)^0 * (whitespace - setsep)^0 - local value = (1-whitespace)^1 / tonumber - local entry = Ct( value * whitespace * value) - local set = Ct((entry * (whitespace-setsep)^0 * comment^0)^1) - local series = Ct((set * whitespace^0)^1) - - local pattern = whitespace^0 * series - - local datasets = { } - mp.datasets = datasets - - function mp.dataset(str) - return lpegmatch(pattern,str) - end - - function datasets.load(tag,filename) - if not filename then - tag, filename = file.basename(tag), tag - end - local data = lpegmatch(pattern,io.loaddata(filename) or "") - datasets[tag] = { - data = data, - line = function(n) mppath(data[n or 1]) end, - size = function() mpsize(data) end, - } - end - - table.setmetatablecall(datasets,function(t,k,f,...) - local d = datasets[k] - local t = type(d) - if t == "table" then - d = d[f] - if type(d) == "function" then - d(...) - else - mpvprint(...) - end - elseif t == "function" then - d(f,...) - end - end) - +function metapost.currentmpx() + return currentmpx end --- \startluacode --- local str = [[ --- 10 20 20 20 --- 30 40 40 60 --- 50 10 --- --- 10 10 20 30 --- 30 50 40 50 --- 50 20 -- the last one --- --- 10 20 % comment --- 20 10 --- 30 40 # comment --- 40 20 --- 50 10 --- ]] --- --- MP.myset = mp.dataset(str) --- --- inspect(MP.myset) --- \stopluacode --- --- \startMPpage --- color c[] ; c[1] := red ; c[2] := green ; c[3] := blue ; --- for i=1 upto lua("mp.print(mp.n(MP.myset))") : --- draw lua("mp.path(MP.myset[" & decimal i & "])") withcolor c[i] ; --- endfor ; --- \stopMPpage - --- texts: - -do - - local mptriplet = mp.triplet - - local bpfactor = number.dimenfactors.bp - local textexts = nil - local mptriplet = mp.triplet - local nbdimensions = nodes.boxes.dimensions - - function mp.mf_tt_initialize(tt) - textexts = tt - end - - function mp.mf_tt_dimensions(n) - local box = textexts and textexts[n] - if box then - -- could be made faster with nuts but not critical - mptriplet(box.width*bpfactor,box.height*bpfactor,box.depth*bpfactor) - else - mptriplet(0,0,0) - end - end - - function mp.mf_tb_dimensions(category,name) - local w, h, d = nbdimensions(category,name) - mptriplet(w*bpfactor,h*bpfactor,d*bpfactor) - end - - function mp.report(a,b,c,...) - if c then - report_message("%s : %s",a,formatters[(gsub(b,"@","%%"))](c,...)) - elseif b then - report_message("%s : %s",a,b) - elseif a then - report_message("%s : %s","message",a) - end - end - -end - -do - - local mpprint = aux.print - local modes = tex.modes - local systemmodes = tex.systemmodes - - function mp.mode(s) - mpprint(modes[s] and true or false) - end - - function mp.systemmode(s) - mpprint(systemmodes[s] and true or false) - end - - mp.processingmode = mp.mode +local status = mplib.status +function metapost.currentmpxstatus() + return status and status(currentmpx) or 0 end - --- for alan's nodes: - -do - - local mpprint = aux.print - local mpquoted = aux.quoted - - function mp.isarray(str) - mpprint(find(str,"%d") and true or false) - end - - function mp.prefix(str) - mpquoted(match(str,"^(.-)[%d%[]") or str) - end - - -- function mp.dimension(str) - -- local n = 0 - -- for s in gmatch(str,"%[?%-?%d+%]?") do --todo: lpeg - -- n = n + 1 - -- end - -- mpprint(n) - -- end - - mp.dimension = lpeg.counter(P("[") * lpegpatterns.integer * P("]") + lpegpatterns.integer,mpprint) - - -- faster and okay as we don't have many variables but probably only - -- basename makes sense and even then it's not called that often - - -- local hash = table.setmetatableindex(function(t,k) - -- local v = find(k,"%d") and true or false - -- t[k] = v - -- return v - -- end) - -- - -- function mp.isarray(str) - -- mpprint(hash[str]) - -- end - -- - -- local hash = table.setmetatableindex(function(t,k) - -- local v = '"' .. (match(k,"^(.-)%d") or k) .. '"' - -- t[k] = v - -- return v - -- end) - -- - -- function mp.prefix(str) - -- mpprint(hash[str]) - -- end - -end - -do - - local getmacro = tex.getmacro - local getdimen = tex.getdimen - local getcount = tex.getcount - local gettoks = tex.gettoks - local setmacro = tex.setmacro - local setdimen = tex.setdimen - local setcount = tex.setcount - local settoks = tex.settoks - - local mpprint = mp.print - local mpquoted = mp.quoted - - local bpfactor = number.dimenfactors.bp - - -- more helpers - - local function getmacro(k) mpprint (getmacro(k)) end - local function getdimen(k) mpprint (getdimen(k)*bpfactor) end - local function getcount(k) mpprint (getcount(k)) end - local function gettoks (k) mpquoted(gettoks (k)) end - - local function setmacro(k,v) setmacro(k,v) end - local function setdimen(k,v) setdimen(k,v/bpfactor) end - local function setcount(k,v) setcount(k,v) end - local function settoks (k,v) settoks (k,v) end - - -- def foo = lua.mp.foo ... enddef ; % loops due to foo in suffix - - mp._get_macro_ = getmacro mp.getmacro = getmacro - mp._get_dimen_ = getdimen mp.getdimen = getdimen - mp._get_count_ = getcount mp.getcount = getcount - mp._get_toks_ = gettoks mp.gettoks = gettoks - - mp._set_macro_ = setmacro mp.setmacro = setmacro - mp._set_dimen_ = setdimen mp.setdimen = setdimen - mp._set_count_ = setcount mp.setcount = setcount - mp._set_toks_ = settoks mp.settoks = settoks - -end - --- position fun - -do - - local mpprint = mp.print - local mpfprint = mp.fprint - local mpquoted = mp.quoted - local jobpositions = job.positions - local getwhd = jobpositions.whd - local getxy = jobpositions.xy - local getposition = jobpositions.position - local getpage = jobpositions.page - local getregion = jobpositions.region - local getmacro = tokens.getters.macro - - function mp.positionpath(name) - local w, h, d = getwhd(name) - if w then - mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",0,-d,w,-d,w,h,0,h) - else - mpprint("(origin--cycle)") - end - end - - function mp.positioncurve(name) - local w, h, d = getwhd(name) - if w then - mpfprint("((%p,%p)..(%p,%p)..(%p,%p)..(%p,%p)..cycle)",0,-d,w,-d,w,h,0,h) - else - mpprint("(origin--cycle)") - end - end - - function mp.positionbox(name) - local p, x, y, w, h, d = getposition(name) - if p then - mpfprint("((%p,%p)--(%p,%p)--(%p,%p)--(%p,%p)--cycle)",x,y-d,x+w,y-d,x+w,y+h,x,y+h) - else - mpprint("(%p,%p)",x,y) - end - end - - function mp.positionxy(name) - local x, y = getxy(name) - if x then - mpfprint("(%p,%p)",x,y) - else - mpprint("origin") - end - end - - function mp.positionpage(name) - mpfprint("%i",getpage(name) or 0) - end - - function mp.positionregion(name) - local r = getregion(name) - if r then - mpquoted(r) - else - mpquoted("unknown") - end - end - - function mp.positionwhd(name) - local w, h, d = getwhd(name) - if w then - mpfprint("(%p,%p,%p)",w,h,d) - else - mpprint("(0,0,0)") - end - end - - function mp.positionpxy(name) - local p, x, y = getposition(name) - if p then - mpfprint("(%p,%p,%p)",p,x,y) - else - mpprint("(0,0,0)") - end - end - - function mp.positionanchor() - mpquoted(getmacro("MPanchorid")) - end - -end - -do - - local mppair = mp.pair - - function mp.textextanchor(s) - local x, y = match(s,"tx_anchor=(%S+) (%S+)") -- todo: make an lpeg - if x and y then - x = tonumber(x) - y = tonumber(y) - end - mppair(x or 0,y or 0) - end - -end - -do - - local mpprint = mp.print - local mpquoted = mp.quoted - local getmacro = tokens.getters.macro - - function mp.texvar(name) - mpprint(getmacro(metapost.namespace .. name)) - end - - function mp.texstr(name) - mpquoted(getmacro(metapost.namespace .. name)) - end - -end - -do - - local mpprint = aux.print - local mpvprint = aux.vprint - - local hashes = { } - - function mp.newhash(name) - if name then - hashes[name] = { } - else - for i=1,#hashes+1 do - if not hashes[i] then - hashes[i] = { } - mpvprint(i) - return - end - end - end - end - - function mp.disposehash(n) - if tonumber(n) then - hashes[n] = false - else - hashes[n] = nil - end - end - - function mp.inhash(n,key) - local h = hashes[n] - mpvprint(h and h[key] and true or false) - end - - function mp.tohash(n,key,value) - local h = hashes[n] - if h then - if value == nil then - h[key] = true - else - h[key] = value - end - end - end - - function mp.fromhash(n,key) - local h = hashes[n] - mpvprint(h and h[key] or false) - end - - interfaces.implement { - name = "MPfromhash", - arguments = "2 strings", - actions = function(name,key) - local h = hashes[name] or hashes[tonumber(name)] - if h then - local v = h[key] or h[tonumber(key)] - if v then - context(v) - end - end - end - } - -end - -do - - -- a bit overkill: just a find(str,"mf_object=") can be enough - -- - -- todo : share with mlib-pps.lua metapost,isobject - - local mpboolean = aux.boolean - - local p1 = P("mf_object=") - local p2 = lpegpatterns.eol * p1 - local pattern = (1-p2)^0 * p2 + p1 - - function mp.isobject(str) - mpboolean(pattern and str ~= "" and lpegmatch(pattern,str)) - end - -end - -function mp.flatten(t) - local tn = #t - - local t1 = t[1] - local t2 = t[2] - local t3 = t[3] - local t4 = t[4] - - for i=1,tn-5,2 do - local t5 = t[i+4] - local t6 = t[i+5] - if t1 == t3 and t3 == t5 and ((t2 <= t4 and t4 <= t6) or (t6 <= t4 and t4 <= t2)) then - t[i+3] = t2 - t4 = t2 - t[i] = false - t[i+1] = false - elseif t2 == t4 and t4 == t6 and ((t1 <= t3 and t3 <= t5) or (t5 <= t3 and t3 <= t1)) then - t[i+2] = t1 - t3 = t1 - t[i] = false - t[i+1] = false - end - t1 = t3 - t2 = t4 - t3 = t5 - t4 = t6 - end - - -- remove duplicates - - local t1 = t[1] - local t2 = t[2] - for i=1,tn-2,2 do - local t3 = t[i+2] - local t4 = t[i+3] - if t1 == t3 and t2 == t4 then - t[i] = false - t[i+1] = false - end - t1 = t3 - t2 = t4 - end - - -- move coordinates - - local m = 0 - for i=1,tn,2 do - if t[i] then - m = m + 1 t[m] = t[i] - m = m + 1 t[m] = t[i+1] - end - end - - -- prune the table (not gc'd) - - for i=tn,m+1,-1 do - t[i] = nil - end - - -- safeguard so that we have at least one segment - - if m == 2 then - t[3] = t[1] - t[4] = t[2] - end - -end - -do - - -- if needed we can optimize the sub (cache last split) - - local utflen = utf.len - local utfsub = utf.sub - - function mp.utflen(s) - mpnumeric(utflen(s)) - end - - function mp.utfsub(s,f,t) - mpquoted(utfsub(s,f,t or f)) - end - -end - -- cgit v1.2.3