about-properties.tex /size: 7043 b    last modification: 2023-12-21 09:43
1% language=us
2
3\startcomponent about-properties
4
5\environment about-environment
6
7\startchapter[title=Properties]
8
9\startsection[title=Introduction]
10
11Attributes are a nice extension to \TEX\ as they permits us to let information
12travel with nodes. Internally they are represented as a linked list that
13travels with a node. Because often a sequence of nodes has the same attributes,
14this mechanism is quite efficient. Access is relatively fast too. Attributes
15have a number and a value (also a number) which is fine. Of course one could
16wish for them to be anything, but imagine the amount of management needed
17in the engine if that were the case. Not only does saving and restoring (due to
18grouping) at the \TEX\ end has no \LUA\ equivalent, an overload of the \LUA\
19registry (the most natural interface for this) is not what we want. Of course
20it is also not acceptable that (future) extensions slow down a run. In fact,
21leaner and meaner should be the main objective.
22
23At some point I thought that packing crucial information in a node using a bitset
24would help to speed up some critical mechanisms (mostly fonts) but although
25managing some 32 or 64 on||off states is possible in a more closed macro package,
26in practice it would lead to conflicts in use. Also, an experimental
27implementation of this idea was not faster than using attributes due to the fact
28that manipulating bits also involves function calls that deal with setting,
29resetting, masking and more. It also makes nodes larger and increases the memory
30footprint.
31
32So, when I discarded that idea, I moved to another one, which is associating a
33\LUA\ table with each node (that makes sense). Again, an implementation where
34some way a reference to a table is carried with a node, is non||trivial because
35it has to go via the \LUA\ registry and will not be too efficient in terms of
36speed. Also, when dealing with such information one wants to stay at the \LUA\
37end and not cross the C||boundary too often.
38
39Therefore a different approach was taken which involves a \LUA\ table. The main
40issue with carrying information with a node is not to associate that information,
41but to make sure that it gets cleaned up when a node is freed and copied when a
42node is copied. All nodes that have attributes, also get properties.
43
44\stopsection
45
46\startsection[title=The implementation]
47
48The implementation is rather minimalistic. This is because hard codes solutions
49don't fit in the \LUATEX\ design philosophy. Also, there are many ways to use
50such a mechanism so too much hard coded behaviour only complicates usage.
51
52When a node is copied, we also copy the associated property entry. Normally its
53type is \type {nil} or \type {table}. Depending on how you enabled this
54mechanism, the table copy is shallow (just a reference to the same table), or we
55assign en empty table with the original as metatable index. The second approach
56as some more overhead.
57
58When a new node is assigned, nothing extra is done with the properties. The
59overhead is zero. This means that when you want to assign properties at the \LUA\
60end, you also have to check if a node property already has a table and if not,
61create one. The same is true for querying properties: you have to test if there
62are properties at all.
63
64When you use the \quote {direct} node model, you can directly access the property
65table. But, with direct as well as wrapped nodes, you can also use setters and
66getters. The property table has no metatable so you can add your own one for
67alternative access if needed. In \CONTEXT\ you can best stay away from such hacks
68and use the provided mechanisms because otherwise you get a performance hit.
69
70\stopsection
71
72\startsection[title=The \LUA\ interface]
73
74The interface (in regular nodes as well as direct ones) is quite simple and
75provides five functions:
76
77\starttyping
78set_properties_mode(boolean,boolean)
79flush_properties_table()
80get_properties_table()
81getproperty(node_id)
82setproperty(node_id,value)
83\stoptyping
84
85By default this mechanism is disabled so that when it's not used, there is no
86overhead involved. With \type {set_properties_mode} the first argument determines
87if you enable or disable this mechanism. The properties themselves are untouched.
88When the second argument is \type {true} copied properties create a new table
89with a metatable pointing to the original. You can flush all properties with
90\type {flush_properties_table}.
91
92You can access and set properties with \type {getproperty} and \type
93{setproperty}. Instead you can also use the table approach, where you can reach
94the table with \type {get_properties_table}. Keep in mind that the normal and
95direct calls to this function return a different table.
96
97\stopsection
98
99\startsection[title=A few examples]
100
101The following examples use \CONTEXT\ but apart from the calls to the \type
102{context} namespace, they are rather generic. We have enabled the property
103mechanism with:
104
105\starttyping
106set_properties_mode(true)
107\stoptyping
108
109We fill a box:
110
111\startbuffer
112\newbox\MyPropertyBox
113
114\setbox\MyPropertyBox=\hbox{test}
115\stopbuffer
116
117\typebuffer \getbuffer
118
119\startbuffer[common]
120local list = tex.getbox("MyPropertyBox").list
121
122local function start()
123    context.starttabulate { "||||" }
124    context.HL()
125end
126
127local function stop()
128    context.HL()
129    context.stoptabulate()
130end
131
132local function row(n,p)
133    context.NC() context(tostring(n==p))
134    context.NC() context(tostring(n))
135    context.NC() context(tostring(p))
136    context.NC() context.NR()
137end
138\stopbuffer
139
140\typebuffer[common]
141
142We will demonstrate the four access models. First regular properties
143using functions:
144
145\startbuffer[example]
146for n in node.traverse(list) do
147    node.setproperty(n,{ vif = n })
148end
149start()
150for n in node.traverse(list) do
151    row(n,node.getproperty(n).vif)
152end
153stop()
154\stopbuffer
155
156\typebuffer[example] {\ttxx\ctxluabuffer[common,example]}
157
158We can use a table instead (in fact, we can use both approaches
159mixed:
160
161\startbuffer[example]
162local n_properties = node.get_properties_table()
163
164for n in node.traverse(list) do
165    n_properties[n] = { vit = n }
166    node.direct.setproperty(n,{ vdf = n })
167end
168start()
169for n in node.traverse(list) do
170    row(n,n_properties[n].vit)
171end
172stop()
173\stopbuffer
174
175\typebuffer[example] {\ttxx\ctxluabuffer[common,example]}
176
177The direct method looks the same, apart from a cast to direct:
178
179\startbuffer[example]
180for n in node.direct.traverse(node.direct.todirect(list)) do
181    node.direct.setproperty(n,{ vdf = n })
182end
183start()
184for n in node.direct.traverse(node.direct.todirect(list)) do
185    row(n,node.direct.getproperty(n).vdf)
186end
187stop()
188\stopbuffer
189
190\typebuffer[example] {\tt\ctxluabuffer[common,example]}
191
192Again, we can use the table approach:
193
194\startbuffer[example]
195local d_properties = node.direct.get_properties_table()
196
197for n in node.direct.traverse(node.direct.todirect(list)) do
198    d_properties[n] = { vdt = n }
199end
200start()
201for n in node.direct.traverse(node.direct.todirect(list)) do
202    row(n,d_properties[n].vdt)
203end
204stop()
205\stopbuffer
206
207\typebuffer[example] {\tt\ctxluabuffer[common,example]}
208
209\stoptext
210