summaryrefslogtreecommitdiff
path: root/tex/context/base/util-tpl.lua
blob: 4cde1863b3dd5150675339e8125435fcc5b903f6 (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
if not modules then modules = { } end modules ['util-tpl'] = {
    version   = 1.001,
    comment   = "companion to luat-lib.mkiv",
    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
    copyright = "PRAGMA ADE / ConTeXt Development Team",
    license   = "see context related readme files"
}

-- This is experimental code. Coming from dos and windows, I've always used %whatever%
-- as template variables so let's stick to it. After all, it's easy to parse and stands
-- out well. A double %% is turned into a regular %.

utilities.templates = utilities.templates or { }
local templates     = utilities.templates

local trace_template  = false  trackers.register("templates.trace",function(v) trace_template = v end)
local report_template = logs.reporter("template")

local format = string.format
local P, C, Cs, Carg, lpegmatch = lpeg.P, lpeg.C, lpeg.Cs, lpeg.Carg, lpeg.match

-- todo: make installable template.new

local replacer

local function replacekey(k,t,recursive)
    local v = t[k]
    if not v then
        if trace_template then
            report_template("unknown key %q",k)
        end
        return ""
    else
        if trace_template then
            report_template("setting key %q to value %q",k,v)
        end
        if recursive then
            return lpegmatch(replacer,v,1,t)
        else
            return v
        end
    end
end

local sqlescape = lpeg.replacer {
    { "'",    "''"   },
    { "\\",   "\\\\" },
    { "\r\n", "\\n"  },
    { "\r",   "\\n"  },
 -- { "\t",   "\\t"  },
}

local escapers = {
    lua = function(s)
        return format("%q",s)
    end,
    sql = function(s)
        return lpegmatch(sqlescape,s)
    end,
}

local function replacekeyunquoted(s,t,how,recurse) -- ".. \" "
    local escaper = how and escapers[how] or escapers.lua
    return escaper(replacekey(s,t,recurse))
end

local single      = P("%")  -- test %test% test   : resolves test
local double      = P("%%") -- test 10%% test     : %% becomes %
local lquoted     = P("%[") -- test %[test]" test : resolves test with escaped "'s
local rquoted     = P("]%") --

local escape      = double  / '%%'
local nosingle    = single  / ''
local nodouble    = double  / ''
local nolquoted   = lquoted / ''
local norquoted   = rquoted / ''

local key         = nosingle * (C((1-nosingle)^1 * Carg(1) * Carg(2) * Carg(3))/replacekey) * nosingle
local unquoted    = nolquoted * ((C((1 - norquoted)^1) * Carg(1) * Carg(2) * Carg(3))/replacekeyunquoted) * norquoted
local any         = P(1)

      replacer    = Cs((unquoted + escape + key + any)^0)

local function replace(str,mapping,how,recurse)
    if mapping then
        return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str
    else
        return str
    end
end

-- print(replace("test '%[x]%' test",{ x = [[a 'x'  a]] }))
-- print(replace("test '%[x]%' test",{ x = [[a 'x'  a]] },'sql'))

templates.replace = replace

function templates.load(filename,mapping,how,recurse)
    local data = io.loaddata(filename) or ""
    if mapping and next(mapping) then
        return replace(data,mapping,how,recurse)
    else
        return data
    end
end

function templates.resolve(t,mapping,how,recurse)
    if not mapping then
        mapping = t
    end
    for k, v in next, t do
        t[k] = replace(v,mapping,how,recurse)
    end
    return t
end

-- inspect(utilities.templates.replace("test %one% test", { one = "%two%", two = "two" }))
-- inspect(utilities.templates.resolve({ one = "%two%", two = "two", three = "%three%" }))