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