diff options
author | Karl Berry <karl@freefriends.org> | 2011-12-03 23:36:05 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2011-12-03 23:36:05 +0000 |
commit | f884cf55d378c5815215caa25f5fb7ff1d8f83cd (patch) | |
tree | 62ee34c74798cbac6b6fbe0448713249097c08a9 /Master/texmf-dist/tex/luatex/interpreter | |
parent | e57737e5e749b3cdff002d8ca4c32c865ec5e03e (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/tex/luatex/interpreter')
3 files changed, 204 insertions, 181 deletions
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}% } |