if not modules then modules = { } end modules ['bibl-bib'] = { version = 1.001, comment = "this module is the basis for the lxml-* ones", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright = "PRAGMA ADE / ConTeXt Development Team", license = "see context related readme files" } --[[ldx--

This is a prelude to integrated bibliography support. This file just loads bibtex files and converts them to xml so that the we access the content in a convenient way. Actually handling the data takes place elsewhere.

--ldx]]-- local lower, format = string.lower, string.format local next = next bibtex = bibtex or { } bibtex.size = 0 bibtex.definitions = 0 bibtex.shortcuts = 0 local shortcuts = { } local data = { } local entries local function do_shortcut(tag,key,value) bibtex.shortcuts = bibtex.shortcuts + 1 if lower(tag) == "@string" then shortcuts[key] = value end end local function do_definition(tag,key,tab) -- maybe check entries here (saves memory) if not entries or entries[key] then bibtex.definitions = bibtex.definitions + 1 local t = { } for i=1,#tab,2 do t[tab[i]] = tab[i+1] end local p = data[tag] if not p then data[tag] = { [key] = t } else p[key] = t end end end local function resolve(s) return shortcuts[s] or "" end local percent = lpeg.P("%") local start = lpeg.P("@") local comma = lpeg.P(",") local hash = lpeg.P("#") local escape = lpeg.P("\\") local single = lpeg.P("'") local double = lpeg.P('"') local left = lpeg.P('{') local right = lpeg.P('}') local both = left + right local lineending = lpeg.S("\n\r") local space = lpeg.S(" \t\n\r\f") local spacing = space^0 local equal = lpeg.P("=") local collapsed = (space^1)/ " " local function add(a,b) if b then return a..b else return a end end local keyword = lpeg.C((lpeg.R("az","AZ","09") + lpeg.S("@_:-"))^1) -- lpeg.C((1-space)^1) local s_quoted = ((escape*single) + collapsed + (1-single))^0 local d_quoted = ((escape*double) + collapsed + (1-double))^0 local balanced = lpeg.P { [1] = ((escape * (left+right)) + (1 - (left+right)) + lpeg.V(2))^0, [2] = left * lpeg.V(1) * right } local s_value = (single/"") * s_quoted * (single/"") local d_value = (double/"") * d_quoted * (double/"") local b_value = (left /"") * balanced * (right /"") local r_value = keyword/resolve local somevalue = s_value + d_value + b_value + r_value local value = lpeg.Cs((somevalue * ((spacing * hash * spacing)/"" * somevalue)^0)) local assignment = spacing * keyword * spacing * equal * spacing * value * spacing local shortcut = keyword * spacing * left * spacing * (assignment * comma^0)^0 * spacing * right local definition = keyword * spacing * left * spacing * keyword * comma * lpeg.Ct((assignment * comma^0)^0) * spacing * right local comment = keyword * spacing * left * (1-right)^0 * spacing * right local forget = percent^1 * (1-lineending)^0 -- todo \% local grammar = (space + forget + shortcut/do_shortcut + definition/do_definition + comment + 1)^0 function bibtex.convert(session,content) statistics.starttiming(bibtex) data, shortcuts, entries = session.data, session.shortcuts, session.entries -- session.size = session.size + #content bibtex.size = bibtex.size + #content grammar:match(content or "") statistics.stoptiming(bibtex) end function bibtex.load(session,filename) local filename = resolvers.find_file(filename,"bib") if filename ~= "" then bibtex.convert(session,io.loaddata(filename) or "") end end function bibtex.new() return { data = { }, shortcuts = { }, xml = xml.convert("\n"), size = 0, entries = nil, } end local escaped_pattern = xml.escaped_pattern function bibtex.toxml(session) -- we can always speed this up if needed -- format slows down things a bit but who cares statistics.starttiming(bibtex) local result = { } local entries = session.entries result[#result+1] = format("") result[#result+1] = format("") for id, categories in next, session.data do result[#result+1] = format(" ",id) for name, entry in next, categories do if not entries or entries[name] then result[#result+1] = format(" ",name) for key, value in next, entry do value = escaped_pattern:match(value) if value ~= "" then result[#result+1] = format(" %s",key,value) end end result[#result+1] = format(" ") end end result[#result+1] = format(" ") end result[#result+1] = format("") session.xml = xml.convert(table.concat(result,"\n")) statistics.stoptiming(bibtex) end statistics.register("bibtex load time", function() local size = bibtex.size if size > 0 then return format("%s seconds (%s bytes, %s definitions, %s shortcuts)", statistics.elapsedtime(bibtex),size,bibtex.definitions,bibtex.shortcuts) else return nil end end) --~ str = [[ --~ @COMMENT { CRAP } --~ @STRING{ hans = "h a n s" } --~ @STRING{ taco = "t a c o" } --~ @SOMETHING{ key1, abc = "t a c o" , def = "h a n s" } --~ @SOMETHING{ key2, abc = hans # taco } --~ @SOMETHING{ key3, abc = "hans" # taco } --~ @SOMETHING{ key4, abc = hans # "taco" } --~ @SOMETHING{ key5, abc = hans # taco # "hans" # "taco"} --~ @SOMETHING{ key6, abc = {oeps {oeps} oeps} } --~ ]] --~ local session = bibtex.new() --~ bibtex.convert(session,str) --~ bibtex.toxml(session) --~ print(session.size,statistics.elapsedtime(bibtex)) --~ local session = bibtex.new() --~ bibtex.load(session,"IEEEabrv.bib") --~ bibtex.load(session,"IEEEfull.bib") --~ bibtex.load(session,"IEEEexample.bib") --~ bibtex.toxml(session) --~ print(session.size,statistics.elapsedtime(bibtex)) --~ local session = bibtex.new() --~ bibtex.load(session,"gut.bib") --~ bibtex.load(session,"komoedie.bib") --~ bibtex.load(session,"texbook1.bib") --~ bibtex.load(session,"texbook2.bib") --~ bibtex.load(session,"texbook3.bib") --~ bibtex.load(session,"texgraph.bib") --~ bibtex.load(session,"texjourn.bib") --~ bibtex.load(session,"texnique.bib") --~ bibtex.load(session,"tugboat.bib") --~ bibtex.toxml(session) --~ print(session.size,statistics.elapsedtime(bibtex)) --~ print(table.serialize(session.data)) --~ print(table.serialize(session.shortcuts)) --~ print(xml.serialize(session.xml)) -- this will move: if commands then local sessions = { } function commands.definebibtexsession(name) sessions[name] = bibtex.new() end function commands.preparebibtexsession(name) bibtex.toxml(sessions[name]) lxml.register("bibtex:"..name,sessions[name].xml) end function commands.registerbibtexfile(name,filename) bibtex.load(sessions[name],filename) end function commands.registerbibtexentry(name,entry) local session = sessions[name] local entries = session.entries if not entries then session.entries = { [entry] = true } else entries[entry] = true end end end