summaryrefslogtreecommitdiff
path: root/Master/texmf-dist
diff options
context:
space:
mode:
authorKarl Berry <karl@freefriends.org>2011-12-03 23:36:05 +0000
committerKarl Berry <karl@freefriends.org>2011-12-03 23:36:05 +0000
commitf884cf55d378c5815215caa25f5fb7ff1d8f83cd (patch)
tree62ee34c74798cbac6b6fbe0448713249097c08a9 /Master/texmf-dist
parente57737e5e749b3cdff002d8ca4c32c865ec5e03e (diff)
interpreter 1.1 (3dec11)
git-svn-id: svn://tug.org/texlive/trunk@24740 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist')
-rw-r--r--Master/texmf-dist/doc/luatex/interpreter/README9
-rw-r--r--Master/texmf-dist/doc/luatex/interpreter/i-doc.lua9
-rw-r--r--Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.pdfbin72802 -> 102479 bytes
-rw-r--r--Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.tex103
-rw-r--r--Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.txt426
-rw-r--r--Master/texmf-dist/tex/luatex/interpreter/interpreter.lua368
-rw-r--r--Master/texmf-dist/tex/luatex/interpreter/interpreter.sty4
-rw-r--r--Master/texmf-dist/tex/luatex/interpreter/interpreter.tex13
8 files changed, 685 insertions, 247 deletions
diff --git a/Master/texmf-dist/doc/luatex/interpreter/README b/Master/texmf-dist/doc/luatex/interpreter/README
index c62c78604ae..c91f26a1d59 100644
--- a/Master/texmf-dist/doc/luatex/interpreter/README
+++ b/Master/texmf-dist/doc/luatex/interpreter/README
@@ -2,12 +2,15 @@ This is the README file for the Interpreter package.
Author: Paul Isambert.
E-mail: zappathustra AT free DOT fr
Comments and suggestions are welcome.
-Date: July 2011.
-Version: 1.0.
+Date: December 2011.
+Version: 1.1.
Interpreter preprocesses input files on the fly (no external program) and
manipulates input lines, e.g. to turn some markup into proper TeX syntax.
-LuaTeX is required.
+Interpreter doesn't work with ConTeXt.
+
+LuaTeX is required, as is, since version 1.1, the Lua side of the
+Gates package (i.e. gates.lua).
Relevant information can be found in interpreter-doc.pdf
or interpreter-doc.txt (source of the doc readable in a text editor).
diff --git a/Master/texmf-dist/doc/luatex/interpreter/i-doc.lua b/Master/texmf-dist/doc/luatex/interpreter/i-doc.lua
index b8b3c106a46..12bc214eda8 100644
--- a/Master/texmf-dist/doc/luatex/interpreter/i-doc.lua
+++ b/Master/texmf-dist/doc/luatex/interpreter/i-doc.lua
@@ -122,7 +122,8 @@ add_pattern{
add_pattern{
pattern = "_..._",
replace = [[\ital{%1}]],
- class = 4 }
+ class = 4
+}
--[[
I use double quotes as protectors; they are replaced with a "\verb" command at
@@ -188,7 +189,7 @@ class is 3). Of course, the first ten space characters are removed.
--]]
local function verbatim (buffer)
for n, l in ipairs(buffer) do
- buffer[n] = gsub(l, "%s%s%s%s%s%s%s%s%s%s","")
+ buffer[n] = gsub(l, "%s%s%s%s%s%s%s%s%s%s","", 1)
end
table.insert(buffer, 1, [[\verbatim]])
if gsub(buffer[#buffer],
@@ -244,10 +245,10 @@ add_pattern{
call = autoverbatim,
class = 1
}
-local function test ()
+local function remove_comment ()
return ""
end
add_pattern{
pattern = nomagic"%^--[[",
- replace = test,
+ replace = remove_comment
}
diff --git a/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.pdf b/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.pdf
index 8e70e34b7fd..ea76a6c65cd 100644
--- a/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.pdf
+++ b/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.pdf
Binary files differ
diff --git a/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.tex b/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.tex
index 8d78ad02963..8a1ab383a81 100644
--- a/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.tex
+++ b/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.tex
@@ -1,36 +1,20 @@
% This is the master file producing interpreter-doc.pdf. The version of the
% documentation readable in a text editor is interpreter-doc.txt (input below).
%
-% Paul Isambert - zappathustra AT free DOT fr - July 2011
+% Paul Isambert - zappathustra AT free DOT fr - December 2011
+
\input pitex
-% Output stuff.
-\gates remove {output_headers}{output_shipout}
-\gates remove {output_postship}{output_shipout}
-\gates remove {output_precheck}{output}
-\gates close {output_shipout}{output}
-
-\newif\ifright
-\newbox\leftbox
-\gates def {twocol}{%
- \ifright
- \global\rightfalse
- \setbox\outputbox=\hbox{\box\leftbox\kern24pt\box\outputbox}%
- \gates ajar {output_shipout}{output}%
- \else
- \global\righttrue
- \global\setbox\leftbox=\box\outputbox
- \fi
- }
-\gates add {twocol}[before output_shipout]{output}
+\overfullrule=0pt
+\OutputRoutine remove {headers}{shipout}
\setparameter page :
- hsize = 20pc
+ hsize = 25pc
left = 60pt
- width = "\dimexpr 144pt + 40pc\relax"
- lines = 45
- height = 24cm
+ width = "\dimexpr 25pc + 120pt\relax"
+ lines = 35
+ height = 20cm
\setparameter section :
font = \bf
@@ -40,36 +24,37 @@
beforeskip = 1
\setparameter navigator :
- open = true
title = "Interpreter documentation"
author = "Paul Isambert"
mode = outlines
-\setparameter font :
- command = \mainfont
- name = "Chaparral Pro"
- bold = Semibold
- big = 18pt
-
-\setparameter font :
- command = \codefont
- name = "Lucida Console"
- bold = none
- size = 8pt
- features = "space = .6, -tlig, -trep, -liga"
+\setfont\mainfont:
+ name = "Chaparral Pro"
+ bold = Semibold
+ big = 18pt
+\setfont\codefont:
+ name = "Lucida Console"
+ slant = 15
+ bold italic = none
+ size = 8pt
+ features = "-tlig, -trep, space = mono"
\parfillskip=0pt plus 1fill
\def\describe#1#2#3{%
- \iflines3{\vskip\baselineskip}{\breakpage}%
+ \unless\ifdim\lastskip=\baselineskip
+ \vskip\baselineskip
+ \fi
+ \needspace{2\baselineskip}%
\noindent\color{.8 0 0}{%
{\outline{#3}{\directlua{%
local t = string.gsub("\luaescapestring{#1}", "[ (].*", "")
tex.print(t)}}%
\codefont#1}%
- \reverse\iffemptystring{#2}
- {\kern1em \hfil\penalty0\hbox{\ital{(#2)}}}}%
- \par\removenextindent}
+ \reverse\iffemptystring{#2}
+ {\kern1em \hfil\penalty0\hbox{\ital{(#2)}}}}%
+ \par
+ \removenextindent}
\newverbatim\source{}
{\vskip\baselineskip
@@ -80,19 +65,51 @@
\pdfcolorstack0 pop
\vskip\baselineskip}
+% The verbatim facilities in PiTeX aren't gated yet, so I must rely on
+% this horrible hack!
+\directlua{
+function do_verbatim (name, exec)
+ if exec then
+ tex.print(pitex.verbatims[name])
+ else
+ for n, l in ipairs(pitex.verbatims[name]) do
+ if n == \string#pitex.verbatims[name] then
+ tex.print("\noexpand\\penalty\noexpand\\widowpenalty")
+ end
+ tex.print(pitex.verbatims[name].regime, l)
+ if n == 1 and \string#pitex.verbatims[name] > 2 then
+ tex.print("\noexpand\\penalty\noexpand\\widowpenalty")
+ end
+ end
+ end
+end
+}
+
\def\arg#1{{\codefont\char"2039 #1\char"203A}}
+\pdfdef\ital#1{#1}
+\pdfdef\verb`#1`{#1}
+
+% Not optimal, but hey, with all the "intepreter.core.classes" stuff...
+\hyphenation{li-nes cla-sses}
\input interpreter
% Title
\vbox to 3\baselineskip{
\hbox to \hsize{\big Interpreter\hfil\normalsize Paul Isambert}
-\hbox to \hsize{v.1.0, July 2011 \hfil \tcode{zappathustra AT free DOT fr}}
+\hbox to \hsize{v.1.1, December 2011 \hfil \tcode{zappathustra AT free DOT fr}}
\vfil
}
+
+
+
% Bulk of the doc.
\interpretfile{doc}{interpreter-doc.txt}
\vskip0pt plus 1filll
-\noindent\ital{Typeset with Lua\TeX\ 0.71 in Chaparral Pro and Lucida Console}
+\noindent
+\bgroup\it
+Typeset with Lua\TeX\ 0.71 in Chaparral Pro and Lucida Console
+... nonetheless this documentation looks dull, I don't know why.
+\egroup
\bye
diff --git a/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.txt b/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.txt
index 5bece3bac7a..0156c3e07d8 100644
--- a/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.txt
+++ b/Master/texmf-dist/doc/luatex/interpreter/interpreter-doc.txt
@@ -1,19 +1,23 @@
TABLE OF CONTENTS
- (Make a search on the tag in the right
- column to jump to the associated section.
- Vim users can simply type * on the tag;
- Emacs users do that with C-s C-w (I think);
- Other editors: I don't know!)
-=======================================
- Introduction intro_tag
- Input files input_tag
- Paragraphs paragraphs_tag
- Declaring patterns patterns_tag
- Classes classes_tag
- Protecting input protect_tag
- Technical stuff technical_tag
- An example: i-doc.lua example_tag
-=======================================
+ (Make a search on the tag in the right column to jump
+ to the associated section.
+ Vim users can simply type * on the tag;
+ Emacs users do that with C-s C-w (I think);
+ Other editors: I don't know!)
+===========================================================
+ Introduction intro_tag
+ Input files input_tag
+ Paragraphs paragraphs_tag
+ Declaring patterns patterns_tag
+ Classes classes_tag
+ Protecting input protect_tag
+ Technical stuff technical_tag
+ An example: i-doc.lua example_tag
+ The Gates in Interpreter gates_tag
+ The interpreter table interpreter_tag
+ The interpreter.core.tools table interpreter_tools_tag
+ The interpreter.core.reader table interpreter_reader_tag
+===========================================================
@@ -41,6 +45,14 @@ modified lines to TeX you can also translate the original lines into, say,
HTML, and write them to an external file, thus creating both PDF and HTML
output at once.
+Interpreter has been rewritten with the Gates package (actually, only
+the Lua side) in version 1.1. That hasn't changed anything to its default
+behavior, but now it can also be customized quite deeply, since its
+code is a collection of small chunks with names that can be externally
+controlled and/or augmented. See the Gates documentation for further
+information. The last sections of this documentation describe the gates
+in Interpreter.
+
================================== input_tag
@@ -224,7 +236,7 @@ precisely, the process is as follows: Interpreter searches the entire
paragraph for the first pattern in class~1, then for the second pattern in the
same class, then for the third, etc., then when there is no pattern left in
class~1 it does the same with class~2, up to class~_n_, where _n_ is the
-hightest class number such that there exists a class _n - 1_ (in other words,
+highest class number such that there exists a class _n - 1_ (in other words,
classes should be numbered consecutively). Finally, the same goes for the
patterns in class~0 (which always exists, even if it contains no pattern).
@@ -381,3 +393,385 @@ does its job; actually you won't learn much anyway.
==================================
\interpretfile{doc}{i-doc.lua}
+
+
+
+================================== gates_tag
+=== The Gates in Interpreter =====
+==================================
+
+Interpreter is written with the Gates package (only the Lua side,
+actually). It means that it can be hacked down to the core. Here I'll
+simply list the gates involved; you should read the Gates
+documentation to learn how to use them.
+
+There are three gates families: "interpreter", associated with the main
+"interpreter" table, contains the user interface; "interpreter_tools",
+associated with "interpreter.core.tools" table, contains internal
+functions; finally "interpreter_reader", associated with the
+"interpreter.core.reader" table, contains the main functions used to
+read the file.
+
+Whenever I mention a conditional or a loop, I mean the local conditionals
+and loops, relative to the l-gate where the gate appears. Also, the
+syntax indicates the arguments a gate uses, not all the arguments that
+are passed to it (which are simply what the previous gate has returned).
+
+As an example of customizing Interpreter with Gates, you could very
+well add a bit of code which does something to all lines. Inserting a
+small gate, say "everyline", after "check_direct" in "aggregate_lines"
+below would do the trick, e.g.:
+
+
+ function interpreter.core.reader.everyline (file, line)
+ line = dosomething(line)
+ return file, line
+ end
+ interpreter.core.reader.add(
+ "everyline", "aggregate_lines", "after check_direct")
+ interpreter.core.reader.conditional(
+ "everyline", "aggregate_lines", function (f, l) return l end)
+
+(Note that it is important to check that the line really exist,
+because one might have hit the end of the file; hence the conditional,
+as with others gates in "aggregate_lines").
+
+
+
+
+================================== interpreter_tag
+=== The "interpreter" table ========
+==================================
+
+All the user functions in "interpreter" are simple m-gates, so they can
+be treated as ordinary functions, except "interpreter.add_pattern",
+which is an l-gate containing, built as:
+
+ add_pattern
+ . ensure_class
+ . apply_nomagic
+ . insert-pattern
+ . . do_insert
+ . . sort_class
+
+> ensure_class (<pattern>) [m-gate]
+ Creates the class of <pattern> if necessary, and set it as the metatable
+ for <pattern>. Classes themselves are kept in the "interpreter.core.classes"
+ table. The gate return <pattern> and the class number.
+
+> apply_nomagic (<pattern>, <class>) [m-gate]
+ Transforms the "pattern" entry in <pattern> with "intepreter.nomagic";
+ tied to a conditional that returns <pattern>'s "nomagic" entry (so
+ the gate is executed only if "nomagic" is true); autoreturns both
+ arguments.
+
+> insert_pattern (<pattern>, <class>) [l-gate]
+ An autoreturning l-gate containing the following two gates.
+
+> do_insert (<pattern>, <class>) [m-gate]
+ Adds <pattern> to <class> (i.e. "interpreter.core.classes[<class>]").
+
+> sort_class (<pattern>, <class>) [m-gate]
+ Sorts <class> with function "interpreter.core.tools.sort". This gate
+ can be skipped to apply the patterns in the order in which they were
+ declared.
+
+
+
+============================================= interpreter_tools_tag
+=== The interpreter.core.tools table ========
+=============================================
+
+All the functions in the "interpreter.core.tools" all are simple m-gates.
+
+> sort (<patt1>, <patt2>) [m-gate]
+ Returns true if the pattern in "patt1" is longer than the one in
+ "patt2", or if they have the same length and the first ranks before
+ the second with respect to alphabetical order. The gate is used in
+ the "interpreter.sort_class" m-gate.
+
+> xsub (<string>, <index>, <pattern>, <replacement>) [m-gate]
+ Returns <string> with <pattern> replaced with <replacement>, but only
+ once, and only after <index>.
+
+> protector (<string>, <index>) [m-gate]
+ Checks whether <index> in <string> isn't between characters declared
+ with "interpreter.protector". If that is the case, the function returns
+ "nil" and the index of the second protector. Otherwise, it returns
+ <index>.
+
+> get_index (<string>, <pattern>, <index>) [m-gate]
+ Checks whether <pattern> occurs in <string>, starting at <index>. If
+ it does, but if <index>-1 is "interpreter.escape", calls itself with
+ <index>+1. Otherwise, calls "interpreter.core.tools.protector" to
+ check whether <index> is in a protected part of the string. If so,
+ calls itself with <right>+1 instead of <index>, where <right> is the
+ second return value of of "interpreter.core.tools.protector", i.e.
+ it searches again after the right protector. If <index> is found,
+ end of story, returns <index>, otherwise returns nothing.
+
+The "interpreter.core.tools" table also contains
+"magic_characters", a table with an entry for each magic character in Lua except
+`"."' and `"%"'; the values to those entries are the same characters
+prefixed with `"%"'. The table is used by "interpreter.nomagic" to spot
+and replace magic characters, with the dot and the percent sign dealt
+with independantly.
+
+
+
+============================================= interpreter_reader_tag
+=== The interpreter.core.reader table =======
+=============================================
+
+Interpreter works by hooking in the "open_read_file" callback; the
+function registered there is the "interpreter.core.reader.input"
+l-gate, built as follows:
+
+ input
+ . unregister
+ . . set_unregister
+ . . use_unregister
+ . open_file
+ . set_reader
+
+> unregister (<filename>) [l-gate]
+ Contains the following two m-gates; <filename> is received from
+ "input", which itself receives it from the callback, i.e. that's the
+ file that's being input (the second argument to "\interpretfile").
+ It is also automatically returned.
+
+> set_unregister () [m-gate]
+ Sets the function to remove "input" from the callback, namely
+ "interpreter.unregister"; the gate is called only if gate
+ "interpreter.unregister" doesn't already exits. If "luatexbase" is
+ detected, the functions there are used; otherwise, "callback.register"
+ is used with "nil" as the second argument.
+
+> use_unregister () [m-gate]
+ Calls "interpreter.unregister()". (You don't want the next input file
+ to be processed with Interpreter by default, that's why you remove
+ the callback function; not that the current one is nonetheless
+ processed with the current file, of course.)
+
+> open_file (<filename>) [m-gate]
+ Returns "io.open(<filename>)".
+
+> set_reader (<file>) [m-gate]
+ Returns a table with a "reader" entry containing a function whose
+ definition is
+
+ function ()
+ return interpreter.core.reader.read_file(f)
+ end
+
+ That's the convention for the "open_read_file" callback: it should
+ return such a table, and the function will be called each time a
+ line is required from the input file.
+
+So most of the work is done by "interpreter.core.reader.read_file",
+which is why it is so heavy; it receives a file handle:
+
+ read_file
+ . make_paragraph
+ . . aggregate_lines
+ . . . read_line
+ . . . check_direct
+ . . . insert_line
+ . . apply_classes
+ . . . initialize
+ . . . pass_class
+ . . . . pass_pattern
+ . . . . . pass_line
+ . . . . . . check_index
+ . . . . . . . call
+ . . . . . . . replace
+ . . . . . . . protect
+ . . . . . . increment_line
+ . . . . . increment_pattern
+ . . . . increment_class
+ . . . apply_class0
+ . . . . initialize0
+ . . . . pass_pattern
+ . . . . . pass_line
+ . . . . . . check_index
+ . . . . . . . call
+ . . . . . . . replace
+ . . . . . . . protect
+ . . . . . . increment_line
+ . . . . . increment_pattern
+ . . . unprotect
+ . . . . undo_protected
+ . . . . unprotect_lines
+ . . . remove_escape
+ . return_line
+
+> make_paragraph (<file>) [l-gate]
+ The big l-gate that contains everything that follows, barring
+ "return_line". It is called if and only if the "interpreter.core.lines"
+ table is empty; that table is where lines of a paragraph are stored,
+ and it is emptied by "return_line".
+
+> aggregate_lines (<file>, <line>) [l-gate]
+ The main l-gate that reads line from <file> and stores them in
+ "interpreter.core.lines". It loops until <line> is "nil" or equivalent
+ to "interpreter.paragraph". (Of course, <line> is "nil" on the first
+ iteration, but the "loopuntil" conditional is evaluated after that
+ first iteration, during which the last subgate "insert_line" will
+ probably returns a line.)
+
+> read_line (<file>) [m-gate]
+ Reads the next line from <file>, and returns <file> and that line.
+
+> check_direct (<file>, <line>) [m-gate]
+ If <line> begins with "interpreter.direct", removes it and use
+ "loadstring" on the resulting string. Returns <file> and <line>, the
+ latter set to an empty string is the previous operation applied. The
+ gate depends on a conditional: <line> should be non-"nil" (of
+ course), and "interpreter.direct" should be defined.
+
+> insert_line (<file>, <line>) [m-gate]
+ Adds <line> to "interpreter.core.lines". Automatically returns the
+ two arguments (and if <line> isn't "nil" or equivalent to
+ "interpreter.paragraph", it will be executed again).
+
+> apply_classes () [l-gate]
+ The l-gate that applies transformations to the lines, once the paragraph
+ has been gathered, with the gates that follow. For each class, it
+ will apply each pattern on each line. It depends on a conditional:
+ "interpreter.core.lines" shouldn't be empty, and "interpreter.active"
+ should be true.
+
+> initialize () [m-gate]
+ Returns "1, 1, 1, 1"; it simply starts the following loop.
+
+> pass_class (<class>) [l-gate]
+ An l-gate called as long as "interpreter.core.reader.protected" isn't
+ a boolean value (see "unprotect" below) and <class> isn't larger than
+ "#interpreter.core.classes"; see "increment_class" below, which returns
+ "<class>+1". In other words, "pass_class" will be repeated for each
+ class.
+
+> pass_pattern (<class>, <pattern>) [l-gate]
+ Same as "pass_class", except it iterates on the patterns in "class":
+ it is executed as long as "interpreter.core.reader.protected" isn't
+ a boolean and "<pattern>" isn't larger than "#interpreter.core.classes[<class>]".
+ See "increment_pattern" below. In other words, it will be repeated
+ for each pattern in <class>.
+
+> pass_line (<class>, <pattern>, <line>), [l-gate]
+ Here we go again, except we check, besides protection, that <line>
+ isn't larger than "#interpreter.core.lines". See "increment_line"
+ below.
+
+> check_index (<class>, <pattern>, <line>, <index>) [l-gate]
+ This time we check that "interpreter.core.classes[<class>][<pattern>].pattern"
+ occurs in "interpreter.core.lines[<line>]", starting at <index>.
+ Protection is checked too, as is the type of "interpreter.core.lines[<line>]";
+ indeed, individual lines are protected by turning them into tables
+ (with a single entry, the line itself), see "protect" below. The
+ occurrence of the pattern in the line is checked with
+ "interpreter.core.tools.get_index".
+
+> call (<class>, <pattern>, <line>, <index>) [m-gate]
+ This is executed if and only if the pattern table (i.e.
+ "interpreter.core.classes[<class>][<pattern>]") has a "call" entry.
+ The index where the pattern occurs is retrieved with
+ "interpreter.core.tools.get_index" and stored in <newindex>, and then
+ the "call" entry is executed as:
+
+ call (interpreter.core.lines,
+ <line>, <newindex>, <pattern table>)
+
+ where <pattern table> is defined as above; if call returns one value,
+ it is assigned to <index>; if it returns two, they are assigned to
+ <line> and <index>. The gate returns the four arguments.
+
+> replace (<class>, <pattern>, <line>, <index>) [m-gate]
+ This is executed only if the pattern table, as defined above, has a
+ "replace" field and no "call" field; it applies "interpreter.core.tools.xsub"
+ as:
+
+ xsub (interpreter.core.lines[<line>],
+ <newindex>, <pattern table>.pattern,
+ <pattern table>.replace)
+
+ where <newindex> is defined as in "call". The return value of "xsub"
+ is assigned to "interpreter.core.lines[<line>]". The gate returns
+ its four argument, with <index> incremented by the value of the
+ "offset" entry of the pattern, if any.
+
+> protect () [m-gate]
+ The "interpreter.protect()" function can either protect the whole
+ paragraph (when no argument is passed) or a single line (when a number
+ is passed). In the first case, "interpreter.core.tools.protected"
+ takes the value "true", which is checked in various gates above. In
+ the second case, "interpreter.core.tools.protected" is a table with
+ each index indicating a line to be protected. This gate implements
+ the protection in that case: for each entry in the table, it protects
+ the line with the same index in "interpreter.core.lines" by transforming
+ it into a table (with a single entry, the string representing the
+ original line); the type of the line is checked in "check_index"
+ above. This gate is executed if and only if "interpreter.core.tools.protected"
+ is a table. The four arguments returned by "call" or "replace" are
+ automatically returned, so that "check_index" is reexecuted if its
+ "loop" conditional evaluates to "true".
+
+> increment_line (<class>, <pattern>, <line>, <index>) [m-gate]
+ Returns <class>, <pattern>, <line> + 1, <index>, so "pass_line" is
+ reexecuted if its "loop" conditional evaluates to "true".
+
+> increment_pattern (<class>, <pattern>, <line>, <index>) [m-gate]
+ Returns <class>, <pattern> + 1, <line>, <index>, so "pass_pattern"
+ is reexecuted if its "loop" conditional evaluates to "true".
+
+> increment_class (<class>, <pattern>, <line>, <index>) [m-gate]
+ Returns <class> + 1, <pattern>, <line>, <index>, so "pass_class" is
+ reexecuted if its "loop" conditional evaluates to "true".
+
+> apply_class0 () [l-gate]
+ The l-gate that does roughly the same job as "apply_classes", but
+ with class 0. It is called if and only if "interpreter.core.reader.protected"
+ isn't a boolean.
+
+> initialize0 () [m-gate]
+ The same as "initialize", except it returns "0, 1, 1, 1", since
+ we're interested in class 0.
+
+> pass_pattern (<class>, <pattern>) [l-gate]
+ The same as above. (Note that this version of the gate doesn't occur
+ in the same l-gate as the previous one, so its local "loop" is also
+ different.)
+
+There is one important difference between "apply_class" and "apply_class0":
+the latter has "pass_pattern" as its immediate subgate, without
+"pass_class" as an intermediary l-gate. That makes sense, since we don't
+want to loop on all the classes here (as that was done in "apply_class"),
+but we're only interested in class 0. Apart from that, the "pass_pattern"
+gate is really the same in both, so all the subgates of "pass_pattern"
+are also reapplied here; I won't describe them again.
+
+> unprotect () [l-gate]
+ Now all the patterns in all the classes have been applied to the
+ entire paragraph, and protection must be removed. This l-gate
+ contains the following two gates.
+
+> undo_protected () [m-gate]
+ Simply sets "interpreter.core.tools.protected" to "nil" so it is
+ ready for the next paragraph.
+
+> unprotect_lines () [m-gate]
+ Restore all the lines in "interpreter.core.lines" as simple strings.
+
+> remove_escape () [m-gate]
+ If "interpreter.escape" is defined, removes all its occurrences in
+ each line of the paragraph.
+
+This is the end of the big "make_paragraph" l-gate. It won't be called
+again until the paragraph has been fully passed to TeX, i.e. when
+"interpreter.core.lines" is empty.
+
+> return_line () [m-gate]
+ Pops the first line from "interpreter.core.lines" and returns it.
+ Since this is the very last subgate of "read_file", the line is
+ passed to TeX.
+
diff --git a/Master/texmf-dist/tex/luatex/interpreter/interpreter.lua b/Master/texmf-dist/tex/luatex/interpreter/interpreter.lua
index 3c2d3391eec..2a87d979195 100644
--- a/Master/texmf-dist/tex/luatex/interpreter/interpreter.lua
+++ b/Master/texmf-dist/tex/luatex/interpreter/interpreter.lua
@@ -1,28 +1,38 @@
-- This is the main Lua file for the Interpreter package.
-- Further information in interpreter-doc.pdf or interpreter-doc.txt.
--- Paul Isambert - zappathustra AT free DOT fr - July 2011
+-- Paul Isambert - zappathustra AT free DOT fr - December 2011
+--
+-- Beware, this is written with Gates. Please read the Gates doc if
+-- you want to understand something.
local find, gsub, match, sub = string.find, string.gsub, string.match, string.sub
-local insert, sort = table.insert, table.sort
+local insert, sort, remove = table.insert, table.sort, table.remove
+local io_open = io.open
+require("gates.lua")
--- Utility function sorting patterns by length (alphabetically if they are of
--- equal length).
-local function string_order (a, b)
- local a, b = a.pattern, b.pattern
- return #a == #b and a < b or #a > #b
-end
+interpreter = gates.new("interpreter")
-interpreter = {
-- *** interpreter.active ***
-- Following paragraphs (as defined by interpreter.paragraph) are interpreted
-- iff this is not set to false.
- active = true,
+interpreter.active = true
-- *** interpreter.default_class ***
-- Sets the default class for patterns which are added without specifying the
-- class. Default 1.
- default_class = 1
- }
-local _classes = {}
+interpreter.default_class = 1
+
+interpreter.core = {
+ classes = {}, -- The classes of patterns.
+ lines = {}, -- The lines of the paragraph.
+ reader = gates.new("interpreter_reader"), -- The main processing functions.
+ tools = gates.new("interpreter_tools")} -- Auxiliary functions.
+
+-- Utility function sorting patterns by length (alphabetically if they are of
+-- equal length).
+function interpreter.core.tools.sort (a, b)
+ local a, b = a.pattern, b.pattern
+ return #a == #b and a < b or #a > #b
+end
-- *** interpreter.add_pattern (table) ***
-- Creates pattern <table>, which can contain the following entries:
@@ -47,29 +57,41 @@ local _classes = {}
-- always applied last). If <class> is not given, the
-- default_class number is used. Classes must be numbered
-- consecutively.
-function interpreter.add_pattern (tb)
- local class = tb.class or interpreter.default_class
- interpreter.set_class(class, {})
- setmetatable(tb, _classes[class].meta)
- if tb.nomagic then
- tb.pattern = interpreter.nomagic(tb.pattern)
- end
- insert(_classes[class], tb)
- sort(_classes[class], string_order)
- return tb
-end
+interpreter.list{"add_pattern",
+ {"ensure_class",
+ function (tb)
+ local class = tb.class or interpreter.default_class
+ interpreter.set_class(class, {})
+ setmetatable(tb, interpreter.core.classes[class].meta)
+ return tb, class
+ end},
+ {"apply_nomagic", conditional = function (tb) return tb.nomagic end,
+ autoreturn = true,
+ function (tb, class)
+ tb.pattern = interpreter.nomagic(tb.pattern)
+ end},
+ {"insert_pattern", autoreturn = true,
+ {"do_insert", autoreturn = true,
+ function (tb, class)
+ insert(interpreter.core.classes[class], tb)
+ end},
+ {"sort_class", autoreturn = true,
+ function (tb, class)
+ sort(interpreter.core.classes[class], interpreter.core.tools.sort)
+ end}}}
-- *** interpreter.set_class (number, table) ***
-- Sets default values (of the table normally specified in add_pattern) for
-- patterns of class <number>; patterns added to this class can still specify
-- different values, which will override defaults. In other words, this is a
-- metatable for patterns (which are tables) of that class.
-function interpreter.set_class(num, tb)
- _classes[num] = _classes[num] or { meta = { __index = function (_, k) return _classes[num].meta[k] end } }
- for a, b in pairs(tb) do
- _classes[num].meta[a] = b
- end
- return _classes[num]
+function interpreter.set_class (num, tb)
+ interpreter.core.classes[num] = interpreter.core.classes[num] or
+ { meta = { __index = function (_, k) return interpreter.core.classes[num].meta[k] end } }
+ for a, b in pairs(tb) do
+ interpreter.core.classes[num].meta[a] = b
+ end
+ return interpreter.core.classes[num]
end
-- Class 0 must exist since it is always used at the end of the paragraph.
@@ -78,7 +100,7 @@ interpreter.set_class(0, {})
-- *** interpreter.nomagic (string) ***
-- Turns a normal string into a string with magic characters escaped, so it
-- can be used as a pattern.
-local magic_characters = {
+interpreter.core.tools.magic_characters = {
["^"] = "%^",
["$"] = "%$",
["("] = "%(",
@@ -92,11 +114,9 @@ local magic_characters = {
["-"] = "%-",
["?"] = "%?",
}
---function interpreter.nomagic (str)
--- return gsub(str, ".", magic_characters)
---end
function interpreter.nomagic (str)
local i, s = 1, ""
+ local magic_characters = interpreter.core.tools.magic_characters
while i <= #str do
local c, c2, c3 = sub(str, i, i), sub(str, i + 1, i + 1), sub(str, i + 2, i + 2)
i = i + 1
@@ -114,28 +134,28 @@ function interpreter.nomagic (str)
end
return s
end
+
-- *** interpreter.protect ([spec]) ***
-- Protects a set of lines in a paragraph; a protected line won't be
-- interpreted. If <spec> is a number, this protects line <spec> in the current
-- paragraph; if <spec> is true, this protects the entire current paragraph. Of
-- course, patterns that were applied to the line(s) or paragraph before
-- protection happened aren't undone.
-local _protected
function interpreter.protect (num)
if type(num) == "number" then
- if type(_protected) ~= "boolean" then
- _protected = _protected or {}
- _protected[num] = true
+ if type(interpreter.core.reader.protected) ~= "boolean" then
+ interpreter.core.reader.protected = interpreter.core.reader.protected or {}
+ interpreter.core.reader.protected[num] = true
end
else
- _protected = true
+ interpreter.core.reader.protected = true
end
end
-- Utility function making a replacement in a string but only from a certain
-- position and only once. We can't let gsub unrestricted, because some
-- part(s) of the string might be protected.
-local function xsub (str, num, patt, rep)
+function interpreter.core.tools.xsub (str, num, patt, rep)
return sub(str, 1, num-1) .. gsub(sub(str, num), patt, rep, 1)
end
@@ -160,7 +180,7 @@ end
-- interpreter.escape.
local P, Cf, Cg, Cp, Ct, V = lpeg.P, lpeg.Cf, lpeg.Cg, lpeg.Cp, lpeg.Ct, lpeg.V
local _grammar
-local function _protector (str, index)
+function interpreter.core.tools.protector (str, index)
local protections = Cf(Ct("") * Cg{ _grammar + 1 * V(1) }^1, rawset)
protections = protections:match(str)
if protections then
@@ -196,93 +216,21 @@ end
-- "this won't be *processed*" will be passed to TeX (note that the escape
-- character has disappeared).
-local function get_index (str, patt, index)
+function interpreter.core.tools.get_index (str, patt, index)
index = find(str, patt, index)
if index then
if sub(str, index-1, index-1) == interpreter.escape then
- return get_index(str, patt, index + 1)
+ return interpreter.core.tools.get_index(str, patt, index + 1)
elseif _grammar then
local right
- index, right = _protector(str, index, patt)
- return index or get_index(str, patt, right + 1)
+ index, right = interpreter.core.tools.protector(str, index, patt)
+ return index or interpreter.core.tools.get_index(str, patt, right + 1)
else
return index
end
end
end
--- The main function that applies all the patterns in a <class> on a given
--- paragraph.
-local function process_buffer (buffer, class)
- for _, tb in ipairs(class) do
- local num, idx = 1, 1
- while num <= #buffer do
- local line = type(buffer[num]) == "string" and buffer[num]
- if line then
- local index = get_index(line, tb.pattern, idx)
- if index then
- if tb.call then
- local l, o = tb.call(buffer, num, index, tb)
- if o then
- num, idx = l, o
- elseif l then
- idx = l
- end
- elseif tb.replace then
- buffer[num] = xsub(line, index, tb.pattern, tb.replace)
- idx = index + (tb.offset or 0)
- end
- -- If there was a function applied, protection might have been
- -- enforced. Acts accordingly.
- if _protected then
- if type(_protected) == "table" then
- for n, _ in pairs(_protected) do
- if type(buffer[n]) == "string" then
- buffer[n] = {buffer[n]}
- end
- end
- if _protected[num] then
- num = num + 1
- end
- else
- break
- end
- end
- else
- num = num + 1
- idx = 1
- end
- else
- num = num + 1
- idx = 1
- end
- end
- end
-end
-
--- The functions that iterates the previous one for each class. Note that
--- class 0 is processed last.
-local function read_buffer (buffer)
- for _, class in ipairs(_classes) do
- if type(_protected) ~= "boolean" then
- process_buffer(buffer, class)
- end
- end
- if type(_protected) ~= "boolean" then
- process_buffer(buffer, _classes[0])
- end
- _protected = nil
- -- Changes protected lines (tables) back to strings.
- for n, _ in ipairs(buffer) do
- if type(buffer[n]) == "table" then
- buffer[n] = buffer[n][1]
- end
- end
- return buffer
-end
-
-local lines = { }
-
-- *** interpreter.paragraph ***
-- The pattern that defines a line acting as a paragraph boundary,
-- prompting Interpreter to process the lines gathered up to now. Default is a
@@ -302,69 +250,143 @@ interpreter.direct = "%%%%I%s+"
-- At last, the function to be registered in open_read_file, defining the
-- function that reads a file.
-function interpreter.read_file (fname)
- -- *** interpreter.unregister () ***
- -- The function used to remove read_file from the "open_read_file" callback.
- -- Uses callback.register by default, or luatexbase.remove_from_callback if
- -- detected.
- if not interpreter.unregister then
- if luatexbase and luatexbase.remove_from_callback then
- function interpreter.unregister ()
- luatexbase.remove_from_callback("open_read_file", "interpreter")
- end
- else
- function interpreter.unregister ()
- callback.register("open_read_file", nil)
- end
- end
- end
- interpreter.unregister()
- local f, line = io.open(fname), true
- local read_line = function ()
-
- -- If no lines is left in store, we gather a new paragraph.
- if #lines == 0 then
- local line = f:read ()
- while line do
- -- Checks for direct code.
- if interpreter.direct and match(line, "^" .. interpreter.direct) then
- loadstring(gsub(line, "^" .. interpreter.direct, ""))()
- line = ""
- end
- table.insert(lines, line)
- -- Checks for paragraph boundary, which breaks line gathering.
- if gsub(line, interpreter.paragraph, "") == "" then
- break
- end
- line = f:read ()
- end
-
- -- Pass the paragraph (a table of strings) to the bug functions above,
- -- if interpreter is active; then remove escape characters, if any.
- if interpreter.active then
- lines = read_buffer (lines)
- if interpreter.escape then
- for num, line in ipairs(lines) do
- lines[num] = gsub(line, interpreter.escape, "")
- end
- end
- end
- end
- return lines and table.remove (lines, 1)
- end
- return {reader = read_line}
-end
+interpreter.core.reader.list{"read_file",
+ {"make_paragraph", conditional = function () return #interpreter.core.lines == 0 end,
+ {"aggregate_lines", loopuntil = function (_, line) return not line or gsub(line, "^" .. interpreter.paragraph .. "$", "") == "" end,
+ {"read_line",
+ function (f)
+ return f, f:read()
+ end},
+ {"check_direct", conditional = function (_, line) return line and interpreter.direct end,
+ function (f, line)
+ if match(line, "^" .. interpreter.direct) then
+ loadstring(gsub(line, "^" .. interpreter.direct, ""))()
+ line = ""
+ end
+ return f, line
+ end},
+ {"insert_line", conditional = function (_, line) return line end,
+ autoreturn = true,
+ function (f, line)
+ insert(interpreter.core.lines, line)
+ end}},
+ {"apply_classes", conditional = function () return #interpreter.core.lines > 0 and interpreter.active end,
+ {"initialize",
+ function ()
+ return 1, 1, 1, 1
+ end},
+ {"pass_class", loop = function (c) return type(interpreter.core.reader.protected) ~= "boolean"
+ and c <= #interpreter.core.classes end,
+ {"pass_pattern", loop = function (c, p) return type(interpreter.core.reader.protected) ~= "boolean"
+ and p <= #interpreter.core.classes[c] end,
+ {"pass_line", loop = function (c, p, l) return type(interpreter.core.reader.protected) ~= "boolean"
+ and l <= #interpreter.core.lines end,
+ {"check_index", loop = function (c, p, l, i)
+ return (type(interpreter.core.lines[l]) == "string" and type(interpreter.core.reader.protected) ~= "boolean")
+ and interpreter.core.tools.get_index(interpreter.core.lines[l], interpreter.core.classes[c][p].pattern, i)
+ end,
+ {"call", conditional = function (c, p) return interpreter.core.classes[c][p].call end,
+ function(c, p, l, i)
+ local line, pattern = interpreter.core.lines[l], interpreter.core.classes[c][p]
+ local index = interpreter.core.tools.get_index(line, pattern.pattern, i)
+ local L, O = pattern.call(interpreter.core.lines, l, index, pattern)
+ if O then
+ l, i = L, O
+ elseif L then
+ i = L
+ end
+ return c, p, l, i
+ end},
+ {"replace", conditional = function (c, p) return not interpreter.core.classes[c][p].call
+ and interpreter.core.classes[c][p].replace end,
+ function(c, p, l, i)
+ local line, pattern = interpreter.core.lines[l], interpreter.core.classes[c][p]
+ local index = interpreter.core.tools.get_index(line, pattern.pattern, i)
+ interpreter.core.lines[l] = interpreter.core.tools.xsub(line, index, pattern.pattern, pattern.replace)
+ i = index + (pattern.offset or 0)
+ return c, p, l, i
+ end},
+ {"protect", conditional = function () return type(interpreter.core.reader.protected) == "table" end,
+ autoreturn = true,
+ function ()
+ for n, _ in pairs(interpreter.core.reader.protected) do
+ if type(interpreter.core.lines[n]) == "string" then
+ interpreter.core.lines[n] = {interpreter.core.lines[n]}
+ end
+ end
+ end}},
+ {"increment_line", function (c, p, l, i) return c, p, l+1, 1 end}},
+ {"increment_pattern", function (c, p, l) return c, p+1, 1, 1 end}},
+ {"increment_class", function (c) return c+1, 1, 1, 1 end}},
+ {"apply_class0", conditional = function () return type(interpreter.core.reader.protected) ~= "boolean" end,
+ {"initialize0",
+ function ()
+ return 0, 1, 1, 1
+ end},
+ {"pass_pattern", loop = function (c, p) return type(interpreter.core.reader.protected) ~= "boolean"
+ and p <= #interpreter.core.classes[c] end}},
+ {"unprotect",
+ {"undo_protected",
+ function ()
+ interpreter.core.reader.protected = nil
+ end},
+ {"unprotect_lines",
+ function ()
+ for n, l in ipairs(interpreter.core.lines) do
+ if type(l) == "table" then
+ interpreter.core.lines[n] = l[1]
+ end
+ end
+ end}},
+ {"remove_escape", conditional = function () return interpreter.escape end,
+ function ()
+ for num, line in ipairs(interpreter.core.lines) do
+ interpreter.core.lines[num] = gsub(line, interpreter.escape, "")
+ end
+ end}}},
+ {"return_line",
+ function ()
+ return remove(interpreter.core.lines, 1)
+ end}}
+
+interpreter.core.reader.list{"input",
+ -- *** interpreter.unregister () ***
+ -- The function used to remove read_file from the "open_read_file" callback.
+ -- Uses callback.register by default, or luatexbase.remove_from_callback if
+ -- detected.
+ {"unregister", autoreturn = true,
+ {"set_unregister", conditional = function () return interpreter.type"unregister" == 0 end,
+ function ()
+ if luatexbase and luatexbase.remove_from_callback then
+ function interpreter.unregister ()
+ luatexbase.remove_from_callback("open_read_file", "interpreter")
+ end
+ else
+ function interpreter.unregister ()
+ callback.register("open_read_file", nil)
+ end
+ end
+ end},
+ {"use_unregister",
+ function () -- You can't use the `unregister' gate directly, because it isn't created yet.
+ interpreter.unregister()
+ end}},
+ {"open_file",
+ function (fname)
+ return io_open(fname)
+ end},
+ {"set_reader",
+ function (f)
+ return {reader = function () return interpreter.core.reader.read_file(f) end}
+ end}}
--- *** interpreter.reset () ***
--- Resets to default values; mostly to be used when calling a new
--- interpretation with \interpretfile.
function interpreter.reset ()
interpreter.active = true
interpreter.default_class = 1
- _classes = {}
+ interpreter.core.classes = {}
interpreter.set_class(0, {})
_grammar = nil
interpreter.escape = nil
diff --git a/Master/texmf-dist/tex/luatex/interpreter/interpreter.sty b/Master/texmf-dist/tex/luatex/interpreter/interpreter.sty
index 9ffcad52f1c..94f94443ad9 100644
--- a/Master/texmf-dist/tex/luatex/interpreter/interpreter.sty
+++ b/Master/texmf-dist/tex/luatex/interpreter/interpreter.sty
@@ -5,10 +5,10 @@
% Author: Paul Isambert.
% E-mail: zappathustra AT free DOT fr
% Comments and suggestions are welcome.
-% Date: July 2011.
+% Date: December 2011.
\NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{interpreter}[2011/08/07 v.1.0 Preprocessing input files on the fly.]
+\ProvidesPackage{interpreter}[2011/12/03 v.1.1 Preprocessing input files on the fly.]
% Needed to prevent LaTeX check input, which would call open_read_file and
% thus remove Interpreter's main function (which is removed as soon as it is
% used).
diff --git a/Master/texmf-dist/tex/luatex/interpreter/interpreter.tex b/Master/texmf-dist/tex/luatex/interpreter/interpreter.tex
index 634c1ef826f..3f58083dd72 100644
--- a/Master/texmf-dist/tex/luatex/interpreter/interpreter.tex
+++ b/Master/texmf-dist/tex/luatex/interpreter/interpreter.tex
@@ -1,9 +1,10 @@
% This is the main TeX file for the Interpreter package.
% Further information in interpreter-doc.pdf or interpreter-doc.txt.
%
-% Paul Isambert - zappathustra AT free DOT fr - July 2011
-\csname Oh no, I won't be loaded twice!\endcsname
-\expandafter\let\csname Oh no, I won't be loaded twice!\endcsname\endinput
+% Paul Isambert - zappathustra AT free DOT fr - December 2011
+%
+\csname Oh no, Interpreter won't be loaded twice!\endcsname
+\expandafter\let\csname Oh no, Interpreter won't be loaded twice!\endcsname\endinput
\directlua{require("interpreter.lua")}
\def\interpretergobble#1{}
\unless\ifdefined\interpreterinput
@@ -15,7 +16,7 @@
if f then
interpreter.reset()
dofile(f)
- if not interpreter.register then
+ if interpreter.type"register" == 0 then
if luatexbase and luatexbase.add_to_callback then
function interpreter.register (f)
luatexbase.add_to_callback("open_read_file", f, "interpreter")
@@ -26,11 +27,11 @@
end
end
end
- interpreter.register(interpreter.read_file)
+ interpreter.register(interpreter.core.reader.input)
tex.print([[\noexpand\interpreterinput]])
else
tex.error("I can't find file `i-#1.lua'. I won't input file `#2'")
- tex.print([[\noexpand\igobble]])
+ tex.print([[\noexpand\interpretergobble]])
end
}{#2}%
}