texlive[74992] Master/texmf-dist: jsonparse (21apr25)

commits+karl at tug.org commits+karl at tug.org
Mon Apr 21 21:16:46 CEST 2025


Revision: 74992
          https://tug.org/svn/texlive?view=revision&revision=74992
Author:   karl
Date:     2025-04-21 21:16:46 +0200 (Mon, 21 Apr 2025)
Log Message:
-----------
jsonparse (21apr25)

Modified Paths:
--------------
    trunk/Master/texmf-dist/doc/latex/jsonparse/README.md
    trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.pdf
    trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex
    trunk/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty

Modified: trunk/Master/texmf-dist/doc/latex/jsonparse/README.md
===================================================================
--- trunk/Master/texmf-dist/doc/latex/jsonparse/README.md	2025-04-20 23:41:54 UTC (rev 74991)
+++ trunk/Master/texmf-dist/doc/latex/jsonparse/README.md	2025-04-21 19:16:46 UTC (rev 74992)
@@ -1,4 +1,4 @@
-![Version 1.4.0](https://img.shields.io/badge/version-1.4.0-blue)
+![Version 1.5.0](https://img.shields.io/badge/version-1.5.0-blue)
 
 ![Jason, the JSON parsing horse](https://github.com/jasperhabicht/jsonparse/assets/6378801/ddfddc70-bf5f-4121-ba45-4b9128875d85)
 

Modified: trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.pdf
===================================================================
(Binary files differ)

Modified: trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex
===================================================================
--- trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex	2025-04-20 23:41:54 UTC (rev 74991)
+++ trunk/Master/texmf-dist/doc/latex/jsonparse/jsonparse-doc.tex	2025-04-21 19:16:46 UTC (rev 74992)
@@ -11,8 +11,8 @@
 % This work has the LPPL maintenance status `maintained'.
 %
 \documentclass[a4paper]{article}
-\def\jsonparsefileversion{1.4.0}
-\def\jsonparsefiledate{10 April 2025}
+\def\jsonparsefileversion{1.5.0}
+\def\jsonparsefiledate{20 April 2025}
 
 \usepackage[T1]{fontenc}
 \usepackage{Alegreya}
@@ -118,7 +118,7 @@
 }
 
 \NewExpandableDocumentCommand{\printdoctitle}{ }{
-    \tl_use:N \l_jsonparse_doc_doctitle_tl
+  \tl_use:N \l_jsonparse_doc_doctitle_tl
 }
 \ExplSyntaxOff
 
@@ -136,8 +136,10 @@
 
 \NewDocumentCommand{\warning}{}{%
   \begin{tikzpicture}[overlay, baseline={(w.base)}]
-    \node[circle, fill, black!25!red!10, text=black!25!red]
+    \node[circle, fill, black!25!red!10, text=black!25!red, outer sep=0pt]
       (w) at (-3em,0pt) {\bfseries !};
+    \fill[black!25!red!10]
+      (w.east) |- (w.south) -- cycle;
   \end{tikzpicture}%
 }
 
@@ -293,6 +295,7 @@
 %\changes{v1.3.1}{2025/03/30}{Fixes in documentation. Bug fixes.}
 %\changes{v1.3.2}{2025/04/07}{Bug fixes and code improvements.}
 \changes{v1.4.0}{2025/04/10}{Enhancements in parsing speed.}
+\changes{v1.5.0}{2025/04/20}{Enhancements in parsing speed. Bug fixes.}
 
 \begin{document}
 \vspace*{-1cm}
@@ -308,13 +311,13 @@
 
 The \macro{jsonparse} package provides a handy way to read in JSON data from files or strings in LaTeX documents, parse the data and store it in a user-defined token variable. The package allows accessing the stored data via a JavaScript-flavored syntax.
 
-The package has been tested, but not exhaustively. The author is grateful for reporting any bugs via GitHub at \url{https://github.com/jasperhabicht/jsonparse/issues}. A site for asking questions about how to use the package and for suggestions for improvement is available at \url{https://github.com/jasperhabicht/jsonparse/discussions}.
+The package is continuously being tested, but bugs cannot be ruled out. The author is grateful for reporting any bugs via GitHub at \url{https://github.com/jasperhabicht/jsonparse/issues}. A site for asking questions about the package and for suggestions for improvement is available at \url{https://github.com/jasperhabicht/jsonparse/discussions}.
 
 \section{Loading the package}
 
 To install the package, copy the package file \macro{jsonparse.sty} into the working directory or into the \macro{texmf} directory. After the package has been installed, the \macro{jsonparse} package is loaded by calling \macro{\usepackage{jsonparse}} in the preamble of the document.
 
-The package does not load any dependencies. It can be used with PDFLaTeX, LuaLaTeX or XeLaTeX. It should also work with upTeX.
+The package can be used with PDFLaTeX, LuaLaTeX or XeLaTeX. It should also work with upTeX. The package does not load any dependencies, but it needs a LaTeX kernel of 1 June 2022 or newer. It is recommended to use the package with an up-to-date TeX distribution.
 
 \begin{macrodef}
 |debug|
@@ -324,47 +327,47 @@
 Let us assume that the following JSON data is parsed:
 
 \begin{codeexample}
-  {
-    "string" : "a" ,
-    "boolean true" : true ,
-    "boolean false" : false ,
-    "null" : null ,
-    "number" : "number" : -1.1e-1 ,
-    "array" : [ "a" , "b" , "c" ]
-  }
+{
+  "string" : "a" ,
+  "boolean true" : true ,
+  "boolean false" : false ,
+  "null" : null ,
+  "number" : "number" : -1.1e-1 ,
+  "array" : [ "a" , "b" , "c" ]
+}
 \end{codeexample}
 
 This will then result in the following output to the log:
 
 \begin{codeexample}
-  Parsing JSON ...
-  (obj begin)
-  (key) string:
-    (str) a
-  (key) boolean true:
-    (tru) true
-  (key) boolean false:
-    (fal) false
-  (key) null:
-    (nul) null
-  (key) number:
-    (num) -1.1e-1
-  (arr begin)
-  (key) array[0]:
-    (str) a
-  (key) array[1]:
-    (str) b
-  (key) array[2]:
-    (str) c
-  (key) array:
-    (arr) [ "a" , "b" , "c" ]
-  (arr end)
-  (key) .:
-    (obj) { "string" : "a" , "boolean true" : true , "boolean false" :
-    false , "null" : null , "number" : "number" : -1.1e-1 , "array" :
-    [ "a" , "b" , "c" ] }
-  (obj end)
-  JSON parsing done.
+Parsing JSON ...
+(obj begin)
+(key) string:
+  (str) a
+(key) boolean true:
+  (tru) true
+(key) boolean false:
+  (fal) false
+(key) null:
+  (nul) null
+(key) number:
+  (num) -1.1e-1
+(arr begin)
+(key) array[0]:
+  (str) a
+(key) array[1]:
+  (str) b
+(key) array[2]:
+  (str) c
+(key) array:
+  (arr) [ "a" , "b" , "c" ]
+(arr end)
+(key) .:
+  (obj) { "string" : "a" , "boolean true" : true , "boolean false" :
+  false , "null" : null , "number" : "number" : -1.1e-1 , "array" :
+  [ "a" , "b" , "c" ] }
+(obj end)
+JSON parsing done.
 \end{codeexample}
 
 The \macro{debug} key can be set either as package option or using \macro{\JSONParseSet}. It can also be set locally as option to the commands \macro{\JSONParse} and \macro{\JSONParseFromFile}.
@@ -388,9 +391,9 @@
 It is possible to insert TeX macros to the JSON source that will eventually be parsed when typesetting. Backslashes of TeX macros need to be escaped by another backslash. The TeX macros \macro{\"} and \macro{\\} must be escaped twice in the JSON source so that they become \macro{\\\"} and \macro{\\\\} respectively.
 
 \begin{macrodef}
-|\x|{<token variable name>}{<key>}
+|\$|{<token variable name>}{<key>}
 \end{macrodef}
-Using the control sequence \macro{\x}, it is possible to nest JSON strings into each other. Used inside the \macro{\JSONParse} command, the control sequence takes two arguments delimited by curly braces. The first argument represents the name of the token variable that holds the parsed JSON data where the inserted JSON string should be taken from. The second argument sets the key that should be selected. The following example shows a simple use case:
+Using the control sequence \macro{\$}, it is possible to nest JSON strings into each other. Used inside the \macro{\JSONParse} command, the control sequence takes two arguments delimited by curly braces. The first argument represents the name of the token variable that holds the parsed JSON data where the inserted JSON string should be taken from. The second argument sets the key that should be selected. The following example shows a simple use case:
 
 \begin{codeexamplecolumns}
 \JSONParse{\myJSONdataA}{
@@ -398,13 +401,13 @@
 }
 
 \JSONParse{\myJSONdataB}{
-  { "d" : \x{myJSONdataA}{a} }
+  { "d" : \${myJSONdataA}{a} }
 }
 
 \JSONParseValue{\myJSONdataB}{d.b}
 \end{codeexamplecolumns}
 
-\warning Note that the control sequence \macro{\x} is replaced by the value exactly. Therefore, if the value happens to be a string, the control sequence \macro{\x} should be placed between quotation marks (\macro{"}) in order for the resulting string to be valid JSON. The control sequence \macro{\x} is only available inside the \macro{\JSONParse} command, but not inside the \macro{\JSONParseFromFile} command.
+\warning Note that the control sequence \macro{\$} is replaced by the value exactly. Therefore, if the value happens to be a string, the control sequence \macro{\$} should be placed between quotation marks (\macro{"}) in order for the resulting string to be valid JSON. The control sequence \macro{\$} is only available inside the \macro{\JSONParse} command, but not inside the \macro{\JSONParseFromFile} command.
 
 \begin{macrodef}
 |escape|={all}
@@ -442,9 +445,9 @@
 \end{macrodef}
 The command \macro{\JSONParse} is used to parse a JSON string and globally store the parsed result in a token variable (a property list). The second argument takes the name of the token variable that is created by the command. The third argument takes the JSON string to be parsed.
 
-For example, using \macro{\JSONParse{\myJSONdata}{ { "key" : "value" } }}, the relevant JSON string will be parsed and the result stored in the token variable \macro{\myJSONdata} as property list. In this case, the property list only consists of one entry with the key \macro{key} and the value \macro{value}. The command \macro{\JSONParseValue{\myJSONdata}{key}}, for example, can then be used to extract the relevant value from this property list (see the description below).
+For example, using \macro{\JSONParse{\myJSONdata}{ { "key" : "value" } }}, the relevant JSON string will be parsed and the result stored in the token variable \macro{\myJSONdata} as property list. In this case, the property list only consists of two entries of which one has the key \macro{key} and the value \macro{value} and the other represents the whole object. Once the JSON string has been parsed, the command \macro{\JSONParseValue{\myJSONdata}{key}}, for example, can be used to extract the relevant value from this property list (see the description below).
 
-The first optional argument can be used to pass options to the command that are then applied locally.
+The first optional argument of the command \macro{\JSONParse} can be used to pass options to the command that are then applied locally.
 
 \warning The command \macro{\JSONParse} takes the JSON string as verbatim argument which means that the command can't be used inside a macro argument. One consequence of this for example is that when using the \macro{beamer} document class, the command \macro{\JSONParse} can only be used inside the \macro{frame} environment if the \macro{fragile} option is set.
 
@@ -651,16 +654,17 @@
 }
 
 \JSONParseArrayMapFunction[
-    code before={
-      \begin{tabular}{ c c }
-        \textbf{key a} &
-        \textbf{key b} \\ \hline
-    },
-    code after={
-      \hline \end{tabular}
-    }
-  ]{\myJSONdata}
-  {array}[key_a,key_b]{\myJSONitem}
+  code before={
+    \begin{tabular}{ c c }
+      \textbf{key a} &
+      \textbf{key b} \\ \hline
+  },
+  code after={
+      \hline
+    \end{tabular}
+  }
+]{\myJSONdata}{array}[key_a,key_b]
+  {\myJSONitem}
 \end{codeexamplecolumns}
 
 Finally, the first optional argument of the command can be used to pass options to the command, such as \macro{escape} or \macro{rescan}, that are then applied locally.
@@ -676,9 +680,9 @@
 \begin{itemize}
   \JSONParseArrayMapInline{\myJSONdata}
     {array}{
-    \item \JSONParseValue{\myJSONdata}
-      {array[#1].key_a}
-  }
+      \item \JSONParseValue{\myJSONdata}
+        {array[#1].key_a}
+    }
 \end{itemize}
 \end{codeexamplecolumns}
 
@@ -687,23 +691,23 @@
 \begin{codeexamplecolumns}
 \JSONParseArrayMapInline{\myJSONdata}
   {array}{
-  \JSONParseKeys[store in=\mykeys]
-    {\myJSONdata}{array[#1]}
-  \JSONParseValue
-    [store in=\mykeya, rescan=false]
-    {\mykeys}{[0]}
-  \JSONParseValue
-    [store in=\mykeyb, rescan=false]
-    {\mykeys}{[1]}
+    \JSONParseKeys[store in=\mykeys]
+      {\myJSONdata}{array[#1]}
+    \JSONParseValue
+      [store in=\mykeya, rescan=false]
+      {\mykeys}{[0]}
+    \JSONParseValue
+      [store in=\mykeyb, rescan=false]
+      {\mykeys}{[1]}
 
-  \emph{\mykeya :}
-  \JSONParseValue{\myJSONdata}
-    {array[#1].\mykeya}\par
+    \emph{\mykeya :}
+    \JSONParseValue{\myJSONdata}
+      {array[#1].\mykeya}\par
 
-  \emph{\mykeyb :}
-  \JSONParseValue{\myJSONdata}
-    {array[#1].\mykeyb}\par\bigskip
-}
+    \emph{\mykeyb :}
+    \JSONParseValue{\myJSONdata}
+      {array[#1].\mykeyb}\par\bigskip
+  }
 \end{codeexamplecolumns}
 
 Note that the underscores in the names of the keys can be printed without changing to math mode in the above example by switching off rescanning via \macro{rescan=false}. This is possible because all JSON data is stored as string where all characters (except for spaces and tabs) have category code 12 (``other'').
@@ -728,7 +732,7 @@
     \JSONParseExpandableValue
       {\myJSONplotdata}{data[#1][1]}
     )
-}
+  }
 
 \begin{tikzpicture}
   \begin{axis}
@@ -843,19 +847,21 @@
 keyword/|false|={<string>}
 keyword/|null|={<string>}
 \end{macrodef}
-With the keys \macro{keyword/true}, \macro{keyword/false} and \macro{keyword/null}, the string that is typeset for true, false and null values can be changed. The default strings that are typeset are \macro{true}, \macro{false} and \macro{null} respectively. Only strings can be used as replacement. These replacements take place already during parsing.
+With the keys \macro{keyword/true}, \macro{keyword/false} and \macro{keyword/null}, the string that is typeset for true, false and null values can be changed. The default strings that are typeset are \macro{true}, \macro{false} and \macro{null} respectively. Only strings should be used as replacement. These replacements take place already during parsing.
 
 These keys can be set using \macro{\JSONParseSet}. They can also be set locally as option to the commands \macro{\JSONParse} and \macro{\JSONParseFromFile}. When set using \macro{\JSONParseSet}, these keys only take effect when set before parsing.
 
 \begin{macrodef}
-replace/|backspace|={<string>}
-replace/|formfeed|={<string>}
-replace/|linefeed|={<string>}
-replace/|carriage return|={<string>}
-replace/|horizontal tab|={<string>}
+replace/|backspace|={<token list>}
+replace/|formfeed|={<token list>}
+replace/|linefeed|={<token list>}
+replace/|carriage return|={<token list>}
+replace/|horizontal tab|={<token list>}
 \end{macrodef}
-These keys can be used to set the replacement text for the JSON escape sequences \macro{\b} (backspace), \macro{\f} (formfeed), \macro{\n} (linefeed), \macro{\r} (carriage return) and \macro{\t} (horizontal tab). The default replacement string is a space in each case. Only strings can be used as replacement. These replacements take place only during typesetting.
+These keys can be used to set the replacement for the JSON escape sequences \macro{\b} (backspace), \macro{\f} (formfeed), \macro{\n} (linefeed), \macro{\r} (carriage return) and \macro{\t} (horizontal tab). The default replacement is a space in each case. These replacements take place only during typesetting.
 
+\warning Note that commands such as \macro{\par} needs to be masked (for example by using a copy created via \macro{\let}) in order to be used as replacement.
+
 These keys can be set using \macro{\JSONParseSet}. They can also be set locally as option to the commands \macro{\JSONParseValue}, \macro{\JSONParseArrayUse} and \macro{\JSONParseArrayMapFunction}.
 
 \begin{macrodef}
@@ -931,6 +937,14 @@
   {<2>}{<3>}[<4>]{\<5>}
 \end{macroreplacement}
 
+\begin{macrodeprecated}*
+|\x|{<1>}{<2>}
+\end{macrodeprecated}
+\hfill
+\begin{macroreplacement}
+|\$|{<1>}{<2>}
+\end{macroreplacement}
+
 The command \macro{\JSONParseArrayMapFunction} takes as last argument a command denoting the relevant mapping function including the preceding backslash, while the deprecated command \macro{\JSONParseArrayValuesMap} required the name of this function without preceding backslash.
 
 To ensure backward compatibility, the deprecated commands are still supported, but their use is not recommended. The commands \macro{\JSONParseSetRescanValue} and \macro{\JSONParseSetKeys} will locally set the relevant token variable.

Modified: trunk/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty
===================================================================
--- trunk/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty	2025-04-20 23:41:54 UTC (rev 74991)
+++ trunk/Master/texmf-dist/tex/latex/jsonparse/jsonparse.sty	2025-04-21 19:16:46 UTC (rev 74992)
@@ -10,7 +10,7 @@
 %
 % This work has the LPPL maintenance status `maintained'.
 %
-\ProvidesExplPackage {jsonparse} {2025-04-10} {1.4.0}
+\ProvidesExplPackage {jsonparse} {2025-04-20} {1.5.0}
   {A handy way to parse, store and access JSON data from files or strings in LaTeX documents}
 
 \msg_new:nnn { jsonparse } { old-kernel } {
@@ -94,6 +94,11 @@
   Control ~ sequence ~ undefined: ~ #1.
 }
 
+\msg_new:nnn { jsonparse } { not-array-item } {
+  \msg_error_text:n { jsonparse } \iow_newline:
+  Key ~ does ~ not ~ represent ~ an ~ array ~ item: ~ #1
+}
+
 \msg_new:nnn { jsonparse } { key-unknown } {
   \msg_warning_text:n { jsonparse } \iow_newline:
   Ignoring ~ key: ~ #1. \iow_newline:
@@ -111,11 +116,6 @@
   Loading ~ from ~ external ~ file: ~ #1.
 }
 
-\msg_new:nnn { jsonparse } { not-array-item } {
-  \msg_info_text:n { jsonparse } \iow_newline:
-  Key ~ does ~ not ~ represent ~ an ~ array ~ item: ~ #1
-}
-
 % ===
 
 \str_new:N \l_jsonparse_externalize_prefix_str
@@ -135,11 +135,11 @@
 \bool_new:N \l__jsonparse_skip_structures_bool
 \bool_new:N \l__jsonparse_rescan_bool
 
-\str_new:N \l__jsonparse_backspace_str
-\str_new:N \l__jsonparse_formfeed_str
-\str_new:N \l__jsonparse_linefeed_str
-\str_new:N \l__jsonparse_carriage_return_str
-\str_new:N \l__jsonparse_horizontal_tab_str
+\tl_new:N \l__jsonparse_backspace_tl
+\tl_new:N \l__jsonparse_formfeed_tl
+\tl_new:N \l__jsonparse_linefeed_tl
+\tl_new:N \l__jsonparse_carriage_return_tl
+\tl_new:N \l__jsonparse_horizontal_tab_tl
 
 \tl_new:N \l__jsonparse_array_map_code_before_tl
 \tl_new:N \l__jsonparse_array_map_code_after_tl
@@ -202,15 +202,15 @@
 
 \keys_define:nn { jsonparse / typeset } {
   replace                     .code:n     = { \keys_set:nn { jsonparse / typeset / replace } {#1} } ,
-  replace / backspace         .str_set:N  = \l__jsonparse_backspace_str ,
+  replace / backspace         .tl_set:N   = \l__jsonparse_backspace_tl ,
   replace / backspace         .initial:n  = { ~ } ,
-  replace / formfeed          .str_set:N  = \l__jsonparse_formfeed_str ,
+  replace / formfeed          .tl_set:N   = \l__jsonparse_formfeed_tl ,
   replace / formfeed          .initial:n  = { ~ } ,
-  replace / linefeed          .str_set:N  = \l__jsonparse_linefeed_str ,
+  replace / linefeed          .tl_set:N   = \l__jsonparse_linefeed_tl ,
   replace / linefeed          .initial:n  = { ~ } ,
-  replace / carriage ~ return .str_set:N  = \l__jsonparse_carriage_return_str ,
+  replace / carriage ~ return .tl_set:N   = \l__jsonparse_carriage_return_tl ,
   replace / carriage ~ return .initial:n  = { ~ } ,
-  replace / horizontal ~ tab  .str_set:N  = \l__jsonparse_horizontal_tab_str ,
+  replace / horizontal ~ tab  .tl_set:N   = \l__jsonparse_horizontal_tab_tl ,
   replace / horizontal ~ tab  .initial:n  = { ~ } ,
   escape                      .code:n     = {
     \str_case:nnF {#1} {
@@ -322,15 +322,12 @@
 \cs_generate_variant:Nn \file_input:n { e }
 \cs_generate_variant:Nn \tl_gset:Nn { Ne , ce }
 \cs_generate_variant:Nn \tl_gset_rescan:Nnn { Nno , Nne }
-\cs_generate_variant:Nn \tl_range:nnn { nne , nen }
-\cs_generate_variant:Nn \tl_range:Nnn { Nne , Nen }
 \cs_generate_variant:Nn \tl_remove_once:Nn { NV }
 \cs_generate_variant:Nn \tl_replace_all:Nnn { Non , Noe }
 \cs_generate_variant:Nn \tl_replace_once:Nnn { Noe }
 \cs_generate_variant:Nn \tl_rescan:nn { no , ne }
 \cs_generate_variant:Nn \tl_set:Nn { Ne }
-\cs_generate_variant:Nn \tl_to_str:n { o , e }
-\cs_generate_variant:Nn \tl_trim_spaces:n { e }
+\cs_generate_variant:Nn \tl_to_str:n { o }
 \cs_generate_variant:Nn \str_case_e:nn { en }
 \cs_generate_variant:Nn \str_casefold:n { o }
 \cs_generate_variant:Nn \str_head_ignore_spaces:n { o }
@@ -431,6 +428,21 @@
 
 % ===
 
+\cs_new_protected:Npn \__jsonparse_tl_trim_spaces:N #1 {
+  \tl_trim_spaces_apply:oN {#1} \__jsonparse_tl_set_trim_spaces_aux:nN #1
+}
+
+\cs_new_protected:Npn \__jsonparse_tl_set_trim_spaces:Nn #1#2 {
+  \tl_trim_spaces_apply:nN {#2} \__jsonparse_tl_set_trim_spaces_aux:nN #1
+}
+\cs_generate_variant:Nn \__jsonparse_tl_set_trim_spaces:Nn { Ne }
+
+\cs_new_protected:Npn \__jsonparse_tl_set_trim_spaces_aux:nN #1#2 {
+  \tl_set:Nn #2 {#1}
+}
+
+% ===
+
 \clist_const:Nn \c__jsonparse_num_nonzero_digits_clist {
   1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9
 }
@@ -444,17 +456,19 @@
   \tl_to_str:n { E }
 }
 
-\clist_const:Ne \c__jsonparse_num_plus_minus_clist {
-  \tl_to_str:n { + } ,
-  \tl_to_str:n { - }
+\clist_const:Nn \c__jsonparse_num_plus_minus_clist {
+  + , -
 }
 
 \prg_new_conditional:Npnn \jsonparse_if_num:n #1 { p , T , F , TF } {
-  \exp_last_unbraced:Ne
-    \__jsonparse_parse_num:w { \tl_to_str:e { \tl_trim_spaces:n {#1} } } \q_stop
+  \tl_trim_spaces_apply:oN { \tl_to_str:n {#1} } \__jsonparse_parse_num:n
 }
 \prg_generate_conditional_variant:Nnn \jsonparse_if_num:n { V } { p , T , F , TF }
 
+\cs_new:Npn \__jsonparse_parse_num:n #1 {
+  \__jsonparse_parse_num:w #1 \q_stop
+}
+
 \cs_new:Npn \__jsonparse_parse_num:w #1#2 \q_stop {
   \cs_if_exist_use:cTF { __jsonparse_parse_num_first_ #1 :w } {
     #2 \q_stop
@@ -634,7 +648,7 @@
 % ===
 
 \cs_new_protected:Npn \jsonparse_parse:n #1 {
-  \tl_set:Ne \l__jsonparse_input_tl { \tl_trim_spaces:e {#1} }
+  \__jsonparse_tl_set_trim_spaces:Nn \l__jsonparse_input_tl {#1}
   \cs_if_exist_use:cTF { __jsonparse_parse_ \str_head_ignore_spaces:o { \l__jsonparse_input_tl } :w } {
     \l__jsonparse_input_tl \q_stop
   } {
@@ -653,7 +667,7 @@
     }
   }
   \prop_gclear:N \g_jsonparse_entries_prop
-  \jsonparse_parse:n {#2}
+  \jsonparse_parse:e {#2}
   \prop_gset_eq:NN #1 \g_jsonparse_entries_prop
   \bool_if:NT \l__jsonparse_debug_mode_bool {
     \msg_log:nne { jsonparse } { debug-info } {
@@ -671,7 +685,7 @@
     }
   }
   \prop_clear:N \g_jsonparse_entries_prop
-  \jsonparse_parse:n {#2}
+  \jsonparse_parse:e {#2}
   \prop_set_eq:NN #1 \g_jsonparse_entries_prop
   \bool_if:NT \l__jsonparse_debug_mode_bool {
     \msg_log:nne { jsonparse } { debug-info } {
@@ -683,7 +697,7 @@
 
 % ===
 
-\cs_new:Npn \__jsonparse_collect_object_array:Nn #1#2 {
+\cs_new_protected:Npn \__jsonparse_collect_object_array:Nn #1#2 {
   \tl_clear:N \l__jsonparse_collect_object_array_store_tl
   \bool_set_false:N \l__jsonparse_collect_object_array_stop_bool
   \int_zero:N \l__jsonparse_collect_object_array_level_int
@@ -692,12 +706,12 @@
 }
 \cs_generate_variant:Nn \__jsonparse_collect_object_array:Nn { No }
 
-\cs_new:Npn \__jsonparse_collect_object_array:w #1 \q_stop {
+\cs_new_protected:Npn \__jsonparse_collect_object_array:w #1 \q_stop {
   \bool_if:NF \l__jsonparse_collect_object_array_stop_bool {
     \cs_if_exist_use:cTF { __jsonparse_collect_object_array_ \str_head:n {#1} :w } {
       #1 \q_stop
     } {
-      \tl_if_blank:eTF { \str_head:n {#1} } {
+      \tl_if_head_is_space:nTF {#1} {
         \__jsonparse_collect_object_array_space:w #1 \q_stop
       } {
         \__jsonparse_collect_object_array_other:w #1 \q_stop
@@ -706,51 +720,51 @@
   }
 }
 
-\cs_new:cpn { __jsonparse_collect_object_array_ " :w } " #1 " #2 \q_stop {
+\cs_new_protected:cpn { __jsonparse_collect_object_array_ " :w } " #1 " #2 \q_stop {
   \tl_put_right:Nn \l__jsonparse_collect_object_array_store_tl { " #1 " }
   \__jsonparse_collect_object_array:w #2 \q_stop
 }
 
 \exp_last_unbraced:Nno
-  \cs_new:cpn { __jsonparse_collect_object_array_ \c_left_brace_str :w }
-    \c_left_brace_str #1 \q_stop {
+  \cs_new_protected:cpn { __jsonparse_collect_object_array_ \c_left_brace_str :w }
+    \c_left_brace_str {
   \int_incr:N \l__jsonparse_collect_object_array_level_int
   \tl_put_right:No \l__jsonparse_collect_object_array_store_tl { \c_left_brace_str }
-  \__jsonparse_collect_object_array:w #1 \q_stop
+  \__jsonparse_collect_object_array:w
 }
 
 \exp_last_unbraced:Nno
-  \cs_new:cpn { __jsonparse_collect_object_array_ \c_right_brace_str :w }
-    \c_right_brace_str #1 \q_stop {
+  \cs_new_protected:cpn { __jsonparse_collect_object_array_ \c_right_brace_str :w }
+    \c_right_brace_str {
   \int_decr:N \l__jsonparse_collect_object_array_level_int
   \tl_put_right:No \l__jsonparse_collect_object_array_store_tl { \c_right_brace_str }
   \int_if_zero:nT { \l__jsonparse_collect_object_array_level_int } {
     \bool_gset_true:N \l__jsonparse_collect_object_array_stop_bool
   }
-  \__jsonparse_collect_object_array:w #1 \q_stop
+  \__jsonparse_collect_object_array:w
 }
 
-\cs_new:cpn { __jsonparse_collect_object_array_ [ :w } [ #1 \q_stop {
+\cs_new_protected:cpn { __jsonparse_collect_object_array_ [ :w } [ {
   \int_incr:N \l__jsonparse_collect_object_array_level_int
   \tl_put_right:Nn \l__jsonparse_collect_object_array_store_tl { [ }
-  \__jsonparse_collect_object_array:w #1 \q_stop
+  \__jsonparse_collect_object_array:w
 }
 
-\cs_new:cpn { __jsonparse_collect_object_array_ ] :w } ] #1 \q_stop {
+\cs_new_protected:cpn { __jsonparse_collect_object_array_ ] :w } ] {
   \int_decr:N \l__jsonparse_collect_object_array_level_int
   \tl_put_right:Nn \l__jsonparse_collect_object_array_store_tl { ] }
   \int_if_zero:nT { \l__jsonparse_collect_object_array_level_int } {
     \bool_gset_true:N \l__jsonparse_collect_object_array_stop_bool
   }
-  \__jsonparse_collect_object_array:w #1 \q_stop
+  \__jsonparse_collect_object_array:w
 }
 
-\cs_new:Npn \__jsonparse_collect_object_array_space:w #1#2 \q_stop {
+\cs_new_protected:Npn \__jsonparse_collect_object_array_space:w #1#2 \q_stop {
   \tl_put_right:Nn \l__jsonparse_collect_object_array_store_tl { ~ }
   \__jsonparse_collect_object_array:w #1#2 \q_stop
 }
 
-\cs_new:Npn \__jsonparse_collect_object_array_other:w #1#2 \q_stop {
+\cs_new_protected:Npn \__jsonparse_collect_object_array_other:w #1#2 \q_stop {
   \tl_put_right:Nn \l__jsonparse_collect_object_array_store_tl { #1 }
   \__jsonparse_collect_object_array:w #2 \q_stop
 }
@@ -757,42 +771,34 @@
 
 % ===
 
-\cs_new_protected:cpn { __jsonparse_parse_ \c_left_brace_str :w } #1 \q_stop {
-  \exp_last_unbraced:No
-    \__jsonparse_parse_object_begin:w #1 \q_stop
+\cs_new_protected:cpn { __jsonparse_parse_ \c_left_brace_str :w } {
+  \exp_last_unbraced:No \__jsonparse_parse_object_begin:w
 }
 
-\cs_new_protected:cpn { __jsonparse_parse_ \c_right_brace_str :w } #1 \q_stop {
-  \exp_last_unbraced:No
-    \__jsonparse_parse_object_end:w #1 \q_stop
+\cs_new_protected:cpn { __jsonparse_parse_ \c_right_brace_str :w } {
+  \exp_last_unbraced:No \__jsonparse_parse_object_end:w
 }
 
-\cs_new_protected:cpn { __jsonparse_parse_ [ :w } #1 \q_stop {
-  \exp_last_unbraced:No
-    \__jsonparse_parse_array_begin:w #1 \q_stop
+\cs_new_protected:cpn { __jsonparse_parse_ [ :w } {
+  \exp_last_unbraced:No \__jsonparse_parse_array_begin:w
 }
 
-\cs_new_protected:cpn { __jsonparse_parse_ ] :w } #1 \q_stop {
-  \exp_last_unbraced:No
-    \__jsonparse_parse_array_end:w #1 \q_stop
+\cs_new_protected:cpn { __jsonparse_parse_ ] :w } {
+  \exp_last_unbraced:No \__jsonparse_parse_array_end:w
 }
 
-\cs_new_protected:cpn { __jsonparse_parse_ , :w } #1 \q_stop {
-  \exp_last_unbraced:No
-    \__jsonparse_parse_delimiter:w #1 \q_stop
+\cs_new_protected:cpn { __jsonparse_parse_ , :w } {
+  \exp_last_unbraced:No \__jsonparse_parse_delimiter:w
 }
 
-\cs_new_protected:cpn { __jsonparse_parse_ " :w } #1 \q_stop {
-  \exp_last_unbraced:No
-    \__jsonparse_parse_string_key:w #1 \q_stop
+\cs_new_protected:cpn { __jsonparse_parse_ " :w } {
+  \exp_last_unbraced:No \__jsonparse_parse_string_key:w
 }
 
 \cs_new_protected:Npn \__jsonparse_array_key_set: {
   \str_if_eq:eVT {
-    \tl_range:Nen \l__jsonparse_prefix_tl {
-      \int_eval:n {
-        -1 * \tl_count:N \l__jsonparse_array_sep_left_str
-      }
+    \tl_range:Nnn \l__jsonparse_prefix_tl {
+      -1 * \tl_count:N \l__jsonparse_array_sep_left_str
     } { -1 }
   } \l__jsonparse_array_sep_left_str {
     \int_incr:N \l__jsonparse_array_index_int
@@ -913,6 +919,12 @@
     % object item without key
     \msg_error:nn { jsonparse } { parsing-error-key }
   }
+  \bool_lazy_and:nnT { \l__jsonparse_parse_array_bool } {
+    \tl_trim_spaces_apply:nN {#1} \tl_if_head_eq_charcode_p:nN ]
+  } {
+    % trailing comma in array
+    \msg_error:nn { jsonparse } { parsing-error-comma }
+  }
   \bool_set_false:N \l__jsonparse_parse_key_bool
   \tl_set:Nn \l__jsonparse_remainder_tl {#1}
   \__jsonparse_parse_remainder:
@@ -976,7 +988,7 @@
 \cs_new_protected:Npn \__jsonparse_parse_other_aux:w #1 \s__jsonparse_split #2 \q_stop {
   \tl_set:Nn \l__jsonparse_remainder_tl {#2}
   \tl_remove_all:Nn \l__jsonparse_remainder_tl { \s__jsonparse_split }
-  \tl_set:Ne \l__jsonparse_temp_tl { \tl_trim_spaces:n {#1} }
+  \__jsonparse_tl_set_trim_spaces:Nn \l__jsonparse_temp_tl {#1}
   \cs_if_exist_use:cF { __jsonparse_parse_ \str_casefold:o { \l__jsonparse_temp_tl } : } {
     \bool_if:NTF \l__jsonparse_validate_numbers_bool {
       \jsonparse_if_num:VTF \l__jsonparse_temp_tl {
@@ -1051,7 +1063,7 @@
 }
 
 \cs_new_protected:Npn \__jsonparse_parse_remainder: {
-  \tl_set:Ne \l__jsonparse_remainder_tl { \tl_trim_spaces:e { \l__jsonparse_remainder_tl } }
+  \__jsonparse_tl_trim_spaces:N \l__jsonparse_remainder_tl
   \tl_if_empty:NF \l__jsonparse_remainder_tl {
     \jsonparse_parse:o { \l__jsonparse_remainder_tl }
   }
@@ -1059,21 +1071,23 @@
 
 \cs_new_protected:Npn \__jsonparse_filter:nn #1#2 {
   \str_case_e:en {
-    \tl_range:nne {#1} { 1 } { \int_eval:n {
-      \tl_count:o { \l__jsonparse_filter_key_str } + 1
-    } }
+    \tl_range:nnn {#1} { 1 } {
+      \tl_count:N \l__jsonparse_filter_key_str + 1
+    }
   } {
     { \l__jsonparse_filter_key_str \l__jsonparse_child_sep_str } {
-      \prop_put:Nen \l__jsonparse_temp_prop
-        { \tl_range:nen {#1} { \int_eval:n {
-          \tl_count:o { \l__jsonparse_filter_key_str } + 2
-        } } { -1 } } {#2}
+      \prop_put:Nen \l__jsonparse_temp_prop {
+        \tl_range:nnn {#1} {
+          \tl_count:N \l__jsonparse_filter_key_str + 2
+        } { -1 }
+      } {#2}
     }
     { \l__jsonparse_filter_key_str \l__jsonparse_array_sep_left_str } {
-      \prop_put:Nen \l__jsonparse_temp_prop
-        { \tl_range:nen {#1} { \int_eval:n {
-          \tl_count:o { \l__jsonparse_filter_key_str } + 1
-        } } { -1 } } {#2}
+      \prop_put:Nen \l__jsonparse_temp_prop {
+        \tl_range:nnn {#1} {
+          \tl_count:N \l__jsonparse_filter_key_str + 1
+        } { -1 }
+      } {#2}
     }
   }
 }
@@ -1149,6 +1163,12 @@
 }
 \cs_generate_variant:Nn \__jsonparse_nested_construct_cs:Nnn { Noo }
 
+\cs_new_protected:Npn \__jsonparse_protect_escape_sequeces: {
+  \clist_map_inline:nn { " , / , \c_backslash_str , b , f , n , r , t , u } {
+    \cs_set:cpn {##1} { \exp_not:c {##1} }
+  }
+}
+
 \NewDocumentCommand { \JSONParse } { O{} m +v } {
   \group_begin:
     \str_set:Ne \l_jsonparse_current_prop_str { \cs_to_str:N #2 }
@@ -1175,16 +1195,11 @@
       }
       \tl_gclear:N \g__jsonparse_json_tl
       \group_begin:
-        \cs_set:Npn \" { \exp_not:N \" }
-        \cs_set:Npn \/ { \exp_not:N \/ }
-        \cs_set:Npn \\ { \exp_not:N \\ }
-        \cs_set:Npn \b { \exp_not:N \b }
-        \cs_set:Npn \f { \exp_not:N \f }
-        \cs_set:Npn \n { \exp_not:N \n }
-        \cs_set:Npn \r { \exp_not:N \r }
-        \cs_set:Npn \t { \exp_not:N \t }
-        \cs_set:Npn \u { \exp_not:N \u }
+        \__jsonparse_protect_escape_sequeces:
+        \__jsonparse_nested_construct_cs:Noo \$ \c_left_brace_str \c_right_brace_str
+        % backward compatibility
         \__jsonparse_nested_construct_cs:Noo \x \c_left_brace_str \c_right_brace_str
+        % ===
         \tl_set:Nn \obeyedline { ~ }
         \tl_gset_rescan:Nne \g__jsonparse_json_tl { \cctab_select:N \c__jsonparse_json_escape_cctab } {#3}
         \jsonparse_parse_to_prop:Ne #2 { \g__jsonparse_json_tl }
@@ -1226,15 +1241,7 @@
       }
       \tl_gclear:N \g__jsonparse_json_tl
       \group_begin:
-        \cs_set:Npn \" { \exp_not:N \" }
-        \cs_set:Npn \/ { \exp_not:N \/ }
-        \cs_set:Npn \\ { \exp_not:N \\ }
-        \cs_set:Npn \b { \exp_not:N \b }
-        \cs_set:Npn \f { \exp_not:N \f }
-        \cs_set:Npn \n { \exp_not:N \n }
-        \cs_set:Npn \r { \exp_not:N \r }
-        \cs_set:Npn \t { \exp_not:N \t }
-        \cs_set:Npn \u { \exp_not:N \u }
+        \__jsonparse_protect_escape_sequeces:
         \file_get:nnN {#3} { \cctab_select:N \c__jsonparse_json_escape_cctab } \l__jsonparse_externalize_file_data_tl
         \tl_gset_eq:NN \g__jsonparse_json_tl \l__jsonparse_externalize_file_data_tl
         \jsonparse_parse_to_prop:Ne #2 { \g__jsonparse_json_tl }
@@ -1246,9 +1253,6 @@
   \group_end:
 }
 
-\cs_new_eq:NN \__jsonparse_tex_quote: \"
-\cs_new_eq:NN \__jsonparse_tex_backslash: \\
-
 \prg_new_conditional:Npnn \jsonparse_unicode_if_high_surrogate:n #1 { p , T , F , TF } {
   \int_compare:nNnTF {#1} > { "D7FF } {
     \int_compare:nNnTF {#1} < { "DC00 } {
@@ -1309,15 +1313,18 @@
   }
 }
 
+\cs_new_eq:NN \__jsonparse_tex_quote: \"
+\cs_new_eq:NN \__jsonparse_tex_backslash: \\
+
 \cs_new_protected:Npn \__jsonparse_rescan_setup:Nn #1#2 {
   \cs_set:Npn \" { " }
   \cs_set:Npn \/ { / }
   \cs_set:Npn \\ { \c_backslash_str }
-  \cs_set:Npn \b { \l__jsonparse_backspace_str }
-  \cs_set:Npn \f { \l__jsonparse_formfeed_str }
-  \cs_set:Npn \n { \l__jsonparse_linefeed_str }
-  \cs_set:Npn \r { \l__jsonparse_carriage_return_str }
-  \cs_set:Npn \t { \l__jsonparse_horizontal_tab_str }
+  \cs_set:Npn \b { \l__jsonparse_backspace_tl }
+  \cs_set:Npn \f { \l__jsonparse_formfeed_tl }
+  \cs_set:Npn \n { \l__jsonparse_linefeed_tl }
+  \cs_set:Npn \r { \l__jsonparse_carriage_return_tl }
+  \cs_set:Npn \t { \l__jsonparse_horizontal_tab_tl }
   \cs_set_eq:NN \u \__jsonparse_unicode_char:NNNNN
   \tl_set:Ne #1 { #2 \s__jsonparse_stop }
   \cs_set_eq:NN \" \__jsonparse_tex_quote:
@@ -1513,7 +1520,9 @@
 }
 
 \cs_new_protected:Npn \__jsonparse_array_count:nn #1#2 {
-  \str_if_eq:eVF { \tl_head:n {#1} } \l__jsonparse_array_sep_left_str {
+  \str_if_eq:eVF {
+    \tl_range:nnn {#1} { 1 } { \tl_count:N \l__jsonparse_array_sep_left_str }
+  } \l__jsonparse_array_sep_left_str {
     \msg_error:nnn { jsonparse } { not-array-item }
       {#1}
   }



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