cldf-int.lmt /size: 6851 b    last modification: 2025-02-21 11:03
1if not modules then modules = { } end modules ['cldf-int'] = {
2    version   = 1.001,
3    comment   = "companion to cldf-int.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
9-- another experiment
10-- needs upgrading
11-- needs checking
12-- todo: multilingual
13
14local byte = string.byte
15local insert, remove, concat = table.insert, table.remove, table.concat
16local unpack, type = unpack or table.unpack, type
17
18local context        = context
19local contextsprint  = context.sprint
20
21local ctxcatcodes    <const> = catcodes.numbers.ctxcatcodes
22
23local trace_define   = false  trackers.register("context.define", function(v) trace_define = v end)
24
25interfaces           = interfaces or { }
26local implement      = interfaces.implement
27local estart         = interfaces.elements.start
28local estop          = interfaces.elements.stop
29
30local scanners       = tokens.scanners
31local shortcuts      = tokens.shortcuts
32
33local peekchar       = scanners.peekchar
34local scankey        = scanners.key
35local scanvalue      = scanners.value
36local scanskip       = scanners.skip
37
38local open           <const> = byte('[')
39local close          <const> = byte(']')
40local equal          <const> = byte('=')
41local comma          <const> = byte(',')
42
43local function scanhash(scanners)
44    if peekchar() == open then
45        local data = { }
46        scanskip()
47        while true do
48            local c = peekchar()
49            if c == comma then
50                scanskip()
51            elseif c == close then
52                scanskip()
53                break
54            else
55                local key = scankey(equal)
56                if key then
57                    if peekchar() == equal then
58                        scanskip()
59                        if scanners then
60                            local scanner = scanners[key]
61                            if scanner then
62                                data[key] = scanner()
63                            else
64                                data[key] = scanvalue(comma,close) or ""
65                            end
66                        else
67                            data[key] = scanvalue(comma,close) or ""
68                        end
69                    else
70                        break
71                    end
72                else
73                    break
74                end
75            end
76        end
77        return data
78    end
79end
80
81local function scanarray()
82    if peekchar() == open then
83        local data = { }
84        local d = 0
85        scanskip()
86        while true do
87            local c = peekchar()
88            if c == comma then
89                scanskip()
90            elseif c == close then
91                scanskip()
92                break
93            else
94                local v = scanvalue(comma,close) or ""
95                d = d + 1
96                data[d] = v
97            end
98        end
99        return data
100    end
101end
102
103shortcuts.scanhash  = scanhash
104shortcuts.scanarray = scanarray
105
106scanners.hash  = scanhash
107scanners.array = scanarray
108
109local function remap(arguments)
110    -- backward compatibility
111    if type(arguments) == "table" then
112        for i=1,#arguments do
113            local a = arguments[i]
114            if type(a) == "table" then
115                local t = a[2]
116                arguments[i] = t == "list" and "array" or t
117            end
118        end
119        return arguments
120    end
121end
122
123function interfaces.definecommand(name,specification) -- name is optional
124    if type(name) == "table" then
125        specification = name
126        name = specification.name
127    end
128    if name and specification then
129        local environment = specification.environment
130        local arguments   = remap(specification.arguments)
131        if environment then
132            local starter = specification.starter
133            local stopper = specification.stopper
134            if starter and stopper then
135                implement {
136                    name      = estart .. name,
137                    arguments = arguments,
138                    public    = true,
139                    protected = true,
140                    actions   = starter,
141                }
142                implement {
143                    name      = estop .. name,
144                    public    = true,
145                    protected = true,
146                    actions   = stopper,
147                }
148            else
149                -- message
150            end
151        end
152        if not environment or environment == "both" then
153            local macro = specification.macro
154            if macro then
155                implement {
156                    name      = name,
157                    arguments = arguments,
158                    public    = true,
159                    protected = true,
160                    actions   = macro,
161                }
162            else
163                -- message
164            end
165        end
166    else
167        -- message
168    end
169end
170
171function interfaces.tolist(t)
172    if t then
173        local r = { }
174        for i=1,#t do
175            r[i] = t[i]
176        end
177        local n = #r
178        for k,v in table.sortedhash(t) do
179            if type(k) ~= "number" then
180                n = n + 1
181                r[n] = k .. "=" .. v
182            end
183        end
184        return concat(r,", ")
185    else
186        return ""
187    end
188end
189
190-- \startluacode
191-- function test(opt_1, opt_2, arg_1)
192--     context.startnarrower()
193--     context("options 1: %s",interfaces.tolist(opt_1))
194--     context.par()
195--     context("options 2: %s",interfaces.tolist(opt_2))
196--     context.par()
197--     context("argument 1: %s",arg_1)
198--     context.stopnarrower()
199-- end
200--
201-- interfaces.definecommand {
202--     name = "test",
203--     arguments = {
204--         { "option", "list" },
205--         { "option", "hash" },
206--         { "content", "string" },
207--     },
208--     macro = test,
209-- }
210-- \stopluacode
211--
212-- test: \test[1][a=3]{whatever}
213--
214-- \startluacode
215-- local function startmore(opt_1)
216--     context.startnarrower()
217--     context("start more, options: %s",interfaces.tolist(opt_1))
218--     context.startnarrower()
219-- end
220--
221-- local function stopmore()
222--     context.stopnarrower()
223--     context("stop more")
224--     context.stopnarrower()
225-- end
226--
227-- interfaces.definecommand ( "more", {
228--     environment = true,
229--     arguments = {
230--         { "option", "list" },
231--     },
232--     starter = startmore,
233--     stopper = stopmore,
234-- } )
235-- \stopluacode
236--
237-- more: \startmore[1] one \startmore[2] two \stopmore one \stopmore
238--
239-- More modern (no need for option or content):
240--
241-- \startluacode
242-- interfaces.definecommand {
243--     name = "test",
244--     arguments = {
245--         "array", -- or list
246--         "hash",
247--         "string",
248--         "number",
249--     },
250--     macro = test,
251-- }
252-- \stopluacode
253--
254