java-ini.lua /size: 8232 b    last modification: 2020-07-01 14:35
1if not modules then modules = { } end modules ['java-ini'] = {
2    version   = 1.001,
3    comment   = "companion to java-ini.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-- todo: don't flush scripts if no JS key
10
11local format, gsub, find = string.format, string.gsub, string.find
12local concat = table.concat
13local lpegmatch, P, S, C, Carg, Cc = lpeg.match, lpeg.P, lpeg.S, lpeg.C, lpeg.Carg, lpeg.Cc
14
15local allocate           = utilities.storage.allocate
16local settings_to_array  = utilities.parsers.settings_to_array
17
18local variables          = interfaces.variables
19local formatters         = string.formatters
20
21local context            = context
22local implement          = interfaces.implement
23
24local trace_javascript   = false  trackers.register("backends.javascript", function(v) trace_javascript = v end)
25
26local report_javascripts = logs.reporter ("interactions","javascripts")
27local status_javascripts = logs.messenger("interactions","javascripts")
28
29local javascripts        = interactions.javascripts or { }
30interactions.javascripts = javascripts
31
32local codes              = allocate()
33local preambles          = allocate()
34local functions          = allocate()
35
36javascripts.codes        = codes
37javascripts.preambles    = preambles
38javascripts.functions    = functions
39
40local preambled = { }
41
42local function storefunction(s,preamble)
43    if trace_javascript then
44        report_javascripts("found function %a",s)
45    end
46    functions[s] = preamble
47end
48
49local uses     = P("uses")
50local used     = P("used")
51local left     = P("{")
52local right    = P("}")
53local space    = S(" \r\n")
54local spaces   = space^0
55local braced   = left * C((1-right-space)^1) * right
56local unbraced = C((1-space)^1)
57local name     = spaces * (braced + unbraced) * spaces
58local any      = P(1)
59local script   = C(any^1)
60local funct    = P("function")
61local leftp    = P("(")
62local rightp   = P(")")
63local fname    = spaces * funct * spaces * (C((1-space-left-leftp)^1) * Carg(1) / storefunction) * spaces * leftp
64
65local parsecode      = name * ((uses * name) + Cc("")) * spaces * script
66local parsepreamble  = name * ((used * name) + Cc("")) * spaces * script
67local parsefunctions = (fname + any)^0
68
69function javascripts.storecode(str)
70    local name, uses, script = lpegmatch(parsecode,str)
71    if name and name ~= "" then
72        script = gsub(script,"%s*([^\n\r]+)%s*[\n\r]+$","%1")
73        codes[name] = { uses, script }
74    end
75end
76
77function javascripts.storepreamble(str) -- now later
78    local name, used, script = lpegmatch(parsepreamble,str)
79    if name and name ~= "" and not preambled[name] then
80        local n = #preambles + 1
81        preambles[n] = { name, used, script }
82        preambled[name] = n
83        if trace_javascript then
84            report_javascripts("stored preamble %a, state %a, order %a",name,used,n)
85        end
86        lpegmatch(parsefunctions,script,1,n)
87    end
88end
89
90function javascripts.setpreamble(name,script) -- now later
91    if name and name ~= "" and not preambled[name] then
92        local n = #preambles + 1
93        preambles[n] = { name, "now", script }
94        preambled[name] = n
95        if trace_javascript then
96            report_javascripts("adapted preamble %a, state %a, order %a",name,"now",n)
97        end
98        lpegmatch(parsefunctions,script,1,n)
99    end
100end
101
102function javascripts.addtopreamble(name,script)
103    if name and name ~= "" then
104        local p = preambled[name]
105        if p then
106            preambles[p] = { "now", preambles[p] .. " ;\n" .. script }
107            if trace_javascript then
108                report_javascripts("extended preamble %a, state %a, order %a",name,"now",p)
109            end
110        else
111            local n = #preambles + 1
112            preambles[n] = { name, "now", script }
113            preambled[name] = n
114            if trace_javascript then
115                report_javascripts("stored preamble %a, state %a, order %a",name,"now",n)
116            end
117            lpegmatch(parsefunctions,script,1,n)
118        end
119    end
120end
121
122function javascripts.usepreamblenow(name) -- now later
123    if name and name ~= "" and name ~= variables.reset then -- todo: reset
124        local names = settings_to_array(name)
125        for i=1,#names do
126            local somename = names[i]
127            local preamble = preambled[somename]
128            if preamble  then
129                preambles[preamble][2] = "now"
130                if trace_javascript then
131                    report_javascripts("used preamble %a, state %a, order %a",somename,"now","auto")
132                end
133            end
134        end
135    end
136end
137
138local splitter = lpeg.tsplitat(lpeg.patterns.commaspacer)
139
140local used, reported = false, { } -- we can cache more
141
142function javascripts.code(name,arguments)
143    local c = codes[name]
144    if c then
145        local u, code = c[1], c[2]
146        if u ~= "" then
147            local p = preambled[u]
148            if p then
149                preambles[p][2] = "now"
150                if trace_javascript and not reported[name] then
151                    reported[name] = true
152                    report_javascripts("used code %a, preamble %a",name,u)
153                end
154            elseif trace_javascript and not reported[name] then
155                reported[name] = true
156                report_javascripts("used code %a",name)
157            end
158        elseif trace_javascript and not reported[name] then
159            reported[name] = true
160            report_javascripts("used code %a",name)
161        end
162        used = true
163        return code
164    end
165    local f = functions[name]
166    if f then
167        used = true
168        if trace_javascript and not reported[name] then
169            reported[name] = true
170            report_javascripts("used function %a",name)
171        end
172        preambles[f][2] = "now" -- automatically tag preambles that define the function (as later)
173        if arguments then
174            local args = lpegmatch(splitter,arguments)
175            for i=1,#args do -- can be a helper
176                args[i] = formatters["%q"](args[i])
177            end
178            return formatters["%s(%s)"](name,concat(args,","))
179        else
180            return formatters["%s()"](name)
181        end
182    end
183end
184
185function javascripts.flushpreambles()
186    local t = { }
187--     if used then -- we want to be able to enforce inclusion
188        for i=1,#preambles do
189            local preamble = preambles[i]
190            if preamble[2] == "now" then
191                if trace_javascript then
192                    report_javascripts("flushed preamble %a",preamble[1])
193                end
194                t[#t+1] = { preamble[1], preamble[3] }
195            end
196        end
197--     end
198    return t
199end
200
201local patterns = {
202    CONTEXTLMTXMODE > 0 and "java-imp-%s.mkxl" or "",
203    "java-imp-%s.mkiv",
204    "java-imp-%s.tex",
205    -- obsolete:
206    "java-%s.mkiv",
207    "java-%s.tex"
208}
209
210local function action(name,foundname)
211    commands.loadlibrary(name,foundname,true)
212    status_javascripts("loaded: library %a",name)
213end
214
215local function failure(name)
216    report_javascripts("unknown library %a",name)
217end
218
219function javascripts.usescripts(name)
220    if name ~= variables.reset then -- reset is obsolete
221        resolvers.uselibrary {
222            name     = name,
223            patterns = patterns,
224            action   = action,
225            failure  = failure,
226            onlyonce = true,
227        }
228    end
229end
230
231-- interface
232
233implement {
234    name      = "storejavascriptcode",
235    actions   = javascripts.storecode,
236    arguments = "string"
237}
238
239implement {
240    name      = "storejavascriptpreamble",
241    actions   = javascripts.storepreamble,
242    arguments = "string"
243}
244
245implement {
246    name      = "setjavascriptpreamble",
247    actions   = javascripts.setpreamble,
248    arguments = "2 strings",
249}
250
251implement {
252    name      = "addtojavascriptpreamble",
253    actions   = javascripts.addtopreamble,
254    arguments = "2 strings",
255}
256
257implement {
258    name      = "usejavascriptpreamble",
259    actions   = javascripts.usepreamblenow,
260    arguments = "string"
261}
262
263implement {
264    name      = "usejavascriptscripts",
265    actions   = javascripts.usescripts,
266    arguments = "string"
267}
268