summaryrefslogtreecommitdiff
path: root/tex/context/base/mkiv/cldf-scn.lua
blob: d793838669e233a8b7ba33ea07783fd2d6c3a4b9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
if not modules then modules = { } end modules ['cldf-scn'] = {
    version   = 1.001,
    comment   = "companion to cldf-ini.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

local load, type, tostring  = load, type, tostring

local formatters  = string.formatters
local char        = string.char
local concat      = table.concat

local lpegmatch   = lpeg.match
local p_unquoted  = lpeg.Cs(lpeg.patterns.unquoted)

local f_action_f  = formatters["action%s(%s)"]
local f_action_s  = formatters["local action%s = action[%s]"]
local f_command   = formatters["local action = tokens._action\n%\nt\nreturn function(%s) return %s end"]

local interfaces  = interfaces
local commands    = commands
local scanners    = interfaces.scanners
local register    = interfaces.registerscanner

local compile     = tokens.compile or function() end
local presets     = tokens.presets

local dummy       = function() end

local report      = logs.reporter("interfaces","implementor")

function interfaces.implement(specification)
    local actions   = specification.actions
    local name      = specification.name
    local arguments = specification.arguments
    local private   = specification.scope == "private"
    local onlyonce  = specification.onlyonce
    if not actions then
        if name then
            report("error: no actions for %a",name)
        else
            report("error: no actions and no name")
        end
        return
    end
    if name == "" then
        name = nil
    end
    local p = arguments and presets[arguments]
    if p then
        arguments = p
    end
    local scanner
    local resetter = onlyonce and name and commands.ctxresetter(name)
    if resetter then
        local scan = compile(specification)
        if private then
            scanner = function()
                resetter()
                return scan()
            end
        else
            scanner = function()
                commands[name] = dummy
                resetter()
                return scan()
            end
        end
    else
        scanner = compile(specification)
    end
    if not name then
        return scanner
    end
    if scanners[name] and not specification.overload then
        report("warning: 'scanners.%s' is redefined",name)
    end
    register(name,scanner,specification.protected,specification.public,specification.valuetype)
    if private then
        return
    end
    local command
    if onlyonce then
        if type(actions) == "function" then
            actions = { actions }
        elseif #actions == 1 then
            actions = { actions[1] }
        end
    end
    if type(actions) == "function" then
        command = actions
    elseif #actions == 1 then
        command = actions[1]
    else
        -- this one is not yet complete .. compare tokens
        tokens._action = actions
        local f = { }
        local args
        if not arguments then
            args = ""
        elseif type(arguments) == "table" then
            local a = { }
            for i=1,#arguments do
                local v = arguments[i]
                local t = type(v)
                if t == "boolean" then
                    a[i] = tostring(v)
                elseif t == "number" then
                    a[i] = tostring(v)
                elseif t == "string" then
                    local s = lpegmatch(p_unquoted,v)
                    if s and v ~= s then
                        a[i] = v -- a string, given as "'foo'" or '"foo"'
                    else
                        a[i] = char(96+i)
                    end
                else
                    -- nothing special for tables
                    a[i] = char(96+i)
                end
            end
            args = concat(a,",")
        else
            args = "a"
        end
        command = args
        for i=1,#actions do
            command = f_action_f(i,command)
            f[i] = f_action_s(i,i)
        end
        command = f_command(f,args,command)
        command = load(command)
        if command then
            if resetter then
                local cmd = command()
                command = function()
                    commands[name] = dummy
                    resetter()
                    cmd()
                end
            else
                command = command()
            end
        end
        tokens._action = nil
    end
    if commands[name] and not specification.overload then
        report("warning: 'commands.%s' is redefined",name)
    end
    commands[name] = command
 -- return scanner, command
end

-- it's convenient to have copies here:

interfaces.defined  = tokens.defined

interfaces.setmacro = tokens.setters.macro
interfaces.setcount = tokens.setters.count
interfaces.setdimen = tokens.setters.dimen

interfaces.strings = table.setmetatableindex(function(t,k)
    local v = { }
    for i=1,k do
        v[i] = "string"
    end
    t[k] = v
    return v
end)