diff options
author | Norbert Preining <norbert@preining.info> | 2022-12-10 03:01:14 +0000 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2022-12-10 03:01:14 +0000 |
commit | 6e1509aea8d6baf0753bf9bedbb4633a4808d0c3 (patch) | |
tree | 4bd104aca10c2b5417d0c1baa0c0c914d910fed0 /macros/latex/contrib/lt3rawobjects/lt3rawobjects.dtx | |
parent | adb3f370448520d00bad059bd946916aa618613f (diff) |
CTAN sync 202212100301
Diffstat (limited to 'macros/latex/contrib/lt3rawobjects/lt3rawobjects.dtx')
-rw-r--r-- | macros/latex/contrib/lt3rawobjects/lt3rawobjects.dtx | 366 |
1 files changed, 346 insertions, 20 deletions
diff --git a/macros/latex/contrib/lt3rawobjects/lt3rawobjects.dtx b/macros/latex/contrib/lt3rawobjects/lt3rawobjects.dtx index a151fc48ee..d0ee88b064 100644 --- a/macros/latex/contrib/lt3rawobjects/lt3rawobjects.dtx +++ b/macros/latex/contrib/lt3rawobjects/lt3rawobjects.dtx @@ -22,11 +22,11 @@ % % \iffalse %<*driver> -\ProvidesFile{lt3rawobjects.dtx}[2022/11/27 2.1.1 Objects and proxies in LaTeX3] +\ProvidesFile{lt3rawobjects.dtx}[2022/12/09 2.2 Objects and proxies in LaTeX3] %</driver> %<package>\NeedsTeXFormat{LaTeX2e} %<package>\RequirePackage{expl3}[2022-04-10] -%<package>\ProvidesExplPackage{lt3rawobjects}{2022/11/27}{ 2.1.1 }{Objects and proxies in LaTeX3} +%<package>\ProvidesExplPackage{lt3rawobjects}{2022/12/09}{ 2.2 }{Objects and proxies in LaTeX3} %<*driver> \documentclass[full]{l3doc} \usepackage{lt3rawobjects} @@ -38,8 +38,8 @@ % \fi % % \NewDocumentCommand{\thpkg}{}{\pkg{lt3rawobjects}} -% \NewDocumentCommand{\thvsn}{}{2.1.1} -% \NewDocumentCommand{\thdta}{}{2022/11/27} +% \NewDocumentCommand{\thvsn}{}{2.2} +% \NewDocumentCommand{\thdta}{}{2022/12/09} % \NewDocumentCommand{\fromV}{ m }{{\ttfamily From: #1}} % % \title{The \pkg{lt3rawobjects} package} @@ -53,21 +53,17 @@ % \begin{documentation} % % \section{Introduction} -% First to all notice that \thpkg\ means ``raw object(s)'', indeed \thpkg\ introduces a new mechanism to create objects like the well known C structures. The functions exported by this package are quite low level, and many important mechanisms like member protection and name resolution aren't already defined and should be introduced by intermediate packages. +% First to all notice that \thpkg\ means ``raw object(s)'', indeed \thpkg\ introduces a new mechanism to create objects like the well known C structures. The functions exported by this package are quite low level, and many important mechanisms like member protection and name resolution aren't already defined and should be introduced by intermediate packages. Higher level libraries built on top of \thpkg\ could also implement an improved and simplified syntax since the main focus of \thpkg\ is versatility and expandability rather than common usage. % % This packages follows the \href{https://semver.org/}{SemVer} specification (\texttt{https://semver.org/}). In particular any major version update (for example from \texttt{1.2} to \texttt{2.0}) may introduce imcompatible changes and so it's not advisable to work with different packages that require different major versions of \thpkg. Instead changes introduced in minor and patch version updates are always backward compatible, and any withdrawn function is declared deprecated instead of being removed. % -% \section{To do} -% \begin{itemize} -% \item Uniform declarations for templated proxies; -% \item Constant objects. -% \end{itemize} -% % \section{Objects and proxies} % Usually an object in programming languages can be seen as a collection of variables (organized in different ways depending on the chosen language) treated as part of a single entity. In \thpkg\ objects are collections of % \begin{itemize} % \item \LaTeX3 variables, called \emph{members}; -% \item \LaTeX3 functions, called \emph{methods}. +% \item \LaTeX3 functions, called \emph{methods}; +% \item generic control sequences, calles simply \emph{macros}; +% \item other \emph{embedded objects}. % \end{itemize} % Both members and methods can be retrieved from a string representing the container object, that is the \emph{address} of the object and act like the address of a structure in C. % @@ -89,11 +85,105 @@ % % Members declared as mutables works as normal variables: you can modify their value and retrieve it at any time. Instead members and methods declared as near constant works as constants: when you create them you must specify their initial value (or function body for methods) and you won't be allowed to modify it later. Remote constants for an object are simply near constants defined in its generator: all near constants defined inside a proxy are automatically visible as remote constants to every object generated from that proxy. Usually functions involving near constants have |nc| inside their name, and |rc| if instead they use remote constants. % -% Instead of creating mutable members in each of your objects you can push their specifications inside the generating proxy via \cs{proxy_push_member}. In this way either object created from such proxy will have the specified members. Specify mutable members in this way allows you to omit that member type in some functions as \cs{object_member_adr} for example, their member type will be deduced automatically from its specification inside generating proxy. +% Instead of creating embedded objects or mutable members in each of your objects you can push their specifications inside the generating proxy via \cs{proxy_push_embedded}, \cs{proxy_push_member}. In this way either object created from such proxy will have the specified members and embedded objects. Specify mutable members in this way allows you to omit that member type in some functions as \cs{object_member_adr} for example, their member type will be deduced automatically from its specification inside generating proxy. % % Objects can be declared public, private and local, global. In a public/private object every nonconstant member and method is declared public/private, but inside local/global object only assignation to mutable members is performed locally/globally since allocation is always performed globally via \cs{\meta{type}_new:Nn} functions (nevertheless members will be accordingly declared |g_| or |l_|). This is intentional in order to follow the \LaTeX3 guidelines about variables managment, for additional motivations you can see \href{https://github.com/latex3/latex3/issues/410}{this thread} in the \LaTeX3 repository. % -% Address of members/methods can be obtained with functions in the form \cs{object_\meta{category}\meta{item}_adr} where \meta{item} is |member| or |method| and \meta{category} is empty for mutable members, |nc| for near constants and |rc| for remote constants. For example \cs{object_rcmethod_adr} retrieves the address of specified remote constant method. +% Address of members/methods can be obtained with functions in the form \cs{object_\meta{item}\meta{category}_adr} where \meta{item} is |member|, |method|, |macro| or |embedded| and \meta{category} is |nc| for near constants, |rc| for remote ones and empty for others. For example \cs{object_rcmethod_adr} retrieves the address of specified remote constant method. +% +% \section{Put objects inside objects} +% Sometimes it's necessary to include other objects inside an object, and since objects are structured data types you can't put them directly inside a variable. However \thpkg\ provides some workarounds that allows you to include objects inside other objects, each with its own advantages and disadvantages. +% +% In the following examples we're in module |mymod| and we want to put inside object |A| another object created with proxy |prx|. +% +% \subsection{Put a pointer variable} +% A simple solution is creating that object outside |A| with \cs{object_create} +% \begin{verbatim} +% \object_create:nnnNN +% { \object_address:nn{ mymod }{ prx } }{ mymod }{ B } .... +% \end{verbatim} +% and then creating a pointer variable inside |A| (usually of type |tl| or |str|) holding the newly created address: +% \begin{verbatim} +% \object_new_member:nnn +% { +% \object_address:nn{ mymod }{ A } +% }{ pointer }{ tl } +% +% \tl_(g)set:cn +% { +% \object_new_member:nnn +% { +% \object_address:nn{ mymod }{ A } +% }{ pointer }{ tl } +% } +% { +% \object_address:nn{ mymod }{ B } +% } +% +% \end{verbatim} +% you can the access the pointed object by calling \cs{object_member_use} on |pointer| member. +% +% \subsubsection*{Advantages} +% \begin{itemize} +% \item Simple and no additional function needed to create and manage included objects; +% \item you can share the same object between different containers; +% \item included objects are objects too, you can use address stored in pointer member just like any object address. +% \end{itemize} +% +% \subsubsection*{Disadvantages} +% \begin{itemize} +% \item You must manually create both the objects and link them; +% \item creating objects also creates additional hidden variables, taking so (little) additional space. +% \end{itemize} +% +% \subsection{Clone the inner structure} +% Instead of referring a complete object you can just clone the inner structure of |prx| and put inside |A|. For example if |prx| declares member |x| of type |str| and member |y| of type |int| then you can do +% \begin{verbatim} +% \object_new_member:nnn +% { +% \object_address:nn{ mymod }{ A } +% }{ prx-x }{ str } +% \object_new_member:nnn +% { +% \object_address:nn{ mymod }{ A } +% }{ prx-y }{ int } +% \end{verbatim} +% and then use |prx-x|, |prx-y| as normal members of |A|. +% +% \subsubsection*{Advantages} +% \begin{itemize} +% \item Simple and no additional function needed to create and manage included objects; +% \item you can put these specifications inside a proxy so that every object created with it will have the required members/methods; +% \item no hidden variable created, lowest overhead among the proposed solutions. +% \end{itemize} +% +% \subsubsection*{Disadvantages} +% \begin{itemize} +% \item Cloning the inner structure doesn't create any object, so you don't have any object address nor you can share the included ``object'' unless you share the container object too. +% \end{itemize} +% +% \subsection{Embedded objects} +% From \thpkg\ \texttt{2.2} you can put \emph{embedded objects} inside objects. Embedded objects are created with \cs{embedded_create} function +% \begin{verbatim} +% \embedded_create:nnn +% { +% \object_address:nn{ mymod }{ A } +% }{ prx }{ B } +% \end{verbatim} +% and addresses of emmbedded objects can be retrieved with function \cs{object_embedded_adr}. You can also put the definition of embedded objects in a proxy by using \cs{proxy_push_embedded} just like \cs{proxy_push_member}. +% +% \subsubsection*{Advantages} +% \begin{itemize} +% \item You can put a declaration inside a proxy so that embedded objects are automatically created during creation of parent object; +% \item included objects are objects too, you can use address stored in pointer member just like any object address. +% \end{itemize} +% +% \subsubsection*{Disadvantages} +% \begin{itemize} +% \item Needs additional functions available for version \texttt{2.2} or later; +% \item embedded objects must have the same scope and visibility of parent one; +% \item creating objects also creates additional hidden variables, taking so (little) additional space. +% \end{itemize} % % \section{Library functions} % \label{sec:lib} @@ -117,6 +207,15 @@ % \fromV{1.1} % \end{function} % +% \begin{function}[EXP]{\object_embedded_adr:nn, \object_embedded_adr:Vn} +% \begin{syntax} +% \cs{object_embedded_adr:nn} \Arg{address} \Arg{id} +% \end{syntax} +% Compose the address of embedded object with name \meta{id} inside the parent object with address \meta{address}. Since an embedded object is also an object you can use this function for any function that accepts object addresses as an argument. +% +% \fromV{2.2} +% \end{function} +% % \begin{function}[pTF]{\object_if_exist:n, \object_if_exist:V} % \begin{syntax} % \cs{object_if_exist_p:n} \marg{address} @@ -212,7 +311,7 @@ % \begin{function}{\object_member_set:nnnn, \object_member_set:nnvn, \object_member_set:Vnnn, \object_member_set:nnn, \object_member_set:Vnn} % \begin{syntax} % \cs{object_member_set:nnnn} \Arg{address} \Arg{member name} \Arg{member type} \Arg{value} -% \cs{object_member_set:nnn} \Arg{address} \Arg{member name} \Arg{valuer} +% \cs{object_member_set:nnn} \Arg{address} \Arg{member name} \Arg{value} % \end{syntax} % Sets the value of specified member to \marg{value}. It calls implicitly \cs{\meta{member type}_(g)set:cn} then be sure to define it before calling this method. % @@ -343,6 +442,28 @@ % \fromV{2.1} % \end{function} % +% \subsection{Macros} +% +% \begin{function}[EXP]{\object_macro_adr:nn, \object_macro_adr:Vn} +% \begin{syntax} +% \cs{object_macro_adr:nn} \Arg{address} \Arg{macro name} +% \end{syntax} +% Address of specified macro. +% +% \fromV{2.2} +% \end{function} +% +% \begin{function}[EXP]{\object_macro_use:nn, \object_macro_use:Vn} +% \begin{syntax} +% \cs{object_macro_use:nn} \Arg{address} \Arg{macro name} +% \end{syntax} +% Uses the specified macro. This function is expandable if and only if the specified macro is it. +% +% \fromV{2.2} +% \end{function} +% +% There isn't any standard function to create macros, and macro declarations can't be inserted in a |proxy| object. In fact a macro is just an unspecialized control sequence at the disposal of users that usually already know how to implement them. +% % \subsection{Proxy utilities and object creation} % % \begin{function}[pTF]{\object_if_proxy:n, \object_if_proxy:V} @@ -394,6 +515,15 @@ % \fromV{1.0} % \end{function} % +% \begin{function}{\embedded_create:nnn, \embedded_create:Vnn, \embedded_create:nvn} +% \begin{syntax} +% \cs{embedded_create:nnn} \Arg{parent object} \Arg{proxy address} \Arg{id} +% \end{syntax} +% Creates an embedded object with name \meta{id} inside \meta{parent object}. +% +% \fromV{2.2} +% \end{function} +% % \begin{variable}{\c_object_local_str, \c_object_global_str} % Possible values for \meta{scope} parameter. % @@ -436,13 +566,22 @@ % % \begin{function}{\proxy_push_member:nnn, \proxy_push_member:Vnn} % \begin{syntax} -% \cs{proxy_push_member:nnn} \marg{proxy address} \marg{ member name } \marg{ member type } +% \cs{proxy_push_member:nnn} \Arg{proxy address} \Arg{member name} \Arg{member type} % \end{syntax} % Updates a proxy object with a new member specification, so that every subsequential object created with this proxy will have a member variable with the specified name and type that can be retrieved with \cs{object_member_type} functions. % % \fromV{1.0} % \end{function} % +% \begin{function}{\proxy_push_embedded:nnn, \proxy_push_embedded:Vnn} +% \begin{syntax} +% \cs{proxy_push_embedded:nnn} \Arg{proxy address} \Arg{embedded object name} \Arg{embedded object proxy} +% \end{syntax} +% Updates a proxy object with a new embedded object specification. +% +% \fromV{2.2} +% \end{function} +% % \begin{function}{\object_assign:nn, \object_assign:Vn, \object_assign:nV, \object_assign:VV} % \begin{syntax} % \cs{object_assign:nn} \marg{to address} \marg{from address} @@ -519,6 +658,91 @@ % \object_member_use:Vn \l_myobj_str { myvar } % \ExplSyntaxOff % +% \subsection*{Example 2} +% In this example we create a proxy object with an embedded object inside. +% +% Internal proxy +% \begin{verbatim} +% \proxy_create:nnN{ mymod }{ INT } \c_object_public_str +% \proxy_push_member:nnn +% { +% \object_address:nn{ mymod }{ INT } +% }{ var }{ tl } +% \end{verbatim} +% +% Container proxy +% \begin{verbatim} +% \proxy_create:nnN{ mymod }{ EXT } \c_object_public_str +% \proxy_push_embedded:nnn +% { +% \object_address:nn{ mymod }{ EXT } +% } +% { emb } +% { +% \object_address:nn{ mymod }{ INT } +% } +% \end{verbatim} +% +% Now we create a new object from proxy |EXT|. It'll contain an embedded object created with |INT| proxy: +% \begin{verbatim} +% \str_new:N \g_EXTobj_str +% \int_new:N \g_intcount_int +% \object_gallocate_gincr:NNnnNN +% \g_EXTobj_str \g_intcount_int +% { +% \object_address:nn{ mymod }{ EXT } +% } +% { mymod } +% \c_object_local_str \c_object_public_str +% \end{verbatim} +% and use the embedded object in the following way: +% \begin{verbatim} +% \object_member_set:nnn +% { +% \object_embedded_adr:Vn \g_EXTobj_str { emb } +% }{ var }{ Hi } +% \object_member_use:nn +% { +% \object_embedded_adr:Vn \g_EXTobj_str { emb } +% }{ var } +% \end{verbatim} +% Output: \ExplSyntaxOn +% \proxy_create:nnN{ mymod }{ INT } \c_object_public_str +% \proxy_push_member:nnn +% { +% \object_address:nn{ mymod }{ INT } +% }{ var }{ tl } +% +% \proxy_create:nnN{ mymod }{ EXT } \c_object_public_str +% \proxy_push_embedded:nnn +% { +% \object_address:nn{ mymod }{ EXT } +% } +% { emb } +% { +% \object_address:nn{ mymod }{ INT } +% } +% +% \str_new:N \g_EXTobj_str +% \int_new:N \g_intcount_int +% \object_gallocate_gincr:NNnnNN +% \g_EXTobj_str \g_intcount_int +% { +% \object_address:nn{ mymod }{ EXT } +% } +% { mymod } +% \c_object_local_str \c_object_public_str +% +% \object_member_set:nnn +% { +% \object_embedded_adr:Vn \g_EXTobj_str { emb } +% }{ var }{ Hi } +% \object_member_use:nn +% { +% \object_embedded_adr:Vn \g_EXTobj_str { emb } +% }{ var } +% \ExplSyntaxOff +% % \section{Templated proxies} % At the current time there isn't a standardized approach to templated proxies. One problem of standardized templated proxies is how to define struct addresses for every kind of argument (token lists, strings, integer expressions, non expandable arguments, ...). % @@ -592,6 +816,20 @@ % \end{macrocode} %\end{macro} % +% \begin{macro}{\object_embedded_adr:nn} +% Address of embedded object +% \begin{macrocode} + +\cs_new:Nn \object_embedded_adr:nn + { + #1 \tl_to_str:n{ _SUB_ #2 } + } + +\cs_generate_variant:Nn \object_embedded_adr:nn{ Vn } + +% \end{macrocode} +% \end{macro} +% %\begin{macro}{\object_address_set:Nnn, \object_address_gset:Nnn} % Saves the address of an object into a string variable % \begin{macrocode} @@ -630,8 +868,6 @@ \cs_generate_variant:Nn \@@_object_visvar:n { V } % \end{macrocode} % - -% %\begin{macro}[pTF]{\object_if_exist:n} %Tests if object exists. % \begin{macrocode} @@ -733,6 +969,29 @@ % \end{macrocode} %\end{macro} % +%\begin{macro}{\object_macro_adr:nn, \object_macro_use:nn} +% Generic macro address +% \begin{macrocode} + +\cs_new:Nn \object_macro_adr:nn + { + #1 \tl_to_str:n{ _MACRO_ #2 } + } + +\cs_generate_variant:Nn \object_macro_adr:nn{ Vn } + +\cs_new:Nn \object_macro_use:nn + { + \use:c + { + \object_macro_adr:nn{ #1 }{ #2 } + } + } + +\cs_generate_variant:Nn \object_macro_use:nn{ Vn } + +% \end{macrocode} +% \end{macro} % %\begin{macro}{\object_member_adr:nnn, \object_member_adr:nn} %Get the address of a member variable @@ -800,6 +1059,7 @@ } \cs_generate_variant:Nn \object_member_adr:nn { Vn } + % \end{macrocode} %\end{macro} % @@ -1293,6 +1553,7 @@ % % Source of \verb|proxy| object % \begin{macrocode} + \str_const:cn { \@@_object_modvar:V \c_proxy_address_str } { rawobjects } \str_const:cV { \@@_object_pxyvar:V \c_proxy_address_str } @@ -1306,9 +1567,16 @@ { \object_member_adr:Vnn \c_proxy_address_str { varlist }{ seq } } - { varlist } + { varlist, objlist } \object_newconst_str:Vnn \c_proxy_address_str { varlist_type }{ seq } +\object_newconst_str:Vnn \c_proxy_address_str { objlist_type }{ seq } + +\seq_const_from_clist:cn + { + \object_member_adr:Vnn \c_proxy_address_str { objlist }{ seq } + } + {} % \end{macrocode} % @@ -1369,7 +1637,7 @@ % \end{macrocode} %\end{macro} % -% \begin{macro}{\object_create:nnnNN, \object_create_set:NnnnNN, \object_create_gset:NnnnNN} +% \begin{macro}{\object_create:nnnNN, \object_create_set:NnnnNN, \object_create_gset:NnnnNN, \embedded_create:nnn} % Creates an object from a proxy % \begin{macrocode} @@ -1408,7 +1676,22 @@ \object_ncmember_adr:nnn { #1 }{ ##1 _ type }{ str } } } + + \seq_map_inline:cn + { + \object_member_adr:nnn { #1 }{ objlist }{ seq } + } + { + \embedded_create:nvn + { #2 } + { + \object_ncmember_adr:nnn { #1 }{ ##1 _ proxy }{ str } + } + { ##1 } + } } + +\cs_generate_variant:Nn \@@_create_anon:nnnNN { nnvcc } \cs_new_protected:Nn \object_create:nnnNN { @@ -1427,10 +1710,29 @@ \object_create:nnnNN { #2 }{ #3 }{ #4 } #5 #6 \str_gset:Nx #1 { \object_address:nn { #3 }{ #4 } } } + +\cs_new_protected:Nn \embedded_create:nnn + { + \@@_create_anon:nnvcc { #2 } + { + \object_embedded_adr:nn + { #1 }{ #3 } + } + { + \@@_object_modvar:n{ #1 } + } + { + \@@_object_scovar:n{ #1 } + } + { + \@@_object_visvar:n{ #1 } + } + } \cs_generate_variant:Nn \object_create:nnnNN { VnnNN } \cs_generate_variant:Nn \object_create_set:NnnnNN { NVnnNN, NnnfNN } \cs_generate_variant:Nn \object_create_gset:NnnnNN { NVnnNN, NnnfNN } +\cs_generate_variant:Nn \embedded_create:nnn { nvn, Vnn } % \end{macrocode} %\end{macro} @@ -1553,6 +1855,30 @@ % \end{macrocode} %\end{macro} % +%\begin{macro}{\proxy_push_embedded:nnn} +% Push a new embedded object inside a proxy. +% \begin{macrocode} + +\cs_generate_variant:Nn \object_newconst_str:nnn { nnx } + +\cs_new_protected:Nn \proxy_push_embedded:nnn + { + \@@_force_scope:n { #1 } + \object_newconst_str:nnx { #1 }{ #2 _ proxy }{ #3 } + \seq_gput_left:cn + { + \object_member_adr:nnn { #1 }{ objlist }{ seq } + } + { #2 } + } + +\cs_generate_variant:Nn \proxy_push_embedded:nnn { Vnn } + +% \end{macrocode} +%\end{macro} +% +% +% %\begin{macro}{\object_assign:nn} %Copy an object to another one. % \begin{macrocode} |