diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/publ-fnd.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkiv/publ-fnd.lua | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/publ-fnd.lua b/Master/texmf-dist/tex/context/base/mkiv/publ-fnd.lua new file mode 100644 index 00000000000..32d0c11beed --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkiv/publ-fnd.lua @@ -0,0 +1,298 @@ +if not modules then modules = { } end modules ['publ-fnd'] = { + version = 1.001, + comment = "this module part of publication support", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +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 = 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 wildcard = C("*") +local word = Cs(lpegpatterns.unquoted + lpegpatterns.argument + valid^1) +local simple = C(valid^1) +local number = C(valid^1) + +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 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 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)"] + +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, +} + +-- -- -- -- -- -- -- -- -- -- -- -- -- +-- -- -- -- -- -- -- -- -- -- -- -- -- + +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.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 +end +]] ] + +local function compile(dataset,expr) + local keys = { } + -- 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) + 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 + report("invalid expression: %s",expr) + return false +end + +-- 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 + +-- 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 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(dataset,expression) + if find then + 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 { } -- { dataset.luadata[expression] } -- ? + end +end + +-- local d = publications.datasets.default +-- +-- local d = publications.load { +-- dataset = "default", +-- filename = "t:/manuals/mkiv/hybrid/tugboat.bib" +-- } +-- +-- 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]])) |