% % ixbase.sty % %%%% package declaration \NeedsTeXFormat{LaTeX2e} \ProvidesPackage{ixbase0}[2010/07/24 v0.1b] %%%% preparation \def\ixbb@pkgname{ixbase0} \def\ixbb@error{\PackageError\ixbb@pkgname} \newif\ifixbb@ok %%------------------ adjustment for environment %% Tests if \luatexversion primitive is available by this name; %% in ordinary LuaLaTeX setting it must be true. \begingroup\expandafter\expandafter\expandafter\endgroup \expandafter\ifx\csname luatexversion\endcsname\relax \ixbb@error{This is not LuaLaTeX!}\@eha \expandafter\endinput\fi %% Here it is assumed that all pdfTeX primitives are available %% by their normal names. %% Ensure the standard settings, where non-pdfTeX primitives %% are available with prefix 'luatex'. \directlua{ tex.enableprimitives('luatex', tex.extraprimitives('core', 'omega', 'aleph', 'luatex')) } %% Makes \latelua available by that name when it is activated %% by the name prefixed with 'luatex-'. \ifcsname latelua\endcsname\else \let\latelua\luatexlatelua \fi %% Same for \luaescapestring. \ifcsname luaescapestring\endcsname\else \let\luaescapestring\luatexluaescapestring \fi %%------------------ additional subprocedures %% \ixbb@ltr % \ixbb@ltr\* (where * is an arbitrary non-letter) expands % to the letter *. \def\ixbb@ltr{\expandafter\@gobble\string} %% \ixbb@Z : singleton token \def\ixbb@Z{\noexpand\ixbb@Z} \let\ixbb@end\relax %% \ixbb@make@ckid{} % Converts the chunk name parameter used in \execluacode into % the form used in \directlua. The result is stored in \ixbb@ckid. \def\ixbb@make@ckid#1{% \ifx\ixbb@Z#1\ixbb@Z \let\ixbb@ckid\@empty \else {\afterassignment\ixbb@make@ckid@a\count@=0#1\ixbb@end}% \ifx\ixbb@g@ckid\relax \def\ixbb@ckid{name{#1}}% \else \let\ixbb@ckid\ixbb@g@ckid \fi \fi} \def\ixbb@make@ckid@a#1\ixbb@end{% \ifx\ixbb@Z#1\ixbb@Z \xdef\ixbb@g@ckid{\the\count@\space}% \else \global\let\ixbb@g@ckid\relax \fi} %%------------------ 'thin' wrapper for lua code executor %%<*> \execluacode[*][]{} % \execluacode[] executes: if is missing then \directlua; if % is a number then \directlua; otherwise \directlua name{}. % \execluacode*[] is the same except that \latelua is used instead % of \directlua. The form with no options, \execluacode{}, is % fully expandable. \def\execluacode{\expandafter\ixbb@execluacd@a\string} \def\ixbb@execluacd@a#1{\csname ixbb@execluacd+#1\endcsname} \@namedef{ixbb@execluacd+\ixbb@ltr\{}{\directlua\bgroup} \@namedef{ixbb@execluacd+*}{\ixbb@execluacd@b\latelua} \@namedef{ixbb@execluacd+[}{\ixbb@execluacd@b\directlua[} \def\ixbb@execluacd@b{% \ifx\protect\@typeset@protect \expandafter\ixbb@execluacd@c \else \expandafter\ixbb@execluacd@err \fi} \def\ixbb@execluacd@c#1{\@ifnextchar[%] {\ixbb@execluacd@d#1}{\ixbb@execluacd@d#1[]}} \def\ixbb@execluacd@d#1[#2]{\ixbb@make@ckid{#2}#1\ixbb@ckid} \begingroup \catcode`\!=11 \gdef\ixbb@execluacd@err#1#{\ixbb@!} \xdef\ixbb@!#1{\expandafter\unexpanded\expandafter{\csname ! Misplaced \string\execluacode\space with options!\endcsname}} \endgroup %%<+> \ixQEscape % Does the following conversion; here \? equals \ixQEscape % (so assigned later), 'a' denotes a letter or digit, '*' denotes % any other character. % (1) \?\* --> * % (2) \?a --> \a [#] conversion % (3) \?* --> (* with [#] applied) from: + / | - ( ) * % (4) \?? --> (print \ and do \?) to: # % \ ~ { } ^ \def\ixQEscape#1{\csname ixbb@?/\string#1\endcsname} \begingroup \def\ixbb@foreachchar#1#2#3{\@tempcnta=#1% \loop\lccode`\?=\@tempcnta \lowercase{#3?}% \advance\@tempcnta\@ne \ifnum\@tempcnta<#2\repeat} \edef\ixbb@ltrbs{\ixbb@ltr\\} \def\ixbb@define#1{\expandafter\xdef\csname ixbb@?/#1\endcsname} \def\ixbb@do@s#1{\ixbb@define{\ixbb@ltrbs#1}{#1}\ixbb@define{#1}{#1}} \def\ixbb@do@l#1{\ixbb@define{#1}{\ixbb@ltrbs#1}} \ixbb@foreachchar{33}{65}\ixbb@do@s \ixbb@foreachchar{91}{97}\ixbb@do@s \ixbb@foreachchar{123}{127}\ixbb@do@s \ixbb@foreachchar{48}{58}\ixbb@do@l \ixbb@foreachchar{97}{123}\ixbb@do@l \def\do#1#2{\ixbb@define{#1}{\ixbb@ltr#2}} \do+\#\do/\%\do|\\\do-\~\do(\{\do)\}\do*\^ \ixbb@define{?}{\ixbb@ltrbs\noexpand\ixQEscape} \endgroup %%<*> \usebacktickescape/\nousebacktickescape % Makes <`> act as \? / act as usual. \begingroup \catcode96=13 \gdef\usebacktickescape{\catcode96=13 \let`\ixQEscape} \gdef\nousebacktickescape{\catcode96=12 } \endgroup %%------------------ TeX-verbatim Lua code blocks %% \ixbb@cctbother % Is the id of a catcode-table where all characters have catcode 12. \ifcsname CatcodeTableOther\endcsname % \CatcodeTableOther is provided by currently distributed packages % for managing catcode-tables, such as luatex and luatexbase-cctb. \let\ixbb@cctbother\CatcodeTableOther \else % if \CatcodeTableOther does not exist, then it is assumed no catcode- % table management is employed and a fixed value is assigned. \chardef\ixbb@cctbother=1 \@firstofone{{% \@tempcnta=0\loop \catcode\@tempcnta=12 \advance\@tempcnta\@ne \ifnum\@tempcnta<256 \repeat \luatexsavecatcodetable\ixbb@cctbother}} \fi %%<*> \begin{execluacodeblock[*]}[]\end{...} % Executes lua code . The *-variant and mean the same as % \execluacode. The body is read verbatim in view of LaTeX; % all characters (including space) has catcode 12. As a result, % the user can write lua code text just in the same way as external % text files. The lua code is then executed in the same catcode % setting as outside the (pseudo-)environment. % NB. when reading \endlinechar is 13 (and \catcode13 is 12); % however, since the lua engine conflates newline sequences to LF, % it sees a newline in the code as a LF character ("\n"). \def\execluacodeblock{\ixbb@execluacdblk@a\ixbb@xlcb@direct} \@namedef{execluacodeblock*}{\ixbb@execluacdblk\ixbb@xlcb@late} \def\ixbb@execluacdblk@a#1{\endgroup\@ifnextchar[% {\ixbb@execluacdblk@b#1}{\ixbb@execluacdblk@b#1[]}} \def\ixbb@execluacdblk@b#1[#2]{% \ixbb@make@ckid{#2}% \expandafter\ixbb@execluacdblk@c#1} \def\ixbb@execluacdblk@c#1#2{% \[...lua]{} \edef\ixbb@resetcctb{\noexpand \luatexcatcodetable\the\luatexcatcodetable}% \begingroup \luatexcatcodetable=\ixbb@cctbother\endlinechar=13\relax \aftergroup\ixbb@resetcctb \long\def\ixbb@xlcb@nxt##1#2{\endgroup #1\ixbb@ckid{##1}\relax}% \ixbb@xlcb@nxt} \begingroup \lccode`\|=`\\%\lccode`\[=`\{\lccode`\]=`\} \lowercase{\def\ixbb@nxt#1#2{\noexpand#1{\detokenize{|end{#2}}}}} \xdef\ixbb@xlcb@direct{\ixbb@nxt\directlua{execluacodeblock}} \xdef\ixbb@xlcb@late{\ixbb@nxt\latelua{execluacodeblock*}} \endgroup %%------------------ start up %%<*> \ixstring{} / \ixescape{} \def\ixstring#1{"\luatexluaescapestring{\detokenize{#1}}"} \def\ixescape#1{\luatexluaescapestring{\detokenize{#1}}} %%<*> \ixnumber{} \def\ixnumber#1{tonumber("\luatexluaescapestring{\detokenize{#1}}")} %%<*> \ixvcoutner{} \def\ixvcounter#1{(\expandafter\number\csname c@#1\endcsname)} %%<*> \ixvlength{\LENGTH} \def\ixvlength#1{(ixbase.to_skip("\the#1"))} %% equalize \? with \ixQEscape \let\?\ixQEscape %\usebacktickescape %%------------------ Library 'ixbase' \begin{execluacodeblock}[ixbase] -- Package ixbase --- Extension to tex.print(). Each argument string may contain -- newline characters, in which case the string is output (to -- TeX input stream) as multiple lines. -- @param ... string to output (string) local function ix_print(...) -- to avoid overriding print... local arg = {...}; local lines = {} if type(arg[1]) == "number" then table.insert(lines, arg[1]); table.remove(arg, 1) end for _, cnk in ipairs(arg) do local ls = cnk:explode("\n") if ls[#ls] == "" then table.remove(ls, #ls) end for _, l in ipairs(ls) do table.insert(lines, l) end end return tex.print(unpack(lines)) end local glue_spec_id = node.id("glue_spec") local function copy_skip(s1, s2) if not s1 then s1 = node.new(glue_spec_id) end s1.width = s2.width or 0 s1.stretch = s2.stretch or 0 s1.stretch_order = s2.stretch_order or 0 s1.shrink = s2.shrink or 0 s1.shrink_order = s2.shrink_order or 0 return s1 end local in_sp = tex.sp if not in_sp then function in_sp(val) -- val is string local old = tex.dimen[0]; tex.dimen[0] = val val = tex.dimen[0]; tex.dimen[0] = old return val end end local function to_dimen(val) if val == nil then return 0 elseif type(val) == "number" then return val else return in_sp(tostring(val)) end end local function parse_dimen(val) val = tostring(val):lower() local r, fil = val:match("([-.%d]+)fi(l*)") if r then val, fil = r.."pt", fil:len() + 1 else fil = 0 end return in_sp(val), fil end local function to_skip(val) if type(val) == "userdata" then return val end local res = node.new(glue_spec_id) if val == nil then res.width = 0 elseif type(val) == "number" then res.width = val elseif type(val) == "table" then copy_skip(res, val) else local t = tostring(val):lower():explode() local w, p, m = t[1], t[3], t[5] if t[2] == "minus" then p, m = nil, t[3] end res.width = in_sp(t[1]) if t[3] then res.stretch, res.stretch_order = parse_dimen(t[3]) end if t[5] then res.shrink, res.shrink_order = parse_dimen(t[5]) end end return res end function dump_skip(s) print(("%s+%s<%s>-%s<%s>"):format( s.width or 0, s.stretch or 0, s.stretch_order or 0, s.shrink or 0, s.shrink_order or 0)) end local length = {} local mt_length = {}; setmetatable(length, mt_length) function mt_length.__index(tbl, key) return tex.skip[key] end function mt_length.__newindex(tbl, key, val) tex.skip[key] = to_skip(val) end local counter = {} local mt_counter = {}; setmetatable(counter, mt_counter) function mt_counter.__index(tbl, key) return tex.count['c@'..key] end function mt_counter.__newindex(tbl, key, val) tex.count['c@'..key] = val end -- export ixbase = { print = ix_print; length = length; to_dimen = to_dimen; to_skip = to_skip; counter = counter; -- _copy = copy_skip; _sp = in_sp; } \end{execluacodeblock} %% 'tangle' library \begin{execluacodeblock} tangle = { _DONE = 0, _TEX = 1, _STOP = 2 } function tangle.execute(func, ...) if tangle._current_co then error("tangle is going now") end local args = { ... } local co = coroutine.create(function() return tangle._DONE, { func(unpack(args)) } end) tangle._current_co = co tangle._interrupted = false return tangle._check(coroutine.resume(co, ...)) end function tangle.resume() return tangle._resume(false) end function tangle.interrupt() return tangle._resume(true) end function tangle.run_tex() coroutine.yield(tangle._TEX, {}) end function tangle.suspend(...) local intr = coroutine.yield(tangle._STOP, { ... }) if intr then tangle._interrupted = true; error("*INTR*") end end function tangle._resume(intr) if not tangle._current_co then error("tangle is not going") end local co = tangle._current_co return tangle._check(coroutine.resume(co, intr)) end function tangle._check(costat, tstat, extra) if not costat then -- error in coroutine tangle._current_co = nil if tangle._interrupted then return end error(tstat) elseif tstat == tangle._DONE then tangle._current_co = nil elseif tstat == tangle._TEX then tex.print("\\directlua{tangle.resume()}\\relax") end return unpack(extra) end \end{execluacodeblock} \let\lua\directlua \lua{ix=ixbase} %%------------------ all done \endinput %% EOF