syst-lua.lua /size: 8407 b    last modification: 2021-10-28 13:50
1if not modules then modules = { } end modules ['syst-lua'] = {
2    version   = 1.001,
3    comment   = "companion to syst-lua.mkiv",
4    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
5    copyright = "PRAGMA ADE / ConTeXt Development Team",
6    license   = "see context related readme files"
7}
8
9local load, type, tonumber = load, type, tonumber
10local concat = table.concat
11local utfchar = utf.char
12local find = string.find
13local S, C, P, lpegmatch, lpegtsplitat = lpeg.S, lpeg.C, lpeg.P, lpeg.match, lpeg.tsplitat
14
15local xmath        = xmath    or math
16local xcomplex     = xcomplex or { }
17
18local cmd          = tokens.commands
19
20local scannext     = token.scan_next or token.get_next
21
22local getcommand   = token.get_command
23local getmode      = token.get_mode
24local getindex     = token.get_index
25local getcsname    = token.get_csname
26local getmacro     = token.get_macro
27local putnext      = token.put_next
28local scantoken    = token.scan_token or token.get_token
29
30local getdimen     = tex.getdimen
31local getglue      = tex.getglue
32local getcount     = tex.getcount
33local gettoks      = tex.gettoks
34local gettex       = tex.get
35
36local context      = context
37local dimenfactors = number.dimenfactors
38
39commands           = commands or { }
40local commands     = commands
41local context      = context
42local implement    = interfaces.implement
43
44local ctx_protected_cs         = context.protected.cs -- more efficient
45local ctx_firstoftwoarguments  = context.firstoftwoarguments
46local ctx_secondoftwoarguments = context.secondoftwoarguments
47local ctx_firstofoneargument   = context.firstofoneargument
48local ctx_gobbleoneargument    = context.gobbleoneargument
49
50implement { -- will be overloaded later
51    name      = "writestatus",
52    arguments = "2 strings",
53    actions   = logs.status,
54}
55
56function commands.doifelse(b)
57    if b then
58        ctx_firstoftwoarguments()
59    else
60        ctx_secondoftwoarguments()
61    end
62end
63
64function commands.doifelsesomething(b)
65    if b and b ~= "" then
66        ctx_firstoftwoarguments()
67    else
68        ctx_secondoftwoarguments()
69    end
70end
71
72function commands.doif(b)
73    if b then
74        ctx_firstofoneargument()
75    else
76        ctx_gobbleoneargument()
77    end
78end
79
80function commands.doifsomething(b)
81    if b and b ~= "" then
82        ctx_firstofoneargument()
83    else
84        ctx_gobbleoneargument()
85    end
86end
87
88function commands.doifnot(b)
89    if b then
90        ctx_gobbleoneargument()
91    else
92        ctx_firstofoneargument()
93    end
94end
95
96function commands.doifnotthing(b)
97    if b and b ~= "" then
98        ctx_gobbleoneargument()
99    else
100        ctx_firstofoneargument()
101    end
102end
103
104commands.testcase = commands.doifelse -- obsolete
105
106function commands.boolcase(b)
107    context(b and 1 or 0)
108end
109
110function commands.doifelsespaces(str)
111    if find(str,"^ +$") then
112        ctx_firstoftwoarguments()
113    else
114        ctx_secondoftwoarguments()
115    end
116end
117
118local pattern = lpeg.patterns.validdimen
119
120function commands.doifelsedimenstring(str)
121    if lpegmatch(pattern,str) then
122        ctx_firstoftwoarguments()
123    else
124        ctx_secondoftwoarguments()
125    end
126end
127
128local p_first = C((1-P(",")-P(-1))^0)
129
130implement {
131    name      = "firstinset",
132    arguments = "string",
133    actions   = function(str) context(lpegmatch(p_first,str or "")) end,
134    public    = true,
135}
136
137implement {
138    name      = "ntimes",
139    arguments = { "string", "integer" },
140    actions   = { string.rep, context }
141}
142
143implement {
144    name      = "execute",
145    arguments = "string",
146    actions   = os.execute -- wrapped in sandbox
147}
148
149implement {
150    name      = "doifelsesame",
151    arguments = "2 strings",
152    actions   = function(a,b)
153        if a == b then
154            ctx_firstoftwoarguments()
155        else
156            ctx_secondoftwoarguments()
157        end
158    end
159}
160
161implement {
162    name      = "doifsame",
163    arguments = "2 strings",
164    actions   = function(a,b)
165        if a == b then
166            ctx_firstofoneargument()
167        else
168            ctx_gobbleoneargument()
169        end
170    end
171}
172
173implement {
174    name      = "doifnotsame",
175    arguments = "2 strings",
176    actions   = function(a,b)
177        if a == b then
178            ctx_gobbleoneargument()
179        else
180            ctx_firstofoneargument()
181        end
182    end
183}
184
185-- This is a bit of a joke as I never really needed floating point expressions (okay,
186-- maybe only with scaling because there one can get numbers that are too large for
187-- dimensions to deal with). Of course one can write a parser in \TEX\ speak but then
188-- one also needs to implement a bunch of functions. It doesn't pay of so we just
189-- stick to the next gimmick. It looks inefficient but performance is actually quite
190-- efficient.
191
192do
193
194    local result = CONTEXTLMTXMODE > 0 and
195        { "local xmath = xmath local xcomplex = xcomplex return " }
196     or { "local xmath =  math local xcomplex = { }      return " }
197    local word   = { }
198    local r      = 1
199    local w      = 0
200
201    local report = logs.reporter("system","expression")
202
203    local function unexpected(c)
204        report("unexpected token %a",c)
205    end
206
207    local function expression()
208        local w = 0
209        local r = 1
210        while true do
211            local t = scannext()
212            local n = getcommand(t)
213            local c = cmd[n]
214            -- todo, helper: returns number
215            if c == "letter"  then
216                w = w + 1 ; word[w] = utfchar(getmode(t))
217            else
218                if w > 0 then
219                    local s = concat(word,"",1,w)
220                    local d = dimenfactors[s]
221                    if d then
222                        r = r + 1 ; result[r] = "*"
223                        r = r + 1 ; result[r] = 1/d
224                    else
225                        if xmath[s] then
226                            r = r + 1 ; result[r] = "xmath."
227                        elseif xcomplex[s] then
228                            r = r + 1 ; result[r] = "xcomplex."
229                        end
230                        r = r + 1 ; result[r] = s
231                    end
232                    w = 0
233                end
234                if     c == "other_char" then
235                    r = r + 1 ; result[r] = utfchar(getmode(t))
236                elseif c == "spacer" then
237                 -- r = r + 1 ; result[r] = " "
238                elseif c == "relax" then
239                    break
240                elseif c == "assign_int" then
241                    r = r + 1 ; result[r] = getcount(getindex(t))
242                elseif c == "assign_dimen" then
243                    r = r + 1 ; result[r] = getdimen(getindex(t))
244                elseif c == "assign_glue" then
245                    r = r + 1 ; result[r] = getglue(getindex(t))
246                elseif c == "assign_toks" then
247                    r = r + 1 ; result[r] = gettoks(getindex(t))
248                elseif c == "char_given" or c == "math_given" or c == "xmath_given" then
249                    r = r + 1 ; result[r] = getmode(t)
250                elseif c == "last_item" then
251                    local n = getcsname(t)
252                    if n then
253                        local s = gettex(n)
254                        if s then
255                            r = r + 1 ; result[r] = s
256                        else
257                            unexpected(c)
258                        end
259                    else
260                        unexpected(c)
261                    end
262                elseif c == "call" then
263                    local n = getcsname(t)
264                    if n then
265                        local s = getmacro(n)
266                        if s then
267                            r = r + 1 ; result[r] = s
268                        else
269                            unexpected(c)
270                        end
271                    else
272                        unexpected(c)
273                    end
274                elseif c == "the" or c == "convert" or c == "lua_expandable_call" then
275                    putnext(t)
276                    scantoken() -- expands
277                else
278                    unexpected(c)
279                end
280            end
281        end
282        local code = concat(result,"",1,r)
283        local func = load(code)
284        if type(func) == "function" then
285            context(func())
286        else
287            report("invalid lua %a",code)
288        end
289    end
290
291    implement {
292        public  = true,
293        name    = "expression",
294        actions = expression,
295    }
296
297end
298