diff options
author | Norbert Preining <norbert@preining.info> | 2022-01-01 03:00:53 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2022-01-01 03:00:53 +0000 |
commit | 39877769ecfa442af71b4ddd1c266f401957cdd8 (patch) | |
tree | 06aaa106eb40b7e0b2ee91073f636bb5975e502c /macros/luatex/generic/luakeys/luakeys.lua | |
parent | 2c09a813df08895666d3755db66718dcd501aee5 (diff) |
CTAN sync 202201010300
Diffstat (limited to 'macros/luatex/generic/luakeys/luakeys.lua')
-rw-r--r-- | macros/luatex/generic/luakeys/luakeys.lua | 227 |
1 files changed, 126 insertions, 101 deletions
diff --git a/macros/luatex/generic/luakeys/luakeys.lua b/macros/luatex/generic/luakeys/luakeys.lua index 05b3d94e1f..c5f51fcfad 100644 --- a/macros/luatex/generic/luakeys/luakeys.lua +++ b/macros/luatex/generic/luakeys/luakeys.lua @@ -1,5 +1,5 @@ -- luakeys.lua --- Copyright 2021 Josef Friedrich +-- Copyright 2021-2022 Josef Friedrich -- -- This work may be distributed and/or modified under the -- conditions of the LaTeX Project Public License, either version 1.3c @@ -26,11 +26,20 @@ -- * `patt1 * patt2` = `expression1 expression2`: Sequence -- * `patt1 + patt2` = `expression1 / expression2`: Ordered choice -- --- * [TUGboat article: Parsing complex data formats in LuaTEX with LPEG](https://tug.org/TUGboat/tb40-2/tb125menke-lpeg.pdf) +-- * [TUGboat article: Parsing complex data formats in LuaTEX with LPEG](https://tug.org/TUGboat/tb40-2/tb125menke-Patterndf) -- -- @module luakeys local lpeg = require('lpeg') +local Variable = lpeg.V +local Pattern = lpeg.P +local Set = lpeg.S +local Range = lpeg.R +local CaptureGroup = lpeg.Cg +local CaptureFolding = lpeg.Cf +local CaptureTable = lpeg.Ct +local CaptureConstant = lpeg.Cc +local CaptureSimple = lpeg.C if not tex then tex = {} @@ -49,70 +58,18 @@ local result_store = {} -- @treturn userdata The parser. local function generate_parser(options) -- Optional whitespace - local white_space = lpeg.S(' \t\n\r') + local white_space = Set(' \t\n\r') --- Match literal string surrounded by whitespace local ws = function(match) - return white_space^0 * lpeg.P(match) * white_space^0 + return white_space^0 * Pattern(match) * white_space^0 end - local boolean_true = - lpeg.P('true') + - lpeg.P('TRUE') + - lpeg.P('True') - - local boolean_false = - lpeg.P('false') + - lpeg.P('FALSE') + - lpeg.P('False') - - local number = lpeg.P({'number', - number = - lpeg.V('int') * - lpeg.V('frac')^-1 * - lpeg.V('exp')^-1, - - int = lpeg.V('sign')^-1 * ( - lpeg.R('19') * lpeg.V('digits') + lpeg.V('digit') - ), - - sign = lpeg.S('+-'), - digit = lpeg.R('09'), - digits = lpeg.V('digit') * lpeg.V('digits') + lpeg.V('digit'), - frac = lpeg.P('.') * lpeg.V('digits'), - exp = lpeg.S('eE') * lpeg.V('sign')^-1 * lpeg.V('digits'), - }) - - --- Define data type dimension. - -- - -- @return Lpeg patterns - local function build_dimension_pattern() - local sign = lpeg.S('-+') - local integer = lpeg.R('09')^1 - local tex_number = (integer^1 * (lpeg.P('.') * integer^1)^0) + (lpeg.P('.') * integer^1) - - -- https://raw.githubusercontent.com/latex3/lualibs/master/lualibs-util-dim.lua - local unit = - lpeg.P('bp') + lpeg.P('BP') + - lpeg.P('cc') + lpeg.P('CC') + - lpeg.P('cm') + lpeg.P('CM') + - lpeg.P('dd') + lpeg.P('DD') + - lpeg.P('em') + lpeg.P('EM') + - lpeg.P('ex') + lpeg.P('EX') + - lpeg.P('in') + lpeg.P('IN') + - lpeg.P('mm') + lpeg.P('MM') + - lpeg.P('nc') + lpeg.P('NC') + - lpeg.P('nd') + lpeg.P('ND') + - lpeg.P('pc') + lpeg.P('PC') + - lpeg.P('pt') + lpeg.P('PT') + - lpeg.P('sp') + lpeg.P('SP') - - local dimension = (sign^0 * white_space^0 * tex_number * white_space^0 * unit) - + local capture_dimension = function (input) if options.convert_dimensions then - return dimension / tex.sp + return tex.sp(input) else - return lpeg.C(dimension) + return input end end @@ -142,59 +99,119 @@ local function generate_parser(options) end end - return lpeg.P({ + return Pattern({ 'list', - list = lpeg.Cf( - lpeg.Ct('') * lpeg.V('list_item')^0, + -- list_item* + list = CaptureFolding( + CaptureTable('') * Variable('list_item')^0, add_to_table ), + -- '{' list '}' list_container = - ws('{') * lpeg.V('list') * ws('}'), + ws('{') * Variable('list') * ws('}'), + -- ( list_container / key_value_pair / value ) ','? list_item = - lpeg.Cg( - lpeg.V('key_value_pair') + - lpeg.V('value') + CaptureGroup( + Variable('list_container') + + Variable('key_value_pair') + + Variable('value') ) * ws(',')^-1, + -- key '=' (list_container / value) key_value_pair = - (lpeg.V('value') * ws('=')) * (lpeg.V('list_container') + lpeg.V('value')), + (Variable('key') * ws('=')) * (Variable('list_container') + Variable('value')), - value = - lpeg.V('boolean') + - lpeg.V('dimension') + - lpeg.V('number') + - lpeg.V('string_quoted') + - lpeg.V('string_unquoted') + - lpeg.V('array'), + -- number / string_quoted / string_unquoted + key = + Variable('number') + + Variable('string_quoted') + + Variable('string_unquoted'), - array = - ws('{') * lpeg.Ct((lpeg.V('value') * ws(',')^-1)^0) * ws('}'), + -- boolean !value / dimension !value / number !value / string_quoted !value / string_unquoted + -- !value -> Not-predicate -> * -Variable('value') + value = + Variable('boolean') * -Variable('value') + + Variable('dimension') * -Variable('value') + + Variable('number') * -Variable('value') + + Variable('string_quoted') * -Variable('value') + + Variable('string_unquoted'), + -- boolean_true / boolean_false boolean = - boolean_true * lpeg.Cc(true) + - boolean_false * lpeg.Cc(false), - - dimension = build_dimension_pattern(), + ( + Variable('boolean_true') * CaptureConstant(true) + + Variable('boolean_false') * CaptureConstant(false) + ), + + boolean_true = + Pattern('true') + + Pattern('TRUE') + + Pattern('True'), + + boolean_false = + Pattern('false') + + Pattern('FALSE') + + Pattern('False'), + + dimension = ( + Variable('sign')^0 * white_space^0 * + Variable('tex_number') * white_space^0 * + Variable('unit') + ) / capture_dimension, number = - white_space^0 * (number / tonumber) * white_space^0, + (white_space^0 * (Variable('lua_number') / tonumber) * white_space^0) , + + tex_number = + (Variable('integer')^1 * (Pattern('.') * Variable('integer')^1)^0) + + (Pattern('.') * Variable('integer')^1), + + -- 'bp' / 'BP' / 'cc' / etc. + -- https://raw.githubusercontent.com/latex3/lualibs/master/lualibs-util-dim.lua + unit = + Pattern('bp') + Pattern('BP') + + Pattern('cc') + Pattern('CC') + + Pattern('cm') + Pattern('CM') + + Pattern('dd') + Pattern('DD') + + Pattern('em') + Pattern('EM') + + Pattern('ex') + Pattern('EX') + + Pattern('in') + Pattern('IN') + + Pattern('mm') + Pattern('MM') + + Pattern('nc') + Pattern('NC') + + Pattern('nd') + Pattern('ND') + + Pattern('pc') + Pattern('PC') + + Pattern('pt') + Pattern('PT') + + Pattern('sp') + Pattern('SP'), + + lua_number = + Variable('int') * + Variable('frac')^-1, + + int = Variable('sign')^-1 * ( + Range('19') * Variable('integer') + Variable('integer') + ), + + frac = Pattern('.') * Variable('integer'), + sign = Set('-+'), + integer = Range('09')^1, + -- '"' ('\"' / !'"')* '"' string_quoted = - white_space^0 * lpeg.P('"') * - lpeg.C((lpeg.P('\\"') + 1 - lpeg.P('"'))^0) * - lpeg.P('"') * white_space^0, + white_space^0 * Pattern('"') * + CaptureSimple((Pattern('\\"') + 1 - Pattern('"'))^0) * + Pattern('"') * white_space^0, string_unquoted = white_space^0 * - lpeg.C( - lpeg.V('word_unquoted')^1 * - (lpeg.S(' \t')^1 * lpeg.V('word_unquoted')^1)^0) * + CaptureSimple( + Variable('word_unquoted')^1 * + (Set(' \t')^1 * Variable('word_unquoted')^1)^0) * white_space^0, - word_unquoted = (1 - white_space - lpeg.S('{},='))^1; + word_unquoted = (1 - white_space - Set('{},='))^1 }) end @@ -306,36 +323,44 @@ local function stringify(input, for_tex) local function stringify_inner(input, depth) local output = {} - depth = depth or 0; + depth = depth or 0 local function add(depth, text) table.insert(output, string.rep(indent, depth) .. text) end + local function format_key(key) + if (type(key) == 'number') then + return string.format('[%s]', key) + else + return string.format('[\'%s\']', key) + end + end + if type(input) ~= 'table' then return tostring(input) end for key, value in pairs(input) do if (key and type(key) == 'number' or type(key) == 'string') then - key = string.format('[\'%s\']', key); + key = format_key(key) if (type(value) == 'table') then if (next(value)) then - add(depth, key .. ' = ' .. start_bracket); - add(0, stringify_inner(value, depth + 1, for_tex)); + add(depth, key .. ' = ' .. start_bracket) + add(0, stringify_inner(value, depth + 1)) add(depth, end_bracket .. ','); else - add(depth, key .. ' = ' .. start_bracket .. end_bracket .. ','); + add(depth, key .. ' = ' .. start_bracket .. end_bracket .. ',') end else if (type(value) == 'string') then - value = string.format('\'%s\'', value); + value = string.format('\'%s\'', value) else - value = tostring(value); + value = tostring(value) end - add(depth, key .. ' = ' .. value .. ','); + add(depth, key .. ' = ' .. value .. ',') end end end @@ -460,14 +485,14 @@ return { if (key and type(key) == 'string') then if (type(value) == 'table') then if (next(value)) then - add(key .. '={'); - add(render_inner(value)); - add('},'); + add(key .. '={') + add(render_inner(value)) + add('},') else - add(key .. '={},'); + add(key .. '={},') end else - add(key .. '=' .. tostring(value) .. ','); + add(key .. '=' .. tostring(value) .. ',') end else add(tostring(value) .. ',') |