diff options
27 files changed, 5595 insertions, 854 deletions
diff --git a/doc/context/documents/general/manuals/xml-mkiv.pdf b/doc/context/documents/general/manuals/xml-mkiv.pdf Binary files differnew file mode 100644 index 000000000..5093710d7 --- /dev/null +++ b/doc/context/documents/general/manuals/xml-mkiv.pdf diff --git a/doc/context/sources/general/manuals/xml/xml-mkiv-01.xml b/doc/context/sources/general/manuals/xml/xml-mkiv-01.xml new file mode 100644 index 000000000..c2feac218 --- /dev/null +++ b/doc/context/sources/general/manuals/xml/xml-mkiv-01.xml @@ -0,0 +1,15 @@ +<name>Land Of Dreams</name> +<tracks> + <track length="248">Dixie Flyer</track> + <track length="212">New Orleans Wins The War</track> + <track length="218">Four Eyes</track> + <track length="181">Falling In Love</track> + <track length="187">Something Special</track> + <track length="168">Bad News From Home</track> + <track length="207">Roll With The Punches</track> + <track length="209">Masterman And Baby J</track> + <track length="134">Follow The Flag</track> + <track length="246">I Want You To Hurt Like I Do</track> + <track length="248">It's Money That Matters</track> + <track length="156">Red Bandana</track> +</tracks> diff --git a/doc/context/sources/general/manuals/xml/xml-mkiv-02.xml b/doc/context/sources/general/manuals/xml/xml-mkiv-02.xml new file mode 100644 index 000000000..997123ad6 --- /dev/null +++ b/doc/context/sources/general/manuals/xml/xml-mkiv-02.xml @@ -0,0 +1,15 @@ +<name>Bad Love</name> +<tracks> + <track length="340">My Country</track> + <track length="295">Shame</track> + <track length="205">I'm Dead (But I Don't Know It)</track> + <track length="213">Every Time It Rains</track> + <track length="206">The Great Nations of Europe</track> + <track length="220">The One You Love</track> + <track length="164">The World Isn't Fair</track> + <track length="264">Big Hat, No Cattle</track> + <track length="243">Better Off Dead</track> + <track length="236">I Miss You</track> + <track length="126">Going Home</track> + <track length="180">I Want Everyone To Like Me</track> +</tracks> diff --git a/doc/context/sources/general/manuals/xml/xml-mkiv.tex b/doc/context/sources/general/manuals/xml/xml-mkiv.tex new file mode 100644 index 000000000..87317b69b --- /dev/null +++ b/doc/context/sources/general/manuals/xml/xml-mkiv.tex @@ -0,0 +1,3651 @@ +% language=uk + +% to be checked: +% +% \Ux in index +% +% undocumented: +% +% \processXMLbuffer +% \processxmlbuffer +% \processxmlfile +% +% kind of special ... tricky explanation needed: +% +% \xmldirect + +\input lxml-ctx.mkiv + +\settrue \xmllshowtitle +\setfalse\xmllshowwarning + +\usemodule[set-11] + +\loadsetups[i-en-xml.xml] + +% \definehspace[squad][1em plus .25em minus .25em] + +\usemodule[abr-02] + +\setuplayout + [location=middle, + marking=on, + backspace=20mm, + cutspace=20mm, + topspace=15mm, + header=15mm, + footer=15mm, + height=middle, + width=middle] + +\setuppagenumbering + [alternative=doublesided, + location=] + +\setupfootertexts + [][pagenumber] + +\setupheadertexts + [][chapter] + +\setupheader + [color=colortwo, + style=bold] + +\setupfooter + [color=colortwo, + style=bold] + +\setuphead + [chapter] + [page={yes,header,right}, + header=empty, + style=\bfc] + +\setupsectionblock + [page={yes,header,right}] + +\starttexdefinition unexpanded section:chapter:number #1 + \doifmode{*sectionnumber} { + \llap{<\enspace}#1\enspace> + } +\stoptexdefinition + +\starttexdefinition unexpanded section:section:number #1 + \doifmode{*sectionnumber} { + \llap{<<\enspace}#1\enspace>> + } +\stoptexdefinition + +\starttexdefinition unexpanded section:subsection:number #1 + \doifmode{*sectionnumber} { + \llap{<<<\enspace}#1\enspace>>> + } +\stoptexdefinition + +\setuphead[chapter] [numbercolor=black,numbercommand=\texdefinition{section:chapter:number}] +\setuphead[section] [numbercolor=black,numbercommand=\texdefinition{section:section:number}] +\setuphead[subsection][numbercolor=black,numbercommand=\texdefinition{section:subsection:number}] + +\setuphead + [section] + [style=\bfa] + +\setuplist + [chapter] + [style=bold] + +\setupinteractionscreen + [option=doublesided] + +\setupalign + [tolerant,stretch] + +\setupwhitespace + [big] + +\setuptolerance + [tolerant] + +\doifelsemode {atpragma} { + \setupbodyfont[lucidaot,10pt] +} { + \setupbodyfont[dejavu,10pt] +} + +\definecolor[colorone] [b=.5] +\definecolor[colortwo] [s=.3] +\definecolor[colorthree][y=.5] + +\setuptype + [color=colorone] + +\setuptyping + [color=colorone] + +\setuphead + [lshowtitle] + [style=\tt, + color=colorone] + +\setuphead + [chapter,section] + [numbercolor=colortwo, + color=colorone] + +\definedescription + [xmlcmd] + [alternative=hanging, + width=line, + distance=1em, + margin=2em, + headstyle=monobold, + headcolor=colorone] + +\setupframedtext + [setuptext] + [framecolor=colorone, + rulethickness=1pt, + corner=round] + +\usemodule[punk] + +\usetypescript[punk] + +\definelayer + [page] + [width=\paperwidth, + height=\paperheight] + +\starttext + +\setuplayout[page] + +\startstandardmakeup + \startfontclass[none] % nil the current fontclass since it may append its features + \EnableRandomPunk + \setlayerframed + [page] + [width=\paperwidth,height=\paperheight, + background=color,backgroundcolor=colorone,backgroundoffset=1ex,frame=off] + {} + \definedfont[demo@punk at 18pt] + \setbox\scratchbox\vbox { + \hsize\dimexpr\paperwidth+2ex\relax + \setupinterlinespace + \baselineskip 1\baselineskip plus 1pt minus 1pt + \raggedcenter + \color[colortwo]{\dorecurse{1000}{XML }} + } + \setlayer + [page] + [preset=middle] + {\vsplit\scratchbox to \dimexpr\paperheight+2ex\relax} + \definedfont[demo@punk at 90pt] + \setstrut + \setlayerframed + [page] + [preset=rightbottom,offset=10mm] + [foregroundcolor=colorthree,align=flushright,offset=overlay,frame=off] + {Dealing\\with XML in\\Con\TeX t MkIV} + \definedfont[demo@punk at 18pt] + \setstrut + \setlayerframed + [page] + [preset=righttop,offset=10mm,x=3mm,rotation=90] + [foregroundcolor=colorthree,align=flushright,offset=overlay,frame=off] + {Hans Hagen, Pragma ADE, \currentdate} + \tightlayer[page] + \stopfontclass +\stopstandardmakeup + +\setuplayout + +\startfrontmatter + +\starttitle[title=Contents] + +\placelist + [chapter,section] + +\stoptitle + +\startchapter[title={Introduction}] + +This manual presents the \MKIV\ way of dealing with \XML. Although the +traditional \MKII\ streaming parser has a charming simplicity in its control, for +complex documents the tree based \MKIV\ method is more convenient. It is for this +reason that the old method has been removed from \MKIV. If you are familiar with +\XML\ processing in \MKII, then you will have noticed that the \MKII\ commands +have \type {XML} in their name. The \MKIV\ commands have a lowercase \type {xml} +in their names. That way there is no danger for confusion or a mixup. + +You may wonder why we do these manipulations in \TEX\ and not use \XSLT\ (or +other transformation methods) instead. The advantage of an integrated approach is +that it simplifies usage. Think of not only processing the document, but also +using \XML\ for managing resources in the same run. An \XSLT\ approach is just as +verbose (after all, you still need to produce \TEX\ code) and probably less +readable. In the case of \MKIV\ the integrated approach is also faster and gives +us the option to manipulate content at runtime using \LUA. It has the additional +advantage that to some extend we can handle a mix of \TEX\ and \XML\ because we +know when we're doing one or the other. + +This manual is dedicated to Taco Hoekwater, one of the first \CONTEXT\ users, and +also the first to use it for processing \XML. Who could have thought at that time +that we would have a more convenient way of dealing with those angle brackets. +The second version for this manual is dedicated to Thomas Schmitz, a power user +who occasionally became victim of the evolving mechanisms. + +\blank + +\startlines +Hans Hagen +\PRAGMA +Hasselt NL +2008\endash2016 +\stoplines + +\stopchapter + +\stopfrontmatter + +\startbodymatter + +\startchapter[title={Setting up a converter}] + +\startsection[title={from structure to setup}] + +We use a very simple document structure for demonstrating how a converter is +defined. In practice a mapping will be more complex, especially when we have a +style with complect chapter openings using data coming from all kind of places, +different styling of sections with the same name, selectively (out of order) +flushed content, special formatting, etc. + +\typefile{manual-demo-1.xml} + +Say that this document is stored in the file \type {demo.xml}, then the following +code can be used as starting point: + +\starttyping +\startxmlsetups xml:demo:base + \xmlsetsetup{#1}{*}{-} + \xmlsetsetup{#1}{document|section|p}{xml:demo:*} +\stopxmlsetups + +\xmlregisterdocumentsetup{demo}{xml:demo:base} + +\startxmlsetups xml:demo:document + \starttitle[title={Contents}] + \placelist[chapter] + \stoptitle + \xmlflush{#1} +\stopxmlsetups + +\startxmlsetups xml:demo:section + \startchapter[title=\xmlfirst{#1}{/title}] + \xmlfirst{#1}{/content} + \stopchapter +\stopxmlsetups + +\startxmlsetups xml:demo:p + \xmlflush{#1}\endgraf +\stopxmlsetups + +\xmlprocessfile{demo}{demo.xml}{} +\stoptyping + +Watch out! These are not just setups, but specific \XML\ setups which get an +argument passed (the \type {#1}). If for some reason your \XML\ processing fails, +it might be that you mistakenly have used a normal setup definition. The argument +\type {#1} represents the current node (element) and is a unique identifier. For +instance a \type {<p>..</p>} can have an identifier {demo::5}. So, we can get +something: + +\starttyping +\xmlflush{demo::5}\endgraf +\stoptyping + +but as well: + +\starttyping +\xmlflush{demo::6}\endgraf +\stoptyping + +Keep in mind that the actual node references are abstractions, you never see +those \type {<id>::<number>}'s, because we will use either the abstract \type +{#1} (any node) or an explicit reference like \type {demo}. The previous setup +when issued will be like: + +\starttyping +\startchapter[title=\xmlfirst{demo::3}{/title}] + \xmlfirst{demo::4}{/content} +\stopchapter +\stoptyping + +Here the \type {title} is used to typeset the chapter title but also for an entry +in the table of contents. At the moment the title is typeset the \XML\ node gets +looked up and expanded in real text. However, for the list it gets stored for +later use. One can argue that this is not needed for \XML, because one can just +filter all the titles and use page references, but then one also looses the +control one normally has over such titles. For instance it can be that some +titles are rendered differently and for that we need to keep track of usage. +Doing that with transformations or filtering is often more complex than leaving +that to \TEX. As soon as the list gets typeset, the reference (\type {demo::#3}) +is used for the lookup. This is because by default the title is stored as given. +So, as long as we make sure the \XML\ source is loaded before the table of +contents is typeset we're ok. Later we will look into this on more detail, for +now it's enough to know that in most cases the abstract \type {#1} reference will +work out ok. + +Contrary to the style definitions this interface looks rather low level (with no +optional arguments) and the main reason for this is that we want processing to be +fast. So, the basic framework is: + +\starttyping +\startxmlsetups xml:demo:base + % associate setups with elements +\stopxmlsetups + +\xmlregisterdocumentsetup{demo}{xml:demo:base} + +% define setups for matches + +\xmlprocessfile{demo}{demo.xml}{} +\stoptyping + +In this example we mostly just flush the content of an element and in the case of +a section we flush explicit child elements. The \type {#1} in the example code +represents the current element. The line: + +\starttyping +\xmlsetsetup{demo}{*}{-} +\stoptyping + +sets the default for each element to \quote {just ignore it}. A \type {+} would +make the default to always flush the content. This means that at this point we +only handle: + +\starttyping +<section> + <title>Some title</title> + <content> + <p>a paragraph of text</p> + </content> +</section> +\stoptyping + +In the next section we will deal with the slightly more complex itemize and +figure placement. At first sight all these setups may look overkill but keep in +mind that normally the number of elements is rather limited. The complexity is +often in the style and having access to each snippet of content is actually +quite handy for that. + +\stopsection + +\startsection[title={alternative solutions}] + +Dealing with an itemize is rather simple (as long as we forget about +attributes that control the behaviour): + +\starttyping +<itemize> + <item>first</item> + <item>second</item> +</itemize> +\stoptyping + +First we need to add \type {itemize} to the setup assignment (unless we've used +the wildcard \type {*}): + +\starttyping +\xmlsetsetup{demo}{document|section|p|itemize}{xml:demo:*} +\stoptyping + +The setup can look like: + +\starttyping +\startxmlsetups xml:demo:itemize + \startitemize + \xmlfilter{#1}{/item/command(xml:demo:itemize:item)} + \stopitemize +\stopxmlsetups + +\startxmlsetups xml:demo:itemize:item + \startitem + \xmlflush{#1} + \stopitem +\stopxmlsetups +\stoptyping + +An alternative is to map item directly: + +\starttyping +\xmlsetsetup{demo}{document|section|p|itemize|item}{xml:demo:*} +\stoptyping + +and use: + +\starttyping +\startxmlsetups xml:demo:itemize + \startitemize + \xmlflush{#1} + \stopitemize +\stopxmlsetups + +\startxmlsetups xml:demo:item + \startitem + \xmlflush{#1} + \stopitem +\stopxmlsetups +\stoptyping + +Sometimes, a more local solution using filters and \type {/command(...)} makes more +sense, especially when the \type {item} tag is used for other purposes as well. + +Explicit flushing with \type {command} is definitely the way to go when you have +complex products. In one of our projects we compose math school books from many +thousands of small \XML\ files, and from one source set several products are +typeset. Within a book sections get done differently, content gets used, ignored +or interpreted differently depending on the kind of content, so there is a +constant checking of attributes that drive the rendering. In that a generic setup +for a title element makes less sense than explicit ones for each case. (We're +talking of huge amounts of files here, including multiple images on each rendered +page.) + +When using \type {command} you can pass two arguments, the first is the setup for +the match, the second one for the miss, as in: + +\starttyping +\xmlfilter{#1}{/element/command(xml:true,xml:false)} +\stoptyping + +Back to the example, this leaves us with dealing with the resources, like +figures: + +\starttyping +<resource type='figure'> + <caption>A picture of a cow.</caption> + <content><external file="cow.pdf"/></content> +</resource> +\stoptyping + +Here we can use a more restricted match: + +\starttyping +\xmlsetsetup{demo}{resource[@type='figure']}{xml:demo:figure} +\xmlsetsetup{demo}{external}{xml:demo:*} +\stoptyping + +and the definitions: + +\starttyping +\startxmlsetups xml:demo:figure + \placefigure + {\xmlfirst{#1}{/caption}} + {\xmlfirst{#1}{/content}} +\stopxmlsetups + +\startxmlsetups xml:demo:external + \externalfigure[\xmlatt{#1}{file}] +\stopxmlsetups +\stoptyping + +At this point it is good to notice that \type {\xmlatt{#1}{file}} is passed as it +is: a macro call. This means that when a macro like \type {\externalfigure} uses +the first argument frequently without first storing its value, the lookup is done +several times. A solution for this is: + +\starttyping +\startxmlsetups xml:demo:external + \expanded{\externalfigure[\xmlatt{#1}{file}]} +\stopxmlsetups +\stoptyping + +Because the lookup is rather fast, normally there is no need to bother about this +too much because internally \CONTEXT\ already makes sure such expansion happens +only once. + +An alternative definition for placement is the following: + +\starttyping +\xmlsetsetup{demo}{resource}{xml:demo:resource} +\stoptyping + +with: + +\starttyping +\startxmlsetups xml:demo:resource + \placefloat + [\xmlatt{#1}{type}] + {\xmlfirst{#1}{/caption}} + {\xmlfirst{#1}{/content}} +\stopxmlsetups +\stoptyping + +This way you can specify \type {table} as type too. Because you can define your +own float types, more complex variants are also possible. In that case it makes +sense to provide some default behaviour too: + +\starttyping +\definefloat[figure-here][figure][default=here] +\definefloat[figure-left][figure][default=left] +\definefloat[table-here] [table] [default=here] +\definefloat[table-left] [table] [default=left] + +\startxmlsetups xml:demo:resource + \placefloat + [\xmlattdef{#1}{type}{figure}-\xmlattdef{#1}{location}{here}] + {\xmlfirst{#1}{/caption}} + {\xmlfirst{#1}{/content}} +\stopxmlsetups +\stoptyping + +In this example we support two types and two locations. We default to a figure +placed (when possible) at the current location. + +\stopsection + +\stopchapter + +\startchapter[title={Filtering content}] + +\startsection[title={\TEX\ versus \LUA}] + +It will not come as a surprise that we can access \XML\ files from \TEX\ as well +as from \LUA. In fact there are two methods to deal with \XML\ in \LUA. First +there are the low level \XML\ functions in the \type {xml} namespace. On top of +those functions there is a set of functions in the \type {lxml} namespace that +deals with \XML\ in a more \TEX ie way. Most of these have similar commands at +the \TEX\ end. + +\startbuffer +\startxmlsetups first:demo:one + \xmlfilter {#1} {artist/name[text()='Randy Newman']/.. + /albums/album[position()=3]/command(first:demo:two)} +\stopxmlsetups + +\startxmlsetups first:demo:two + \blank \start \tt + \xmldisplayverbatim{#1} + \stop \blank +\stopxmlsetups + +\xmlprocessfile{demo}{music-collection.xml}{first:demo:one} +\stopbuffer + +\typebuffer + +This gives the following snippet of verbatim \XML\ code. The indentation is +conform the indentation in the whole \XML\ file. \footnote {The (probably +outdated) \XML\ file contains the collection stores on my slimserver instance. +You can use the \type {mtxrun --script flac} to generate such files.} + +\doifmodeelse {atpragma} { + \getbuffer +} { + \typefile{xml-mkiv-01.xml} +} + +An alternative written in \LUA\ looks as follows: + +\startbuffer +\blank \start \tt \startluacode + local m = lxml.load("mine","music-collection.xml") -- m == lxml.id("mine") + local p = "artist/name[text()='Randy Newman']/../albums/album[position()=4]" + local l = lxml.filter(m,p) -- returns a list (with one entry) + lxml.displayverbatim(l[1]) +\stopluacode \stop \blank +\stopbuffer + +\typebuffer + +This produces: + +\doifmodeelse {atpragma} { + \getbuffer +} { + \typefile{xml-mkiv-02.xml} +} + +You can use both methods mixed but in practice we will use the \TEX\ commands in +regular styles and the mixture in modules, for instance in those dealing with +\MATHML\ and cals tables. For complex matters you can write your own finalizers +(the last action to be taken in a match) in \LUA\ and use them at the \TEX\ end. + +\stopsection + +\startsection[title={a few details}] + +In \CONTEXT\ setups are a rather common variant on macros (\TEX\ commands) but +with their own namespace. An example of a setup is: + +\starttyping +\startsetup doc:print + \setuppapersize[A4][A4] +\stopsetup + +\startsetup doc:screen + \setuppapersize[S6][S4] +\stopsetup +\stoptyping + +Given the previous definitions, later on we can say something like: + +\starttyping +\doifmodeelse {paper} { + \setup[doc:print] +} { + \setup[doc:screen] +} +\stoptyping + +Another example is: + +\starttyping +\startsetup[doc:header] + \marking[chapter] + \space + -- + \space + \pagenumber +\stopsetup +\stoptyping + +in combination with: + +\starttyping +\setupheadertexts[\setup{doc:header}] +\stoptyping + +Here the advantage is that instead of ending up with an unreadable header +definitions, we use a nicely formatted setup. An important property of setups and +the reason why they were introduced long ago is that spaces and newlines are +ignored in the definition. This means that we don't have to worry about so called +spurious spaces but it also means that when we do want a space, we have to use +the \type {\space} command. + +The only difference between setups and \XML\ setups is that the later ones get an +argument (\type {#1}) that reflects the current node in the \XML\ tree. + +\stopsection + +\startsection[title={CDATA}] + +What to do with \type {CDATA}? There are a few methods at tle \LUA\ end for +dealing with it but here we just mention how you can influence the rendering. +There are four macros that play a role here: + +\starttyping +\unexpanded\def\xmlcdataobeyedline {\obeyedline} +\unexpanded\def\xmlcdataobeyedspace{\strut\obeyedspace} +\unexpanded\def\xmlcdatabefore {\begingroup\tt} +\unexpanded\def\xmlcdataafter {\endgroup} +\stoptyping + +Technically you can overload them but beware of side effects. Normally you won't +see much \type {CDATA} and whenever we do, it involves special data that needs +very special treatment anyway. + +\stopsection + +\startsection[title={Entities}] + +As usual with any way of encoding documents you need escapes in order to encode +the characters that are used in tagging the content, embedding comments, escaping +special characters in strings (in programming languages), etc. In \XML\ this +means that in order characters like \type {<} you need an escape like \type +{<} and in order then to encode an \type {&} you need \type {&}. + +In a typesetting workflow using a programming language like \TEX, another problem +shows up. There we have different special characters, like \type {$ $} for triggering +math, but also the backslash, braces etc. Even one such special character is already +enough to have yet another escaping mechanism at work. + +Ideally a user should not worry about these issues but it helps figuring out issues +when you know what happens under the hood. Also it is good to know that in the +code there are several ways to deal with these issues. Take the following document: + +\starttyping +<text> + Here we have a bit of a <&mess>: + + # # + % % + \ \ + { { + | | + } } + ~ ~ +</text> +\stoptyping + +When the file is read the \type {<} entity will be replaced by \type {<} and +the \type {>} by \type {>}. The numeric entities will be replaced by the +characters they refer to. The \type {&mess} is kind of special. We do preload +a huge list of more of less standardized entities but \type {mess} is not in +there. However, it is possible to have it defined in the document preamble, like: + +\starttyping +<!DOCTYPE dummy SYSTEM "dummy.dtd" [ + <!ENTITY mess "what a mess" > +]> +\stoptyping + +or even this: + +\starttyping +<!DOCTYPE dummy SYSTEM "dummy.dtd" [ + <!ENTITY mess "<p>what a mess</p>" > +]> +\stoptyping + +You can also define it in your document style using one of: + +\startxmlcmd {\cmdbasicsetup{xmlsetentity}} + replaces entity with name \cmdinternal {cd:name} by \cmdinternal {cd:text} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmltexentity}} + replaces entity with name \cmdinternal {cd:name} by \cmdinternal {cd:text} + typeset under a \TEX\ regime +\stopxmlcmd + +Such a definition will always have a higher priority than the one defined +in the document. Anyway, when the document is read in all entities are +resolved and those that need a special treatment because they map to some +text are stored in such a way that we can roundtrip them. As a consequence, +as soon as the content gets pushed into \TEX, we need not only to intercept +special characters but also have to make sure that the following works: + +\starttyping +\xmltexentity {tex} {\TEX} +\stoptyping + +Here the backslash starts a control sequence while in regular content a +backslash is just that: a backslash. + +Special characters are really special when we have to move text around +in a \TEX\ ecosystem. + +\starttyping +<text> + <title>About #3</title> +</text> +\stoptyping + +If we map and define title as follows: + +\starttyping +\startxmlsetup xml:title + \title{\xmlflush{#1}} +\stopxmlsetup +\stoptyping + +normally something \type {\xmlflush {id::123}} will be written to the +auxiliary file and in most cases that is quite okay, but if we have this: + +\starttyping +\setuphead[title][expansion=yes] +\stoptyping + +then we don't want the \type {#} to end up as hash because later on \TEX\ +can get very confused about it because it sees some argument then in a +probably unexpected way. This is solved by escaping the hash like this: + +\starttyping +About \Ux{23}3 +\stoptyping + +The \type {\Ux} command will convert its hexadecimal argument into a +character. Of course one then needs to typeset such a text under a \TEX\ +character regime but that is normally the case anyway. + +\stopsection + +\stopchapter + +\startchapter[title={Commands}] + +\startsection[title={nodes and lpaths}] + +The amount of commands available for manipulating the \XML\ file is rather large. +Many of the commands cooperate with the already discussed setups, a fancy name +for a collection of macro calls either or not mixed with text. + +Most of the commands are just shortcuts to \LUA\ calls, which means that the real +work is done by \LUA. In fact, what happens is that we have a continuous transfer +of control from \TEX\ to \LUA, where \LUA\ prints back either data (like element +content or attribute values) or just invokes a setup whereby it passes a +reference to the node resolved conform the path expression. The invoked setup +itself might return control to \LUA\ again, etc. + +This sounds complicated but examples will show what we mean here. First we +present the whole repertoire of commands. Because users can read the source code, +they might uncover more commands, but only the ones discussed here are official. +The commands are grouped in categories. + +In the following sections \cmdinternal {cd:node} means a reference to a node: +this can be the identifier of the root (the loaded xml tree) or a reference to a +node in that tree (often the result of some lookup. A \cmdinternal {cd:lpath} is +a fancy name for a path expression (as with \XSLT) but resolved by \LUA. + +\stopsection + +\startsection[title={commands}] + +There are a lot of commands available but you probably can ignore most of them. +We try to be complete which means that there is for instance \type {\xmlfirst} as +well as \type {\xmllast} but you probably never need the last one. There are also +commands that were used when testing this interface and we see no reason to +remove them. Some obscure ones are used in modules and after a while even I often +forget that they exist. To give you an idea of what commands are important we +show their use in generating the \CONTEXT\ command definitions (\type +{x-set-11.mkiv}) per Januari 2016: + +\startcolumns[n=2,balance=yes] +\starttabulate[|l|r|] +\NC \type {\xmlall} \NC 1 \NC \NR +\NC \type {\xmlatt} \NC 23 \NC \NR +\NC \type {\xmlattribute} \NC 1 \NC \NR +\NC \type {\xmlcount} \NC 1 \NC \NR +\NC \type {\xmldoif} \NC 2 \NC \NR +\NC \type {\xmldoifelse} \NC 1 \NC \NR +\NC \type {\xmlfilterlist} \NC 4 \NC \NR +\NC \type {\xmlflush} \NC 5 \NC \NR +\NC \type {\xmlinclude} \NC 1 \NC \NR +\NC \type {\xmlloadonly} \NC 1 \NC \NR +\NC \type {\xmlregisterdocumentsetup} \NC 1 \NC \NR +\NC \type {\xmlsetsetup} \NC 1 \NC \NR +\NC \type {\xmlsetup} \NC 4 \NC \NR +\stoptabulate +\stopcolumns + +As you can see filtering, flushing and accessing attributes score high. Below we show +the statistics of a quite complex rendering (5 variants of schoolbooks: basic book, +answers, teachers guide, worksheets, full blown version with extensive tracing). + +\startcolumns[n=2,balance=yes] +\starttabulate[|l|r|] +\NC \type {\xmladdindex} \NC 3 \NC \NR +\NC \type {\xmlall} \NC 5 \NC \NR +\NC \type {\xmlappendsetup} \NC 1 \NC \NR +\NC \type {\xmlapplyselectors} \NC 1 \NC \NR +\NC \type {\xmlatt} \NC 40 \NC \NR +\NC \type {\xmlattdef} \NC 9 \NC \NR +\NC \type {\xmlattribute} \NC 10 \NC \NR +\NC \type {\xmlbadinclusions} \NC 3 \NC \NR +\NC \type {\xmlconcat} \NC 3 \NC \NR +\NC \type {\xmlcount} \NC 1 \NC \NR +\NC \type {\xmldelete} \NC 11 \NC \NR +\NC \type {\xmldoif} \NC 39 \NC \NR +\NC \type {\xmldoifelse} \NC 28 \NC \NR +\NC \type {\xmldoifelsetext} \NC 13 \NC \NR +\NC \type {\xmldoifnot} \NC 2 \NC \NR +\NC \type {\xmldoifnotselfempty} \NC 1 \NC \NR +\NC \type {\xmlfilter} \NC 100 \NC \NR +\NC \type {\xmlfirst} \NC 51 \NC \NR +\NC \type {\xmlflush} \NC 69 \NC \NR +\NC \type {\xmlflushcontext} \NC 2 \NC \NR +\NC \type {\xmlinclude} \NC 1 \NC \NR +\NC \type {\xmlincludeoptions} \NC 5 \NC \NR +\NC \type {\xmlinclusion} \NC 16 \NC \NR +\NC \type {\xmlinjector} \NC 1 \NC \NR +\NC \type {\xmlloaddirectives} \NC 1 \NC \NR +\NC \type {\xmlmapvalue} \NC 4 \NC \NR +\NC \type {\xmlmatch} \NC 1 \NC \NR +\NC \type {\xmlprependsetup} \NC 5 \NC \NR +\NC \type {\xmlregisterdocumentsetup} \NC 2 \NC \NR +\NC \type {\xmlregistersetup} \NC 1 \NC \NR +\NC \type {\xmlremapnamespace} \NC 1 \NC \NR +\NC \type {\xmlsetfunction} \NC 2 \NC \NR +\NC \type {\xmlsetinjectors} \NC 2 \NC \NR +\NC \type {\xmlsetsetup} \NC 11 \NC \NR +\NC \type {\xmlsetup} \NC 76 \NC \NR +\NC \type {\xmlstrip} \NC 1 \NC \NR +\NC \type {\xmlstripanywhere} \NC 1 \NC \NR +\NC \type {\xmltag} \NC 1 \NC \NR +\NC \type {\xmltext} \NC 53 \NC \NR +\NC \type {\xmlvalue} \NC 2 \NC \NR +\stoptabulate +\stopcolumns + +Here many more are used but this is an exceptional case. The top is again +dominated by filtering, flushing and attribute consulting. The list can actually +be smaller. For instance, the \type {\xmlcount} can just as well be \type +{\xmlfilter} with a \type {count} finalizer. There are also some special ones, +like the injectors, that are needed for finetuning the final result. + +\stopsection + +\startsection[title={loading}] + +\startxmlcmd {\cmdbasicsetup{xmlloadfile}} + loads the file \cmdinternal {cd:file} and registers it under \cmdinternal + {cd:name} and applies either given or standard \cmdinternal + {cd:xmlsetup} (alias: \type {\xmlload}) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlloadbuffer}} + loads the buffer \cmdinternal {cd:buffer} and registers it under + \cmdinternal {cd:name} and applies either given or standard + \cmdinternal {cd:xmlsetup} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlloaddata}} + loads \cmdinternal {cd:text} and registers it under \cmdinternal + {cd:name} and applies either given or standard \cmdinternal + {cd:xmlsetup} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlloadonly}} + loads \cmdinternal {cd:text} and registers it under \cmdinternal + {cd:name} and applies either given or standard \cmdinternal + {cd:xmlsetup} but doesn't flush the content +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlinclude}} + includes the file specified by attribute \cmdinternal {cd:name} of the + element located by \cmdinternal {cd:lpath} at node \cmdinternal {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlprocessfile}} + registers file \cmdinternal {cd:file} as \cmdinternal {cd:name} and + process the tree starting with \cmdinternal {cd:xmlsetup} (alias: + \type {\xmlprocess}) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlprocessbuffer}} + registers buffer \cmdinternal {cd:name} as \cmdinternal {cd:name} and process + the tree starting with \cmdinternal {cd:xmlsetup} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlprocessdata}} + registers \cmdinternal {cd:text} as \cmdinternal {cd:name} and process + the tree starting with \cmdinternal {cd:xmlsetup} +\stopxmlcmd + +The initial setup defaults to \type {xml:process} that is defined +as follows: + +\starttyping +\startsetups xml:process + \xmlregistereddocumentsetups\xmldocument + \xmlmain\xmldocument +\stopsetups +\stoptyping + +First we apply the setups associated with the document (including common setups) +and then we flush the whole document. The macro \type {\xmldocument} expands to +the current document id. There is also \type {\xmlself} which expands to the +current node number (\type {#1} in setups). + +\startxmlcmd {\cmdbasicsetup{xmlmain}} + returns the whole documents +\stopxmlcmd + +Normally such a flush will trigger a chain reaction of setups associated with the +child elements. + +\stopsection + +\startsection[title={saving}] + +\startxmlcmd {\cmdbasicsetup{xmlsave}} + saves the given node \cmdinternal {cd:node} in the file \cmdinternal {cd:file} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmltofile}} + saves the match of \cmdinternal {cd:lpath} in the file \cmdinternal {cd:file} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmltobuffer}} + saves the match of \cmdinternal {cd:lpath} in the buffer \cmdinternal {cd:buffer} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmltobufferverbose}} + saves the match of \cmdinternal {cd:lpath} verbatim in the buffer \cmdinternal + {cd:buffer} +\stopxmlcmd + +% \startxmlcmd {\cmdbasicsetup{xmltoparameters}} +% converts the match of \cmdinternal {cd:lpath} to key|/|values (for tracing) +% \stopxmlcmd + +The next command is only needed when you have messed with the tree using +\LUA\ code. + +\startxmlcmd {\cmdbasicsetup{xmladdindex}} + (re)indexes a tree +\stopxmlcmd + +The following macros are only used in special situations and are not really meant +for users. + +\startxmlcmd {\cmdbasicsetup{xmlraw}} + flush the content if \cmdinternal {cd:node} with original entities +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{startxmlraw}} + flush the wrapped content with original entities +\stopxmlcmd + +\stopsection + +\startsection[title={flushing data}] + +When we flush an element, the associated \XML\ setups are expanded. The most +straightforward way to flush an element is the following. Keep in mind that the +returned valus itself can trigger setups and therefore flushes. + +\startxmlcmd {\cmdbasicsetup{xmlflush}} + returns all nodes under \cmdinternal {cd:node} +\stopxmlcmd + +You can restrict flushing by using commands that accept a specification. + +\startxmlcmd {\cmdbasicsetup{xmltext}} + returns the text of the matching \cmdinternal {cd:lpath} under \cmdinternal + {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlnonspace}} + returns the text of the matching \cmdinternal {cd:lpath} under \cmdinternal + {cd:node} without embedded spaces +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlall}} + returns all nodes under \cmdinternal {cd:node} that matches \cmdinternal + {cd:lpath} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmllastmatch}} + returns all nodes found in the last match +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlfirst}} + returns the first node under \cmdinternal {cd:node} that matches \cmdinternal + {cd:lpath} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmllast}} + returns the last node under \cmdinternal {cd:node} that matches \cmdinternal + {cd:lpath} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlfilter}} + at a match of \cmdinternal {cd:lpath} a given filter \type {filter} is applied + and the result is returned +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlsnippet}} + returns the \cmdinternal {cd:number}\high{th} element under \cmdinternal + {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlposition}} + returns the \cmdinternal {cd:number}\high{th} match of \cmdinternal + {cd:lpath} at node \cmdinternal {cd:node}; a negative number starts at the + end (alias: \type {\xmlindex}) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlelement}} + returns the \cmdinternal {cd:number}\high{th} child of node \cmdinternal {cd:node}; + a negative number starts at the end +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlpos}} + returns the index (position) in the parent node of \cmdinternal {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlconcat}} + returns the sequence of nodes that match \cmdinternal {cd:lpath} at + \cmdinternal {cd:node} whereby \cmdinternal {cd:text} is put between each + match +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlconcatrange}} + returns the \cmdinternal {cd:first}\high {th} upto \cmdinternal + {cd:last}\high {th} of nodes that match \cmdinternal {cd:lpath} at + \cmdinternal {cd:node} whereby \cmdinternal {cd:text} is put between each + match +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlcommand}} + apply the given \cmdinternal {cd:xmlsetup} to each match of \cmdinternal + {cd:lpath} at node \cmdinternal {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlstrip}} + remove leading and trailing spaces from nodes under \cmdinternal {cd:node} + that match \cmdinternal {cd:lpath} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlstripped}} + remove leading and trailing spaces from nodes under \cmdinternal {cd:node} + that match \cmdinternal {cd:lpath} and return the content afterwards +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlstripnolines}} + remove leading and trailing spaces as well as collapse embedded spaces + from nodes under \cmdinternal {cd:node} that match \cmdinternal {cd:lpath} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlstrippednolines}} + remove leading and trailing spaces as well as collapse embedded spaces from + nodes under \cmdinternal {cd:node} that match \cmdinternal {cd:lpath} and + return the content afterwards +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlverbatim}} + flushes the content verbatim code (without any wrapping, i.e. no fonts + are selected and such) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlinlineverbatim}} + return the content of the node as inline verbatim code, that is no further + interpretation (expansion) takes place and spaces are honoured; it uses the + following wrapper +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{startxmlinlineverbatim}} + wraps inline verbatim mode using the environment specified (a prefix \type + {xml:} is added to the environment name) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldisplayverbatim}} + return the content the node as display verbatim code, that is no further + interpretation (expansion) takes place and leading and trailing spaces and + newlines are treated special; it uses the following wrapper +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{startxmldisplayverbatim}} + wraps the content in display verbatim using the environment specified (a prefix + \type {xml:} is added to the environment name) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlprettyprint}} + pretty print (with colors) the node \cmdinternal {cd:node}; use the \CONTEXT\ + \SCITE\ lexers when available (\type {\usemodule [scite]}) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlflushspacewise}} + flush node \cmdinternal {cd:node} obeying spaces and newlines +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlflushlinewise}} + flush node \cmdinternal {cd:node} obeying newlines +\stopxmlcmd + +\stopsection + +\startsection[title={information}] + +The following commands return strings. Normally these are used in tests. + +\startxmlcmd {\cmdbasicsetup{xmlname}} + returns the complete name (including namespace prefix) of the + given \cmdinternal {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlnamespace}} + returns the namespace of the given \cmdinternal {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmltag}} + returns the tag of the element, without namespace prefix +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlcount}} + returns the number of matches of \cmdinternal {cd:lpath} at node \cmdinternal + {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlatt}} + returns the value of attribute \cmdinternal {cd:name} or empty if no such + attribute exists +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlattdef}} + returns the value of attribute \cmdinternal {cd:name} or \cmdinternal + {cd:string} if no such attribute exists +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlrefatt}} + returns the value of attribute \cmdinternal {cd:name} or empty if no such + attribute exists; a leading \type {#} is removed (nicer for tex) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlchainatt}} + returns the value of attribute \cmdinternal {cd:name} or empty if no such + attribute exists; backtracks till a match is found +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlchainattdef}} + returns the value of attribute \cmdinternal {cd:name} or \cmdinternal + {cd:string} if no such attribute exists; backtracks till a match is found +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlattribute}} + finds a first match for \cmdinternal {cd:lpath} at \cmdinternal {cd:node} and + returns the value of attribute \cmdinternal {cd:name} or empty if no such + attribute exists +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlattributedef}} + finds a first match for \cmdinternal {cd:lpath} at \cmdinternal {cd:node} and + returns the value of attribute \cmdinternal {cd:name} or \cmdinternal + {cd:text} if no such attribute exists +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmllastatt}} + returns the last attribute found (this avoids a lookup) +\stopxmlcmd + +\stopsection + +\startsection[title={manipulation}] + +You can use \LUA\ code to manipulate the tree and it makes no sense to duplicate +this in \TEX. In the future we might provide an interface to some of this +functionality. Keep in mind that manipuating the tree might have side effects as +we maintain several indices into the tree that also needs to be updated then. + +\stopsection + +\startsection[title={integration}] + +If you write a module that deals with \XML, for instance processing cals tables, +then you need ways to control specific behaviour. For instance, you might want to +add a background to the table. Such directives are collected in \XML\ files and +can be loaded on demand. + +\startxmlcmd {\cmdbasicsetup{xmlloaddirectives}} + loads \CONTEXT\ directives from \cmdinternal {cd:file} that will get + interpreted when processing documents +\stopxmlcmd + +A directives definition file looks as follows: + +\starttyping +<?xml version="1.0" standalone="yes"?> + +<directives> + <directive attribute='id' value="100" + setup="cdx:100"/> + <directive attribute='id' value="101" + setup="cdx:101"/> + <directive attribute='cdx' value="colors" element="cals:table" + setup="cdx:cals:table:colors"/> + <directive attribute='cdx' value="vertical" element="cals:table" + setup="cdx:cals:table:vertical"/> + <directive attribute='cdx' value="noframe" element="cals:table" + setup="cdx:cals:table:noframe"/> + <directive attribute='cdx' value="*" element="cals:table" + setup="cdx:cals:table:*"/> +</directives> +\stoptyping + +Examples of usage can be found in \type {x-cals.mkiv}. The directive is triggered +by an attribute. Instead of a setup you can specify a setup to be applied before +and after the node gets flushed. + +\startxmlcmd {\cmdbasicsetup{xmldirectives}} + apply the setups directive associated with the node +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldirectivesbefore}} + apply the before directives associated with the node +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldirectivesafter}} + apply the after directives associated with the node +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlinstalldirective}} + defines a directive that hooks into a handler +\stopxmlcmd + +Normally a directive will be put in the \XML\ file, for instance as: + +\starttyping +<?context-mathml-directive minus reduction yes ?> +\stoptyping + +Here the \type {mathml} is the general class of directives and \type {minus} a +subclass, in our case a specific element. + +\stopsection + +\startsection[title={setups}] + +The basic building blocks of \XML\ processing are setups. These are just +collections of macros that are expanded. These setups get one argument passed +(\type {#1}): + +\starttyping +\startxmlsetups somedoc:somesetup + \xmlflush{#1} +\stopxmlsetups +\stoptyping + +This argument is normally a number that internally refers to a specific node in +the \XML\ tree. The user should see it as an abstract reference and not depend on +its numeric property. Just think of it as \quote {the current node}. You can (and +probably will) call such setups using: + +\startxmlcmd {\cmdbasicsetup{xmlsetup}} + expands setup \cmdinternal {cd:setup} and pass \cmdinternal {cd:node} as + argument +\stopxmlcmd + +However, in most cases the setups are associated to specific elements, +something that users of \XSLT\ might recognize as templates. + +\startxmlcmd {\cmdbasicsetup{xmlsetfunction}} + associates function \cmdinternal {cd:luafunction} to the elements in + namespace \cmdinternal {cd:name} that match \cmdinternal {cd:lpath} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlsetsetup}} + associates setups (\TEX\ code) \cmdinternal {cd:setup} to the elements to + \cmdinternal {cd:node} that match \cmdinternal {cd:lpath} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlprependsetup}} + pushes \cmdinternal {cd:setup} to the front of global list of setups +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlappendsetup}} + adds \cmdinternal {cd:setup} to the global list of setups to be applied + (alias: \type{\xmlregistersetup}) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlbeforesetup}} + pushes \cmdinternal {cd:setup} into the global list of setups; the + last setup is the position +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlaftersetup}} + adds \cmdinternal {cd:setup} to the global list of setups; the last setup + is the position +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlremovesetup}} + removes \cmdinternal {cd:setup} from the global list of setups +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlprependdocumentsetup}} + pushes \cmdinternal {cd:setup} to the front of list of setups to be applied + to \cmdinternal {cd:name} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlappenddocumentsetup}} + adds \cmdinternal {cd:setup} to the list of setups to be applied to + \cmdinternal {cd:name} (alias: \type{\xmlregisterdocumentsetup}) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlbeforedocumentsetup}} + pushes \cmdinternal {cd:setup} into the setups to be applied to \cmdinternal + {cd:name}; the last setup is the position +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlafterdocumentsetup}} + adds \cmdinternal {cd:setup} to the setups to be applied to \cmdinternal + {cd:name}; the last setup is the position +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlremovedocumentsetup}} + removes \cmdinternal {cd:setup} from the global list of setups to be applied + to \cmdinternal {cd:name} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlresetsetups}} + removes all global setups +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlresetdocumentsetups}} + removes all setups from the \cmdinternal {cd:name} specific list of setups to + be applied +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlflushdocumentsetups}{setup}} + applies \cmdinternal {cd:setup} (can be a list) to \cmdinternal {cd:name} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlregisteredsetups}} + applies all global setups to the current document +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlregistereddocumentsetups}} + applies all document specific \cmdinternal {cd:setup} to document + \cmdinternal {cd:name} +\stopxmlcmd + +\stopsection + +\startsection[title={testing}] + +The following test macros all take a \cmdinternal {cd:node} as first argument +and an \cmdinternal {cd:lpath} as second: + +\startxmlcmd {\cmdbasicsetup{xmldoif}} + expands to \cmdinternal {cd:true} when \cmdinternal {cd:lpath} matches at + node \cmdinternal {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldoifnot}} + expands to \cmdinternal {cd:true} when \cmdinternal {cd:lpath} does not match + at node \cmdinternal {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldoifelse}{yes}} + expands to \cmdinternal {cd:true} when \cmdinternal {cd:lpath} matches at + node \cmdinternal {cd:node} and to \cmdinternal {cd:false} otherwise +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldoiftext}} + expands to \cmdinternal {cd:true} when the node matching \cmdinternal + {cd:lpath} at node \cmdinternal {cd:node} has some content +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldoifnottext}} + expands to \cmdinternal {cd:true} when the node matching \cmdinternal + {cd:lpath} at node \cmdinternal {cd:node} has no content +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldoifelsetext}} + expands to \cmdinternal {cd:true} when the node matching \cmdinternal + {cd:lpath} at node \cmdinternal {cd:node} has content and to \cmdinternal + {cd:false} otherwise +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldoifelseempty}} + expands to \cmdinternal {cd:true} when the node matching \cmdinternal + {cd:lpath} at node \cmdinternal {cd:node} is empty and to \cmdinternal + {cd:false} otherwise +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldoifelseselfempty}} + expands to \cmdinternal {cd:true} when the node is empty and to \cmdinternal + {cd:false} otherwise +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldoifselfempty}} + expands to \cmdinternal {cd:true} when \cmdinternal {cd:node} is empty +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldoifnotselfempty}} + expands to \cmdinternal {cd:true} when \cmdinternal {cd:node} is not empty +\stopxmlcmd + +\stopsection + +\startsection[title={initialization}] + +The general setup command (not to be confused with setups) that deals with the +\MKIV\ tree handler is \type {\setupxml}. There are currently only a few options. + +\cmdfullsetup{setupxml} + +When you set \type {default} to \cmdinternal {cd:text} elements with no setup +assigned will end up as text. When set to \type {hidden} such elements will be +hidden. You can apply the default yourself using: + +\startxmlcmd {\cmdbasicsetup{xmldefaulttotext}} + presets the tree with root \cmdinternal {cd:node} to the handlers set up with + \type {\setupxml} option \cmdinternal{default} +\stopxmlcmd + +You can set \type {compress} to \type {yes} in which case comment is stripped +from the tree when the file is read. When \type {entities} is set to \type {yes} +(this is the default) entities are replaced. + +\startxmlcmd {\cmdbasicsetup{xmlregisterns}} + associates an internal namespace (like \type {mml}) with one given in the + document as \URL\ (like mathml) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlremapname}} + changes the namespace and tag of the matching elements +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlremapnamespace}} + replaces all references to the given namespace to a new one (applied + recursively) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlchecknamespace}} + sets the namespace of the matching elements unless a namespace is already set +\stopxmlcmd + +\stopsection + +\startsection[title={helpers}] + +Often an attribute will determine the rendering and this may result in many +tests. Especially when we have multiple attributes that control the output such +tests can become rather extensive and redundant because one gets $n\times m$ or +more such tests. + +Therefore we have a convenient way to map attributes onto for instance strings or +commands. + +\startxmlcmd {\cmdbasicsetup{xmlmapvalue}} + associate a \cmdinternal {cd:text} with a \cmdinternal {cd:category} and + \cmdinternal {cd:name} (alias: \type{\xmlmapval}) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlvalue}} + expand the value associated with a \cmdinternal {cd:category} and + \cmdinternal {cd:name} and if not resolved, expand to the \cmdinternal + {cd:text} (alias: \type{\xmlval}) +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmldoifelsevalue}} + associate a \cmdinternal {cd:text} with a \cmdinternal {cd:category} and + \cmdinternal {cd:name} +\stopxmlcmd + +This is used as follows. We define a couple of mappings in the same category: + +\starttyping +\xmlmapvalue{emph}{bold} {\bf} +\xmlmapvalue{emph}{italic}{\it} +\stoptyping + +Assuming that we have associated the following setup with the \type {emph} +element, we can say (with \type {#1} being the current element): + +\starttyping +\startxmlsetups demo:emph + \begingroup + \xmlvalue{emph}{\xmlatt{#1}{type}}{} + \endgroup +\stopxmlsetups +\stoptyping + +In this case we have no default. The \type {type} attribute triggers the actions, +as in: + +\starttyping +normal <emph type='bold'>bold</emph> normal +\stoptyping + +This mechanism is not really bound to elements and attributes so you can use this +mechanism for other purposes as well. + +\stopsection + +\stopchapter + +\startchapter[title={Expressions and filters}] + +\startsection[title={path expressions}] + +In the previous chapters we used \cmdinternal {cd:lpath} expressions, which are a variant +on \type {xpath} expressions as in \XSLT\ but in this case more geared towards +usage in \TEX. This mechanisms will be extended when demands are there. + +A path is a sequence of matches. A simple path expression is: + +\starttyping +a/b/c/d +\stoptyping + +Here each \type {/} goes one level deeper. We can go backwards in a lookup with +\type {..}: + +\starttyping +a/b/../d +\stoptyping + +We can also combine lookups, as in: + +\starttyping +a/(b|c)/d +\stoptyping + +A negated lookup is preceded by a \type {!}: + +\starttyping +a/(b|c)/!d +\stoptyping + +A wildcard is specified with a \type {*}: + +\starttyping +a/(b|c)/!d/e/*/f +\stoptyping + +In addition to these tag based lookups we can use attributes: + +\starttyping +a/(b|c)/!d/e/*/f[@type=whatever] +\stoptyping + +An \type {@} as first character means that we are dealing with an attribute. +Within the square brackets there can be boolean expressions: + +\starttyping +a/(b|c)/!d/e/*/f[@type=whatever and @id>100] +\stoptyping + +You can use functions as in: + +\starttyping +a/(b|c)/!d/e/*/f[something(text()) == "oeps"] +\stoptyping + +There are a couple of predefined functions: + +\starttabulate[|l|l|p|] +\NC \type{rootposition} \type{order} \NC number \NC the index of the matched root element (kind of special) \NC \NR +\NC \type{position} \NC number \NC the current index of the matched element in the match list \NC \NR +\NC \type{match} \NC number \NC the current index of the matched element sub list with the same parent \NC \NR +\NC \type{first} \NC number \NC \NC \NR +\NC \type{last} \NC number \NC \NC \NR +\NC \type{index} \NC number \NC the current index of the matched element in its parent list \NC \NR +\NC \type{firstindex} \NC number \NC \NC \NR +\NC \type{lastindex} \NC number \NC \NC \NR +\NC \type{element} \NC number \NC the element's index \NC \NR +\NC \type{firstelement} \NC number \NC \NC \NR +\NC \type{lastelement} \NC number \NC \NC \NR +\NC \type{text} \NC string \NC the textual representation of the matched element \NC \NR +\NC \type{content} \NC table \NC the node of the matched element \NC \NR +\NC \type{name} \NC string \NC the full name of the matched element: namespace and tag \NC \NR +\NC \type{namespace} \type{ns} \NC string \NC the namespace of the matched element \NC \NR +\NC \type{tag} \NC string \NC the tag of the matched element \NC \NR +\NC \type{attribute} \NC string \NC the value of the attribute with the given name of the matched element \NC \NR +\stoptabulate + +There are fundamental differences between \type {position}, \type {match} and +\type {index}. Each step results in a new list of matches. The \type {position} +is the index in this new (possibly intermediate) list. The \type {match} is also +an index in this list but related to the specific match of element names. The +\type {index} refers to the location in the parent element. + +Say that we have: + +\starttyping +<collection> + <resources> + <manual> + <screen>.1.</screen> + <paper>.1.</paper> + </manual> + <manual> + <paper>.2.</paper> + <screen>.2.</screen> + </manual> + <resources> + <resources> + <manual> + <screen>.3.</screen> + <paper>.3.</paper> + </manual> + <resources> +<collection> +\stoptyping + +The following then applies: + +\starttabulate[|l|l|] +\NC \type {collection/resources/manual[position()==1]/paper} \NC \type{.1.} \NC \NR +\NC \type {collection/resources/manual[match()==1]/paper} \NC \type{.1.} \type{.3.} \NC \NR +\NC \type {collection/resources/manual/paper[index()==1]} \NC \type{.2.} \NC \NR +\stoptabulate + +In most cases the \type {position} test is more restrictive than the \type +{match} test. + +You can pass your own functions too. Such functions are defined in the the \type +{xml.expressions} namespace. We have defined a few shortcuts: + +\starttabulate[|l|l|] +\type {find(str,pattern)} \NC \type{string.find} \NC \NR +\type {contains(str)} \NC \type{string.find} \NC \NR +\type {oneof(str,...)} \NC is \type{str} in list \NC \NR +\type {upper(str)} \NC \type{characters.upper} \NC \NR +\type {lower(str)} \NC \type{characters.lower} \NC \NR +\type {number(str)} \NC \type{tonumber} \NC \NR +\type {boolean(str)} \NC \type{toboolean} \NC \NR +\type {idstring(str)} \NC removes leading hash \NC \NR +\type {name(index)} \NC full tag name \NC \NR +\type {tag(index)} \NC tag name \NC \NR +\type {namespace(index)} \NC namespace of tag \NC \NR +\type {text(index)} \NC content \NC \NR +\type {error(str)} \NC quit and show error \NC \NR +\type {quit()} \NC quit \NC \NR +\type {print()} \NC print message \NC \NR +\type {count(pattern)} \NC number of matches \NC \NR +\type {child(pattern)} \NC take child that matches \NC \NR +\stoptabulate + + +You can also use normal \LUA\ functions as long as you make sure that you pass +the right arguments. There are a few predefined variables available inside such +functions. + +\starttabulate[|Tl|l|p|] +\NC \type{list} \NC table \NC the list of matches \NC \NR +\NC \type{l} \NC number \NC the current index in the list of matches \NC \NR +\NC \type{ll} \NC element \NC the current element that matched \NC \NR +\NC \type{order} \NC number \NC the position of the root of the path \NC \NR +\stoptabulate + +The given expression between \type {[]} is converted to a \LUA\ expression so you +can use the usual ingredients: + +\starttyping +== ~= <= >= < > not and or () +\stoptyping + +In addition, \type {=} equals \type {==} and \type {!=} is the same as \type +{~=}. If you mess up the expression, you quite likely get a \LUA\ error message. + +\stopsection + +\startsection[title={functions as filters}] + +At the \LUA\ end a whole \cmdinternal {cd:lpath} expression results in a (set of) node(s) +with its environment, but that is hardly usable in \TEX. Think of code like: + +\starttyping +for e in xml.collected(xml.load('text.xml'),"title") do + -- e = the element that matched +end +\stoptyping + +The older variant is still supported but you can best use the previous variant. + +\starttyping +for r, d, k in xml.elements(xml.load('text.xml'),"title") do + -- r = root of the title element + -- d = data table + -- k = index in data table +end +\stoptyping + +Here \type {d[k]} points to the \type {title} element and in this case all titles +in the tree pass by. In practice this kind of code is encapsulated in function +calls, like those returning elements one by one, or returning the first or last +match. The result is then fed back into \TEX, possibly after being altered by an +associated setup. We've seen the wrappers to such functions already in a previous +chapter. + +In addition to the previously discussed expressions, one can add so called +filters to the expression, for instance: + +\starttyping +a/(b|c)/!d/e/text() +\stoptyping + +In a filter, the last part of the \cmdinternal {cd:lpath} expression is a function call. +The previous example returns the text of each element \type {e} that results from +matching the expression. When running \TEX\ the following functions are available. +Some are also also available when using pure \LUA. In \TEX\ you can often use one of +the macros like \type {\xmlfirst} instead of a \type {\xmlfilter} with finalizer +\type {first()}. The filter can be somewhat faster but that is hardly noticeable. + +\starttabulate[|l|l|p|] +\NC \type {context()} \NC string \NC the serialized text with \TEX\ catcode regime \NC \NR +%NC \type {ctxtext()} \NC string \NC \NC \NR +\NC \type {function()} \NC string \NC depends on the function \NC \NR +% +\NC \type {name()} \NC string \NC the (remapped) namespace \NC \NR +\NC \type {tag()} \NC string \NC the name of the element \NR +\NC \type {tags()} \NC list \NC the names of the element \NR +% +\NC \type {text()} \NC string \NC the serialized text \NC \NR +\NC \type {upper()} \NC string \NC the serialized text uppercased \NC \NR +\NC \type {lower()} \NC string \NC the serialized text lowercased \NC \NR +\NC \type {stripped()} \NC string \NC the serialized text stripped \NC \NR +\NC \type {lettered()} \NC string \NC the serialized text only letters (cf. \UNICODE) \NC \NR +% +\NC \type {count()} \NC number \NC the number of matches \NC \NR +\NC \type {index()} \NC number \NC the matched index in the current path \NC \NR +\NC \type {match()} \NC number \NC the matched index in the preceding path \NC \NR +% +%NC \type {lowerall()} \NC string \NC \NC \NR +%NC \type {upperall()} \NC string \NC \NC \NR +% +\NC \type {attribute(name)} \NC content \NC returns the attribute with the given name \NC \NR +\NC \type {chainattribute(name)} \NC content \NC sidem, but backtracks till one is found \NC \NR +\NC \type {command(name)} \NC content \NC expands the setup with the given name for each found element \NC \NR +\NC \type {position(n)} \NC content \NC processes the \type {n}\high{th} instance of the found element \NC \NR +\NC \type {all()} \NC content \NC processes all instances of the found element \NC \NR +%NC \type {default} \NC content \NC all \NC \NR +\NC \type {reverse()} \NC content \NC idem in reverse order \NC \NR +\NC \type {first()} \NC content \NC processes the first instance of the found element \NC \NR +\NC \type {last()} \NC content \NC processes the last instance of the found element \NC \NR +\NC \type {concat(...)} \NC content \NC concatinates the match \NC \NC \NR +\NC \type {concatrange(from,to,...)} \NC content \NC concatinates a range of matches \NC \NC \NR +\stoptabulate + +The extra arguments of the concatinators are: \type {separator} (string), \type +{lastseparator} (string) and \type {textonly} (a boolean). + +These filters are in fact \LUA\ functions which means that if needed more of them +can be added. Indeed this happens in some of the \XML\ related \MKIV\ modules, +for instance in the \MATHML\ processor. + +\stopsection + +\startsection[title={example}] + +The number of commands is rather large and if you want to avoid them this is +often possible. Take for instance: + +\starttyping +\xmlall{#1}{/a/b[position()>3]} +\stoptyping + +Alternatively you can use: + +\starttyping +\xmlfilter{#1}{/a/b[position()>3]/all()} +\stoptyping + +and actually this is also faster as internally it avoids a function call. Of +course in practice this is hardly measurable. + +In previous examples we've already seen quite some expressions, and it might be +good to point out that the syntax is modelled after \XSLT\ but is not quite the +same. The reason is that we started with a rather minimal system and have already +styles in use that depend on compatibility. + +\starttyping +namespace:// axis node(set) [expr 1]..[expr n] / ... / filter +\stoptyping + +When we are inside a \CONTEXT\ run, the namespace is \type {tex}. Hoewever, if +you want not to print back to \TEX\ you need to be more explicit. Say that we +typeset examns and have a (not that logical) structure like: + +\starttyping +<question> + <text>...</text> + <answer> + <item>one</item> + <item>two</item> + <item>three</item> + </answer> + <alternative> + <condition>true</condition> + <score>1</score> + </alternative> + <alternative> + <condition>false</condition> + <score>0</score> + </alternative> + <alternative> + <condition>true</condition> + <score>2</score> + </alternative> +</question> +\stoptyping + +Say that we typeset the questions with: + +\starttyping +\startxmlsetups question + \blank + score: \xmlfunction{#1}{totalscore} + \blank + \xmlfirst{#1}{text} + \startitemize + \xmlfilter{#1}{/answer/item/command(answer:item)} + \stopitemize + \endgraf + \blank +\stopxmlsetups +\stoptyping + +Each item in the answer results in a call to: + +\starttyping +\startxmlsetups answer:item + \startitem + \xmlflush{#1} + \endgraf + \xmlfilter{#1}{../../alternative[position()=rootposition()]/ + condition/command(answer:condition)} + \stopitem +\stopxmlsetups +\stoptyping + +\starttyping +\startxmlsetups answer:condition + \endgraf + condition: \xmlflush{#1} + \endgraf +\stopxmlsetups +\stoptyping + +Now, there are two rather special filters here. The first one involves +calculating the total score. As we look forward we use a function to deal with +this. + +\starttyping +\startluacode +function xml.functions.totalscore(root) + local score = 0 + for e in xml.collected(root,"/alternative") do + score = score + xml.filter(e,"xml:///score/number()") or 0 + end + tex.write(score) +end +\stopluacode +\stoptyping + +Watch how we use the namespace to keep the results at the \LUA\ end. + +The second special trick shown here is to limit a match using the current +position of the root (\type {#}) match. + +As you can see, a path expression can be more than just filtering a few nodes. At +the end of this manual you will find a bunch of examples. + +\stopsection + +\startsection[title={tables}] + +If you want to know how the internal \XML\ tables look you can print such a +table: + +\starttyping +print(table.serialize(e)) +\stoptyping + +This produces for instance: + +% s = xml.convert("<document><demo label='whatever'>some text</demo></document>") +% print(table.serialize(xml.filter(s,"demo")[1])) + +\starttyping +t={ + ["at"]={ + ["label"]="whatever", + }, + ["dt"]={ "some text" }, + ["ns"]="", + ["rn"]="", + ["tg"]="demo", +} +\stoptyping + +The \type {rn} entry is the renamed namespace (when renaming is applied). If you +see tags like \type {@pi@} this means that we don't have an element, but (in this +case) a processing instruction. + +\starttabulate[|l|p|] +\NC \type {@rt@} \NC the root element \NC \NR +\NC \type {@dd@} \NC document definition \NC \NR +\NC \type {@cm@} \NC comment, like \type {<!-- whatever -->} \NC \NR +\NC \type {@cd@} \NC so called \type {CDATA} \NC \NR +\NC \type {@pi@} \NC processing instruction, like \type {<?whatever we want ?>} \NC \NR +\stoptabulate + +There are many ways to deal with the content, but in the perspective of \TEX\ +only a few matter. + +\starttabulate[|l|p|] +\NC \type {xml.sprint(e)} \NC print the content to \TEX\ and apply setups if needed \NC \NR +\NC \type {xml.tprint(e)} \NC print the content to \TEX\ (serialize elements verbose) \NC \NR +\NC \type {xml.cprint(e)} \NC print the content to \TEX\ (used for special content) \NC \NR +\stoptabulate + +Keep in mind that anything low level that you uncover is not part of the official +interface unless mentioned in this manual. + +\stopsection + +\stopchapter + +\startchapter[title={Tips and tricks}] + +\startsection[title={Tracing}] + +It can be hard to debug code as much happens kind of behind the screens. +Therefore we have a couple of tracing options. Of course you can typeset some +status information, using for instance: + +\startxmlcmd {\cmdbasicsetup{xmlshow}} + typeset the tree given by \cmdinternal {cd:node} +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlinfo}} + typeset the name if the element given by \cmdinternal {cd:node} +\stopxmlcmd + +We also have a bunch of trackers that can be enabled, like: + +\starttyping +\enabletrackers[xml.show,xml.parse] +\stoptyping + +The full list (currently) is: + +\starttabulate[|lT|p|] +\NC xml.entities \NC show what entities are seen and replaced \NC \NR +\NC xml.path \NC show the result of parsing an lpath expression \NC \NR +\NC xml.parse \NC show stepwise resolving of expressions \NC \NR +\NC xml.profile \NC report all parsed lpath expressions (in the log) \NC \NR +\NC xml.remap \NC show what namespaces are remapped \NC \NR +\NC lxml.access \NC report errors with respect to resolving (symbolic) nodes \NC \NR +\NC lxml.comments \NC show the comments that are encountered (if at all) \NC \NR +\NC lxml.loading \NC show what files are loaded and converted \NC \NR +\NC lxml.setups \NC show what setups are being associated to elements \NC \NR +\stoptabulate + +In one of our workflows we produce books from \XML\ where the (educational) +content is organized in many small files. Each book has about 5~chapters and each +chapter is made of sections that contain text, exercises, resources, etc.\ and so +the document is assembled from thousands of files (don't worry, runtime inclusion +is pretty fast). In order to see where in the sources content resides we can +trace the filename. + +\startxmlcmd {\cmdbasicsetup{xmlinclusion}} + returns the file where the node comes from +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlinclusions}} + returns the list of files where the node comes from +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlinclusions}} + returns a list of files that were not included due to some problem +\stopxmlcmd + +Of course you have to make sure that these names end up somewhere visible, for +instance in the margin. + +\stopsection + +\startsection[title={Expansion}] + +For novice users the concept of expansion might sound frightening and to some +extend it is. However, it is important enough to spend some words on it here. + +It is good to realize that most setups are sort of immediate. When one setup is +issued, it can call another one and so on. Normally you won't notice that but +there are cases where that can be an problem. In \TEX\ you can define a macro, +take for instance: + +\starttyping +\startxmlsetups xml:foo + \def\foobar{\xmlfirst{#1}{/bar}} +\stopxmlsetups +\stoptyping + +you store the reference top node \type {bar} in \type {\foobar} maybe for later use. In +this case the content is not yet fetched, it will be done when \type {\foobar} is +called. + +\starttyping +\startxmlsetups xml:foo + \edef\foobar{\xmlfirst{#1}{/bar}} +\stopxmlsetups +\stoptyping + +Here the content of \type {bar} becomes the body of the macro. But what if +\type {bar} itself contains elements that also contain elements. When there +is a setup for \type {bar} it will be triggered and so on. + +When that setup looks like: + +\starttyping +\startxmlsetups xml:bar + \def\barfoo{\xmlflush{#1}} +\stopxmlsetups +\stoptyping + +Here we get something like: + +\starttyping +\foobar => {\def\barfoo{...}} +\stoptyping + +When \type {\barfoo} is not defined we get an error and when it is know and expands +to something weird we might also get an error. + +Especially when you don't know what content can show up, this can result in errors +when an expansion fails, for example because some macro being used is not defined. +To prevent this we can define a macro: + +\starttyping +\starttexdefinition unexpanded xml:bar:macro #1 + \def\barfoo{\xmlflush{#1}} +\stoptexdefinition + +\startxmlsetups xml:bar + \texdefinition{xml:bar:macro}{#1} +\stopxmlsetups +\stoptyping + +The setup \type {xml:bar} will still expand but the replacement text now is just the +call to the macro, think of: + +\starttyping +\foobar => {\texdefinition{xml:bar:macro}{#1}} +\stoptyping + +But this is often not needed, most \CONTEXT\ commands can handle the expansions +quite well but it's good to know that there is a away out. So, now to some +examples. Imagine that we have an \XML\ file that looks as follows: + +\starttyping +<?xml version='1.0' ?> +<demo> + <chapter> + <title>Some <em>short</em> title</title> + <content> + zeta + <index> + <key>zeta</key> + <content>zeta again</content> + </index> + alpha + <index> + <key>alpha</key> + <content>alpha <em>again</em></content> + </index> + gamma + <index> + <key>gamma</key> + <content>gamma</content> + </index> + beta + <index> + <key>beta</key> + <content>beta</content> + </index> + delta + <index> + <key>delta</key> + <content>delta</content> + </index> + done! + </content> + </chapter> +</demo> +\stoptyping + +There are a few structure related elements here: a chapter (with its list entry) +and some index entries. Both are multipass related and therefore travel around. +This means that when we let data end up in the auxiliary file, we need to make +sure that we end up with either expanded data (i.e.\ no references to the \XML\ +tree) or with robust forward and backward references to elements in the tree. + +Here we discuss three approaches (and more may show up later): pushing \XML\ into +the auxiliary file and using references to elements either or not with an +associated setup. We control the variants with a switch. + +\starttyping +\newcount\TestMode + +\TestMode=0 % expansion=xml +\TestMode=1 % expansion=yes, index, setup +\TestMode=2 % expansion=yes +\stoptyping + +We apply a couple of setups: + +\starttyping +\startxmlsetups xml:mysetups + \xmlsetsetup{\xmldocument}{demo|index|content|chapter|title|em}{xml:*} +\stopxmlsetups + +\xmlregistersetup{xml:mysetups} +\stoptyping + +The main document is processed with: + +\starttyping +\startxmlsetups xml:demo + \xmlflush{#1} + \subject{contents} + \placelist[chapter][criterium=all] + \subject{index} + \placeregister[index][criterium=all] + \page % else buffer is forgotten when placing header +\stopxmlsetups +\stoptyping + +First we show three alternative ways to deal with the chapter. The first case +expands the \XML\ reference so that we have an \XML\ stream in the auxiliary +file. This stream is processed as a small independent subfile when needed. The +second case registers a reference to the current element (\type {#1}). This means +that we have access to all data of this element, like attributes, title and +content. What happens depends on the given setup. The third variant does the same +but here the setup is part of the reference. + +\starttyping +\startxmlsetups xml:chapter + \ifcase \TestMode + % xml code travels around + \setuphead[chapter][expansion=xml] + \startchapter[title=eh: \xmltext{#1}{title}] + \xmlfirst{#1}{content} + \stopchapter + \or + % index is used for access via setup + \setuphead[chapter][expansion=yes,xmlsetup=xml:title:flush] + \startchapter[title=\xmlgetindex{#1}] + \xmlfirst{#1}{content} + \stopchapter + \or + % tex call to xml using index is used + \setuphead[chapter][expansion=yes] + \startchapter[title=hm: \xmlreference{#1}{xml:title:flush}] + \xmlfirst{#1}{content} + \stopchapter + \fi +\stopxmlsetups + +\startxmlsetups xml:title:flush + \xmltext{#1}{title} +\stopxmlsetups +\stoptyping + +We need to deal with emphasis and the content of the chapter. + +\starttyping +\startxmlsetups xml:em + \begingroup\em\xmlflush{#1}\endgroup +\stopxmlsetups + +\startxmlsetups xml:content + \xmlflush{#1} +\stopxmlsetups +\stoptyping + +A similar approach is followed with the index entries. Watch how we use the +numbered entries variant (in this case we could also have used just \type +{entries} and \type {keys}. + +\starttyping +\startxmlsetups xml:index + \ifcase \TestMode + \setupregister[index][expansion=xml,xmlsetup=] + \setstructurepageregister + [index] + [entries:1=\xmlfirst{#1}{content}, + keys:1=\xmltext{#1}{key}] + \or + \setupregister[index][expansion=yes,xmlsetup=xml:index:flush] + \setstructurepageregister + [index] + [entries:1=\xmlgetindex{#1}, + keys:1=\xmltext{#1}{key}] + \or + \setupregister[index][expansion=yes,xmlsetup=] + \setstructurepageregister + [index] + [entries:1=\xmlreference{#1}{xml:index:flush}, + keys:1=\xmltext{#1}{key}] + \fi +\stopxmlsetups + +\startxmlsetups xml:index:flush + \xmlfirst{#1}{content} +\stopxmlsetups +\stoptyping + +Instead of this flush, you can use the predefined setup \type {xml:flush} +unless it is overloaded by you. + +The file is processed by: + +\starttyping +\starttext + \xmlprocessfile{main}{test.xml}{} +\stoptext +\stoptyping + +We don't show the result here. If you're curious what the output is, you can test +it yourself. In that case it also makes sense to peek into the \type {test.tuc} +file to see how the information travels around. The \type {metadata} fields carry +information about how to process the data. + +The first case, the \XML\ expansion one, is somewhat special in the sense that +internally we use small pseudo files. You can control the rendering by tweaking +the following setups: + +\starttyping +\startxmlsetups xml:ctx:sectionentry + \xmlflush{#1} +\stopxmlsetups + +\startxmlsetups xml:ctx:registerentry + \xmlflush{#1} +\stopxmlsetups +\stoptyping + +{\em When these methods work out okay the other structural elements will be +dealt with in a similar way.} + +\stopsection + +\startsection[title={Special cases}] + +Normally the content will be flushed under a special (so called) catcode regime. +This means that characters that have a special meaning in \TEX\ will have no such +meaning in an \XML\ file. If you want content to be treated as \TEX\ code, you can +use one of the following: + +\startxmlcmd {\cmdbasicsetup{xmlflushcontext}} + flush the given \cmdinternal {cd:node} using the \TEX\ character + interpretation scheme +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlcontext}} + flush the match of \cmdinternal {cd:lpath} for the given \cmdinternal + {cd:node} using the \TEX\ character interpretation scheme +\stopxmlcmd + +We use this in cases like: + +\starttyping +.... + \xmlsetsetup {#1} { + tm|texformula| + } {xml:*} +.... + +\startxmlsetups xml:tm + \mathematics{\xmlflushcontext{#1}} +\stopxmlsetups + +\startxmlsetups xml:texformula + \placeformula\startformula\xmlflushcontext{#1}\stopformula +\stopxmlsetups +\stoptyping + +\stopsection + +\startsection[title={collecting}] + +Say that your document has + +\starttyping +<table> + <tr> + <td>foo</td> + <td>bar<td> + </tr> +</table> +\stoptyping + +And that you need to convert that to \TEX\ speak like: + +\starttyping +\bTABLE + \bTR + \bTD foo \eTD + \bTD bar \eTD + \eTR +\eTABLE +\stoptyping + +A simple mapping is: + +\starttyping +\startxmlsetups xml:table + \bTABLE \xmlflush{#1} \eTABLE +\stopxmlsetups +\startxmlsetups xml:tr + \bTR \xmlflush{#1} \eTR +\stopxmlsetups +\startxmlsetups xml:td + \bTD \xmlflush{#1} \eTD +\stopxmlsetups +\stoptyping + +The \type {\bTD} command is a so called delimited command which means that +it picks up its argument by looking for an \type {\eTD}. For a simple case +like here this works quite well because the flush is inside the pair. This +is not the case in the following variant: + +\starttyping +\startxmlsetups xml:td:start + \bTD +\stopxmlsetups +\startxmlsetups xml:td:stop + \eTD +\stopxmlsetups +\startxmlsetups xml:td + \xmlsetup{#1}{xml:td:start} + \xmlflush{#1} + \xmlsetup{#1}{xml:td:stop} +\stopxmlsetups +\stoptyping + +When for some reason \TEX\ gets confused you can revert to a mechanism that +collects content. + +\starttyping +\startxmlsetups xml:td:start + \startcollect + \bTD + \stopcollect +\stopxmlsetups +\startxmlsetups xml:td:stop + \startcollect + \eTD + \stopcollect +\stopxmlsetups +\startxmlsetups xml:td + \startcollecting + \xmlsetup{#1}{xml:td:start} + \xmlflush{#1} + \xmlsetup{#1}{xml:td:stop} + \stopcollecting +\stopxmlsetups +\stoptyping + +You can even implement solutions that effectively do this: + +\starttyping +\startcollecting + \startcollect \bTABLE \stopcollect + \startcollect \bTR \stopcollect + \startcollect \bTD \stopcollect + \startcollect foo\stopcollect + \startcollect \eTD \stopcollect + \startcollect \bTD \stopcollect + \startcollect bar\stopcollect + \startcollect \eTD \stopcollect + \startcollect \eTR \stopcollect + \startcollect \eTABLE \stopcollect +\stopcollecting +\stoptyping + +Of course you only need to go that complex when the situation demands it. Here is +another weird one: + +\starttyping +\startcollecting + \startcollect \setupsomething[\stopcollect + \startcollect foo=\stopcollect + \startcollect FOO,\stopcollect + \startcollect bar=\stopcollect + \startcollect BAR,\stopcollect + \startcollect ]\stopcollect +\stopcollecting +\stoptyping + +\stopsection + +\startsection[title={Selectors and injectors}] + +This chapter describes a bit special feature, one that we needed for a project +where we could not touch the original content but could add specific sections for +our own purpose. Hopefully the example demonstrates its useability. + +\enabletrackers[lxml.selectors] + +\startbuffer[foo] +<?xml version="1.0" encoding="UTF-8"?> + +<?context-directive message info 1: this is a demo file ?> +<?context-message-directive info 2: this is a demo file ?> + +<one> + <two> + <?context-select begin t1 t2 t3 ?> + <three> + t1 t2 t3 + <?context-directive injector crlf t1 ?> + t1 t2 t3 + </three> + <?context-select end ?> + <?context-select begin t4 ?> + <four> + t4 + </four> + <?context-select end ?> + <?context-select begin t8 ?> + <four> + t8.0 + t8.0 + </four> + <?context-select end ?> + <?context-include begin t4 ?> + <!-- + <three> + t4.t3 + <?context-directive injector crlf t1 ?> + t4.t3 + </three> + --> + <three> + t3 + <?context-directive injector crlf t1 ?> + t3 + </three> + <?context-include end ?> + <?context-select begin t8 ?> + <four> + t8.1 + t8.1 + </four> + <?context-select end ?> + <?context-select begin t8 ?> + <four> + t8.2 + t8.2 + </four> + <?context-select end ?> + <?context-select begin t4 ?> + <four> + t4 + t4 + </four> + <?context-select end ?> + <?context-directive injector page t7 t8 ?> + foo + <?context-directive injector blank t1 ?> + bar + <?context-directive injector page t7 t8 ?> + bar + </two> +</one> +\stopbuffer + +\typebuffer[foo] + +First we show how to plug in a directive. Processing instructions like the +following are normally ignored by an \XML\ processor, unless they make sense +to it. + +\starttyping +<?context-directive message info 1: this is a demo file ?> +<?context-message-directive info 2: this is a demo file ?> +\stoptyping + +We can define a message handler as follows: + +\startbuffer +\def\MyMessage#1#2#3{\writestatus{#1}{#2 #3}} + +\xmlinstalldirective{message}{MyMessage} +\stopbuffer + +\typebuffer \getbuffer + +When this file is process you will see this on the console: + +\startbuffer +info > 1: this is a demo file +info > 2: this is a demo file +\stopbuffer + +The file has some sections that can be used or ignored. The recipe for +obeying \type {t1} and \type {t4} is the following: + +\startbuffer +\xmlsetinjectors[t1] +\xmlsetinjectors[t4] + +\startxmlsetups xml:initialize + \xmlapplyselectors{#1} + \xmlsetsetup {#1} { + one|two|three|four + } {xml:*} +\stopxmlsetups + +\xmlregistersetup{xml:initialize} + +\startxmlsetups xml:one + [ONE \xmlflush{#1} ONE] +\stopxmlsetups + +\startxmlsetups xml:two + [TWO \xmlflush{#1} TWO] +\stopxmlsetups + +\startxmlsetups xml:three + [THREE \xmlflush{#1} THREE] +\stopxmlsetups + +\startxmlsetups xml:four + [FOUR \xmlflush{#1} FOUR] +\stopxmlsetups +\stopbuffer + +\typebuffer \getbuffer + +This typesets: + +\startnarrower +\xmlprocessbuffer{main}{foo}{} +\stopnarrower + +The include coding is kind of special: it permits adding content (in a comment) +and ignoring the rest so that we indeed can add something withou tinterfering +with the original. Of course in a normal workflow such messy solutions are +not needed, but alas, often workflows are not that clean, especially when one +has no real control over the source. + +\startxmlcmd {\cmdbasicsetup{xmlsetinjectors}} + enables a list of injectors that will be used +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlresetinjectors}} + resets the list of injectors +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlinjector}} + expands an injection (command); normally this one is only used + (in some setup) or for testing +\stopxmlcmd + +\startxmlcmd {\cmdbasicsetup{xmlapplyselectors}} + analyze the tree \cmdinternal {cd:node} for marked sections that + will be injected +\stopxmlcmd + +We have some injections predefined: + +\starttyping +\startsetups xml:directive:injector:page + \page +\stopsetups + +\startsetups xml:directive:injector:column + \column +\stopsetups + +\startsetups xml:directive:injector:blank + \blank +\stopsetups +\stoptyping + +In the example we see: + +\starttyping +<?context-directive injector page t7 t8 ?> +\stoptyping + +When we \type {\xmlsetinjector[t7]} a pagebreak will injected in that spot. Tags +like \type {t7}, \type {t8} etc.\ can represent versions. + +\stopsection + +\stopchapter + +\startchapter[title={Lookups using lpaths}] + +\startsection[title={introduction}] + +There is not that much system in the following examples. They resulted from tests +with different documents. The current implementation evolved out if the +experimental code. For instance, I decided to add the multiple expressions in row +handling after a few email exchanges with Jean|-|Michel Huffen. + +One of the main differences between the way \XSLT\ resolves a path and our way is +the anchor. Take: + +\starttyping +/something +something +\stoptyping + +The first one anchors in the current (!) element so it will only consider direct +children. The second one does a deep lookup and looks at the descendants as well. +Furthermore we have a few extra shortcuts like \type {**} in \type {a/**/b} which +represents all descendants. + +The expressions (between square brackets) has to be valid \LUA\ and some +preprocessing is done to resolve the built in functions. So, you might use code +like: + +\starttyping +my_lpeg_expression:match(text()) == "whatever" +\stoptyping + +given that \type {my_lpeg_expression} is known. In the examples below we use the +visualizer to show the steps. Some are shown more than once as part of a set. + +\stopsection + +\startsection[title={special cases}] + +\xmllshow{} +\xmllshow{*} +\xmllshow{.} +\xmllshow{/} + +\stopsection + +\startsection[title={wildcards}] + +\xmllshow{*} +\xmllshow{*:*} +\xmllshow{/*} +\xmllshow{/*:*} +\xmllshow{*/*} +\xmllshow{*:*/*:*} + +\xmllshow{a/*} +\xmllshow{a/*:*} +\xmllshow{/a/*} +\xmllshow{/a/*:*} + +\xmllshow{/*} +\xmllshow{/**} +\xmllshow{/***} + +\stopsection + +\startsection[title={multiple steps}] + +\xmllshow{answer} +\xmllshow{answer/test/*} +\xmllshow{answer/test/child::} +\xmllshow{answer/*} +\xmllshow{answer/*[tag()='p' and position()=1 and text()!='']} + +\stopsection + +\startsection[title={pitfals}] + +\xmllshow{[oneof(lower(@encoding),'tex','context','ctx')]} +\xmllshow{.[oneof(lower(@encoding),'tex','context','ctx')]} + +\stopsection + +\startsection[title={more special cases}] + +\xmllshow{**} +\xmllshow{*} +\xmllshow{..} +\xmllshow{.} +\xmllshow{//} +\xmllshow{/} + +\xmllshow{**/} +\xmllshow{**/*} +\xmllshow{**/.} +\xmllshow{**//} + +\xmllshow{*/} +\xmllshow{*/*} +\xmllshow{*/.} +\xmllshow{*//} + +\xmllshow{/**/} +\xmllshow{/**/*} +\xmllshow{/**/.} +\xmllshow{/**//} + +\xmllshow{/*/} +\xmllshow{/*/*} +\xmllshow{/*/.} +\xmllshow{/*//} + +\xmllshow{./} +\xmllshow{./*} +\xmllshow{./.} +\xmllshow{.//} + +\xmllshow{../} +\xmllshow{../*} +\xmllshow{../.} +\xmllshow{..//} + +\stopsection + +\startsection[title={more wildcards}] + +\xmllshow{one//two} +\xmllshow{one/*/two} +\xmllshow{one/**/two} +\xmllshow{one/***/two} +\xmllshow{one/x//two} +\xmllshow{one//x/two} +\xmllshow{//x/two} + +\stopsection + +\startsection[title={special axis}] + +\xmllshow{descendant::whocares/ancestor::whoknows} +\xmllshow{descendant::whocares/ancestor::whoknows/parent::} +\xmllshow{descendant::whocares/ancestor::} +\xmllshow{child::something/child::whatever/child::whocares} +\xmllshow{child::something/child::whatever/child::whocares|whoknows} +\xmllshow{child::something/child::whatever/child::(whocares|whoknows)} +\xmllshow{child::something/child::whatever/child::!(whocares|whoknows)} +\xmllshow{child::something/child::whatever/child::(whocares)} +\xmllshow{child::something/child::whatever/child::(whocares)[position()>2]} +\xmllshow{child::something/child::whatever[position()>2][position()=1]} +\xmllshow{child::something/child::whatever[whocares][whocaresnot]} +\xmllshow{child::something/child::whatever[whocares][not(whocaresnot)]} +\xmllshow{child::something/child::whatever/self::whatever} + +There is also \type {last-match::} that starts with the last found set of nodes. +This can save some runtime when you do lots of tests combined with a same check +afterwards. + +\stopsection + +\startsection[title={some more examples}] + +\xmllshow{/something/whatever} +\xmllshow{something/whatever} +\xmllshow{/**/whocares} +\xmllshow{whoknows/whocares} +\xmllshow{whoknows} +\xmllshow{whocares[contains(text(),'f') or contains(text(),'g')]} +\xmllshow{whocares/first()} +\xmllshow{whocares/last()} +\xmllshow{whatever/all()} +\xmllshow{whocares/position(2)} +\xmllshow{whocares/position(-2)} +\xmllshow{whocares[1]} +\xmllshow{whocares[-1]} +\xmllshow{whocares[2]} +\xmllshow{whocares[-2]} +\xmllshow{whatever[3]/attribute(id)} +\xmllshow{whatever[2]/attribute('id')} +\xmllshow{whatever[3]/text()} +\xmllshow{/whocares/first()} +\xmllshow{/whocares/last()} + +\xmllshow{xml://whatever/all()} +\xmllshow{whatever/all()} +\xmllshow{//whocares} +\xmllshow{..[2]} +\xmllshow{../*[2]} + +\xmllshow{/(whocares|whocaresnot)} +\xmllshow{/!(whocares|whocaresnot)} +\xmllshow{/!whocares} + +\xmllshow{/interface/command/command(xml:setups:register)} +\xmllshow{/interface/command[@name='xxx']/command(xml:setups:typeset)} +\xmllshow{/arguments/*} +\xmllshow{/sequence/first()} +\xmllshow{/arguments/text()} +\xmllshow{/sequence/variable/first()} +\xmllshow{/interface/define[@name='xxx']/first()} +\xmllshow{/parameter/command(xml:setups:parameter:measure)} + +\xmllshow{/(*:library|figurelibrary)/*:figure/*:label} +\xmllshow{/(*:library|figurelibrary)/figure/*:label} +\xmllshow{/(*:library|figurelibrary)/figure/label} +\xmllshow{/(*:library|figurelibrary)/figure:*/label} + +\xmlshow {whatever//br[tag(1)='br']} + +\stopsection + +\stopchapter + +\startchapter[title=Examples] + +\startsection[title=attribute chains] + +In \CSS, when an attribute is not present, the parent element is checked, and when +not found again, the lookup follows the chain till a match is found or the root is +reached. The following example demonstrates how such a chain lookup works. + +\startbuffer[test] +<something mine="1" test="one" more="alpha"> + <whatever mine="2" test="two"> + <whocares mine="3"> + <!-- this is a test --> + </whocares> + </whatever> +</something> +\stopbuffer + +\typebuffer[test] + +We apply the following setups to this tree: + +\startbuffer[setups] +\startxmlsetups xml:common + [ + \xmlchainatt{#1}{mine}, + \xmlchainatt{#1}{test}, + \xmlchainatt{#1}{more}, + \xmlchainatt{#1}{none} + ]\par +\stopxmlsetups + +\startxmlsetups xml:something + something: \xmlsetup{#1}{xml:common} + \xmlflush{#1} +\stopxmlsetups + +\startxmlsetups xml:whatever + whatever: \xmlsetup{#1}{xml:common} + \xmlflush{#1} +\stopxmlsetups + +\startxmlsetups xml:whocares + whocares: \xmlsetup{#1}{xml:common} + \xmlflush{#1} +\stopxmlsetups + +\startxmlsetups xml:mysetups + \xmlsetsetup{#1}{something|whatever|whocares}{xml:*} +\stopxmlsetups + +\xmlregisterdocumentsetup{example-1}{xml:mysetups} + +\xmlprocessbuffer{example-1}{test}{} +\stopbuffer + +\typebuffer[setups] + +This gives: + +\start + \getbuffer[setups] +\stop + +\stopsection + +\startsection[title=Conditional setups] + +Say that we have this code: + +\starttyping +\xmldoifelse {#1} {/what[@a='1']} { + \xmlfilter {#1} {/what/command('xml:yes')} +} { + \xmlfilter {#1} {/what/command('xml:nop')} +} +\stoptyping + +Here we first determine if there is a child \type {what} with attribute \type {a} +set to \type {1}. Depending on the outcome again we check the child nodes for +being named \type {what}. A faster solution which also takes less code is this: + +\starttyping +\xmlfilter {#1} {/what[@a='1']/command('xml:yes','xml:nop')} +\stoptyping + +\stopsection + +\startsection[title=Manipulating] + +Assume that we have the following \XML\ data: + +\startbuffer[test] +<A> + <B>right</B> + <B>wrong</B> +</A> +\stopbuffer + +\typebuffer[test] + +But, instead of \type {right} we want to see \type {okay}. We can do that with a +finalizer: + +\startbuffer +\startluacode +local rehash = { + ["right"] = "okay", +} + +function xml.finalizers.tex.Okayed(collected,what) + for i=1,#collected do + if what == "all" then + local str = xml.text(collected[i]) + context(rehash[str] or str) + else + context(str) + end + end +end +\stopluacode +\stopbuffer + +\typebuffer \getbuffer + +\startbuffer +\startxmlsetups xml:A + \xmlflush{#1} +\stopxmlsetups + +\startxmlsetups xml:B + (It's \xmlfilter{#1}{./Okayed("all")}) +\stopxmlsetups + +\startxmlsetups xml:testsetups + \xmlsetsetup{#1}{A|B}{xml:*} +\stopxmlsetups + +\xmlregisterdocumentsetup{example-2}{xml:testsetups} +\xmlprocessbuffer{example-2}{test}{} +\stopbuffer + +\typebuffer + +The result is: \start \inlinebuffer \stop + +\stopsection + +\startsection[title=Cross referencing] + +A rather common way to add cross references to \XML\ files is to borrow the +asymmetrical id's from \HTML. This means that one cannot simply use a value +of (say) \type {href} to locate an \type {id}. The next example came up on +the \CONTEXT\ mailing list. + +\startbuffer[test] +<doc> + <p>Text + <a href="#fn1" class="footnoteref" id="fnref1"><sup>1</sup></a> and + <a href="#fn2" class="footnoteref" id="fnref2"><sup>2</sup></a> + </p> + <div class="footnotes"> + <hr /> + <ol> + <li id="fn1"><p>A footnote.<a href="#fnref1">↩</a></p></li> + <li id="fn2"><p>A second footnote.<a href="#fnref2">↩</a></p></li> + </ol> + </div> +</doc> +\stopbuffer + +\typebuffer[test] + +We give two variants for dealing with such references. The first solution does +lookups and depending on the size of the file can be somewhat inefficient. + +\startbuffer +\startxmlsetups xml:doc + \blank + \xmlflush{#1} + \blank +\stopxmlsetups + +\startxmlsetups xml:p + \xmlflush{#1} +\stopxmlsetups + +\startxmlsetups xml:footnote + (variant 1)\footnote + {\xmlfirst + {example-3-1} + {div[@class='footnotes']/ol/li[@id='\xmlrefatt{#1}{href}']}} +\stopxmlsetups + +\startxmlsetups xml:initialize + \xmlsetsetup{#1}{p|doc}{xml:*} + \xmlsetsetup{#1}{a[@class='footnoteref']}{xml:footnote} + \xmlsetsetup{#1}{div[@class='footnotes']}{xml:nothing} +\stopxmlsetups + +\xmlresetdocumentsetups{*} +\xmlregisterdocumentsetup{example-3-1}{xml:initialize} + +\xmlprocessbuffer{example-3-1}{test}{} +\stopbuffer + +\typebuffer + +This will typeset two footnotes. + +\getbuffer + +The second variant collects the references so that the tiem spend on lookups is +less. + +\startbuffer +\startxmlsetups xml:doc + \blank + \xmlflush{#1} + \blank +\stopxmlsetups + +\startxmlsetups xml:p + \xmlflush{#1} +\stopxmlsetups + +\startluacode + userdata.notes = {} +\stopluacode + +\startxmlsetups xml:collectnotes + \ctxlua{userdata.notes['\xmlrefatt{#1}{id}'] = '#1'} +\stopxmlsetups + +\startxmlsetups xml:footnote + (variant 2)\footnote + {\xmlflush + {\cldcontext{userdata.notes['\xmlrefatt{#1}{href}']}}} +\stopxmlsetups + +\startxmlsetups xml:initialize + \xmlsetsetup{#1}{p|doc}{xml:*} + \xmlsetsetup{#1}{a[@class='footnoteref']}{xml:footnote} + \xmlfilter{#1}{div[@class='footnotes']/ol/li/command(xml:collectnotes)} + \xmlsetsetup{#1}{div[@class='footnotes']}{} +\stopxmlsetups + +\xmlregisterdocumentsetup{example-3-2}{xml:initialize} + +\xmlprocessbuffer{example-3-2}{test}{} +\stopbuffer + +\typebuffer + +This will again typeset two footnotes: + +\getbuffer + +\stopsection + +\startsection[title=mapping values] + +One way to process options \type {frame} in the example below is to map the +values to values known by \CONTEXT. + +\startbuffer[test] +<a> + <nattable frame="on"> + <tr><td>#1</td><td>#2</td><td>#3</td><td>#4</td></tr> + <tr><td>#5</td><td>#6</td><td>#7</td><td>#8</td></tr> + </nattable> + <nattable frame="off"> + <tr><td>#1</td><td>#2</td><td>#3</td><td>#4</td></tr> + <tr><td>#5</td><td>#6</td><td>#7</td><td>#8</td></tr> + </nattable> + <nattable frame="no"> + <tr><td>#1</td><td>#2</td><td>#3</td><td>#4</td></tr> + <tr><td>#5</td><td>#6</td><td>#7</td><td>#8</td></tr> + </nattable> +</a> +\stopbuffer + +\typebuffer[test] + +\startbuffer +\startxmlsetups xml:a + \xmlflush{#1} +\stopxmlsetups + +\xmlmapvalue {nattable:frame} {on} {on} +\xmlmapvalue {nattable:frame} {yes} {on} +\xmlmapvalue {nattable:frame} {off} {off} +\xmlmapvalue {nattable:frame} {no} {off} + +\startxmlsetups xml:nattable + \startplacetable[title=#1] + \setupTABLE[frame=\xmlval{nattable:frame}{\xmlatt{#1}{frame}}{on}]% + \bTABLE + \xmlflush{#1} + \eTABLE + \stopplacetable +\stopxmlsetups + +\startxmlsetups xml:tr + \bTR + \xmlflush{#1} + \eTR +\stopxmlsetups + +\startxmlsetups xml:td + \bTD + \xmlflush{#1} + \eTD +\stopxmlsetups + +\startxmlsetups xml:testsetups + \xmlsetsetup{example-4}{a|nattable|tr|td|}{xml:*} +\stopxmlsetups + +\xmlregisterdocumentsetup{example-4}{xml:testsetups} + +\xmlprocessbuffer{example-4}{test}{} +\stopbuffer + +The \type {\xmlmapvalue} mechanism is rather efficient and involves a minimum +of testing. + +\typebuffer + +We get: + +\getbuffer + +\stopsection + +\startsection[title=using \LUA] + +In this example we demonstrate how you can delegate rendering to \LUA. We +will construct a so called extreme table. The input is: + +\startbuffer[demo] +<?xml version="1.0" encoding="utf-8"?> + +<a> + <b> <c>1</c> <d>Text</d> </b> + <b> <c>2</c> <d>More text</d> </b> + <b> <c>2</c> <d>Even more text</d> </b> + <b> <c>2</c> <d>And more</d> </b> + <b> <c>3</c> <d>And even more</d> </b> + <b> <c>2</c> <d>The last text</d> </b> +</a> +\stopbuffer + +\typebuffer[demo] + +The processor code is: + +\startbuffer[process] +\startxmlsetups xml:test_setups + \xmlsetsetup{#1}{a|b|c|d}{xml:*} +\stopxmlsetups + +\xmlregisterdocumentsetup{example-5}{xml:test_setups} + +\xmlprocessbuffer{example-5}{demo}{} +\stopbuffer + +\typebuffer + +We color a sequence of the same titles (numbers here) in red. The first +solution remembers the last title: + +\startbuffer +\startxmlsetups xml:a + \startembeddedxtable + \xmlflush{#1} + \stopembeddedxtable +\stopxmlsetups + +\startxmlsetups xml:b + \xmlfunction{#1}{test_ba} +\stopxmlsetups + +\startluacode +local lasttitle = nil + +function xml.functions.test_ba(t) + local title = xml.text(t, "/c") + local content = xml.text(t, "/d") + context.startxrow() + context.startxcell { + background = "color", + backgroundcolor = lasttitle == title and "colorone" or "colortwo", + foregroundstyle = "bold", + foregroundcolor = "white", + } + context(title) + lasttitle = title + context.stopxcell() + context.startxcell() + context(content) + context.stopxcell() + context.stopxrow() +end +\stopluacode +\stopbuffer + +\typebuffer + +The \type {embeddedxtable} environment is needed because the table is picked up +as argument. + +\getbuffer \getbuffer[process] + +The second implemetation remembers what titles are already processed so here we +can color the last one too. + +\startbuffer +\startxmlsetups xml:a + \ctxlua{xml.functions.reset_bb()} + \startembeddedxtable + \xmlflush{#1} + \stopembeddedxtable +\stopxmlsetups + +\startxmlsetups xml:b + \xmlfunction{#1}{test_bb} +\stopxmlsetups + +\startluacode +local titles + +function xml.functions.reset_bb(t) + titles = { } +end + +function xml.functions.test_bb(t) + local title = xml.text(t, "/c") + local content = xml.text(t, "/d") + context.startxrow() + context.startxcell { + background = "color", + backgroundcolor = titles[title] and "colorone" or "colortwo", + foregroundstyle = "bold", + foregroundcolor = "white", + } + context(title) + titles[title] = true + context.stopxcell() + context.startxcell() + context(content) + context.stopxcell() + context.stopxrow() +end +\stopluacode +\stopbuffer + +\typebuffer \getbuffer \getbuffer[process] + +A solution without any state variable is given below. + +\startbuffer +\startxmlsetups xml:a + \startembeddedxtable + \xmlflush{#1} + \stopembeddedxtable +\stopxmlsetups + +\startxmlsetups xml:b + \xmlfunction{#1}{test_bc} +\stopxmlsetups + +\startluacode +function xml.functions.test_bc(t) + local title = xml.text(t, "/c") + local content = xml.text(t, "/d") + context.startxrow() + local okay = xml.text(t,"./preceding-sibling::/[-1]") == title + context.startxcell { + background = "color", + backgroundcolor = okay and "colorone" or "colortwo", + foregroundstyle = "bold", + foregroundcolor = "white", + } + context(title) + context.stopxcell() + context.startxcell() + context(content) + context.stopxcell() + context.stopxrow() +end +\stopluacode +\stopbuffer + +\typebuffer \getbuffer \getbuffer[process] + +Here is a solution that delegates even more to \LUA. The previous variants were +actually not that safe with repect to special characters and didn't handle +nested elements either but the next one does. + +\startbuffer[demo] +<?xml version="1.0" encoding="utf-8"?> + +<a> + <b> <c>#1</c> <d>Text</d> </b> + <b> <c>#2</c> <d>More text</d> </b> + <b> <c>#2</c> <d>Even more text</d> </b> + <b> <c>#2</c> <d>And more</d> </b> + <b> <c>#3</c> <d>And even more</d> </b> + <b> <c>#2</c> <d>Something <i>nested</i> </d> </b> +</a> +\stopbuffer + +\typebuffer[demo] + +We also need to map the \type {i} element. + +\startbuffer +\startxmlsetups xml:a + \starttexcode + \xmlfunction{#1}{test_a} + \stoptexcode +\stopxmlsetups + +\startxmlsetups xml:c + \xmlflush{#1} +\stopxmlsetups + +\startxmlsetups xml:d + \xmlflush{#1} +\stopxmlsetups + +\startxmlsetups xml:i + {\em\xmlflush{#1}} +\stopxmlsetups + +\startluacode +function xml.functions.test_a(t) + context.startxtable() + local previous = false + for b in xml.collected(lxml.getid(t),"/b") do + context.startxrow() + local current = xml.text(b,"/c") + context.startxcell { + background = "color", + backgroundcolor = (previous == current) and "colorone" or "colortwo", + foregroundstyle = "bold", + foregroundcolor = "white", + } + lxml.first(b,"/c") + context.stopxcell() + context.startxcell() + lxml.first(b,"/d") + context.stopxcell() + previous = current + context.stopxrow() + end + context.stopxtable() +end +\stopluacode + +\startxmlsetups xml:test_setups + \xmlsetsetup{#1}{a|b|c|d|i}{xml:*} +\stopxmlsetups + +\xmlregisterdocumentsetup{example-5}{xml:test_setups} + +\xmlprocessbuffer{example-5}{demo}{} +\stopbuffer + +\typebuffer \getbuffer + +The question is, do we really need \LUA ? Often we don't, apart maybe from an +occasional special finalizer. A pure \TEX\ solution is given next: + +\startbuffer +\startxmlsetups xml:a + \glet\MyPreviousTitle\empty + \glet\MyCurrentTitle \empty + \startembeddedxtable + \xmlflush{#1} + \stopembeddedxtable +\stopxmlsetups + +\startxmlsetups xml:b + \startxrow + \xmlflush{#1} + \stopxrow +\stopxmlsetups + +\startxmlsetups xml:c + \xdef\MyCurrentTitle{\xmltext{#1}{.}} + \doifelse {\MyPreviousTitle} {\MyCurrentTitle} { + \startxcell + [background=color, + backgroundcolor=colorone, + foregroundstyle=bold, + foregroundcolor=white] + } { + \glet\MyPreviousTitle\MyCurrentTitle + \startxcell + [background=color, + backgroundcolor=colortwo, + foregroundstyle=bold, + foregroundcolor=white] + } + \xmlflush{#1} + \stopxcell +\stopxmlsetups + +\startxmlsetups xml:d + \startxcell + \xmlflush{#1} + \stopxcell +\stopxmlsetups + +\startxmlsetups xml:i + {\em\xmlflush{#1}} +\stopxmlsetups + +\startxmlsetups xml:test_setups + \xmlsetsetup{#1}{*}{xml:*} +\stopxmlsetups + +\xmlregisterdocumentsetup{example-5}{xml:test_setups} + +\xmlprocessbuffer{example-5}{demo}{} +\stopbuffer + +\typebuffer \getbuffer + +You can even save a few lines of code: + +\starttyping +\startxmlsetups xml:c + \xdef\MyCurrentTitle{\xmltext{#1}{.}} + \startxcell + [background=color, + backgroundcolor=color\ifx\MyPreviousTitle\MyCurrentTitle one\else two\fi, + foregroundstyle=bold, + foregroundcolor=white] + \xmlflush{#1} + \stopxcell + \glet\MyPreviousTitle\MyCurrentTitle +\stopxmlsetups +\stoptyping + +Or if you prefer: + +\startxmlsetups xml:c + \xdef\MyCurrentTitle{\xmltext{#1}{.}} + \doifelse {\MyPreviousTitle} {\MyCurrentTitle} { + \xmlsetup{#1}{xml:c:one} + } { + \xmlsetup{#1}{xml:c:two} + } +\stopxmlsetups + +\startxmlsetups xml:c:one + \startxcell + [background=color, + backgroundcolor=colorone, + foregroundstyle=bold, + foregroundcolor=white] + \xmlflush{#1} + \stopxcell +\stopxmlsetups + +\startxmlsetups xml:c:two + \startxcell + [background=color, + backgroundcolor=colortwo, + foregroundstyle=bold, + foregroundcolor=white] + \xmlflush{#1} + \stopxcell + \global\let\MyPreviousTitle\MyCurrentTitle +\stopxmlsetups + +These examples demonstrate that it doesn't hurt to know a little bit of \TEX\ +programming: defining macros and basic comparisons can come in handy. There are +examples in the test suite, you can peek in the source code, you can consult +the wiki or you can just ask on the list. + +\stopsection + +\startsection[title=Last match] + +For the next example we use the following \XML\ input: + +\startbuffer[demo] +<?xml version "1.0"?> +<document> + <section id="1"> + <content> + <p>first</p> + <p>second</p> + </content> + </section> + <section id="2"> + <content> + <p>third</p> + <p>fourth</p> + </content> + </section> +</document> +\stopbuffer + +\typebuffer[demo] + +If you check if some element is present and then act accordingly, you can +end up with doing the same lookup twice. Although it might sound inefficient, +in practice it's often not measureable. + +\startbuffer +\startxmlsetups xml:demo:document + \type{\xmlall{#1}{/section[@id='2']/content/p}}\par + \xmldoif{#1}{/section[@id='2']/content/p} { + \xmlall{#1}{/section[@id='2']/content/p} + } + \type{\xmllastmatch}\par + \xmldoif{#1}{/section[@id='2']/content/p} { + \xmllastmatch + } + \type{\xmlall{#1}{last-match::}}\par + \xmldoif{#1}{/section[@id='2']/content/p} { + \xmlall{#1}{last-match::} + } + \type{\xmlfilter{#1}{last-match::/command(xml:demo:p)}}\par + \xmldoif{#1}{/section[@id='2']/content/p} { + \xmlfilter{#1}{last-match::/command(xml:demo:p)} + } +\stopxmlsetups + +\startxmlsetups xml:demo:p + \quad\xmlflush{#1}\endgraf +\stopxmlsetups + +\startxmlsetups xml:demo:base + \xmlsetsetup{#1}{document|p}{xml:demo:*} +\stopxmlsetups + +\xmlregisterdocumentsetup{example-6}{xml:demo:base} + +\xmlprocessbuffer{example-6}{demo}{} +\stopbuffer + +\typebuffer + +In the second check we just flush the last match, so effective we do an \type +{\xmlall} here. The third and fourth alternatives demonstrate how we can use +\type {last-match} as axis. The gain is 10\% or more on the lookup but of course +typesetting often takes relatively more time than the lookup. + +\startpacked +\getbuffer +\stoppacked + +\stopsection + +\stopchapter + +\stopbodymatter + +\stoptext diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua index 0167ac5e8..9ecd30560 100644 --- a/scripts/context/lua/mtxrun.lua +++ b/scripts/context/lua/mtxrun.lua @@ -11261,7 +11261,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true --- original size: 51229, stripped down to: 31529 +-- original size: 53877, stripped down to: 32495 if not modules then modules={} end modules ['lxml-lpt']={ version=1.001, @@ -11276,10 +11276,23 @@ local format,upper,lower,gmatch,gsub,find,rep=string.format,string.upper,string. local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local setmetatableindex=table.setmetatableindex local formatters=string.formatters -local trace_lpath=false if trackers then trackers.register("xml.path",function(v) trace_lpath=v end) end -local trace_lparse=false if trackers then trackers.register("xml.parse",function(v) trace_lparse=v end) end -local trace_lprofile=false if trackers then trackers.register("xml.profile",function(v) trace_lpath=v trace_lparse=v trace_lprofile=v end) end +local trace_lpath=false +local trace_lparse=false +local trace_lprofile=false local report_lpath=logs.reporter("xml","lpath") +if trackers then + trackers.register("xml.path",function(v) + trace_lpath=v + end) + trackers.register("xml.parse",function(v) + trace_lparse=v + end) + trackers.register("xml.profile",function(v) + trace_lpath=v + trace_lparse=v + trace_lprofile=v + end) +end local xml=xml local lpathcalls=0 function xml.lpathcalls () return lpathcalls end local lpathcached=0 function xml.lpathcached() return lpathcached end @@ -11716,6 +11729,7 @@ local template_f_y=[[ local template_f_n=[[ return xml.finalizers['%s']['%s'] ]] +local register_last_match={ kind="axis",axis="last-match" } local register_self={ kind="axis",axis="self" } local register_parent={ kind="axis",axis="parent" } local register_descendant={ kind="axis",axis="descendant" } @@ -11793,7 +11807,7 @@ local pathparser=Ct { "patterns", ), protocol=Cg(V("letters"),"protocol")*P("://")+Cg(Cc(nil),"protocol"), step=((V("shortcuts")+P("/")+V("axis"))*spaces*V("nodes")^0+V("error"))*spaces*V("expressions")^0*spaces*V("finalizer")^0, - axis=V("descendant")+V("child")+V("parent")+V("self")+V("root")+V("ancestor")+V("descendant_or_self")+V("following_sibling")+V("following")+V("reverse_sibling")+V("preceding_sibling")+V("preceding")+V("ancestor_or_self")+#(1-P(-1))*Cc(register_auto_child), + axis=V("last_match")+V("descendant")+V("child")+V("parent")+V("self")+V("root")+V("ancestor")+V("descendant_or_self")+V("following_sibling")+V("following")+V("reverse_sibling")+V("preceding_sibling")+V("preceding")+V("ancestor_or_self")+#(1-P(-1))*Cc(register_auto_child), special=special_1+special_2+special_3, initial=(P("/")*spaces*Cc(register_initial_child))^-1, error=(P(1)^1)/register_error, @@ -11819,6 +11833,7 @@ local pathparser=Ct { "patterns", preceding=P('preceding::')*Cc(register_preceding ), preceding_sibling=P('preceding-sibling::')*Cc(register_preceding_sibling ), reverse_sibling=P('reverse-sibling::')*Cc(register_reverse_sibling ), + last_match=P('last-match::')*Cc(register_last_match ), nodes=(V("nodefunction")*spaces*P("(")*V("nodeset")*P(")")+V("nodetest")*V("nodeset"))/register_nodes, expressions=expression/register_expression, letters=R("az")^1, @@ -11936,140 +11951,168 @@ lpath=function (pattern) end end xml.lpath=lpath -local profiled={} xml.profiled=profiled -local function profiled_apply(list,parsed,nofparsed,order) - local p=profiled[parsed.pattern] - if p then - p.tested=p.tested+1 - else - p={ tested=1,matched=0,finalized=0 } - profiled[parsed.pattern]=p - end - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - collected=apply_axis[pi.axis](collected) - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - elseif kind=="finalizer" then - collected=pi.finalizer(collected) - p.matched=p.matched+1 - p.finalized=p.finalized+1 - return collected - end - if not collected or #collected==0 then - local pn=i<nofparsed and parsed[nofparsed] - if pn and pn.kind=="finalizer" then - collected=pn.finalizer(collected) +do + local profiled={} + xml.profiled=profiled + local lastmatch=nil + local keepmatch=nil + if directives then + directives.register("xml.path.keeplastmatch",function(v) + keepmatch=v + lastmatch=nil + end) + end + apply_axis["last-match"]=function() + return lastmatch or {} + end + local function profiled_apply(list,parsed,nofparsed,order) + local p=profiled[parsed.pattern] + if p then + p.tested=p.tested+1 + else + p={ tested=1,matched=0,finalized=0 } + profiled[parsed.pattern]=p + end + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + collected=apply_axis[pi.axis](collected) + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + elseif kind=="finalizer" then + collected=pi.finalizer(collected) + p.matched=p.matched+1 p.finalized=p.finalized+1 return collected end - return nil + if not collected or #collected==0 then + local pn=i<nofparsed and parsed[nofparsed] + if pn and pn.kind=="finalizer" then + collected=pn.finalizer(collected) + p.finalized=p.finalized+1 + return collected + end + return nil + end end - end - if collected then - p.matched=p.matched+1 - end - return collected -end -local function traced_apply(list,parsed,nofparsed,order) - if trace_lparse then - lshow(parsed) - end - report_lpath("collecting: %s",parsed.pattern) - report_lpath("root tags : %s",tagstostring(list)) - report_lpath("order : %s",order or "unset") - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - collected=apply_axis[pi.axis](collected) - report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) - elseif kind=="finalizer" then - collected=pi.finalizer(collected) - report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") - return collected + if collected then + p.matched=p.matched+1 end - if not collected or #collected==0 then - local pn=i<nofparsed and parsed[nofparsed] - if pn and pn.kind=="finalizer" then - collected=pn.finalizer(collected) - report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "") + return collected + end + local function traced_apply(list,parsed,nofparsed,order) + if trace_lparse then + lshow(parsed) + end + report_lpath("collecting: %s",parsed.pattern) + report_lpath("root tags : %s",tagstostring(list)) + report_lpath("order : %s",order or "unset") + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + collected=apply_axis[pi.axis](collected) + report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) + elseif kind=="finalizer" then + collected=pi.finalizer(collected) + report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") return collected end - return nil - end - end - return collected -end -local function normal_apply(list,parsed,nofparsed,order) - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - local axis=pi.axis - if axis~="self" then - collected=apply_axis[axis](collected) + if not collected or #collected==0 then + local pn=i<nofparsed and parsed[nofparsed] + if pn and pn.kind=="finalizer" then + collected=pn.finalizer(collected) + report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "") + return collected + end + return nil end - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - elseif kind=="finalizer" then - return pi.finalizer(collected) end - if not collected or #collected==0 then - local pf=i<nofparsed and parsed[nofparsed].finalizer - if pf then - return pf(collected) + return collected + end + local function normal_apply(list,parsed,nofparsed,order) + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + local axis=pi.axis + if axis~="self" then + collected=apply_axis[axis](collected) + end + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + elseif kind=="finalizer" then + return pi.finalizer(collected) + end + if not collected or #collected==0 then + local pf=i<nofparsed and parsed[nofparsed].finalizer + if pf then + return pf(collected) + end + return nil end - return nil end + return collected end - return collected -end -local function applylpath(list,pattern) - if not list then - return - end - local parsed=cache[pattern] - if parsed then - lpathcalls=lpathcalls+1 - lpathcached=lpathcached+1 - elseif type(pattern)=="table" then - lpathcalls=lpathcalls+1 - parsed=pattern - else - parsed=lpath(pattern) or pattern - end - if not parsed then - return + local apply=normal_apply + if trackers then + trackers.register("xml.path,xml.parse,xml.profile",function() + if trace_lprofile then + apply=profiled_apply + elseif trace_lpath then + apply=traced_apply + else + apply=normal_apply + end + end) end - local nofparsed=#parsed - if nofparsed==0 then - return + function xml.applylpath(list,pattern) + if not list then + lastmatch=nil + return + end + local parsed=cache[pattern] + if parsed then + lpathcalls=lpathcalls+1 + lpathcached=lpathcached+1 + elseif type(pattern)=="table" then + lpathcalls=lpathcalls+1 + parsed=pattern + else + parsed=lpath(pattern) or pattern + end + if not parsed then + lastmatch=nil + return + end + local nofparsed=#parsed + if nofparsed==0 then + lastmatch=nil + return + end + local collected=apply({ list },parsed,nofparsed,list.mi) + lastmatch=keepmatch and collected or nil + return collected end - if not trace_lpath then - return normal_apply ({ list },parsed,nofparsed,list.mi) - elseif trace_lprofile then - return profiled_apply({ list },parsed,nofparsed,list.mi) - else - return traced_apply ({ list },parsed,nofparsed,list.mi) + function xml.lastmatch() + return lastmatch end end -xml.applylpath=applylpath +local applylpath=xml.applylpath function xml.filter(root,pattern) return applylpath(root,pattern) end @@ -18638,8 +18681,8 @@ end -- of closure -- used libraries : l-lua.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-mrg.lua util-tpl.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua -- skipped libraries : - --- original bytes : 791821 --- stripped bytes : 286453 +-- original bytes : 794469 +-- stripped bytes : 288135 -- end library merge diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua index 0167ac5e8..9ecd30560 100644 --- a/scripts/context/stubs/mswin/mtxrun.lua +++ b/scripts/context/stubs/mswin/mtxrun.lua @@ -11261,7 +11261,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true --- original size: 51229, stripped down to: 31529 +-- original size: 53877, stripped down to: 32495 if not modules then modules={} end modules ['lxml-lpt']={ version=1.001, @@ -11276,10 +11276,23 @@ local format,upper,lower,gmatch,gsub,find,rep=string.format,string.upper,string. local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local setmetatableindex=table.setmetatableindex local formatters=string.formatters -local trace_lpath=false if trackers then trackers.register("xml.path",function(v) trace_lpath=v end) end -local trace_lparse=false if trackers then trackers.register("xml.parse",function(v) trace_lparse=v end) end -local trace_lprofile=false if trackers then trackers.register("xml.profile",function(v) trace_lpath=v trace_lparse=v trace_lprofile=v end) end +local trace_lpath=false +local trace_lparse=false +local trace_lprofile=false local report_lpath=logs.reporter("xml","lpath") +if trackers then + trackers.register("xml.path",function(v) + trace_lpath=v + end) + trackers.register("xml.parse",function(v) + trace_lparse=v + end) + trackers.register("xml.profile",function(v) + trace_lpath=v + trace_lparse=v + trace_lprofile=v + end) +end local xml=xml local lpathcalls=0 function xml.lpathcalls () return lpathcalls end local lpathcached=0 function xml.lpathcached() return lpathcached end @@ -11716,6 +11729,7 @@ local template_f_y=[[ local template_f_n=[[ return xml.finalizers['%s']['%s'] ]] +local register_last_match={ kind="axis",axis="last-match" } local register_self={ kind="axis",axis="self" } local register_parent={ kind="axis",axis="parent" } local register_descendant={ kind="axis",axis="descendant" } @@ -11793,7 +11807,7 @@ local pathparser=Ct { "patterns", ), protocol=Cg(V("letters"),"protocol")*P("://")+Cg(Cc(nil),"protocol"), step=((V("shortcuts")+P("/")+V("axis"))*spaces*V("nodes")^0+V("error"))*spaces*V("expressions")^0*spaces*V("finalizer")^0, - axis=V("descendant")+V("child")+V("parent")+V("self")+V("root")+V("ancestor")+V("descendant_or_self")+V("following_sibling")+V("following")+V("reverse_sibling")+V("preceding_sibling")+V("preceding")+V("ancestor_or_self")+#(1-P(-1))*Cc(register_auto_child), + axis=V("last_match")+V("descendant")+V("child")+V("parent")+V("self")+V("root")+V("ancestor")+V("descendant_or_self")+V("following_sibling")+V("following")+V("reverse_sibling")+V("preceding_sibling")+V("preceding")+V("ancestor_or_self")+#(1-P(-1))*Cc(register_auto_child), special=special_1+special_2+special_3, initial=(P("/")*spaces*Cc(register_initial_child))^-1, error=(P(1)^1)/register_error, @@ -11819,6 +11833,7 @@ local pathparser=Ct { "patterns", preceding=P('preceding::')*Cc(register_preceding ), preceding_sibling=P('preceding-sibling::')*Cc(register_preceding_sibling ), reverse_sibling=P('reverse-sibling::')*Cc(register_reverse_sibling ), + last_match=P('last-match::')*Cc(register_last_match ), nodes=(V("nodefunction")*spaces*P("(")*V("nodeset")*P(")")+V("nodetest")*V("nodeset"))/register_nodes, expressions=expression/register_expression, letters=R("az")^1, @@ -11936,140 +11951,168 @@ lpath=function (pattern) end end xml.lpath=lpath -local profiled={} xml.profiled=profiled -local function profiled_apply(list,parsed,nofparsed,order) - local p=profiled[parsed.pattern] - if p then - p.tested=p.tested+1 - else - p={ tested=1,matched=0,finalized=0 } - profiled[parsed.pattern]=p - end - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - collected=apply_axis[pi.axis](collected) - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - elseif kind=="finalizer" then - collected=pi.finalizer(collected) - p.matched=p.matched+1 - p.finalized=p.finalized+1 - return collected - end - if not collected or #collected==0 then - local pn=i<nofparsed and parsed[nofparsed] - if pn and pn.kind=="finalizer" then - collected=pn.finalizer(collected) +do + local profiled={} + xml.profiled=profiled + local lastmatch=nil + local keepmatch=nil + if directives then + directives.register("xml.path.keeplastmatch",function(v) + keepmatch=v + lastmatch=nil + end) + end + apply_axis["last-match"]=function() + return lastmatch or {} + end + local function profiled_apply(list,parsed,nofparsed,order) + local p=profiled[parsed.pattern] + if p then + p.tested=p.tested+1 + else + p={ tested=1,matched=0,finalized=0 } + profiled[parsed.pattern]=p + end + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + collected=apply_axis[pi.axis](collected) + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + elseif kind=="finalizer" then + collected=pi.finalizer(collected) + p.matched=p.matched+1 p.finalized=p.finalized+1 return collected end - return nil + if not collected or #collected==0 then + local pn=i<nofparsed and parsed[nofparsed] + if pn and pn.kind=="finalizer" then + collected=pn.finalizer(collected) + p.finalized=p.finalized+1 + return collected + end + return nil + end end - end - if collected then - p.matched=p.matched+1 - end - return collected -end -local function traced_apply(list,parsed,nofparsed,order) - if trace_lparse then - lshow(parsed) - end - report_lpath("collecting: %s",parsed.pattern) - report_lpath("root tags : %s",tagstostring(list)) - report_lpath("order : %s",order or "unset") - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - collected=apply_axis[pi.axis](collected) - report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) - elseif kind=="finalizer" then - collected=pi.finalizer(collected) - report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") - return collected + if collected then + p.matched=p.matched+1 end - if not collected or #collected==0 then - local pn=i<nofparsed and parsed[nofparsed] - if pn and pn.kind=="finalizer" then - collected=pn.finalizer(collected) - report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "") + return collected + end + local function traced_apply(list,parsed,nofparsed,order) + if trace_lparse then + lshow(parsed) + end + report_lpath("collecting: %s",parsed.pattern) + report_lpath("root tags : %s",tagstostring(list)) + report_lpath("order : %s",order or "unset") + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + collected=apply_axis[pi.axis](collected) + report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) + elseif kind=="finalizer" then + collected=pi.finalizer(collected) + report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") return collected end - return nil - end - end - return collected -end -local function normal_apply(list,parsed,nofparsed,order) - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - local axis=pi.axis - if axis~="self" then - collected=apply_axis[axis](collected) + if not collected or #collected==0 then + local pn=i<nofparsed and parsed[nofparsed] + if pn and pn.kind=="finalizer" then + collected=pn.finalizer(collected) + report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "") + return collected + end + return nil end - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - elseif kind=="finalizer" then - return pi.finalizer(collected) end - if not collected or #collected==0 then - local pf=i<nofparsed and parsed[nofparsed].finalizer - if pf then - return pf(collected) + return collected + end + local function normal_apply(list,parsed,nofparsed,order) + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + local axis=pi.axis + if axis~="self" then + collected=apply_axis[axis](collected) + end + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + elseif kind=="finalizer" then + return pi.finalizer(collected) + end + if not collected or #collected==0 then + local pf=i<nofparsed and parsed[nofparsed].finalizer + if pf then + return pf(collected) + end + return nil end - return nil end + return collected end - return collected -end -local function applylpath(list,pattern) - if not list then - return - end - local parsed=cache[pattern] - if parsed then - lpathcalls=lpathcalls+1 - lpathcached=lpathcached+1 - elseif type(pattern)=="table" then - lpathcalls=lpathcalls+1 - parsed=pattern - else - parsed=lpath(pattern) or pattern - end - if not parsed then - return + local apply=normal_apply + if trackers then + trackers.register("xml.path,xml.parse,xml.profile",function() + if trace_lprofile then + apply=profiled_apply + elseif trace_lpath then + apply=traced_apply + else + apply=normal_apply + end + end) end - local nofparsed=#parsed - if nofparsed==0 then - return + function xml.applylpath(list,pattern) + if not list then + lastmatch=nil + return + end + local parsed=cache[pattern] + if parsed then + lpathcalls=lpathcalls+1 + lpathcached=lpathcached+1 + elseif type(pattern)=="table" then + lpathcalls=lpathcalls+1 + parsed=pattern + else + parsed=lpath(pattern) or pattern + end + if not parsed then + lastmatch=nil + return + end + local nofparsed=#parsed + if nofparsed==0 then + lastmatch=nil + return + end + local collected=apply({ list },parsed,nofparsed,list.mi) + lastmatch=keepmatch and collected or nil + return collected end - if not trace_lpath then - return normal_apply ({ list },parsed,nofparsed,list.mi) - elseif trace_lprofile then - return profiled_apply({ list },parsed,nofparsed,list.mi) - else - return traced_apply ({ list },parsed,nofparsed,list.mi) + function xml.lastmatch() + return lastmatch end end -xml.applylpath=applylpath +local applylpath=xml.applylpath function xml.filter(root,pattern) return applylpath(root,pattern) end @@ -18638,8 +18681,8 @@ end -- of closure -- used libraries : l-lua.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-mrg.lua util-tpl.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua -- skipped libraries : - --- original bytes : 791821 --- stripped bytes : 286453 +-- original bytes : 794469 +-- stripped bytes : 288135 -- end library merge diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun index 0167ac5e8..9ecd30560 100644 --- a/scripts/context/stubs/unix/mtxrun +++ b/scripts/context/stubs/unix/mtxrun @@ -11261,7 +11261,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true --- original size: 51229, stripped down to: 31529 +-- original size: 53877, stripped down to: 32495 if not modules then modules={} end modules ['lxml-lpt']={ version=1.001, @@ -11276,10 +11276,23 @@ local format,upper,lower,gmatch,gsub,find,rep=string.format,string.upper,string. local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local setmetatableindex=table.setmetatableindex local formatters=string.formatters -local trace_lpath=false if trackers then trackers.register("xml.path",function(v) trace_lpath=v end) end -local trace_lparse=false if trackers then trackers.register("xml.parse",function(v) trace_lparse=v end) end -local trace_lprofile=false if trackers then trackers.register("xml.profile",function(v) trace_lpath=v trace_lparse=v trace_lprofile=v end) end +local trace_lpath=false +local trace_lparse=false +local trace_lprofile=false local report_lpath=logs.reporter("xml","lpath") +if trackers then + trackers.register("xml.path",function(v) + trace_lpath=v + end) + trackers.register("xml.parse",function(v) + trace_lparse=v + end) + trackers.register("xml.profile",function(v) + trace_lpath=v + trace_lparse=v + trace_lprofile=v + end) +end local xml=xml local lpathcalls=0 function xml.lpathcalls () return lpathcalls end local lpathcached=0 function xml.lpathcached() return lpathcached end @@ -11716,6 +11729,7 @@ local template_f_y=[[ local template_f_n=[[ return xml.finalizers['%s']['%s'] ]] +local register_last_match={ kind="axis",axis="last-match" } local register_self={ kind="axis",axis="self" } local register_parent={ kind="axis",axis="parent" } local register_descendant={ kind="axis",axis="descendant" } @@ -11793,7 +11807,7 @@ local pathparser=Ct { "patterns", ), protocol=Cg(V("letters"),"protocol")*P("://")+Cg(Cc(nil),"protocol"), step=((V("shortcuts")+P("/")+V("axis"))*spaces*V("nodes")^0+V("error"))*spaces*V("expressions")^0*spaces*V("finalizer")^0, - axis=V("descendant")+V("child")+V("parent")+V("self")+V("root")+V("ancestor")+V("descendant_or_self")+V("following_sibling")+V("following")+V("reverse_sibling")+V("preceding_sibling")+V("preceding")+V("ancestor_or_self")+#(1-P(-1))*Cc(register_auto_child), + axis=V("last_match")+V("descendant")+V("child")+V("parent")+V("self")+V("root")+V("ancestor")+V("descendant_or_self")+V("following_sibling")+V("following")+V("reverse_sibling")+V("preceding_sibling")+V("preceding")+V("ancestor_or_self")+#(1-P(-1))*Cc(register_auto_child), special=special_1+special_2+special_3, initial=(P("/")*spaces*Cc(register_initial_child))^-1, error=(P(1)^1)/register_error, @@ -11819,6 +11833,7 @@ local pathparser=Ct { "patterns", preceding=P('preceding::')*Cc(register_preceding ), preceding_sibling=P('preceding-sibling::')*Cc(register_preceding_sibling ), reverse_sibling=P('reverse-sibling::')*Cc(register_reverse_sibling ), + last_match=P('last-match::')*Cc(register_last_match ), nodes=(V("nodefunction")*spaces*P("(")*V("nodeset")*P(")")+V("nodetest")*V("nodeset"))/register_nodes, expressions=expression/register_expression, letters=R("az")^1, @@ -11936,140 +11951,168 @@ lpath=function (pattern) end end xml.lpath=lpath -local profiled={} xml.profiled=profiled -local function profiled_apply(list,parsed,nofparsed,order) - local p=profiled[parsed.pattern] - if p then - p.tested=p.tested+1 - else - p={ tested=1,matched=0,finalized=0 } - profiled[parsed.pattern]=p - end - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - collected=apply_axis[pi.axis](collected) - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - elseif kind=="finalizer" then - collected=pi.finalizer(collected) - p.matched=p.matched+1 - p.finalized=p.finalized+1 - return collected - end - if not collected or #collected==0 then - local pn=i<nofparsed and parsed[nofparsed] - if pn and pn.kind=="finalizer" then - collected=pn.finalizer(collected) +do + local profiled={} + xml.profiled=profiled + local lastmatch=nil + local keepmatch=nil + if directives then + directives.register("xml.path.keeplastmatch",function(v) + keepmatch=v + lastmatch=nil + end) + end + apply_axis["last-match"]=function() + return lastmatch or {} + end + local function profiled_apply(list,parsed,nofparsed,order) + local p=profiled[parsed.pattern] + if p then + p.tested=p.tested+1 + else + p={ tested=1,matched=0,finalized=0 } + profiled[parsed.pattern]=p + end + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + collected=apply_axis[pi.axis](collected) + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + elseif kind=="finalizer" then + collected=pi.finalizer(collected) + p.matched=p.matched+1 p.finalized=p.finalized+1 return collected end - return nil + if not collected or #collected==0 then + local pn=i<nofparsed and parsed[nofparsed] + if pn and pn.kind=="finalizer" then + collected=pn.finalizer(collected) + p.finalized=p.finalized+1 + return collected + end + return nil + end end - end - if collected then - p.matched=p.matched+1 - end - return collected -end -local function traced_apply(list,parsed,nofparsed,order) - if trace_lparse then - lshow(parsed) - end - report_lpath("collecting: %s",parsed.pattern) - report_lpath("root tags : %s",tagstostring(list)) - report_lpath("order : %s",order or "unset") - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - collected=apply_axis[pi.axis](collected) - report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) - elseif kind=="finalizer" then - collected=pi.finalizer(collected) - report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") - return collected + if collected then + p.matched=p.matched+1 end - if not collected or #collected==0 then - local pn=i<nofparsed and parsed[nofparsed] - if pn and pn.kind=="finalizer" then - collected=pn.finalizer(collected) - report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "") + return collected + end + local function traced_apply(list,parsed,nofparsed,order) + if trace_lparse then + lshow(parsed) + end + report_lpath("collecting: %s",parsed.pattern) + report_lpath("root tags : %s",tagstostring(list)) + report_lpath("order : %s",order or "unset") + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + collected=apply_axis[pi.axis](collected) + report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) + elseif kind=="finalizer" then + collected=pi.finalizer(collected) + report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") return collected end - return nil - end - end - return collected -end -local function normal_apply(list,parsed,nofparsed,order) - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - local axis=pi.axis - if axis~="self" then - collected=apply_axis[axis](collected) + if not collected or #collected==0 then + local pn=i<nofparsed and parsed[nofparsed] + if pn and pn.kind=="finalizer" then + collected=pn.finalizer(collected) + report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "") + return collected + end + return nil end - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - elseif kind=="finalizer" then - return pi.finalizer(collected) end - if not collected or #collected==0 then - local pf=i<nofparsed and parsed[nofparsed].finalizer - if pf then - return pf(collected) + return collected + end + local function normal_apply(list,parsed,nofparsed,order) + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + local axis=pi.axis + if axis~="self" then + collected=apply_axis[axis](collected) + end + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + elseif kind=="finalizer" then + return pi.finalizer(collected) + end + if not collected or #collected==0 then + local pf=i<nofparsed and parsed[nofparsed].finalizer + if pf then + return pf(collected) + end + return nil end - return nil end + return collected end - return collected -end -local function applylpath(list,pattern) - if not list then - return - end - local parsed=cache[pattern] - if parsed then - lpathcalls=lpathcalls+1 - lpathcached=lpathcached+1 - elseif type(pattern)=="table" then - lpathcalls=lpathcalls+1 - parsed=pattern - else - parsed=lpath(pattern) or pattern - end - if not parsed then - return + local apply=normal_apply + if trackers then + trackers.register("xml.path,xml.parse,xml.profile",function() + if trace_lprofile then + apply=profiled_apply + elseif trace_lpath then + apply=traced_apply + else + apply=normal_apply + end + end) end - local nofparsed=#parsed - if nofparsed==0 then - return + function xml.applylpath(list,pattern) + if not list then + lastmatch=nil + return + end + local parsed=cache[pattern] + if parsed then + lpathcalls=lpathcalls+1 + lpathcached=lpathcached+1 + elseif type(pattern)=="table" then + lpathcalls=lpathcalls+1 + parsed=pattern + else + parsed=lpath(pattern) or pattern + end + if not parsed then + lastmatch=nil + return + end + local nofparsed=#parsed + if nofparsed==0 then + lastmatch=nil + return + end + local collected=apply({ list },parsed,nofparsed,list.mi) + lastmatch=keepmatch and collected or nil + return collected end - if not trace_lpath then - return normal_apply ({ list },parsed,nofparsed,list.mi) - elseif trace_lprofile then - return profiled_apply({ list },parsed,nofparsed,list.mi) - else - return traced_apply ({ list },parsed,nofparsed,list.mi) + function xml.lastmatch() + return lastmatch end end -xml.applylpath=applylpath +local applylpath=xml.applylpath function xml.filter(root,pattern) return applylpath(root,pattern) end @@ -18638,8 +18681,8 @@ end -- of closure -- used libraries : l-lua.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-mrg.lua util-tpl.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua -- skipped libraries : - --- original bytes : 791821 --- stripped bytes : 286453 +-- original bytes : 794469 +-- stripped bytes : 288135 -- end library merge diff --git a/scripts/context/stubs/win64/mtxrun.lua b/scripts/context/stubs/win64/mtxrun.lua index 0167ac5e8..9ecd30560 100644 --- a/scripts/context/stubs/win64/mtxrun.lua +++ b/scripts/context/stubs/win64/mtxrun.lua @@ -11261,7 +11261,7 @@ do -- create closure to overcome 200 locals limit package.loaded["lxml-lpt"] = package.loaded["lxml-lpt"] or true --- original size: 51229, stripped down to: 31529 +-- original size: 53877, stripped down to: 32495 if not modules then modules={} end modules ['lxml-lpt']={ version=1.001, @@ -11276,10 +11276,23 @@ local format,upper,lower,gmatch,gsub,find,rep=string.format,string.upper,string. local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local setmetatableindex=table.setmetatableindex local formatters=string.formatters -local trace_lpath=false if trackers then trackers.register("xml.path",function(v) trace_lpath=v end) end -local trace_lparse=false if trackers then trackers.register("xml.parse",function(v) trace_lparse=v end) end -local trace_lprofile=false if trackers then trackers.register("xml.profile",function(v) trace_lpath=v trace_lparse=v trace_lprofile=v end) end +local trace_lpath=false +local trace_lparse=false +local trace_lprofile=false local report_lpath=logs.reporter("xml","lpath") +if trackers then + trackers.register("xml.path",function(v) + trace_lpath=v + end) + trackers.register("xml.parse",function(v) + trace_lparse=v + end) + trackers.register("xml.profile",function(v) + trace_lpath=v + trace_lparse=v + trace_lprofile=v + end) +end local xml=xml local lpathcalls=0 function xml.lpathcalls () return lpathcalls end local lpathcached=0 function xml.lpathcached() return lpathcached end @@ -11716,6 +11729,7 @@ local template_f_y=[[ local template_f_n=[[ return xml.finalizers['%s']['%s'] ]] +local register_last_match={ kind="axis",axis="last-match" } local register_self={ kind="axis",axis="self" } local register_parent={ kind="axis",axis="parent" } local register_descendant={ kind="axis",axis="descendant" } @@ -11793,7 +11807,7 @@ local pathparser=Ct { "patterns", ), protocol=Cg(V("letters"),"protocol")*P("://")+Cg(Cc(nil),"protocol"), step=((V("shortcuts")+P("/")+V("axis"))*spaces*V("nodes")^0+V("error"))*spaces*V("expressions")^0*spaces*V("finalizer")^0, - axis=V("descendant")+V("child")+V("parent")+V("self")+V("root")+V("ancestor")+V("descendant_or_self")+V("following_sibling")+V("following")+V("reverse_sibling")+V("preceding_sibling")+V("preceding")+V("ancestor_or_self")+#(1-P(-1))*Cc(register_auto_child), + axis=V("last_match")+V("descendant")+V("child")+V("parent")+V("self")+V("root")+V("ancestor")+V("descendant_or_self")+V("following_sibling")+V("following")+V("reverse_sibling")+V("preceding_sibling")+V("preceding")+V("ancestor_or_self")+#(1-P(-1))*Cc(register_auto_child), special=special_1+special_2+special_3, initial=(P("/")*spaces*Cc(register_initial_child))^-1, error=(P(1)^1)/register_error, @@ -11819,6 +11833,7 @@ local pathparser=Ct { "patterns", preceding=P('preceding::')*Cc(register_preceding ), preceding_sibling=P('preceding-sibling::')*Cc(register_preceding_sibling ), reverse_sibling=P('reverse-sibling::')*Cc(register_reverse_sibling ), + last_match=P('last-match::')*Cc(register_last_match ), nodes=(V("nodefunction")*spaces*P("(")*V("nodeset")*P(")")+V("nodetest")*V("nodeset"))/register_nodes, expressions=expression/register_expression, letters=R("az")^1, @@ -11936,140 +11951,168 @@ lpath=function (pattern) end end xml.lpath=lpath -local profiled={} xml.profiled=profiled -local function profiled_apply(list,parsed,nofparsed,order) - local p=profiled[parsed.pattern] - if p then - p.tested=p.tested+1 - else - p={ tested=1,matched=0,finalized=0 } - profiled[parsed.pattern]=p - end - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - collected=apply_axis[pi.axis](collected) - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - elseif kind=="finalizer" then - collected=pi.finalizer(collected) - p.matched=p.matched+1 - p.finalized=p.finalized+1 - return collected - end - if not collected or #collected==0 then - local pn=i<nofparsed and parsed[nofparsed] - if pn and pn.kind=="finalizer" then - collected=pn.finalizer(collected) +do + local profiled={} + xml.profiled=profiled + local lastmatch=nil + local keepmatch=nil + if directives then + directives.register("xml.path.keeplastmatch",function(v) + keepmatch=v + lastmatch=nil + end) + end + apply_axis["last-match"]=function() + return lastmatch or {} + end + local function profiled_apply(list,parsed,nofparsed,order) + local p=profiled[parsed.pattern] + if p then + p.tested=p.tested+1 + else + p={ tested=1,matched=0,finalized=0 } + profiled[parsed.pattern]=p + end + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + collected=apply_axis[pi.axis](collected) + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + elseif kind=="finalizer" then + collected=pi.finalizer(collected) + p.matched=p.matched+1 p.finalized=p.finalized+1 return collected end - return nil + if not collected or #collected==0 then + local pn=i<nofparsed and parsed[nofparsed] + if pn and pn.kind=="finalizer" then + collected=pn.finalizer(collected) + p.finalized=p.finalized+1 + return collected + end + return nil + end end - end - if collected then - p.matched=p.matched+1 - end - return collected -end -local function traced_apply(list,parsed,nofparsed,order) - if trace_lparse then - lshow(parsed) - end - report_lpath("collecting: %s",parsed.pattern) - report_lpath("root tags : %s",tagstostring(list)) - report_lpath("order : %s",order or "unset") - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - collected=apply_axis[pi.axis](collected) - report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) - elseif kind=="finalizer" then - collected=pi.finalizer(collected) - report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") - return collected + if collected then + p.matched=p.matched+1 end - if not collected or #collected==0 then - local pn=i<nofparsed and parsed[nofparsed] - if pn and pn.kind=="finalizer" then - collected=pn.finalizer(collected) - report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "") + return collected + end + local function traced_apply(list,parsed,nofparsed,order) + if trace_lparse then + lshow(parsed) + end + report_lpath("collecting: %s",parsed.pattern) + report_lpath("root tags : %s",tagstostring(list)) + report_lpath("order : %s",order or "unset") + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + collected=apply_axis[pi.axis](collected) + report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) + elseif kind=="finalizer" then + collected=pi.finalizer(collected) + report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") return collected end - return nil - end - end - return collected -end -local function normal_apply(list,parsed,nofparsed,order) - local collected=list - for i=1,nofparsed do - local pi=parsed[i] - local kind=pi.kind - if kind=="axis" then - local axis=pi.axis - if axis~="self" then - collected=apply_axis[axis](collected) + if not collected or #collected==0 then + local pn=i<nofparsed and parsed[nofparsed] + if pn and pn.kind=="finalizer" then + collected=pn.finalizer(collected) + report_lpath("% 10i : fi : %s : %s(%s)",(type(collected)=="table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "") + return collected + end + return nil end - elseif kind=="nodes" then - collected=apply_nodes(collected,pi.nodetest,pi.nodes) - elseif kind=="expression" then - collected=apply_expression(collected,pi.evaluator,order) - elseif kind=="finalizer" then - return pi.finalizer(collected) end - if not collected or #collected==0 then - local pf=i<nofparsed and parsed[nofparsed].finalizer - if pf then - return pf(collected) + return collected + end + local function normal_apply(list,parsed,nofparsed,order) + local collected=list + for i=1,nofparsed do + local pi=parsed[i] + local kind=pi.kind + if kind=="axis" then + local axis=pi.axis + if axis~="self" then + collected=apply_axis[axis](collected) + end + elseif kind=="nodes" then + collected=apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind=="expression" then + collected=apply_expression(collected,pi.evaluator,order) + elseif kind=="finalizer" then + return pi.finalizer(collected) + end + if not collected or #collected==0 then + local pf=i<nofparsed and parsed[nofparsed].finalizer + if pf then + return pf(collected) + end + return nil end - return nil end + return collected end - return collected -end -local function applylpath(list,pattern) - if not list then - return - end - local parsed=cache[pattern] - if parsed then - lpathcalls=lpathcalls+1 - lpathcached=lpathcached+1 - elseif type(pattern)=="table" then - lpathcalls=lpathcalls+1 - parsed=pattern - else - parsed=lpath(pattern) or pattern - end - if not parsed then - return + local apply=normal_apply + if trackers then + trackers.register("xml.path,xml.parse,xml.profile",function() + if trace_lprofile then + apply=profiled_apply + elseif trace_lpath then + apply=traced_apply + else + apply=normal_apply + end + end) end - local nofparsed=#parsed - if nofparsed==0 then - return + function xml.applylpath(list,pattern) + if not list then + lastmatch=nil + return + end + local parsed=cache[pattern] + if parsed then + lpathcalls=lpathcalls+1 + lpathcached=lpathcached+1 + elseif type(pattern)=="table" then + lpathcalls=lpathcalls+1 + parsed=pattern + else + parsed=lpath(pattern) or pattern + end + if not parsed then + lastmatch=nil + return + end + local nofparsed=#parsed + if nofparsed==0 then + lastmatch=nil + return + end + local collected=apply({ list },parsed,nofparsed,list.mi) + lastmatch=keepmatch and collected or nil + return collected end - if not trace_lpath then - return normal_apply ({ list },parsed,nofparsed,list.mi) - elseif trace_lprofile then - return profiled_apply({ list },parsed,nofparsed,list.mi) - else - return traced_apply ({ list },parsed,nofparsed,list.mi) + function xml.lastmatch() + return lastmatch end end -xml.applylpath=applylpath +local applylpath=xml.applylpath function xml.filter(root,pattern) return applylpath(root,pattern) end @@ -18638,8 +18681,8 @@ end -- of closure -- used libraries : l-lua.lua l-package.lua l-lpeg.lua l-function.lua l-string.lua l-table.lua l-io.lua l-number.lua l-set.lua l-os.lua l-file.lua l-gzip.lua l-md5.lua l-url.lua l-dir.lua l-boolean.lua l-unicode.lua l-math.lua util-str.lua util-tab.lua util-fil.lua util-sac.lua util-sto.lua util-prs.lua util-fmt.lua trac-set.lua trac-log.lua trac-inf.lua trac-pro.lua util-lua.lua util-deb.lua util-mrg.lua util-tpl.lua util-env.lua luat-env.lua lxml-tab.lua lxml-lpt.lua lxml-mis.lua lxml-aux.lua lxml-xml.lua trac-xml.lua data-ini.lua data-exp.lua data-env.lua data-tmp.lua data-met.lua data-res.lua data-pre.lua data-inp.lua data-out.lua data-fil.lua data-con.lua data-use.lua data-zip.lua data-tre.lua data-sch.lua data-lua.lua data-aux.lua data-tmf.lua data-lst.lua util-lib.lua luat-sta.lua luat-fmt.lua -- skipped libraries : - --- original bytes : 791821 --- stripped bytes : 286453 +-- original bytes : 794469 +-- stripped bytes : 288135 -- end library merge diff --git a/tex/context/base/context-version.pdf b/tex/context/base/context-version.pdf Binary files differindex 053e8e353..04f9a3448 100644 --- a/tex/context/base/context-version.pdf +++ b/tex/context/base/context-version.pdf diff --git a/tex/context/base/mkiv/cldf-ini.lua b/tex/context/base/mkiv/cldf-ini.lua index f4819b11a..c6cc39abc 100644 --- a/tex/context/base/mkiv/cldf-ini.lua +++ b/tex/context/base/mkiv/cldf-ini.lua @@ -706,7 +706,7 @@ local containseol = patterns.containseol local writer -if luafunctions then +-- if luafunctions then writer = function (parent,command,first,...) -- already optimized before call local t = { first, ... } @@ -772,6 +772,7 @@ if luafunctions then local tj = ti[1] if type(tj) == "function" then flush(currentcatcodes,"[\\cldl",storefunction(tj),"]") + -- flush(currentcatcodes,"[",storefunction(tj),"]") else flush(currentcatcodes,"[",tj,"]") end @@ -782,8 +783,10 @@ if luafunctions then if type(tj) == "function" then if j == tn then flush(currentcatcodes,"\\cldl",storefunction(tj),"]") + -- flush(currentcatcodes,"",storefunction(tj),"]") else flush(currentcatcodes,"\\cldl",storefunction(tj),",") + -- flush(currentcatcodes,"",storefunction(tj),",") end else if j == tn then @@ -796,6 +799,7 @@ if luafunctions then end elseif typ == "function" then flush(currentcatcodes,"{\\cldl ",storefunction(ti),"}") -- todo: ctx|prt|texcatcodes + -- flush(currentcatcodes,"{",storefunction(ti),"}") -- todo: ctx|prt|texcatcodes elseif typ == "boolean" then if ti then flushdirect(currentcatcodes,"\r") @@ -806,109 +810,110 @@ if luafunctions then report_context("coroutines not supported as we cannot yield across boundaries") elseif isnode(ti) then -- slow flush(currentcatcodes,"{\\cldl",storenode(ti),"}") + -- flush(currentcatcodes,"{",storenode(ti),"}") else report_context("error: %a gets a weird argument %a",command,ti) end end end -else - - writer = function (parent,command,first,...) -- already optimized before call - local t = { first, ... } - flush(currentcatcodes,command) -- todo: ctx|prt|texcatcodes - local direct = false - for i=1,#t do - local ti = t[i] - local typ = type(ti) - if direct then - if typ == "string" or typ == "number" then - flush(currentcatcodes,ti) - else -- node.write - report_context("error: invalid use of direct in %a, only strings and numbers can be flushed directly, not %a",command,typ) - end - direct = false - elseif ti == nil then - -- nothing - elseif ti == "" then - flush(currentcatcodes,"{}") - elseif typ == "string" then - -- is processelines seen ? - if processlines and lpegmatch(containseol,ti) then - flush(currentcatcodes,"{") - local flushlines = parent.__flushlines or flushlines - flushlines(ti) - flush(currentcatcodes,"}") - elseif currentcatcodes == contentcatcodes then - flush(currentcatcodes,"{",ti,"}") - else - flush(currentcatcodes,"{") - flush(contentcatcodes,ti) - flush(currentcatcodes,"}") - end - elseif typ == "number" then - -- numbers never have funny catcodes - flush(currentcatcodes,"{",ti,"}") - elseif typ == "table" then - local tn = #ti - if tn == 0 then - local done = false - for k, v in next, ti do - if done then - if v == "" then - flush(currentcatcodes,",",k,'=') - else - flush(currentcatcodes,",",k,"={",v,"}") - end - else - if v == "" then - flush(currentcatcodes,"[",k,"=") - else - flush(currentcatcodes,"[",k,"={",v,"}") - end - done = true - end - end - if done then - flush(currentcatcodes,"]") - else - flush(currentcatcodes,"[]") - end - elseif tn == 1 then -- some 20% faster than the next loop - local tj = ti[1] - if type(tj) == "function" then - flush(currentcatcodes,"[\\cldf{",storefunction(tj),"}]") - else - flush(currentcatcodes,"[",tj,"]") - end - else -- is concat really faster than flushes here? probably needed anyway (print artifacts) - for j=1,tn do - local tj = ti[j] - if type(tj) == "function" then - ti[j] = "\\cldf{" .. storefunction(tj) .. "}" - end - end - flush(currentcatcodes,"[",concat(ti,","),"]") - end - elseif typ == "function" then - flush(currentcatcodes,"{\\cldf{",storefunction(ti),"}}") -- todo: ctx|prt|texcatcodes - elseif typ == "boolean" then - if ti then - flushdirect(currentcatcodes,"\r") - else - direct = true - end - elseif typ == "thread" then - report_context("coroutines not supported as we cannot yield across boundaries") - elseif isnode(ti) then -- slow - flush(currentcatcodes,"{\\cldn{",storenode(ti),"}}") - else - report_context("error: %a gets a weird argument %a",command,ti) - end - end - end - -end +-- else +-- +-- writer = function (parent,command,first,...) -- already optimized before call +-- local t = { first, ... } +-- flush(currentcatcodes,command) -- todo: ctx|prt|texcatcodes +-- local direct = false +-- for i=1,#t do +-- local ti = t[i] +-- local typ = type(ti) +-- if direct then +-- if typ == "string" or typ == "number" then +-- flush(currentcatcodes,ti) +-- else -- node.write +-- report_context("error: invalid use of direct in %a, only strings and numbers can be flushed directly, not %a",command,typ) +-- end +-- direct = false +-- elseif ti == nil then +-- -- nothing +-- elseif ti == "" then +-- flush(currentcatcodes,"{}") +-- elseif typ == "string" then +-- -- is processelines seen ? +-- if processlines and lpegmatch(containseol,ti) then +-- flush(currentcatcodes,"{") +-- local flushlines = parent.__flushlines or flushlines +-- flushlines(ti) +-- flush(currentcatcodes,"}") +-- elseif currentcatcodes == contentcatcodes then +-- flush(currentcatcodes,"{",ti,"}") +-- else +-- flush(currentcatcodes,"{") +-- flush(contentcatcodes,ti) +-- flush(currentcatcodes,"}") +-- end +-- elseif typ == "number" then +-- -- numbers never have funny catcodes +-- flush(currentcatcodes,"{",ti,"}") +-- elseif typ == "table" then +-- local tn = #ti +-- if tn == 0 then +-- local done = false +-- for k, v in next, ti do +-- if done then +-- if v == "" then +-- flush(currentcatcodes,",",k,'=') +-- else +-- flush(currentcatcodes,",",k,"={",v,"}") +-- end +-- else +-- if v == "" then +-- flush(currentcatcodes,"[",k,"=") +-- else +-- flush(currentcatcodes,"[",k,"={",v,"}") +-- end +-- done = true +-- end +-- end +-- if done then +-- flush(currentcatcodes,"]") +-- else +-- flush(currentcatcodes,"[]") +-- end +-- elseif tn == 1 then -- some 20% faster than the next loop +-- local tj = ti[1] +-- if type(tj) == "function" then +-- flush(currentcatcodes,"[\\cldf{",storefunction(tj),"}]") +-- else +-- flush(currentcatcodes,"[",tj,"]") +-- end +-- else -- is concat really faster than flushes here? probably needed anyway (print artifacts) +-- for j=1,tn do +-- local tj = ti[j] +-- if type(tj) == "function" then +-- ti[j] = "\\cldf{" .. storefunction(tj) .. "}" +-- end +-- end +-- flush(currentcatcodes,"[",concat(ti,","),"]") +-- end +-- elseif typ == "function" then +-- flush(currentcatcodes,"{\\cldf{",storefunction(ti),"}}") -- todo: ctx|prt|texcatcodes +-- elseif typ == "boolean" then +-- if ti then +-- flushdirect(currentcatcodes,"\r") +-- else +-- direct = true +-- end +-- elseif typ == "thread" then +-- report_context("coroutines not supported as we cannot yield across boundaries") +-- elseif isnode(ti) then -- slow +-- flush(currentcatcodes,"{\\cldn{",storenode(ti),"}}") +-- else +-- report_context("error: %a gets a weird argument %a",command,ti) +-- end +-- end +-- end +-- +-- end local generics = { } context.generics = generics local indexer = nil @@ -1022,7 +1027,7 @@ end local caller -if luafunctions then +-- if luafunctions then caller = function(parent,f,a,...) if not parent then @@ -1053,6 +1058,7 @@ if luafunctions then elseif typ == "function" then -- ignored: a ... flush(currentcatcodes,"{\\cldl",storefunction(f),"}") -- todo: ctx|prt|texcatcodes + -- flush(currentcatcodes,"{",storefunction(f),"}") -- todo: ctx|prt|texcatcodes elseif typ == "boolean" then if f then if a ~= nil then @@ -1074,6 +1080,7 @@ if luafunctions then elseif isnode(f) then -- slow -- writenode(f) flush(currentcatcodes,"\\cldl",storenode(f)," ") + -- flush(currentcatcodes,"",storenode(f)," ") else report_context("error: %a gets a weird argument %a","context",f) end @@ -1082,71 +1089,72 @@ if luafunctions then function context.flushnode(n) flush(currentcatcodes,"\\cldl",storenode(n)," ") + -- flush(currentcatcodes,"",storenode(n)," ") end -else - - caller = function(parent,f,a,...) - if not parent then - -- so we don't need to test in the calling (slower but often no issue) - elseif f ~= nil then - local typ = type(f) - if typ == "string" then - if f == "" then - -- new, can save a bit sometimes - -- if trace_context then - -- report_context("empty argument to context()") - -- end - elseif a then - flush(contentcatcodes,formatters[f](a,...)) -- was currentcatcodes - -- flush(contentcatcodes,splitformatters[f](a,...)) -- was currentcatcodes - elseif processlines and lpegmatch(containseol,f) then - local flushlines = parent.__flushlines or flushlines - flushlines(f) - else - flush(contentcatcodes,f) - end - elseif typ == "number" then - if a then - flush(currentcatcodes,f,a,...) - else - flush(currentcatcodes,f) - end - elseif typ == "function" then - -- ignored: a ... - flush(currentcatcodes,"{\\cldf{",storefunction(f),"}}") -- todo: ctx|prt|texcatcodes - elseif typ == "boolean" then - if f then - if a ~= nil then - local flushlines = parent.__flushlines or flushlines - flushlines(a) - else - flushdirect(currentcatcodes,"\n") -- no \r, else issues with \startlines ... use context.par() otherwise - end - else - if a ~= nil then - -- no command, same as context(a,...) - writer(parent,"",a,...) - else - -- ignored - end - end - elseif typ == "thread" then - report_context("coroutines not supported as we cannot yield across boundaries") - elseif isnode(f) then -- slow - -- writenode(f) - flush(currentcatcodes,"\\cldn{",storenode(f),"}") - else - report_context("error: %a gets a weird argument %a","context",f) - end - end - end - - function context.flushnode(n) - flush(currentcatcodes,"\\cldn{",storenode(n),"}") - end - -end +-- else +-- +-- caller = function(parent,f,a,...) +-- if not parent then +-- -- so we don't need to test in the calling (slower but often no issue) +-- elseif f ~= nil then +-- local typ = type(f) +-- if typ == "string" then +-- if f == "" then +-- -- new, can save a bit sometimes +-- -- if trace_context then +-- -- report_context("empty argument to context()") +-- -- end +-- elseif a then +-- flush(contentcatcodes,formatters[f](a,...)) -- was currentcatcodes +-- -- flush(contentcatcodes,splitformatters[f](a,...)) -- was currentcatcodes +-- elseif processlines and lpegmatch(containseol,f) then +-- local flushlines = parent.__flushlines or flushlines +-- flushlines(f) +-- else +-- flush(contentcatcodes,f) +-- end +-- elseif typ == "number" then +-- if a then +-- flush(currentcatcodes,f,a,...) +-- else +-- flush(currentcatcodes,f) +-- end +-- elseif typ == "function" then +-- -- ignored: a ... +-- flush(currentcatcodes,"{\\cldf{",storefunction(f),"}}") -- todo: ctx|prt|texcatcodes +-- elseif typ == "boolean" then +-- if f then +-- if a ~= nil then +-- local flushlines = parent.__flushlines or flushlines +-- flushlines(a) +-- else +-- flushdirect(currentcatcodes,"\n") -- no \r, else issues with \startlines ... use context.par() otherwise +-- end +-- else +-- if a ~= nil then +-- -- no command, same as context(a,...) +-- writer(parent,"",a,...) +-- else +-- -- ignored +-- end +-- end +-- elseif typ == "thread" then +-- report_context("coroutines not supported as we cannot yield across boundaries") +-- elseif isnode(f) then -- slow +-- -- writenode(f) +-- flush(currentcatcodes,"\\cldn{",storenode(f),"}") +-- else +-- report_context("error: %a gets a weird argument %a","context",f) +-- end +-- end +-- end +-- +-- function context.flushnode(n) +-- flush(currentcatcodes,"\\cldn{",storenode(n),"}") +-- end +-- +-- end local defaultcaller = caller @@ -1211,6 +1219,8 @@ local currenttrace = nil local nofwriters = 0 local nofflushes = 0 +local tracingpermitted = true + local visualizer = lpeg.replacer { { "\n","<<newline>>" }, { "\r","<<par>>" }, @@ -1231,15 +1241,24 @@ local tracedwriter = function(parent,...) -- also catcodes ? local savedflush = flush local savedflushdirect = flushdirect -- unlikely to be used here local t, n = { "w : - : " }, 1 - local traced = function(normal,catcodes,...) -- todo: check for catcodes + local traced = function(catcodes,...) -- todo: check for catcodes local s = concat({...}) s = lpegmatch(visualizer,s) n = n + 1 t[n] = s - normal(catcodes,...) end - flush = function(...) traced(normalflush, ...) end - flushdirect = function(...) traced(normalflushdirect,...) end + flush = function(...) + normalflush(...) + if tracingpermitted then + traced(...) + end + end + flushdirect = function(...) + normalflushdirect(...) + if tracingpermitted then + traced(...) + end + end normalwriter(parent,...) flush = savedflush flushdirect = savedflushdirect @@ -1248,11 +1267,9 @@ end -- we could reuse collapsed -local traced = function(normal,one,two,...) - nofflushes = nofflushes + 1 +local traced = function(one,two,...) if two then -- only catcodes if 'one' is number - normal(one,two,...) local catcodes = type(one) == "number" and one local arguments = catcodes and { two, ... } or { one, two, ... } local collapsed, c = { formatters["f : %s : "](catcodes or '-') }, 1 @@ -1271,7 +1288,6 @@ local traced = function(normal,one,two,...) currenttrace(concat(collapsed)) else -- no catcodes - normal(one) local argtype = type(one) if argtype == "string" then currenttrace(formatters["f : - : %s"](lpegmatch(visualizer,one))) @@ -1283,8 +1299,29 @@ local traced = function(normal,one,two,...) end end -local tracedflush = function(...) traced(normalflush, ...) end -local tracedflushdirect = function(...) traced(normalflushdirect,...) end +local tracedflush = function(one,two,...) + nofflushes = nofflushes + 1 + if two then + normalflush(one,two,...) + else + normalflush(one) + end + if tracingpermitted then + traced(...) + end +end + +local tracedflushdirect = function(one,two,...) + nofflushes = nofflushes + 1 + if two then + normalflushdirect(one,two,...) + else + normalflushdirect(one) + end + if tracingpermitted then + traced(...) + end +end local function pushlogger(trace) trace = trace or report_context @@ -1404,8 +1441,10 @@ do -- end local collectdirect = collect + local permitted = true - -- doesn't work well with tracing do we need to avoid that then + -- doesn't work well with tracing do we need to avoid that when + -- collecting stuff function context.startcollecting() if level == 0 then @@ -1414,6 +1453,7 @@ do -- flush = collect flushdirect = collectdirect + permitted = tracingpermitted -- context.__flush = flush context.__flushdirect = flushdirect @@ -1424,8 +1464,9 @@ do function context.stopcollecting() level = level - 1 if level < 1 then - flush = normalflush - flushdirect = normalflushdirect + flush = normalflush + flushdirect = normalflushdirect + tracingpermitted = permitted -- context.__flush = flush context.__flushdirect = flushdirect @@ -1682,8 +1723,8 @@ local function caller(parent,f,a,...) end elseif typ == "function" then -- ignored: a ... --- flush(currentcatcodes,mpdrawing,"{\\cldf{",store_(f),"}}") flush(currentcatcodes,mpdrawing,"{\\cldl",store_(f),"}") + -- flush(currentcatcodes,mpdrawing,"{",store_(f),"}") elseif typ == "boolean" then -- ignored: a ... if f then diff --git a/tex/context/base/mkiv/cldf-ini.mkiv b/tex/context/base/mkiv/cldf-ini.mkiv index 12ada1383..a985f204e 100644 --- a/tex/context/base/mkiv/cldf-ini.mkiv +++ b/tex/context/base/mkiv/cldf-ini.mkiv @@ -33,15 +33,17 @@ % \zerocount removes as it's the default -\def\cldf#1{\directlua{_cldf_(#1)}} % global (functions) -\def\cldn#1{\directlua{_cldn_(#1)}} % global (nodes) - -\ifx\luafunction\undefined - \def\luafunction#1{\directlua{_cldl_(#1)}} -\fi +% \def\cldf#1{\directlua{_cldf_(#1)}} % global (functions) +% \def\cldn#1{\directlua{_cldn_(#1)}} % global (nodes) +% +% \ifx\luafunction\undefined +% \def\luafunction#1{\directlua{_cldl_(#1)}} +% \fi \let\cldl\luafunction +% \catcode`=\activecatcode \let\luafunction % saves 10% on the call + % \catcodetable\ctxcatcodes \catcode`^=\superscriptcatcode\catcode1=\activecatcode \global\let^^A=\cldf % \catcodetable\ctxcatcodes \catcode`^=\superscriptcatcode\catcode2=\activecatcode \global\let^^B=\cldn diff --git a/tex/context/base/mkiv/cont-new.mkiv b/tex/context/base/mkiv/cont-new.mkiv index 72b9eeeb2..e9dddbb0f 100644 --- a/tex/context/base/mkiv/cont-new.mkiv +++ b/tex/context/base/mkiv/cont-new.mkiv @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2016.01.28 22:35} +\newcontextversion{2016.01.29 19:00} %D This file is loaded at runtime, thereby providing an excellent place for %D hacks, patches, extensions and new features. diff --git a/tex/context/base/mkiv/context.mkiv b/tex/context/base/mkiv/context.mkiv index 384a2de9b..727363db8 100644 --- a/tex/context/base/mkiv/context.mkiv +++ b/tex/context/base/mkiv/context.mkiv @@ -39,7 +39,7 @@ %D up and the dependencies are more consistent. \edef\contextformat {\jobname} -\edef\contextversion{2016.01.28 22:35} +\edef\contextversion{2016.01.29 19:00} \edef\contextkind {beta} %D For those who want to use this: diff --git a/tex/context/base/mkiv/lxml-ini.lua b/tex/context/base/mkiv/lxml-ini.lua index f17f28a7c..c44c61644 100644 --- a/tex/context/base/mkiv/lxml-ini.lua +++ b/tex/context/base/mkiv/lxml-ini.lua @@ -40,6 +40,7 @@ implement { name = "xmldoifelseselfempty", actions = lxml.doifelseempty, arg --------- { name = "xmlcontent", actions = lxml.content, arguments = "string" } --------- { name = "xmlflushstripped", actions = lxml.strip, arguments = { "string", true } } implement { name = "xmlall", actions = lxml.all, arguments = { "string", "string" } } +implement { name = "xmllastmatch", actions = lxml.lastmatch } implement { name = "xmlatt", actions = lxml.att, arguments = { "string", "string" } } implement { name = "xmllastatt", actions = lxml.lastatt } implement { name = "xmlattdef", actions = lxml.att, arguments = { "string", "string", "string" } } diff --git a/tex/context/base/mkiv/lxml-ini.mkiv b/tex/context/base/mkiv/lxml-ini.mkiv index 3d3ef8733..8175d21e6 100644 --- a/tex/context/base/mkiv/lxml-ini.mkiv +++ b/tex/context/base/mkiv/lxml-ini.mkiv @@ -45,6 +45,7 @@ % aliased \let\xmlall \clf_xmlall +\let\xmllastmatch \clf_xmllastmatch \let\xmlatt \clf_xmlatt \let\xmllastatt \clf_xmllastatt \let\xmlattdef \clf_xmlattdef diff --git a/tex/context/base/mkiv/lxml-lpt.lua b/tex/context/base/mkiv/lxml-lpt.lua index 62250be00..b722fc318 100644 --- a/tex/context/base/mkiv/lxml-lpt.lua +++ b/tex/context/base/mkiv/lxml-lpt.lua @@ -43,11 +43,24 @@ a/b/c/text() a/b/c/text(1) a/b/c/text(-1) a/b/c/text(n) </typing> --ldx]]-- -local trace_lpath = false if trackers then trackers.register("xml.path", function(v) trace_lpath = v end) end -local trace_lparse = false if trackers then trackers.register("xml.parse", function(v) trace_lparse = v end) end -local trace_lprofile = false if trackers then trackers.register("xml.profile", function(v) trace_lpath = v trace_lparse = v trace_lprofile = v end) end - -local report_lpath = logs.reporter("xml","lpath") +local trace_lpath = false +local trace_lparse = false +local trace_lprofile = false +local report_lpath = logs.reporter("xml","lpath") + +if trackers then + trackers.register("xml.path", function(v) + trace_lpath = v + end) + trackers.register("xml.parse", function(v) + trace_lparse = v + end) + trackers.register("xml.profile", function(v) + trace_lpath = v + trace_lparse = v + trace_lprofile = v + end) +end --[[ldx-- <p>We've now arrived at an interesting part: accessing the tree using a subset @@ -659,6 +672,7 @@ local template_f_n = [[ -- +local register_last_match = { kind = "axis", axis = "last-match" } -- , apply = apply_axis["self"] } local register_self = { kind = "axis", axis = "self" } -- , apply = apply_axis["self"] } local register_parent = { kind = "axis", axis = "parent" } -- , apply = apply_axis["parent"] } local register_descendant = { kind = "axis", axis = "descendant" } -- , apply = apply_axis["descendant"] } @@ -759,21 +773,41 @@ local pathparser = Ct { "patterns", -- can be made a bit faster by moving some p protocol = Cg(V("letters"),"protocol") * P("://") + Cg(Cc(nil),"protocol"), -- the / is needed for // as descendant or self is somewhat special + -- -- step = (V("shortcuts") + V("axis") * spaces * V("nodes")^0 + V("error")) * spaces * V("expressions")^0 * spaces * V("finalizer")^0, step = ((V("shortcuts") + P("/") + V("axis")) * spaces * V("nodes")^0 + V("error")) * spaces * V("expressions")^0 * spaces * V("finalizer")^0, - axis = V("descendant") + V("child") + V("parent") + V("self") + V("root") + V("ancestor") + - V("descendant_or_self") + V("following_sibling") + V("following") + - V("reverse_sibling") + V("preceding_sibling") + V("preceding") + V("ancestor_or_self") + - #(1-P(-1)) * Cc(register_auto_child), - - special = special_1 + special_2 + special_3, + axis = V("last_match") + + V("descendant") + + V("child") + + V("parent") + + V("self") + + V("root") + + V("ancestor") + + V("descendant_or_self") + + V("following_sibling") + + V("following") + + V("reverse_sibling") + + V("preceding_sibling") + + V("preceding") + + V("ancestor_or_self") + + #(1-P(-1)) * Cc(register_auto_child), + + special = special_1 + + special_2 + + special_3, initial = (P("/") * spaces * Cc(register_initial_child))^-1, error = (P(1)^1) / register_error, - shortcuts_a = V("s_descendant_or_self") + V("s_descendant") + V("s_child") + V("s_parent") + V("s_self") + V("s_root") + V("s_ancestor"), + shortcuts_a = V("s_descendant_or_self") + + V("s_descendant") + + V("s_child") + + V("s_parent") + + V("s_self") + + V("s_root") + + V("s_ancestor"), shortcuts = V("shortcuts_a") * (spaces * "/" * spaces * V("shortcuts_a"))^0, @@ -785,6 +819,8 @@ local pathparser = Ct { "patterns", -- can be made a bit faster by moving some p s_root = P("^^") * Cc(register_root ), s_ancestor = P("^") * Cc(register_ancestor ), + -- we can speed this up when needed but we cache anyway so ... + descendant = P("descendant::") * Cc(register_descendant ), child = P("child::") * Cc(register_child ), parent = P("parent::") * Cc(register_parent ), @@ -800,6 +836,7 @@ local pathparser = Ct { "patterns", -- can be made a bit faster by moving some p preceding = P('preceding::') * Cc(register_preceding ), preceding_sibling = P('preceding-sibling::') * Cc(register_preceding_sibling ), reverse_sibling = P('reverse-sibling::') * Cc(register_reverse_sibling ), + last_match = P('last-match::') * Cc(register_last_match ), nodes = (V("nodefunction") * spaces * P("(") * V("nodeset") * P(")") + V("nodetest") * V("nodeset")) / register_nodes, @@ -953,146 +990,194 @@ xml.lpath = lpath -- can be cases that a finalizer returns (or does) something in case -- there is no match; an example of this is count() -local profiled = { } xml.profiled = profiled +do -local function profiled_apply(list,parsed,nofparsed,order) - local p = profiled[parsed.pattern] - if p then - p.tested = p.tested + 1 - else - p = { tested = 1, matched = 0, finalized = 0 } - profiled[parsed.pattern] = p + local profiled = { } + xml.profiled = profiled + local lastmatch = nil -- we remember the last one .. drawback: no collection till new collect + local keepmatch = nil -- we remember the last one .. drawback: no collection till new collect + + if directives then + directives.register("xml.path.keeplastmatch",function(v) + keepmatch = v + lastmatch = nil + end) end - local collected = list - for i=1,nofparsed do - local pi = parsed[i] - local kind = pi.kind - if kind == "axis" then - collected = apply_axis[pi.axis](collected) - elseif kind == "nodes" then - collected = apply_nodes(collected,pi.nodetest,pi.nodes) - elseif kind == "expression" then - collected = apply_expression(collected,pi.evaluator,order) - elseif kind == "finalizer" then - collected = pi.finalizer(collected) -- no check on # here - p.matched = p.matched + 1 - p.finalized = p.finalized + 1 - return collected + + apply_axis["last-match"] = function() + return lastmatch or { } + end + + local function profiled_apply(list,parsed,nofparsed,order) + local p = profiled[parsed.pattern] + if p then + p.tested = p.tested + 1 + else + p = { tested = 1, matched = 0, finalized = 0 } + profiled[parsed.pattern] = p end - if not collected or #collected == 0 then - local pn = i < nofparsed and parsed[nofparsed] - if pn and pn.kind == "finalizer" then - collected = pn.finalizer(collected) + local collected = list + for i=1,nofparsed do + local pi = parsed[i] + local kind = pi.kind + if kind == "axis" then + collected = apply_axis[pi.axis](collected) + elseif kind == "nodes" then + collected = apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind == "expression" then + collected = apply_expression(collected,pi.evaluator,order) + elseif kind == "finalizer" then + collected = pi.finalizer(collected) -- no check on # here + p.matched = p.matched + 1 p.finalized = p.finalized + 1 return collected end - return nil + if not collected or #collected == 0 then + local pn = i < nofparsed and parsed[nofparsed] + if pn and pn.kind == "finalizer" then + collected = pn.finalizer(collected) -- collected can be nil + p.finalized = p.finalized + 1 + return collected + end + return nil + end end + if collected then + p.matched = p.matched + 1 + end + return collected end - if collected then - p.matched = p.matched + 1 - end - return collected -end -local function traced_apply(list,parsed,nofparsed,order) - if trace_lparse then - lshow(parsed) - end - report_lpath("collecting: %s",parsed.pattern) - report_lpath("root tags : %s",tagstostring(list)) - report_lpath("order : %s",order or "unset") - local collected = list - for i=1,nofparsed do - local pi = parsed[i] - local kind = pi.kind - if kind == "axis" then - collected = apply_axis[pi.axis](collected) - report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) - elseif kind == "nodes" then - collected = apply_nodes(collected,pi.nodetest,pi.nodes) - report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) - elseif kind == "expression" then - collected = apply_expression(collected,pi.evaluator,order) - report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) - elseif kind == "finalizer" then - collected = pi.finalizer(collected) - report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") - return collected + local function traced_apply(list,parsed,nofparsed,order) + if trace_lparse then + lshow(parsed) end - if not collected or #collected == 0 then - local pn = i < nofparsed and parsed[nofparsed] - if pn and pn.kind == "finalizer" then - collected = pn.finalizer(collected) - report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "") + report_lpath("collecting: %s",parsed.pattern) + report_lpath("root tags : %s",tagstostring(list)) + report_lpath("order : %s",order or "unset") + local collected = list + for i=1,nofparsed do + local pi = parsed[i] + local kind = pi.kind + if kind == "axis" then + collected = apply_axis[pi.axis](collected) + report_lpath("% 10i : ax : %s",(collected and #collected) or 0,pi.axis) + elseif kind == "nodes" then + collected = apply_nodes(collected,pi.nodetest,pi.nodes) + report_lpath("% 10i : ns : %s",(collected and #collected) or 0,nodesettostring(pi.nodes,pi.nodetest)) + elseif kind == "expression" then + collected = apply_expression(collected,pi.evaluator,order) + report_lpath("% 10i : ex : %s -> %s",(collected and #collected) or 0,pi.expression,pi.converted) + elseif kind == "finalizer" then + collected = pi.finalizer(collected) + report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pi.name,pi.arguments or "") return collected end - return nil + if not collected or #collected == 0 then + local pn = i < nofparsed and parsed[nofparsed] + if pn and pn.kind == "finalizer" then + collected = pn.finalizer(collected) + report_lpath("% 10i : fi : %s : %s(%s)",(type(collected) == "table" and #collected) or 0,parsed.protocol or xml.defaultprotocol,pn.name,pn.arguments or "") + return collected + end + return nil + end end + return collected end - return collected -end -local function normal_apply(list,parsed,nofparsed,order) - local collected = list - for i=1,nofparsed do - local pi = parsed[i] - local kind = pi.kind - if kind == "axis" then - local axis = pi.axis - if axis ~= "self" then - collected = apply_axis[axis](collected) + local function normal_apply(list,parsed,nofparsed,order) + local collected = list + for i=1,nofparsed do + local pi = parsed[i] + local kind = pi.kind + if kind == "axis" then + local axis = pi.axis + if axis ~= "self" then + collected = apply_axis[axis](collected) + end + elseif kind == "nodes" then + collected = apply_nodes(collected,pi.nodetest,pi.nodes) + elseif kind == "expression" then + collected = apply_expression(collected,pi.evaluator,order) + elseif kind == "finalizer" then + return pi.finalizer(collected) end - elseif kind == "nodes" then - collected = apply_nodes(collected,pi.nodetest,pi.nodes) - elseif kind == "expression" then - collected = apply_expression(collected,pi.evaluator,order) - elseif kind == "finalizer" then - return pi.finalizer(collected) - end - if not collected or #collected == 0 then - local pf = i < nofparsed and parsed[nofparsed].finalizer - if pf then - return pf(collected) -- can be anything + if not collected or #collected == 0 then + local pf = i < nofparsed and parsed[nofparsed].finalizer + if pf then + return pf(collected) -- can be anything + end + return nil end - return nil end + return collected end - return collected -end -local function applylpath(list,pattern) - if not list then - return - end - local parsed = cache[pattern] - if parsed then - lpathcalls = lpathcalls + 1 - lpathcached = lpathcached + 1 - elseif type(pattern) == "table" then - lpathcalls = lpathcalls + 1 - parsed = pattern - else - parsed = lpath(pattern) or pattern - end - if not parsed then - return + local apply = normal_apply + + if trackers then + -- local function check() + -- if trace_lprofile or then + -- apply = profiled_apply + -- elseif trace_lpath then + -- apply = traced_apply + -- else + -- apply = normal_apply + -- end + -- end + -- trackers.register("xml.path", check) -- can be "xml.path,xml.parse,xml.profile + -- trackers.register("xml.parse", check) + -- trackers.register("xml.profile",check) + + trackers.register("xml.path,xml.parse,xml.profile",function() + if trace_lprofile then + apply = profiled_apply + elseif trace_lpath then + apply = traced_apply + else + apply = normal_apply + end + end) end - local nofparsed = #parsed - if nofparsed == 0 then - return -- something is wrong + + + function xml.applylpath(list,pattern) + if not list then + lastmatch = nil + return + end + local parsed = cache[pattern] + if parsed then + lpathcalls = lpathcalls + 1 + lpathcached = lpathcached + 1 + elseif type(pattern) == "table" then + lpathcalls = lpathcalls + 1 + parsed = pattern + else + parsed = lpath(pattern) or pattern + end + if not parsed then + lastmatch = nil + return + end + local nofparsed = #parsed + if nofparsed == 0 then + lastmatch = nil + return -- something is wrong + end + local collected = apply({ list },parsed,nofparsed,list.mi) + lastmatch = keepmatch and collected or nil + return collected end - if not trace_lpath then - return normal_apply ({ list },parsed,nofparsed,list.mi) - elseif trace_lprofile then - return profiled_apply({ list },parsed,nofparsed,list.mi) - else - return traced_apply ({ list },parsed,nofparsed,list.mi) + + function xml.lastmatch() + return lastmatch end -end -xml.applylpath = applylpath -- takes a table as first argment, which is what xml.filter will do +end +local applylpath = xml.applylpath --[[ldx-- <p>This is the main filter function. It returns whatever is asked for.</p> --ldx]]-- diff --git a/tex/context/base/mkiv/lxml-tex.lua b/tex/context/base/mkiv/lxml-tex.lua index 09f1e10f9..0ec981d68 100644 --- a/tex/context/base/mkiv/lxml-tex.lua +++ b/tex/context/base/mkiv/lxml-tex.lua @@ -52,6 +52,9 @@ local xmlinclusion = xml.inclusion local xmlinclusions = xml.inclusions local xmlbadinclusions = xml.badinclusions local xmlcontent = xml.content +local xmllastmatch = xml.lastmatch + +directives.enable("xml.path.keeplastmatch") local variables = interfaces and interfaces.variables or { } @@ -1849,6 +1852,13 @@ function lxml.flush(id) end end +function lxml.lastmatch() + local collected = xmllastmatch() + if collected then + all(collected) + end +end + function lxml.snippet(id,i) local e = getid(id) if e then diff --git a/tex/context/base/mkiv/node-res.lua b/tex/context/base/mkiv/node-res.lua index 892cd62f2..eec7f0c07 100644 --- a/tex/context/base/mkiv/node-res.lua +++ b/tex/context/base/mkiv/node-res.lua @@ -369,36 +369,38 @@ if context and _cldo_ then local setfield_node = nodes.setfield local setfield_nut = nuts .setfield - function nodepool.lateluafunction(f) - local n = copy_node(latelua_node) - setfield_node(n,"string",f_cldo(register(f))) - return n - end - function nutpool.lateluafunction(f) - local n = copy_nut(latelua_nut) - setfield_nut(n,"string",f_cldo(register(f))) - return n - end - - -- when function in latelua: - -- function nodepool.lateluafunction(f) -- local n = copy_node(latelua_node) - -- setfield_node(n,"string",f) + -- setfield_node(n,"string",f_cldo(register(f))) -- return n -- end + -- function nutpool.lateluafunction(f) -- local n = copy_nut(latelua_nut) - -- setfield_nut(n,"string",f) + -- setfield_nut(n,"string",f_cldo(register(f))) -- return n -- end + -- when function in latelua: + + function nodepool.lateluafunction(f) + local n = copy_node(latelua_node) + setfield_node(n,"string",f) + return n + end + + function nutpool.lateluafunction(f) + local n = copy_nut(latelua_nut) + setfield_nut(n,"string",f) + return n + end + local latefunction = nodepool.lateluafunction local flushnode = context.flushnode - function context.lateluafunction(f) - flushnode(latefunction(f)) -- hm, quite some indirect calls - end + -- function context.lateluafunction(f) + -- flushnode(latefunction(f)) -- hm, quite some indirect calls + -- end -- when function in latelua: @@ -420,11 +422,11 @@ if context and _cldo_ then -- when function in latelua: - -- function context.lateluafunction(f) - -- local n = copy_node(latelua_node) - -- setfield_node(n,"string",f) - -- contextsprint(ctxcatcodes,"\\cldl",storenode(n)," ") - -- end + function context.lateluafunction(f) + local n = copy_node(latelua_node) + setfield_node(n,"string",f) + contextsprint(ctxcatcodes,"\\cldl",storenode(n)," ") + end end diff --git a/tex/context/base/mkiv/publ-imp-apa.mkvi b/tex/context/base/mkiv/publ-imp-apa.mkvi index 1411042c9..b9d265105 100644 --- a/tex/context/base/mkiv/publ-imp-apa.mkvi +++ b/tex/context/base/mkiv/publ-imp-apa.mkvi @@ -872,7 +872,6 @@ \btxflush{withauthor} \btxrightparenthesis } - \btxperiod } \stoptexdefinition diff --git a/tex/context/base/mkiv/status-files.pdf b/tex/context/base/mkiv/status-files.pdf Binary files differindex 392bf9a1f..35df55af7 100644 --- a/tex/context/base/mkiv/status-files.pdf +++ b/tex/context/base/mkiv/status-files.pdf diff --git a/tex/context/base/mkiv/status-lua.pdf b/tex/context/base/mkiv/status-lua.pdf Binary files differindex f7b9aa479..8d31b4caa 100644 --- a/tex/context/base/mkiv/status-lua.pdf +++ b/tex/context/base/mkiv/status-lua.pdf diff --git a/tex/context/base/mkiv/strc-flt.mkvi b/tex/context/base/mkiv/strc-flt.mkvi index 334d98a93..fb621c997 100644 --- a/tex/context/base/mkiv/strc-flt.mkvi +++ b/tex/context/base/mkiv/strc-flt.mkvi @@ -186,16 +186,36 @@ \unexpanded\def\definefloat {\dotripleempty\strc_floats_define} -\def\strc_floats_define[#1][#2][#3]% #1=naam #2=meervoud #3=parent +\def\strc_floats_define[#1][#2][#3]% name+plural+parent | name+parent+settings {\ifthirdargument - \strc_floats_define_cloned[#1][#2][#3]% + \doifassignmentelse{#3} + {\strc_floats_define_b[#1][#2][#3]}% + {\strc_floats_define_a[#1][#2][#3]}% \else\ifsecondargument - \strc_floats_define_normal[#1][#2]% + \doifelsecommandhandler\??float{#2}% + {\strc_floats_define_a[#1][#1][#2]}% + {\strc_floats_define_c[#1][#2]}% \else - \strc_floats_define_normal[#1][#1]% + \strc_floats_define_c[#1][#1]% \fi\fi} -\def\strc_floats_define_normal[#1][#2]% +\def\strc_floats_define_a[#1][#2][#3]% name names parent + {\definefloatcaption[#1][#3]% + \definecounter[#1][#3]% + \definelist[#1][#3]% + \copylabeltext[#1=#3]% + \strc_floats_define_saved[#1][#3]% + \strc_floats_define_commands{#1}{#2}} + +\def\strc_floats_define_b[#1][#2][#3]% name parent settings + {\definefloatcaption[#1][#2]% + \definecounter[#1][#2]% + \definelist[#1][#2]% + \copylabeltext[#1=#2]% + \strc_floats_define_saved[#1][#2][#3]% + \strc_floats_define_commands{#1}{#1}} + +\def\strc_floats_define_c[#1][#2]% name names {\registerfloatcaptioncounter{#1}% \definefloatcaption[#1]% \definecounter[#1]% @@ -205,15 +225,6 @@ \strc_floats_define_saved[#1]% \strc_floats_define_commands{#1}{#2}} -\def\strc_floats_define_cloned[#1][#2][#3]% - {\definefloatcaption[#1][#3]% - \definecounter[#1][#3]% - \definelist[#1][#3]% - \copylabeltext[#1=#3]% - %\presetheadtext[#2=\Word{#2}]% - \strc_floats_define_saved[#1][#3]% - \strc_floats_define_commands{#1}{#2}} - \def\strc_floats_define_commands#1#2% {\setuvalue {\e!place\e!listof#2}{\dodoubleempty\strc_lists_place[#1]}% call will change \setuvalue {\e!complete\e!listof#2}{\dotripleempty\strc_lists_complete_indeed[#1][#2]}% call will change diff --git a/tex/context/base/mkiv/syst-ini.mkiv b/tex/context/base/mkiv/syst-ini.mkiv index e165ee8a3..37ffc5768 100644 --- a/tex/context/base/mkiv/syst-ini.mkiv +++ b/tex/context/base/mkiv/syst-ini.mkiv @@ -1010,15 +1010,12 @@ \edef\pdfimagehicolor {\pdfvariable imagehicolor} \pdfimagehicolor \plusone \edef\pdfimageaddfilename {\pdfvariable imageaddfilename} \pdfimageaddfilename \plusone \edef\pdfpkresolution {\pdfvariable pkresolution} \pdfpkresolution 1200 -\edef\pdfpkfixeddpi {\pdfvariable pkfixeddpi} \pdfpkfixeddpi 1 \edef\pdfinclusioncopyfonts {\pdfvariable inclusioncopyfonts} \pdfinclusioncopyfonts \plusone \edef\pdfinclusionerrorlevel {\pdfvariable inclusionerrorlevel} \pdfinclusionerrorlevel \zerocount -\edef\pdfignoreunknownimages {\pdfvariable ignoreunknownimages} \pdfignoreunknownimages \zerocount \edef\pdfgentounicode {\pdfvariable gentounicode} \pdfgentounicode \plusone \edef\pdfpagebox {\pdfvariable pagebox} \pdfpagebox \zerocount \edef\pdfminorversion {\pdfvariable minorversion} \pdfminorversion \plusseven \edef\pdfuniqueresname {\pdfvariable uniqueresname} \pdfuniqueresname \zerocount - \edef\pdfhorigin {\pdfvariable horigin} \pdfhorigin 1in \edef\pdfvorigin {\pdfvariable vorigin} \pdfvorigin \pdfhorigin \edef\pdflinkmargin {\pdfvariable linkmargin} \pdflinkmargin \zeropoint @@ -1026,6 +1023,14 @@ \edef\pdfthreadmargin {\pdfvariable threadmargin} \pdfthreadmargin \zeropoint \edef\pdfxformmargin {\pdfvariable xformmargin} \pdfxformmargin \zeropoint +\ifnum\luatexversion>88 + \edef\pdfpkfixeddpi {\pdfvariable pkfixeddpi} \pdfpkfixeddpi \plusone + \edef\pdfignoreunknownimages {\pdfvariable ignoreunknownimages} \pdfignoreunknownimages \zerocount +\else + \newcount\pdfpkfixeddpi + \newcount\pdfignoreunknownimages +\fi + \edef\pdfpagesattr {\pdfvariable pagesattr} \edef\pdfpageattr {\pdfvariable pageattr} \edef\pdfpageresources {\pdfvariable pageresources} diff --git a/tex/context/interface/common/i-en-xml.xml b/tex/context/interface/common/i-en-xml.xml new file mode 100644 index 000000000..441ff07d2 --- /dev/null +++ b/tex/context/interface/common/i-en-xml.xml @@ -0,0 +1,722 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<cd:interface file="lxml-ini.mkiv"> + + <!-- some common definitions --> + + <cd:define name="xml-node"> + <cd:argument><cd:constant type="cd:node"/></cd:argument> + </cd:define> + + <cd:define name="xml-path"> + <cd:argument><cd:constant type="cd:path"/></cd:argument> + </cd:define> + + <cd:define name="xml-node-path"> + <cd:argument><cd:constant type="cd:node"/></cd:argument> + <cd:argument><cd:constant type="cd:lpath"/></cd:argument> + </cd:define> + + <cd:define name="xml-true"> + <cd:argument><cd:constant type="cd:true"/></cd:argument> + </cd:define> + + <cd:define name="xml-true-false"> + <cd:argument><cd:constant type="cd:true"/></cd:argument> + <cd:argument><cd:constant type="cd:false"/></cd:argument> + </cd:define> + + <!-- the definitions themselves --> + + <!-- cd:sequence><cd:string value="xmlmain"/></cd:sequence --> + + <cd:command name="xmlmain"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlflush"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmltext"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmlnonspace"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmlflushspacewise"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlflushlinewise"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlall"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmllastmatch" /> + + <cd:command name="xmlfirst"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmllast"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmlcontext"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmlflushcontext"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlshow"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlinfo"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlfilter"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmlstrip"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmlstripped"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmlstripnolines"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmlstrippednolines"> + <cd:arguments><cd:resolve name="xml-node-path"/></cd:arguments> + </cd:command> + + <cd:command name="xmlposition"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:number"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlelement"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:number"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlpos"> + <cd:arguments> + <cd:resolve name="xml-node"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmlsnippet"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:number"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlconcat"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlconcatrange"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + <cd:argument><cd:constant type="cd:first"/></cd:argument> + <cd:argument><cd:constant type="cd:last"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlcommand"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:xmlsetup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlinlineverbatim"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlinlineverbatim" type="environment"> + <cd:arguments> + <cd:keywords> + <cd:constant type="cd:name"/> + </cd:keywords> + </cd:arguments> + </cd:command> + + <cd:command name="xmldisplayverbatim"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmldisplayverbatim" type="environment"> + <cd:arguments> + <cd:keywords> + <cd:constant type="cd:name"/> + </cd:keywords> + </cd:arguments> + </cd:command> + + <cd:command name="xmlverbatim"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlprettyprint"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlloadfile"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:file"/></cd:argument> + <cd:argument><cd:constant type="cd:xmlsetup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlloadbuffer"> + <cd:arguments> + <cd:argument><cd:constant type="cd:buffer"/></cd:argument> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:xmlsetup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlloaddata"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + <cd:argument><cd:constant type="cd:xmlsetup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlloadonly"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:file"/></cd:argument> + <cd:argument><cd:constant type="cd:xmlsetup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlprocessfile"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:file"/></cd:argument> + <cd:argument><cd:constant type="cd:xmlsetup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlprocessbuffer"> + <cd:arguments> + <cd:argument><cd:constant type="cd:buffer"/></cd:argument> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:xmlsetup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlprocessdata"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + <cd:argument><cd:constant type="cd:xmlsetup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlinclude"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlname"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlnamespace"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmltag"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlcount"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlatt"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlattdef"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlrefatt"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmllastatt" /> + + <cd:command name="xmlattribute"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlattributedef"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlchainatt"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlchainattdef"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmldirectives"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmldirectivesbefore"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmldirectivesafter"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlinstalldirective"> + <cd:arguments><cd:resolve name="name"/></cd:arguments> + <cd:arguments><cd:resolve name="name"/></cd:arguments> + </cd:command> + + <cd:command name="xmlsetup"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlsetsetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:lpath"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlremovesetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlremovedocumentsetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlflushdocumentsetups"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlresetsetups"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlresetdocumentsetups"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlprependsetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlappendsetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlbeforesetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlaftersetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlprependdocumentsetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlappenddocumentsetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlbeforedocumentsetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlafterdocumentsetup"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlregisteredsetups"> + </cd:command> + + <cd:command name="xmlregistereddocumentsetups"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:setup"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlsetfunction"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:luafunction"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoif"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:resolve name="xml-true"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoifnot"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:resolve name="xml-true"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoifelse"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:resolve name="xml-true-false"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoiftext"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:resolve name="xml-true"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoifnottext"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:resolve name="xml-true"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoifelsetext"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:resolve name="xml-true-false"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoifselfempty"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:resolve name="xml-true"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoifnotselfempty"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:resolve name="xml-true"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoifelseselfempty"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:resolve name="xml-true-false"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoifelseempty"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:resolve name="xml-true-false"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmlregisterns"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:url"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlregisterns"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlremapname"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlremapnamespace"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlchecknamespace"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlmapvalue"> + <cd:arguments> + <cd:argument><cd:constant type="cd:category"/></cd:argument> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlvalue"> + <cd:arguments> + <cd:argument><cd:constant type="cd:category"/></cd:argument> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmldoifelsevalue"> + <cd:arguments> + <cd:argument><cd:constant type="cd:category"/></cd:argument> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:resolve name="xml-true-false"/> + </cd:arguments> + </cd:command> + + <cd:command name="xmlloaddirectives"> + <cd:arguments> + <cd:argument><cd:constant type="cd:file"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlsave"> + <cd:arguments> + <cd:resolve name="xml-node"/> + <cd:argument><cd:constant type="cd:file"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmltofile"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:file"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmltobuffer"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:buffer"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmltobufferverbose"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:buffer"/></cd:argument> + </cd:arguments> + </cd:command> + + <!-- not public cq. might disappear + + <cd:command name="xmltoparameters"> + <cd:arguments> + <cd:resolve name="xml-node-path"/> + <cd:argument><cd:constant type="cd:buffer"/></cd:argument> + </cd:arguments> + </cd:command> + + --> + + <cd:command name="xmladdindex"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlinclusion"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlinclusions"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlbadinclusions"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlraw"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + + <cd:command name="xmlraw" type="environment" /> + + <!-- injectors --> + + <cd:command name="xmlresetinjectors"/> + + <cd:command name="xmlsetinjectors"> + <cd:arguments> + <cd:keywords list="yes"><cd:constant type="cd:name"/></cd:keywords> + </cd:arguments> + </cd:command> + + <cd:command name="xmlinjector"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmlapplyselectors"> + <cd:arguments> + <cd:argument><cd:constant type="cd:node"/></cd:argument> + </cd:arguments> + </cd:command> + + <!-- entities --> + + <cd:command name="xmlsetentity"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + </cd:arguments> + </cd:command> + + <cd:command name="xmltexentity"> + <cd:arguments> + <cd:argument><cd:constant type="cd:name"/></cd:argument> + <cd:argument><cd:constant type="cd:text"/></cd:argument> + </cd:arguments> + </cd:command> + + <!-- general setup --> + + <cd:command name="setupxml"> + <cd:arguments> + <cd:assignments list="yes"> + <cd:parameter name="default"> + <cd:constant type="hidden"/> + <cd:constant type="none"/> + <cd:constant type="text"/> + </cd:parameter> + <cd:parameter name="compress"> + <cd:constant type="yes"/> + <cd:constant type="no"/> + </cd:parameter> + </cd:assignments> + </cd:arguments> + </cd:command> + + <cd:command name="xmldefaulttotext"> + <cd:arguments><cd:resolve name="xml-node"/></cd:arguments> + </cd:command> + +</cd:interface> diff --git a/tex/context/modules/mkiv/x-set-11.mkiv b/tex/context/modules/mkiv/x-set-11.mkiv index 91979cbbe..3e9dfed8c 100644 --- a/tex/context/modules/mkiv/x-set-11.mkiv +++ b/tex/context/modules/mkiv/x-set-11.mkiv @@ -557,7 +557,11 @@ \startxmlsetups xml:setups:assemblename \doifelse {\xmlatt{#1}{type}} {environment} { - \let\currentSETUPprefix\e!start + \doifsomethingelse {\xmlatt{#1}{begin}} { + \edef\currentSETUPprefix{\xmllastatt}% + } { + \let\currentSETUPprefix\e!start + } } { \let\currentSETUPprefix\empty } diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua index 89137591a..836470ca2 100644 --- a/tex/generic/context/luatex/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : c:/data/develop/context/sources/luatex-fonts-merged.lua -- parent file : c:/data/develop/context/sources/luatex-fonts.lua --- merge date : 01/28/16 22:35:09 +-- merge date : 01/29/16 19:00:08 do -- begin closure to overcome local limits and interference diff --git a/tex/generic/context/luatex/luatex-pdf.tex b/tex/generic/context/luatex/luatex-pdf.tex index 8b80addc2..1263f21e2 100644 --- a/tex/generic/context/luatex/luatex-pdf.tex +++ b/tex/generic/context/luatex/luatex-pdf.tex @@ -127,6 +127,11 @@ \xdef\pdfminorversion {\pdfvariable minorversion} \xdef\pdfuniqueresname {\pdfvariable uniqueresname} + \ifnum\luatexversion>88 + \edef\pdfpkfixeddpi {\pdfvariable pkfixeddpi} + \edef\pdfignoreunknownimages {\pdfvariable ignoreunknownimages} + \fi + \xdef\pdfhorigin {\pdfvariable horigin} \xdef\pdfvorigin {\pdfvariable vorigin} \xdef\pdflinkmargin {\pdfvariable linkmargin} |