texlive[61652] Master/texmf-dist: yamlvars (18jan22)

commits+karl at tug.org commits+karl at tug.org
Tue Jan 18 22:31:33 CET 2022


Revision: 61652
          http://tug.org/svn/texlive?view=revision&revision=61652
Author:   karl
Date:     2022-01-18 22:31:33 +0100 (Tue, 18 Jan 2022)
Log Message:
-----------
yamlvars (18jan22)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/lualatex/yamlvars/README.md
    trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.pdf
    trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.tex
    trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.lua
    trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.sty

Removed Paths:
-------------
    trunk/Master/texmf-dist/tex/lualatex/yamlvars/tinyyaml.lua

Modified: trunk/Master/texmf-dist/doc/lualatex/yamlvars/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/yamlvars/README.md	2022-01-18 21:31:12 UTC (rev 61651)
+++ trunk/Master/texmf-dist/doc/lualatex/yamlvars/README.md	2022-01-18 21:31:33 UTC (rev 61652)
@@ -1,13 +1,13 @@
-# YAMLvars -- parse a YAML document and create LaTeX definitons
+# YAMLvars -- parse a YAML document and create definitions in LaTeX
 
 This LuaLaTeX package provides a YAML parser and some functions to declare and define LaTeX definitions using YAML files. 
