summaryrefslogtreecommitdiff
path: root/Master/texmf-dist/tex/context/base/publ-fnd.lua
diff options
context:
space:
mode:
Diffstat (limited to 'Master/texmf-dist/tex/context/base/publ-fnd.lua')
-rw-r--r--Master/texmf-dist/tex/context/base/publ-fnd.lua411
1 files changed, 312 insertions, 99 deletions
diff --git a/Master/texmf-dist/tex/context/base/publ-fnd.lua b/Master/texmf-dist/tex/context/base/publ-fnd.lua
index 383bcac73ca..909b102dba2 100644
--- a/Master/texmf-dist/tex/context/base/publ-fnd.lua
+++ b/Master/texmf-dist/tex/context/base/publ-fnd.lua
@@ -6,167 +6,380 @@ if not modules then modules = { } end modules ['publ-fnd'] = {
license = "see context related readme files"
}
-local tonumber, next = tonumber, next
-local P, R, C, Cs, Cp, Carg = lpeg.P, lpeg.R, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Carg
-local lpegmatch = lpeg.match
+if not characters then
+ dofile(resolvers.findfile("char-def.lua"))
+ dofile(resolvers.findfile("char-utf.lua"))
+end
+
+-- this tracker is only for real debugging and not for the average user
+
+local trace_match = false trackers.register("publications.match", function(v) trace_match = v end)
+
+local publications = publications
+
+local tonumber, next, type = tonumber, next, type
+local find = string.find
+local P, R, S, C, Cs, Cp, Cc, Carg, Ct, V = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc, lpeg.Carg, lpeg.Ct, lpeg.V
+local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
local concat = table.concat
local formatters = string.formatters
local lowercase = characters.lower
+local topattern = string.topattern
+
+publications = publications or { } -- for testing
+
+local report = logs.reporter("publications","match")
local colon = P(":")
local dash = P("-")
local lparent = P("(")
local rparent = P(")")
-local space = lpeg.patterns.whitespace
+local space = lpegpatterns.whitespace
+local utf8char = lpegpatterns.utf8character
local valid = 1 - colon - space - lparent - rparent
----- key = C(valid^1)
local key = C(R("az","AZ")^1)
-local word = Cs(lpeg.patterns.unquoted + valid^1)
+local wildcard = C("*")
+local word = Cs(lpegpatterns.unquoted + lpegpatterns.argument + valid^1)
+local simple = C(valid^1)
local number = C(valid^1)
------ f_string_key = formatters[" local s_%s = entry[%q]"]
-local f_string_key = formatters[" local s_%s = entry[%q] if s_%s then s_%s = lower(s_%s) end "]
-local f_number_key = formatters[" local n_%s = tonumber(entry[%q]) or 0"]
-local f_field_key = formatters[" local f_%s = entry[%q] or ''"]
+----- f_string_key = formatters[" local s_%s = entry[%q] if s_%s then s_%s = lower(s_%s) end "]
+----- f_number_key = formatters[" local n_%s = tonumber(entry[%q]) or 0"]
+----- f_field_key = formatters[" local f_%s = entry[%q] or ''"]
------ f_string_match = formatters["(s_%s and find(lower(s_%s),%q))"]
-local f_string_match = formatters["(s_%s and find(s_%s,%q))"]
-local f_number_match = formatters["(n_%s and n_%s >= %s and n_%s <= %s)"]
-local f_field_match = formatters["f_%s"]
+-- getfuzzy(entry,%q,categories)
-local match = key * (colon/"") * word * Carg(1) / function(key,_,word,keys)
- -- keys[key] = f_string_key(key,key)
- keys[key] = f_string_key(key,key,key,key,key)
- return f_string_match(key,key,lowercase(word))
-end
+-- local f_string_key = formatters[" local s_%s = get(entry,%q)\n if s_%s then s_%s = lower(s_%s) end"]
+-- local f_field_key = formatters[" local f_%s = get(entry,%q) or ''"]
+-- local f_field_key_c = formatters[" local c_%s = get(entry,%q,categories) or ''"]
+-- local f_number_key = formatters[" local n_%s = tonumber(get(entry,%q)) or 0"]
+--
+-- local f_string_match = formatters["(s_%s and find(s_%s,%q))"]
+-- local f_number_match = formatters["(n_%s and n_%s >= %s and n_%s <= %s)"]
+-- local f_field_match = formatters["f_%s"]
+-- local f_field_match_c = formatters["c_%s"]
+--
+-- local f_all_match = formatters["anywhere(entry,%q)"]
-local range = key * (colon/"") * number * (dash/"") * number * Carg(1) / function(key,_,first,_,last,keys)
- keys[key] = f_number_key(key,key)
- return f_number_match(key,key,tonumber(first) or 0,key,tonumber(last) or 0)
-end
+-- topattern(lowercase(word)) : utflowercase + only *?
-local field = (P("field:")/"") * key * Carg(1) / function(_,key,keys)
- keys[key] = f_field_key(key,key)
- return f_field_match(key)
-end
+-- local match = ( (key + wildcard) * (colon/"") ) * word * Carg(1) / function(key,_,word,keys)
+-- if key == "*" or key == "any" then
+-- keys.anywhere = true
+-- if word == "" or word == "*" then
+-- return "true"
+-- else
+-- return f_all_match(topattern(lowercase(word)))
+-- end
+-- else
+-- keys[key] = f_string_key(key,key,key,key,key)
+-- return f_string_match(key,key,topattern(lowercase(word)))
+-- end
+-- end
+--
+-- local default = simple * Carg(1) / function(word,keys)
+-- keys.anywhere = true
+-- if word == "" or word == "*" then
+-- return "true"
+-- else
+-- return f_all_match(topattern(lowercase(word)))
+-- end
+-- end
+--
+-- local range = key * (colon/"") * number * (dash/"") * number * Carg(1) / function(key,_,first,_,last,keys)
+-- keys[key] = f_number_key(key,key)
+-- return f_number_match(key,key,tonumber(first) or 0,key,tonumber(last) or 0)
+-- end
+--
+-- local field = (P("field:")/"") * key * Carg(1) / function(_,key,keys)
+-- keys[key] = f_field_key(key,key)
+-- return f_field_match(key)
+-- end
+--
+-- local cast = (P("cast:")/"") * key * Carg(1) / function(_,key,keys)
+-- keys[key] = f_field_key_c(key,key)
+-- return f_field_match_c(key)
+-- end
+--
+-- local compare = C("==") + P("=")/"==" + P("!=")/"~=" + P("<>")/"~="
+--
+-- local b_match = lparent
+-- local e_match = rparent * space^0 * (#P(-1) + P(",")/" or ") -- maybe also + -> and
+-- local f_match = ((field + cast + range + match + space + compare + P(1))-e_match)^1
+-- local p_match = b_match * default * e_match
+-- + b_match * f_match * e_match
+--
+-- local pattern = Cs(Cc("(") * (P("match")/"" * space^0 * p_match)^1 * Cc(")"))
------ pattern = Cs((field + range + match + P(1))^1)
------ b_match = P("match")/"" * lparent
-local b_match = lparent
-local e_match = rparent * space^0 * P(-1)
-local pattern = Cs(b_match * ((field + range + match + space + P(1))-e_match)^1 * e_match)
+-- field contains fieldname:nonspaces|"whatever"|'whatever'|{whatever}
+-- field exact fieldname=nonspaces|"whatever"|'whatever'|{whatever}
+-- set contains [fieldname]:nonspaces|"whatever"|'whatever'|{whatever}
+-- set exact [fieldname]=nonspaces|"whatever"|'whatever'|{whatever}
+--
+-- with * : any sequence
+-- ? : one character
+--
+-- and match(),match() ... equivalent to () and ()
+--
+-- <123 444> : range
+--
+-- unquoted = field
+-- [..] = set
+--
+-- () and or not
+--
+-- spaces are mandate (at least for now)
--- -- -- -- -- -- -- -- -- -- -- -- --
--- -- -- -- -- -- -- -- -- -- -- -- --
+local key = C(R("az","AZ")^1)
+local contains = S(":~")
+local exact = P("=")
+local valid = (1 - space - lparent -rparent)^1
+local wildcard = P("*") / ".*"
+local single = P("?") / "."
+local dash = P("-") / "%."
+local percent = P("-") / "%%"
+local word = Cs(lpegpatterns.unquoted + lpegpatterns.argument + valid)
+local range = P("<") * space^0 * C((1-space)^1) * space^1 * C((1-space- P(">"))^1) * space^0 * P(">")
-local tolower = lpeg.patterns.tolower
-local lower = string.lower
+local f_key_fld = formatters[" local kf_%s = get(entry,%q) \n if kf_%s then kf_%s = lower(kf_%s) end"]
+local f_key_set = formatters[" local ks_%s = get(entry,%q,categories)\n if ks_%s then ks_%s = lower(ks_%s) end"]
+local f_number_fld = formatters[" local nf_%s = tonumber(get(entry,%q))"]
+local f_number_set = formatters[" local ns_%s = tonumber(get(entry,%q,categories))"]
-local allascii = R("\000\127")^1 * P(-1)
+local f_fld_exact = formatters["(kf_%s == %q)"]
+local f_set_exact = formatters["(ks_%s == %q)"]
+local f_fld_contains = formatters["(kf_%s and find(kf_%s,%q))"]
+local f_set_contains = formatters["(ks_%s and find(ks_%s,%q))"]
+local f_fld_between = formatters["(nf_%s and nf_%s >= %s and nf_%s <= %s)"]
+local f_set_between = formatters["(ns_%s and ns_%s >= %s and ns_%s <= %s)"]
-function characters.checkedlower(str)
- return lpegmatch(allascii,str) and lower(str) or lpegmatch(tolower,str) or str
+local f_all_match = formatters["anywhere(entry,%q)"]
+
+local function test_key_value(keys,where,key,first,last)
+ if not key or key == "" then
+ return "(false)"
+ elseif key == "*" then
+ last = "^.*" .. topattern(lowercase(last)) .. ".*$" -- todo: make an lpeg
+ return f_all_match(last)
+ elseif first == false then
+ -- exact
+ last = lowercase(last)
+ if where == "set" then
+ keys[key] = f_key_set(key,key,key,key,key)
+ return f_set_exact(key,last)
+ else
+ keys[key] = f_key_fld(key,key,key,key,key)
+ return f_fld_exact(key,last)
+ end
+ elseif first == true then
+ -- contains
+ last = "^.*" .. topattern(lowercase(last)) .. ".*$"
+ if where == "set" then
+ keys[key] = f_key_set(key,key,key,key,key)
+ return f_set_contains(key,key,last)
+ else
+ keys[key] = f_key_fld(key,key,key,key,key)
+ return f_fld_contains(key,key,last)
+ end
+ else
+ -- range
+ if where == "set" then
+ keys[key] = f_number_set(key,key)
+ return f_set_between(key,key,tonumber(first),key,tonumber(last))
+ else
+ keys[key] = f_number_fld(key,key)
+ return f_fld_between(key,key,tonumber(first),key,tonumber(last))
+ end
+ end
end
+local p_compare = P { "all",
+ all = (V("one") + V("operator") + V("nested") + C(" "))^1,
+ nested = C("(") * V("all") * C(")"), -- C really needed?
+ operator = C("and")
+ + C("or")
+ + C("not"),
+ one = Carg(1)
+ * V("where")
+ * V("key")
+ * (V("how") * V("word") + V("range"))
+ / test_key_value,
+ key = key
+ + C("*"),
+ where = C("set") * P(":")
+ + Cc(""),
+ how = contains * Cc(true)
+ + exact * Cc(false),
+ word = word,
+ range = range,
+}
+
+-- local p_combine = space^0 * (P(",")/" or ") * space^0
+
+-- local pattern = Cs((P("match")/"" * space^0 * p_compare + p_combine)^1)
+
+local comma = P(",")
+local p_spaces = space^0
+local p_combine = p_spaces * comma * p_spaces / " or "
+local p_expression = P("match")/"" * Cs(p_compare)
+ + Carg(1)
+ * Cc("")
+ * Cc("tag")
+ * Cc(false)
+ * (
+ P("tag") * p_spaces * P("(") * Cs((1-S(")")-space)^1) * p_spaces * P(")")
+ + p_spaces * Cs((1-space-comma)^1) * p_spaces
+ ) / test_key_value
+
+local pattern = Cs {
+ [1] = V(2) * (p_combine * V(2))^0,
+ [2] = p_expression,
+}
+
-- -- -- -- -- -- -- -- -- -- -- -- --
-- -- -- -- -- -- -- -- -- -- -- -- --
-local f_template = string.formatters[ [[
+function publications.anywhere(entry,str) -- helpers
+ for k, v in next, entry do
+ if find(lowercase(v),str) then
+ return true
+ end
+ end
+end
+
+-- todo: use an environment instead of
+
+-- table={
+-- { "match", "((kf_editor and find(kf_editor,\"^.*braslau.*$\")))" },
+-- { "hash", "foo1234" },
+-- { "tag", "bar5678" },
+-- }
+
+local f_template = formatters[ [[
local find = string.find
-local lower = characters.checkedlower
+local lower = characters.lower
+local anywhere = publications.anywhere
+local get = publications.getfuzzy
+local specification = publications.currentspecification
+local categories = specification and specification.categories
return function(entry)
%s
-return %s and true or false
+ return %s and true or false
end
]] ]
-local function compile(expr,start)
+local function compile(dataset,expr)
local keys = { }
- local expression = lpegmatch(pattern,expr,start,keys)
- -- print("!!!!",expression)
+ -- local expression = lpegmatch(pattern,expr,start,keys)
+ local expression = lpegmatch(pattern,expr,1,keys)
+ if trace_match then
+ report("compiling expression: %s",expr)
+ end
local definitions = { }
for k, v in next, keys do
definitions[#definitions+1] = v
end
+ if #definitions == 0 then
+ report("invalid expression: %s",expr)
+ elseif trace_match then
+ for i=1,#definitions do
+ report("% 3i : %s",i,definitions[i])
+ end
+ end
definitions = concat(definitions,"\n")
local code = f_template(definitions,expression)
- -- print(code)
- code = loadstring(code)
- if type(code) == "function" then
- code = code()
- if type(code) == "function" then
- return code
+ if trace_match then
+ report("generated code: %s",code)
+ end
+ local finder = loadstring(code) -- use an environment
+ if type(finder) == "function" then
+ finder = finder()
+ if type(finder) == "function" then
+ return finder, code
end
end
- print("no valid expression",expression)
+ report("invalid expression: %s",expr)
return false
end
-local cache = { } -- todo: make weak, or just remember the last one (trial typesetting)
+-- local function test(str)
+-- local keys = { }
+-- local definitions = { }
+-- local expression = lpegmatch(pattern,str,1,keys)
+-- for k, v in next, keys do
+-- definitions[#definitions+1] = v
+-- end
+-- definitions = concat(definitions,"\n")
+-- print(f_template(definitions,expression))
+-- end
-local check = P("match") * space^0 * Cp()
+-- test("match(foo:bar and (foo:bar or foo:bar))")
+-- test("match(foo=bar and (foo=bar or foo=bar))")
+-- test("match(set:foo:bar),match(set:foo:bar)")
+-- test("match(set:foo=bar)")
+-- test("match(foo:{bar bar})")
+-- test("match(foo={bar bar})")
+-- test("match(set:foo:'bar bar')")
+-- test("match(set:foo='bar bar')")
+-- test("match(set:foo<1000 2000>)")
+-- test("match(set:foo<1000 2000>)")
+-- test("match(*:foo)")
+-- test("match(*:*)")
-local function finder(expression)
- local found = cache[expression]
- if found == nil then
- local e = lpegmatch(check,expression)
- found = e and compile(expression,e) or false
- cache[expression] = found
+local trigger = (P("match") + P("tag")) * p_spaces * P("(")
+local check = (1-trigger)^0 * trigger
+
+local function finder(dataset,expression)
+ local found = lpegmatch(check,expression) and compile(dataset,expression) or false
+ if found then
+ local okay, message = pcall(found,{})
+ if not okay then
+ found = false
+ report("error in match: %s",message)
+ end
end
return found
end
+-- finder("match(author:foo)")
+-- finder("match(author:foo and author:bar)")
+-- finder("match(author:foo or (author:bar and page:123))")
+-- finder("match(author:foo),match(author:foo)")
+
publications.finder = finder
function publications.search(dataset,expression)
- local find = finder(expression)
- local source = dataset.luadata
+ local find = finder(dataset,expression)
if find then
- local target = { }
- for k, v in next, source do
- if find(v) then
- target[k] = v
+ local ordered = dataset.ordered
+ local target = { }
+ for i=1,#ordered do
+ local entry = ordered[i]
+ if find(entry) then
+ local tag = entry.tag
+ if not target[tag] then
+ -- we always take the first
+ target[tag] = entry
+ end
end
end
return target
else
- return { source[expression] }
+ return { } -- { dataset.luadata[expression] } -- ?
end
end
-
--- local dataset = publications.new()
--- publications.load(dataset,"t:/manuals/hybrid/tugboat.bib")
---
--- local n = 500
+-- local d = publications.datasets.default
--
--- local function test(dataset,str)
--- local found
--- local start = os.clock()
--- for i=1,n do
--- found = search(dataset,str)
--- end
--- local elapsed = os.clock() - start
--- print(elapsed,elapsed/500,#table.keys(dataset.luadata),str)
--- print(table.concat(table.sortedkeys(found)," "))
--- return found
--- end
+-- local d = publications.load {
+-- dataset = "default",
+-- filename = "t:/manuals/mkiv/hybrid/tugboat.bib"
+-- }
--
--- local found = test(dataset,[[match(author:hagen)]])
--- local found = test(dataset,[[match(author:hagen and author:hoekwater and year:1990-2010)]])
--- local found = test(dataset,[[match(author:"Bogusław Jackowski")]])
--- local found = test(dataset,[[match(author:"Bogusław Jackowski" and (tonumber(field:year) or 0) > 2000)]])
--- local found = test(dataset,[[Hagen:TB19-3-304]])
-
--- 1.328 0.002656 2710 author:hagen
--- Berdnikov:TB21-2-129 Guenther:TB5-1-24 Hagen:TB17-1-54 Hagen:TB19-3-304 Hagen:TB19-3-311 Hagen:TB19-3-317 Hagen:TB22-1-58 Hagen:TB22-3-118 Hagen:TB22-3-136 Hagen:TB22-3-160 Hagen:TB23-1-49 Hagen:TB25-1-108 Hagen:TB25-1-48 Hagen:TB26-2-152
-
--- 1.812 0.003624 2710 author:hagen and author:hoekwater and year:1990-2010
--- Berdnikov:TB21-2-129
-
--- 1.344 0.002688 2710 author:"Bogusław Jackowski"
--- Berdnikov:TB21-2-129 Jackowski:TB16-4-388 Jackowski:TB19-3-267 Jackowski:TB19-3-272 Jackowski:TB20-2-104 Jackowski:TB24-1-64 Jackowski:TB24-3-575 Nowacki:TB19-3-242 Rycko:TB14-3-171
-
--- 1.391 0.002782 2710 author:"Bogusław Jackowski" and (tonumber(field:year) or 0) > 2000
--- Jackowski:TB24-1-64 Jackowski:TB24-3-575
+-- inspect(publications.search(d,[[match(author:hagen)]]))
+-- inspect(publications.search(d,[[match(author:hagen and author:hoekwater and year:1990-2010)]]))
+-- inspect(publications.search(d,[[match(author:"Bogusław Jackowski")]]))
+-- inspect(publications.search(d,[[match(author:"Bogusław Jackowski" and (tonumber(field:year) or 0) > 2000)]]))
+-- inspect(publications.search(d,[[Hagen:TB19-3-304]]))