-It uses the [`tinyyaml`](https://github.com/api7/lua-tinyyaml) parser.
+It uses the [`markdown-tinyyaml`](https://github.com/api7/lua-tinyyaml) parser that is shipped with the [`markdown`](https://ctan.org/pkg/markdown) package.
 
 
 
 # License
 
-Copyright (C) 2021 Kale Ewasiuk
+Copyright (C) 2021-2022 Kale Ewasiuk
 
 Permission is hereby granted, free of charge, to any person obtaining a copy
 of this software and associated documentation files (the "Software"), to deal

Modified: trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.tex
===================================================================
--- trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.tex	2022-01-18 21:31:12 UTC (rev 61651)
+++ trunk/Master/texmf-dist/doc/lualatex/yamlvars/YAMLvars.tex	2022-01-18 21:31:33 UTC (rev 61652)
@@ -1,6 +1,6 @@
 % Kale Ewasiuk (kalekje at gmail.com)
-% 2021-12-15
-% Copyright (C) 2021 Kale Ewasiuk
+% 2022-01-18
+% Copyright (C) 2021-2022 Kale Ewasiuk
 %
 % Permission is hereby granted, free of charge, to any person obtaining a copy
 % of this software and associated documentation files (the "Software"), to deal
@@ -29,6 +29,7 @@
 \newcommand{\cmd}[1]{\texttt{\detokenize{#1}}}
 \newcommand{\qcmd}[1]{``\cmd{#1}''}
 \usepackage{url}
+\usepackage{xcolor}
 \usepackage{showexpl}
 \lstset{explpreset={justification=\raggedright,pos=r,wide=true}}
 \setlength\ResultBoxRule{0mm}
@@ -53,13 +54,26 @@
 
 
 
-\usepackage{YAMLvars}
+\usepackage[overwritedefs]{YAMLvars}
 \title{YAMLvars}
 \subtitle{a YAML variable parser for LuaLaTeX}
 
 \begin{document}
+
+
 \maketitle
 
+
+%%%%%
+
+%{NOTE::: \Huge todo use LTXexample and improve examples/testing\\
+%todo need way better error tracing for this pkg}
+
+
+
+%%%%%%%%%%%
+
+
 YAMLvars is a LuaLaTeX-based package to help make definitions or produce LaTeX code using a YAML file.
 This package might be useful for you if you want to batch create docummnts
 by pushing various sets YAML data to a fixed LaTeX template,
@@ -85,25 +99,31 @@
 This option is offered to help with automation scripts.
 An example is showin in Section \ref{example}.
 
-\leavevmode\llap{\texttt{allowundeclared}\ \ \ }%
+\hspace*{3ex}\llcmd{allowundeclared}%
 It might be helpful to define something in your YAML parsing doc without declaring it.
 If you want this flexibility, use this setting. Note that eisting definitions will not be overwritten and an error
 will br thrown if the name exists.
 
+\llcmd{overwritedefs}Danger! This will allow you to \cmd{gdef} commands with YAML. Caution should be taken to not set definitions like \cmd{begin}, \cmd{section}, etc.
+
 \section{Dependencies}
-This package contains the \texttt{tinyyaml} Lua package.
-If you want to use it for other purposes, you can bring it into Lua by either:
+%This package contains the \texttt{tinyyaml} Lua package.
+\llcmd{Note:}This package requires that the \cmd{markdown} (\url{https://ctan.org/pkg/markdown}) be installed. This package does not use the package in its entirety, but rather depends on the YAML interpreter it comes with: \cmd{markdown-tinyyaml.lua}. This dependency is chosen to avoid redundancy in your TeX installation and align development of the \cmd{tinyyaml} Lua package.
+If you want to use the YAML interpreter for other purposes, you can bring it into Lua by either:
 \begin{verbatim}
 \directlua{yaml = YAMLvars.yaml}           or
-\directlua{yaml = require('tinyyaml')}
+\directlua{yaml = require'markdown-tinyyaml'}
 \end{verbatim}
-The distribution: \url{https://github.com/peposso/lua-tinyyaml}\\
+The distribution: \url{https://github.com/api7/lua-tinyyaml}\\
 The YAML specification: \url{https://yaml.org/spec/}\\
 
-Many of the ``transform'' and ``processing'' functions built-in to this package rely on other packages.
-\texttt{penlight},
-\texttt{xspace},
-\texttt{hyperref} for example.
+Many of the ``transform'' and ``processing'' functions built-in to this package rely on other packages,
+like
+\texttt{hyperref}, for example, but it is not loaded, and this package will only load
+\cmd{penlight},
+\cmd{luacode},
+\cmd{xspace}, and
+\cmd{etoolbox}.
 
 \section{Declaring variables}
 A declaration file can either be parsed with the command \texttt{declareYAMLvarsFile} command,
@@ -117,6 +137,9 @@
 \texttt{prc} (processing, must be a single string), or \\
 \texttt{dft} (default value, if being defined. Must be a string).
 
+If you want to change the way a variable is initialized, you can change the function
+\cmd{YAMLvars.dec.PRC = function (var) ... end } where \cmd{PRC} is how the variable will be processed (\cmd{gdef}, \cmd{yvdef}, \cmd{length}, or something of your choosing).
+
 The default value for variables is the Lua \texttt{nil}.
 YAMLvars will first check if the definition exists, if so, an error will be thrown
 so that we avoid overwriting.
@@ -210,8 +233,50 @@
 \texttt{title, author, date} to set \texttt{\textbackslash @title, \textbackslash @author, \textbackslash @date},
 respectively
 
-    \section{Example}\label{example}
+\pagebreak
 
+\section{Some Examples}
+
+\begin{LTXexample}
+%! language = yaml
+\begin{declareYAMLvars}
+address:
+  xfm:
+    - list2nl
+    - = x..'!!!'
+name: null
+
+title:
+    xfm:
+        - lb2nl
+#        - / YAMLvars.prvcmd(titletext, YAMLvars.varsvals['atitle']:gsub('\n', ' ')..'\\xspace{}')
+\end{declareYAMLvars}
+
+%! language = yaml
+\begin{parseYAMLvars}
+title: |-
+    A Multiline
+    Monumental Title!
+
+name: Joe Smith
+address:
+  - 1234 Fake St.
+  - City
+\end{parseYAMLvars}
+
+\title
+
+%\titletext!
+
+\name
+
+\address
+
+\end{LTXexample}
+
+\pagebreak
+    \section{Automation Example}\label{example}
+
     Suppose you had a number of bills of sales in yaml format and wanted to produce some nice pdfs.
     The following code shows how this could be done.
 

Modified: trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.lua	2022-01-18 21:31:12 UTC (rev 61651)
+++ trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.lua	2022-01-18 21:31:33 UTC (rev 61652)
@@ -1,6 +1,6 @@
 --% Kale Ewasiuk (kalekje at gmail.com)
---% 2021-12-15
---% Copyright (C) 2021 Kale Ewasiuk
+--% 2022-01-18
+--% Copyright (C) 2021-2022 Kale Ewasiuk
 --%
 --% Permission is hereby granted, free of charge, to any person obtaining a copy
 --% of this software and associated documentation files (the "Software"), to deal
@@ -61,6 +61,7 @@
 YAMLvars.xfmDefault = {}
 
 YAMLvars.allowUndeclared = false
+YAMLvars.overwritedefs = false
 
 YAMLvars.valTemp = ''
 YAMLvars.varTemp = ''
@@ -69,8 +70,10 @@
 
 YAMLvars.debug = false
 
-YAMLvars.yaml = require('tinyyaml')
+YAMLvars.yaml = require'markdown-tinyyaml' -- note: YAMLvars.sty will have checked existence of this already
+local pl = _G['penlight'] or _G['pl'] -- penlight for this namespace is pl
 
+
 function YAMLvars.debugtalk(s, ss)
     if YAMLvars.debug then
         help_wrt(s, ss)
@@ -77,9 +80,10 @@
     end
 end
 
+
 -- xfm functions (transforms) -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
 function YAMLvars.xfm.addxspace(var, val)
-    return val .. '\\xspace'
+    return val .. '\\xspace{}'
 end
 
 function YAMLvars.xfm.tab2arr(var, val)
@@ -99,11 +103,11 @@
 end
 
 
-function YAMLvars.xfm.arr2itemize(var, val)
+function YAMLvars.xfm.list2items(var, val) -- todo should be list2item
      return pl.List(val):map('\\item '.._1):join(' ')
 end
+YAMLvars.xfm.arr2itemize = YAMLvars.xfm.list2items
 
-
 function YAMLvars.xfm.arrsortAZ(var, val)
      return pl.List(val):sort(pl.operator.strlt)
 end
@@ -126,12 +130,15 @@
 end
 -- todo need distinction beyyween table and penlight list ???
 function YAMLvars.xfm.list2nl(var, val)
-    val = pl.array2d.map_slice1(_1..'\\\\', val, 1,-2)
-    return val:join('')
-    --return pl.tablex.reduce(_1.._2, val, '')
+    return pl.tablex.join(val,'\\\\ ')
 end
 
-function YAMLvars.xfm.lb2nl(var, val)
+    --val = pl.array2d.map_slice1(_1..'\\\\', val, 1,-2)
+    --return val:join('')
+    --return pl.tablex.reduce(_1.._2, val, '')
+
+
+function YAMLvars.xfm.lb2nl(var, val) --linebreak in text 2 new line
     val, _ = val:gsub('\n','\\\\ ')
     return val
 end
@@ -265,7 +272,7 @@
 
 
 function YAMLvars.prvcmd(cs, val) -- provide command via lua
-   if token.is_defined(cs) then
+   if token.is_defined(cs) and (not YAMLvars.overwritedefs) then
         tex.print('\\PackageError{YAMLvars}{Variable '..cs..' already defined, could not declare}{}')
     else
         token.set_macro(cs, val, 'global')
@@ -339,10 +346,12 @@
     return s:gsub('([%A?%-?])('..v1..')([%W?%-?])', '%1'..v2..'%3') -- replace x variables
 end
 
+local _YV_invalid_expression = '\1 invalid expression'
+local _YV_no_return = '\2 no return val'
 local function eval_expr(func, var, val)
-    local s, c = func:gsub('^[=/]', {['/'] = '', ['='] = 'YAMLvars.valTemp = '}, 1) -- / is run code, = sets val = code
+    local s, c = func:gsub('^[=/]', {['/'] = '\2', ['='] = 'YAMLvars.valTemp = '}, 1) -- / is run code, = sets val = code
     if c == 0 then
-        return nil
+        return _YV_invalid_expression
     else
         --help_wrt(s, var)
         --help_wrt(val, var)
@@ -349,11 +358,17 @@
         YAMLvars.valTemp = val
         YAMLvars.varTemp = var
         --help_wrt(s, var)
+        s, c = s:gsub('\2', '') -- strip \2 that might have appeared if / was applied
         s = sub_lua_var(' '..s, 'x', 'YAMLvars.valTemp')
         s = sub_lua_var(s, 'v', 'YAMLvars.varTemp')
         --help_wrt(s, var)
-        loadstring(s)()
-        --help_wrt(val, var)
+        local f, err = pcall(loadstring(s))
+        if not f then
+            tex.print('\\PackageError{YAMLvars}{xfm with "= or /" error on var "'..var..'"}{}') --
+        end
+        if c > 0 then
+            return _YV_no_return
+        end
         return YAMLvars.valTemp
     end
 end
@@ -363,8 +378,9 @@
         local f = YAMLvars.xfm[func]
         if f == nil then
             local val2 =  eval_expr(func, var, val)
-            if val2 == nil then
-                tex.print('\\PackageWarning{YAMLvars}{xfm function "'..func..'" not defined, skipping}{}')
+            if val2 == _YV_invalid_expression then
+                tex.print('\\PackageError{YAMLvars}{xfm function "'..func..'" not defined or invalid expression passed on var "'..var..'"}{}')
+            elseif val == _YV_no_return then
             else
                 val = val2
             end
@@ -374,7 +390,7 @@
     end
     f = YAMLvars.prc[YAMLvars.varspecs[var]['prc']]
     if f == nil then
-        tex.print('\\PackageError{YAMLvars}{prc function "'..YAMLvars.varspecs[var]['prc']..'" not defined}{}')
+        tex.print('\\PackageError{YAMLvars}{prc function "'..YAMLvars.varspecs[var]['prc']..'" on var "'..var..'" not defined}{}')
     end
     f(var, val) -- prc the value of the variable
 end
@@ -383,7 +399,10 @@
     YAMLvars.varsvals = YAMLvars.yaml.parse(y)
     for var, val in pairs(YAMLvars.varsvals) do
         if YAMLvars.varspecs[var] == nil then
-            check_def(var, val)
+            check_def(var, val) -- if not declared
+            -- todo consider free form parse declaring
+            -- variable name: {xfm:, dec:, prc:, val: }
+            -- definitely doable here
         else
             transform_and_prc(var, val)
         end

Modified: trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.sty
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.sty	2022-01-18 21:31:12 UTC (rev 61651)
+++ trunk/Master/texmf-dist/tex/lualatex/yamlvars/YAMLvars.sty	2022-01-18 21:31:33 UTC (rev 61652)
@@ -1,6 +1,6 @@
 % Kale Ewasiuk (kalekje at gmail.com)
-% 2021-12-15
-% Copyright (C) 2021 Kale Ewasiuk
+% 2022-01-18
+% Copyright (C) 2021-2022 Kale Ewasiuk
 %
 % Permission is hereby granted, free of charge, to any person obtaining a copy
 % of this software and associated documentation files (the "Software"), to deal
@@ -23,15 +23,17 @@
 % OR OTHER DEALINGS IN THE SOFTWARE.
 
 
+
 \NeedsTeXFormat{LaTeX2e}
-\ProvidesPackage{YAMLvars}[2021-12-15]
+\ProvidesPackage{YAMLvars}[2022-01-18]
 
+\IfFileExists{markdown-tinyyaml.lua}{}{\PackageError{YAMLvars}{This package requires installation of the 'markdown' package, please install it and try again}{}}
+
 \RequirePackage{luacode}
 \RequirePackage{xspace}
 \RequirePackage{etoolbox}
 \RequirePackage[pl,extras]{penlight}
 
-
 \luadirect{YAMLvars = require('YAMLvars')}
 
 \DeclareOption{useyv}{
@@ -43,7 +45,11 @@
 }
 \DeclareOption{allowundeclared}{
         \luadirect{YAMLvars.allowUndeclared = true}
-}\DeclareOption{debug}{
+}
+\DeclareOption{overwritedefs}{
+        \luadirect{YAMLvars.overwritedefs = true}
+}
+\DeclareOption{debug}{
         \luadirect{YAMLvars.debug = true}
 }
 

Deleted: trunk/Master/texmf-dist/tex/lualatex/yamlvars/tinyyaml.lua
===================================================================
--- trunk/Master/texmf-dist/tex/lualatex/yamlvars/tinyyaml.lua	2022-01-18 21:31:12 UTC (rev 61651)
+++ trunk/Master/texmf-dist/tex/lualatex/yamlvars/tinyyaml.lua	2022-01-18 21:31:33 UTC (rev 61652)
@@ -1,816 +0,0 @@
--------------------------------------------------------------------------------
--- tinyyaml - YAML subset parser
--------------------------------------------------------------------------------
-
-local table = table
-local string = string
-local schar = string.char
-local ssub, gsub = string.sub, string.gsub
-local sfind, smatch = string.find, string.match
-local tinsert, tconcat, tremove = table.insert, table.concat, table.remove
-local setmetatable = setmetatable
-local pairs = pairs
-local type = type
-local tonumber = tonumber
-local math = math
-local getmetatable = getmetatable
-local error = error
-
-local UNESCAPES = {
-  ['0'] = "\x00", z = "\x00", N    = "\x85",
-  a = "\x07",     b = "\x08", t    = "\x09",
-  n = "\x0a",     v = "\x0b", f    = "\x0c",
-  r = "\x0d",     e = "\x1b", ['\\'] = '\\',
-};
-
--------------------------------------------------------------------------------
--- utils
-local function select(list, pred)
-  local selected = {}
-  for i = 0, #list do
-    local v = list[i]
-    if v and pred(v, i) then
-      tinsert(selected, v)
-    end
-  end
-  return selected
-end
-
-local function startswith(haystack, needle)
-  return ssub(haystack, 1, #needle) == needle
-end
-
-local function ltrim(str)
-  return smatch(str, "^%s*(.-)$")
-end
-
-local function rtrim(str)
-  return smatch(str, "^(.-)%s*$")
-end
-
-local function trim(str)
-  return smatch(str, "^%s*(.-)%s*$")
-end
-
--------------------------------------------------------------------------------
--- Implementation.
---
-local class = {__meta={}}
-function class.__meta.__call(cls, ...)
-  local self = setmetatable({}, cls)
-  if cls.__init then
-    cls.__init(self, ...)
-  end
-  return self
-end
-
-function class.def(base, typ, cls)
-  base = base or class
-  local mt = {__metatable=base, __index=base}
-  for k, v in pairs(base.__meta) do mt[k] = v end
-  cls = setmetatable(cls or {}, mt)
-  cls.__index = cls
-  cls.__metatable = cls
-  cls.__type = typ
-  cls.__meta = mt
-  return cls
-end
-
-
-local types = {
-  null = class:def('null'),
-  map = class:def('map'),
-  omap = class:def('omap'),
-  pairs = class:def('pairs'),
-  set = class:def('set'),
-  seq = class:def('seq'),
-  timestamp = class:def('timestamp'),
-}
-
-local Null = types.null
-function Null.__tostring() return 'yaml.null' end
-function Null.isnull(v)
-  if v == nil then return true end
-  if type(v) == 'table' and getmetatable(v) == Null then return true end
-  return false
-end
-local null = Null()
-
-function types.timestamp:__init(y, m, d, h, i, s, f, z)
-  self.year = tonumber(y)
-  self.month = tonumber(m)
-  self.day = tonumber(d)
-  self.hour = tonumber(h or 0)
-  self.minute = tonumber(i or 0)
-  self.second = tonumber(s or 0)
-  if type(f) == 'string' and sfind(f, '^%d+$') then
-    self.fraction = tonumber(f) * math.pow(10, 3 - #f)
-  elseif f then
-    self.fraction = f
-  else
-    self.fraction = 0
-  end
-  self.timezone = z
-end
-
-function types.timestamp:__tostring()
-  return string.format(
-    '%04d-%02d-%02dT%02d:%02d:%02d.%03d%s',
-    self.year, self.month, self.day,
-    self.hour, self.minute, self.second, self.fraction,
-    self:gettz())
-end
-
-function types.timestamp:gettz()
-  if not self.timezone then
-    return ''
-  end
-  if self.timezone == 0 then
-    return 'Z'
-  end
-  local sign = self.timezone > 0
-  local z = sign and self.timezone or -self.timezone
-  local zh = math.floor(z)
-  local zi = (z - zh) * 60
-  return string.format(
-    '%s%02d:%02d', sign and '+' or '-', zh, zi)
-end
-
-
-local function countindent(line)
-  local _, j = sfind(line, '^%s+')
-  if not j then
-    return 0, line
-  end
-  return j, ssub(line, j+1)
-end
-
-local function parsestring(line, stopper)
-  stopper = stopper or ''
-  local q = ssub(line, 1, 1)
-  if q == ' ' or q == '\t' then
-    return parsestring(ssub(line, 2))
-  end
-  if q == "'" then
-    local i = sfind(line, "'", 2, true)
-    if not i then
-      return nil, line
-    end
-    return ssub(line, 2, i-1), ssub(line, i+1)
-  end
-  if q == '"' then
-    local i, buf = 2, ''
-    while i < #line do
-      local c = ssub(line, i, i)
-      if c == '\\' then
-        local n = ssub(line, i+1, i+1)
-        if UNESCAPES[n] ~= nil then
-          buf = buf..UNESCAPES[n]
-        elseif n == 'x' then
-          local h = ssub(i+2,i+3)
-          if sfind(h, '^[0-9a-fA-F]$') then
-            buf = buf..schar(tonumber(h, 16))
-            i = i + 2
-          else
-            buf = buf..'x'
-          end
-        else
-          buf = buf..n
-        end
-        i = i + 1
-      elseif c == q then
-        break
-      else
-        buf = buf..c
-      end
-      i = i + 1
-    end
-    return buf, ssub(line, i+1)
-  end
-  if q == '{' or q == '[' then  -- flow style
-    return nil, line
-  end
-  if q == '|' or q == '>' then  -- block
-    return nil, line
-  end
-  if q == '-' or q == ':' then
-    if ssub(line, 2, 2) == ' ' or ssub(line, 2, 2) == '\n' or #line == 1 then
-      return nil, line
-    end
-  end
-  local buf = ''
-  while #line > 0 do
-    local c = ssub(line, 1, 1)
-    if sfind(stopper, c, 1, true) then
-      break
-    elseif c == ':' and (ssub(line, 2, 2) == ' ' or ssub(line, 2, 2) == '\n' or #line == 1) then
-      break
-    elseif c == '#' and (ssub(buf, #buf, #buf) == ' ') then
-      break
-    else
-      buf = buf..c
-    end
-    line = ssub(line, 2)
-  end
-  return rtrim(buf), line
-end
-
-local function isemptyline(line)
-  return line == '' or sfind(line, '^%s*$') or sfind(line, '^%s*#')
-end
-
-local function equalsline(line, needle)
-  return startswith(line, needle) and isemptyline(ssub(line, #needle+1))
-end
-
-local function compactifyemptylines(lines)
-  -- Appends empty lines as "\n" to the end of the nearest preceding non-empty line
-  local compactified = {}
-  local lastline = {}
-  for i = 1, #lines do
-    local line = lines[i]
-    if isemptyline(line) then
-      if #compactified > 0 and i < #lines then
-        tinsert(lastline, "\n")
-      end
-    else
-      if #lastline > 0 then
-        tinsert(compactified, tconcat(lastline, ""))
-      end
-      lastline = {line}
-    end
-  end
-  if #lastline > 0 then
-    tinsert(compactified, tconcat(lastline, ""))
-  end
-  return compactified
-end
-
-local function checkdupekey(map, key)
-  if map[key] ~= nil then
-    -- print("found a duplicate key '"..key.."' in line: "..line)
-    local suffix = 1
-    while map[key..'_'..suffix] do
-      suffix = suffix + 1
-    end
-    key = key ..'_'..suffix
-  end
-  return key
-end
-
-local function parseflowstyle(line, lines)
-  local stack = {}
-  while true do
-    if #line == 0 then
-      if #lines == 0 then
-        break
-      else
-        line = tremove(lines, 1)
-      end
-    end
-    local c = ssub(line, 1, 1)
-    if c == '#' then
-      line = ''
-    elseif c == ' ' or c == '\t' or c == '\r' or c == '\n' then
-      line = ssub(line, 2)
-    elseif c == '{' or c == '[' then
-      tinsert(stack, {v={},t=c})
-      line = ssub(line, 2)
-    elseif c == ':' then
-      local s = tremove(stack)
-      tinsert(stack, {v=s.v, t=':'})
-      line = ssub(line, 2)
-    elseif c == ',' then
-      local value = tremove(stack)
-      if value.t == ':' or value.t == '{' or value.t == '[' then error() end
-      if stack[#stack].t == ':' then
-        -- map
-        local key = tremove(stack)
-        key.v = checkdupekey(stack[#stack].v, key.v)
-        stack[#stack].v[key.v] = value.v
-      elseif stack[#stack].t == '{' then
-        -- set
-        stack[#stack].v[value.v] = true
-      elseif stack[#stack].t == '[' then
-        -- seq
-        tinsert(stack[#stack].v, value.v)
-      end
-      line = ssub(line, 2)
-    elseif c == '}' then
-      if stack[#stack].t == '{' then
-        if #stack == 1 then break end
-        stack[#stack].t = '}'
-        line = ssub(line, 2)
-      else
-        line = ','..line
-      end
-    elseif c == ']' then
-      if stack[#stack].t == '[' then
-        if #stack == 1 then break end
-        stack[#stack].t = ']'
-        line = ssub(line, 2)
-      else
-        line = ','..line
-      end
-    else
-      local s, rest = parsestring(line, ',{}[]')
-      if not s then
-        error('invalid flowstyle line: '..line)
-      end
-      tinsert(stack, {v=s, t='s'})
-      line = rest
-    end
-  end
-  return stack[1].v, line
-end
-
-local function parseblockstylestring(line, lines, indent)
-  if #lines == 0 then
-    error("failed to find multi-line scalar content")
-  end
-  local s = {}
-  local firstindent = -1
-  local endline = -1
-  for i = 1, #lines do
-    local ln = lines[i]
-    local idt = countindent(ln)
-    if idt <= indent then
-      break
-    end
-    if ln == '' then
-      tinsert(s, '')
-    else
-      if firstindent == -1 then
-        firstindent = idt
-      elseif idt < firstindent then
-        break
-      end
-      tinsert(s, ssub(ln, firstindent + 1))
-    end
-    endline = i
-  end
-
-  local striptrailing = true
-  local sep = '\n'
-  local newlineatend = true
-  if line == '|' then
-    striptrailing = true
-    sep = '\n'
-    newlineatend = true
-  elseif line == '|+' then
-    striptrailing = false
-    sep = '\n'
-    newlineatend = true
-  elseif line == '|-' then
-    striptrailing = true
-    sep = '\n'
-    newlineatend = false
-  elseif line == '>' then
-    striptrailing = true
-    sep = ' '
-    newlineatend = true
-  elseif line == '>+' then
-    striptrailing = false
-    sep = ' '
-    newlineatend = true
-  elseif line == '>-' then
-    striptrailing = true
-    sep = ' '
-    newlineatend = false
-  else
-    error('invalid blockstyle string:'..line)
-  end
-  local _, eonl = s[#s]:gsub('\n', '\n')
-  s[#s] = rtrim(s[#s])
-  if striptrailing then
-    eonl = 0
-  end
-  if newlineatend then
-    eonl = eonl + 1
-  end
-  for i = endline, 1, -1 do
-    tremove(lines, i)
-  end
-  return tconcat(s, sep)..string.rep('\n', eonl)
-end
-
-local function parsetimestamp(line)
-  local _, p1, y, m, d = sfind(line, '^(%d%d%d%d)%-(%d%d)%-(%d%d)')
-  if not p1 then
-    return nil, line
-  end
-  if p1 == #line then
-    return types.timestamp(y, m, d), ''
-  end
-  local _, p2, h, i, s = sfind(line, '^[Tt ](%d+):(%d+):(%d+)', p1+1)
-  if not p2 then
-    return types.timestamp(y, m, d), ssub(line, p1+1)
-  end
-  if p2 == #line then
-    return types.timestamp(y, m, d, h, i, s), ''
-  end
-  local _, p3, f = sfind(line, '^%.(%d+)', p2+1)
-  if not p3 then
-    p3 = p2
-    f = 0
-  end
-  local zc = ssub(line, p3+1, p3+1)
-  local _, p4, zs, z = sfind(line, '^ ?([%+%-])(%d+)', p3+1)
-  if p4 then
-    z = tonumber(z)
-    local _, p5, zi = sfind(line, '^:(%d+)', p4+1)
-    if p5 then
-      z = z + tonumber(zi) / 60
-    end
-    z = zs == '-' and -tonumber(z) or tonumber(z)
-  elseif zc == 'Z' then
-    p4 = p3 + 1
-    z = 0
-  else
-    p4 = p3
-    z = false
-  end
-  return types.timestamp(y, m, d, h, i, s, f, z), ssub(line, p4+1)
-end
-
-local function parsescalar(line, lines, indent)
-  line = trim(line)
-  line = gsub(line, '^%s*#.*$', '')  -- comment only -> ''
-  line = gsub(line, '^%s*', '')  -- trim head spaces
-
-  if line == '' or line == '~' then
-    return null
-  end
-
-  local ts, _ = parsetimestamp(line)
-  if ts then
-    return ts
-  end
-
-  local s, _ = parsestring(line)
-  -- startswith quote ... string
-  -- not startswith quote ... maybe string
-  if s and (startswith(line, '"') or startswith(line, "'")) then
-    return s
-  end
-
-  if startswith('!', line) then  -- unexpected tagchar
-    error('unsupported line: '..line)
-  end
-
-  if equalsline(line, '{}') then
-    return {}
-  end
-  if equalsline(line, '[]') then
-    return {}
-  end
-
-  if startswith(line, '{') or startswith(line, '[') then
-    return parseflowstyle(line, lines)
-  end
-
-  if startswith(line, '|') or startswith(line, '>') then
-    return parseblockstylestring(line, lines, indent)
-  end
-
-  -- Regular unquoted string
-  line = gsub(line, '%s*#.*$', '')  -- trim tail comment
-  local v = line
-  if v == 'null' or v == 'Null' or v == 'NULL'then
-    return null
-  elseif v == 'true' or v == 'True' or v == 'TRUE' then
-    return true
-  elseif v == 'false' or v == 'False' or v == 'FALSE' then
-    return false
-  elseif v == '.inf' or v == '.Inf' or v == '.INF' then
-    return math.huge
-  elseif v == '+.inf' or v == '+.Inf' or v == '+.INF' then
-    return math.huge
-  elseif v == '-.inf' or v == '-.Inf' or v == '-.INF' then
-    return -math.huge
-  elseif v == '.nan' or v == '.NaN' or v == '.NAN' then
-    return 0 / 0
-  elseif sfind(v, '^[%+%-]?[0-9]+$') or sfind(v, '^[%+%-]?[0-9]+%.$')then
-    return tonumber(v)  -- : int
-  elseif sfind(v, '^[%+%-]?[0-9]+%.[0-9]+$') then
-    return tonumber(v)
-  end
-  return s or v
-end
-
-local parsemap;  -- : func
-
-local function parseseq(line, lines, indent)
-  local seq = setmetatable({}, types.seq)
-  if line ~= '' then
-    error()
-  end
-  while #lines > 0 do
-    -- Check for a new document
-    line = lines[1]
-    if startswith(line, '---') then
-      while #lines > 0 and not startswith(lines, '---') do
-        tremove(lines, 1)
-      end
-      return seq
-    end
-
-    -- Check the indent level
-    local level = countindent(line)
-    if level < indent then
-      return seq
-    elseif level > indent then
-      error("found bad indenting in line: ".. line)
-    end
-
-    local i, j = sfind(line, '%-%s+')
-    if not i then
-      i, j = sfind(line, '%-$')
-      if not i then
-        return seq
-      end
-    end
-    local rest = ssub(line, j+1)
-
-    if sfind(rest, '^[^\'\"%s]*:%s*$') or sfind(rest, '^[^\'\"%s]*:%s+.') then
-      -- Inline nested hash
-      -- There are two patterns need to match as inline nested hash
-      --   first one should have no other characters except whitespace after `:`
-      --   and the second one should have characters besides whitespace after `:`
-      --
-      --  value:
-      --    - foo:
-      --        bar: 1
-      --
-      -- and
-      --
-      --  value:
-      --    - foo: bar
-      --
-      -- And there is one pattern should not be matched, where there is no space after `:`
-      --   in below, `foo:bar` should be parsed into a single string
-      --
-      -- value:
-      --   - foo:bar
-      local indent2 = j
-      lines[1] = string.rep(' ', indent2)..rest
-      tinsert(seq, parsemap('', lines, indent2))
-    elseif sfind(rest, '^%-%s+') then
-      -- Inline nested seq
-      local indent2 = j
-      lines[1] = string.rep(' ', indent2)..rest
-      tinsert(seq, parseseq('', lines, indent2))
-    elseif isemptyline(rest) then
-      tremove(lines, 1)
-      if #lines == 0 then
-        tinsert(seq, null)
-        return seq
-      end
-      if sfind(lines[1], '^%s*%-') then
-        local nextline = lines[1]
-        local indent2 = countindent(nextline)
-        if indent2 == indent then
-          -- Null seqay entry
-          tinsert(seq, null)
-        else
-          tinsert(seq, parseseq('', lines, indent2))
-        end
-      else
-        -- - # comment
-        --   key: value
-        local nextline = lines[1]
-        local indent2 = countindent(nextline)
-        tinsert(seq, parsemap('', lines, indent2))
-      end
-    elseif rest then
-      -- Array entry with a value
-      tremove(lines, 1)
-      tinsert(seq, parsescalar(rest, lines))
-    end
-  end
-  return seq
-end
-
-local function parseset(line, lines, indent)
-  if not isemptyline(line) then
-    error('not seq line: '..line)
-  end
-  local set = setmetatable({}, types.set)
-  while #lines > 0 do
-    -- Check for a new document
-    line = lines[1]
-    if startswith(line, '---') then
-      while #lines > 0 and not startswith(lines, '---') do
-        tremove(lines, 1)
-      end
-      return set
-    end
-
-    -- Check the indent level
-    local level = countindent(line)
-    if level < indent then
-      return set
-    elseif level > indent then
-      error("found bad indenting in line: ".. line)
-    end
-
-    local i, j = sfind(line, '%?%s+')
-    if not i then
-      i, j = sfind(line, '%?$')
-      if not i then
-        return set
-      end
-    end
-    local rest = ssub(line, j+1)
-
-    if sfind(rest, '^[^\'\"%s]*:') then
-      -- Inline nested hash
-      local indent2 = j
-      lines[1] = string.rep(' ', indent2)..rest
-      set[parsemap('', lines, indent2)] = true
-    elseif sfind(rest, '^%s+$') then
-      tremove(lines, 1)
-      if #lines == 0 then
-        tinsert(set, null)
-        return set
-      end
-      if sfind(lines[1], '^%s*%?') then
-        local indent2 = countindent(lines[1])
-        if indent2 == indent then
-          -- Null array entry
-          set[null] = true
-        else
-          set[parseseq('', lines, indent2)] = true
-        end
-      end
-
-    elseif rest then
-      tremove(lines, 1)
-      set[parsescalar(rest, lines)] = true
-    else
-      error("failed to classify line: "..line)
-    end
-  end
-  return set
-end
-
-function parsemap(line, lines, indent)
-  if not isemptyline(line) then
-    error('not map line: '..line)
-  end
-  local map = setmetatable({}, types.map)
-  while #lines > 0 do
-    -- Check for a new document
-    line = lines[1]
-    if startswith(line, '---') then
-      while #lines > 0 and not startswith(lines, '---') do
-        tremove(lines, 1)
-      end
-      return map
-    end
-
-    -- Check the indent level
-    local level, _ = countindent(line)
-    if level < indent then
-      return map
-    elseif level > indent then
-      error("found bad indenting in line: ".. line)
-    end
-
-    -- Find the key
-    local key
-    local s, rest = parsestring(line)
-
-    -- Quoted keys
-    if s and startswith(rest, ':') then
-      local sc = parsescalar(s, {}, 0)
-      if sc and type(sc) ~= 'string' then
-        key = sc
-      else
-        key = s
-      end
-      line = ssub(rest, 2)
-    else
-      error("failed to classify line: "..line)
-    end
-
-    key = checkdupekey(map, key)
-    line = ltrim(line)
-
-    if ssub(line, 1, 1) == '!' then
-      -- ignore type
-      local rh = ltrim(ssub(line, 3))
-      local typename = smatch(rh, '^!?[^%s]+')
-      line = ltrim(ssub(rh, #typename+1))
-    end
-
-    if not isemptyline(line) then
-      tremove(lines, 1)
-      line = ltrim(line)
-      map[key] = parsescalar(line, lines, indent)
-    else
-      -- An indent
-      tremove(lines, 1)
-      if #lines == 0 then
-        map[key] = null
-        return map;
-      end
-      if sfind(lines[1], '^%s*%-') then
-        local indent2 = countindent(lines[1])
-        map[key] = parseseq('', lines, indent2)
-      elseif sfind(lines[1], '^%s*%?') then
-        local indent2 = countindent(lines[1])
-        map[key] = parseset('', lines, indent2)
-      else
-        local indent2 = countindent(lines[1])
-        if indent >= indent2 then
-          -- Null hash entry
-          map[key] = null
-        else
-          map[key] = parsemap('', lines, indent2)
-        end
-      end
-    end
-  end
-  return map
-end
-
-
--- : (list<str>)->dict
-local function parsedocuments(lines)
-  lines = compactifyemptylines(lines)
-
-  if sfind(lines[1], '^%%YAML') then tremove(lines, 1) end
-
-  local root = {}
-  local in_document = false
-  while #lines > 0 do
-    local line = lines[1]
-    -- Do we have a document header?
-    local docright;
-    if sfind(line, '^%-%-%-') then
-      -- Handle scalar documents
-      docright = ssub(line, 4)
-      tremove(lines, 1)
-      in_document = true
-    end
-    if docright then
-      if (not sfind(docright, '^%s+$') and
-          not sfind(docright, '^%s+#')) then
-        tinsert(root, parsescalar(docright, lines))
-      end
-    elseif #lines == 0 or startswith(line, '---') then
-      -- A naked document
-      tinsert(root, null)
-      while #lines > 0 and not sfind(lines[1], '---') do
-        tremove(lines, 1)
-      end
-      in_document = false
-    -- XXX The final '-+$' is to look for -- which ends up being an
-    -- error later.
-    elseif not in_document and #root > 0 then
-      -- only the first document can be explicit
-      error('parse error: '..line)
-    elseif sfind(line, '^%s*%-') then
-      -- An array at the root
-      tinsert(root, parseseq('', lines, 0))
-    elseif sfind(line, '^%s*[^%s]') then
-      -- A hash at the root
-      local level = countindent(line)
-      tinsert(root, parsemap('', lines, level))
-    else
-      -- Shouldn't get here.  @lines have whitespace-only lines
-      -- stripped, and previous match is a line with any
-      -- non-whitespace.  So this clause should only be reachable via
-      -- a perlbug where \s is not symmetric with \S
-
-      -- uncoverable statement
-      error('parse error: '..line)
-    end
-  end
-  if #root > 1 and Null.isnull(root[1]) then
-    tremove(root, 1)
-    return root
-  end
-  return root
-end
-
---- Parse yaml string into table.
-local function parse(source)
-  local lines = {}
-  for line in string.gmatch(source .. '\n', '(.-)\r?\n') do
-    tinsert(lines, line)
-  end
-
-  local docs = parsedocuments(lines)
-  if #docs == 1 then
-    return docs[1]
-  end
-
-  return docs
-end
-
-return {
-  version = 0.1,
-  parse = parse,
-}



More information about the tex-live-commits mailing list.