diff options
Diffstat (limited to 'tex/context/base/lang-rep.lua')
-rw-r--r-- | tex/context/base/lang-rep.lua | 174 |
1 files changed, 140 insertions, 34 deletions
diff --git a/tex/context/base/lang-rep.lua b/tex/context/base/lang-rep.lua index 31ae36e6d..95a5e545a 100644 --- a/tex/context/base/lang-rep.lua +++ b/tex/context/base/lang-rep.lua @@ -7,9 +7,21 @@ if not modules then modules = { } end modules ['lang-rep'] = { } -- A BachoTeX 2013 experiment, probably not that useful. Eventually I used a simpler --- more generic example. +-- more generic example. I'm sure no one ever notices of even needs this code. +-- +-- As a follow up on a question by Alan about special treatment of dropped caps I wonder +-- if I can make this one more clever (probably in a few more dev steps). For instance +-- injecting nodes or replacing nodes. It's a prelude to a kind of lpeg for nodes, +-- although (given experiences so far) we don't really need that. After all, each problem +-- is somewhat unique. +local type, tonumber = type, tonumber local utfbyte, utfsplit = utf.byte, utf.split +local P, C, U, Cc, Ct, lpegmatch = lpeg.P, lpeg.C, lpeg.patterns.utf8character, lpeg.Cc, lpeg.Ct, lpeg.match +local find = string.find + +local grouped = P("{") * ( Ct((U/utfbyte-P("}"))^1) + Cc(false) ) * P("}")-- grouped +local splitter = Ct((Ct(Cc("discretionary") * grouped * grouped * grouped) + U/utfbyte)^1) local trace_replacements = false trackers.register("languages.replacements", function(v) trace_replacements = v end) local trace_detail = false trackers.register("languages.replacements.detail", function(v) trace_detail = v end) @@ -18,15 +30,34 @@ local report_replacement = logs.reporter("languages","replacements") local glyph_code = nodes.nodecodes.glyph -local insert_node_before = nodes.insert_before -local remove_node = nodes.remove -local copy_node = nodes.copy +local nuts = nodes.nuts +local tonut = nuts.tonut +local tonode = nuts.tonode + +local setfield = nuts.setfield +local getnext = nuts.getnext +local getprev = nuts.getprev +local getattr = nuts.getattr +local getid = nuts.getid +local getchar = nuts.getchar + +local insert_node_before = nuts.insert_before +local remove_node = nuts.remove +local copy_node = nuts.copy +local flush_list = nuts.flush_list +local insert_after = nuts.insert_after + +local nodepool = nuts.pool +local new_glyph = nodepool.glyph +local new_disc = nodepool.disc local texsetattribute = tex.setattribute local unsetvalue = attributes.unsetvalue local v_reset = interfaces.variables.reset +local implement = interfaces.implement + local replacements = languages.replacements or { } languages.replacements = replacements @@ -46,23 +77,32 @@ table.setmetatableindex(lists,function(lists,name) return data end) +lists[v_reset].attribute = unsetvalue -- so we discard 0 + +-- todo: glue kern + local function add(root,word,replacement) local list = utfsplit(word,true) - for i=1,#list do + local size = #list + for i=1,size do local l = utfbyte(list[i]) if not root[l] then root[l] = { } end - if i == #list then - local newlist = utfsplit(replacement,true) - for i=1,#newlist do - newlist[i] = utfbyte(newlist[i]) - end + if i == size then + -- local newlist = utfsplit(replacement,true) + -- for i=1,#newlist do + -- newlist[i] = utfbyte(newlist[i]) + -- end + local special = find(replacement,"{",1,true) + local newlist = lpegmatch(splitter,replacement) + -- root[l].final = { word = word, replacement = replacement, - oldlength = #list, + oldlength = size, newcodes = newlist, + special = special, } end root = root[l] @@ -83,13 +123,13 @@ end local function hit(a,head) local tree = trees[a] if tree then - local root = tree[head.char] + local root = tree[getchar(head)] if root then - local current = head.next + local current = getnext(head) local lastrun = false local lastfinal = false - while current and current.id == glyph_code do - local newroot = root[current.char] + while current and getid(current) == glyph_code do + local newroot = root[getchar(current)] if not newroot then return lastrun, lastfinal else @@ -104,7 +144,7 @@ local function hit(a,head) root = newroot end end - current = current.next + current = getnext(current) end if lastrun then return lastrun, lastfinal @@ -113,11 +153,27 @@ local function hit(a,head) end end +local function tonodes(list,template) + local head, current + for i=1,#list do + local new = copy_node(template) + setfield(new,"char",list[i]) + if head then + head, current = insert_after(head,current,new) + else + head, current = new, new + end + end + return head +end + + function replacements.handler(head) + head = tonut(head) local current = head local done = false while current do - if current.id == glyph_code then + if getid(current) == glyph_code then local a = getattr(current,a_replacements) if a then local last, final = hit(a,current) @@ -125,46 +181,90 @@ function replacements.handler(head) local oldlength = final.oldlength local newcodes = final.newcodes local newlength = #newcodes - if report_replacement then + if trace_replacement then report_replacement("replacing word %a by %a",final.word,final.replacement) end - if oldlength == newlength then -- #old == #new + if final.special then + -- easier is to delete and insert (a simple callout to tex would be more efficient) + -- maybe just walk over a replacement string instead + local prev = getprev(current) + local next = getnext(last) + local list = current + setfield(last,"next",nil) + setfield(prev,"next",next) + if next then + setfield(next,"prev",prev) + end + current = prev + if not current then + head = nil + end + for i=1,newlength do + local codes = newcodes[i] + local new = nil + if type(codes) == "table" then + local method = codes[1] + if method == "discretionary" then + local pre, post, replace = codes[2], codes[3], codes[4] + new = new_disc() + if pre then + setfield(new,"pre",tonodes(pre,last)) + end + if post then + setfield(new,"post",tonodes(post,last)) + end + if replace then + setfield(new,"replace",tonodes(replace,last)) + end + else + -- todo + end + else + new = copy_node(last) + setfield(new,"char",codes) + end + if new then + head, current = insert_after(head,current,new) + end + end + flush_list(list) + elseif oldlength == newlength then -- #old == #new for i=1,newlength do - current.char = newcodes[i] - current = current.next + setfield(current,"char",newcodes[i]) + current = getnext(current) end elseif oldlength < newlength then -- #old < #new for i=1,newlength-oldlength do local n = copy_node(current) - n.char = newcodes[i] + setfield(n,"char",newcodes[i]) head, current = insert_node_before(head,current,n) - current = current.next + current = getnext(current) end for i=newlength-oldlength+1,newlength do - current.char = newcodes[i] - current = current.next + setfield(current,"char",newcodes[i]) + current = getnext(current) end else -- #old > #new for i=1,oldlength-newlength do head, current = remove_node(head,current,true) end for i=1,newlength do - current.char = newcodes[i] - current = current.next + setfield(current,"char",newcodes[i]) + current = getnext(current) end end done = true end end end - current = current.next + current = getnext(current) end - return head, done + return tonode(head), done end local enabled = false -function replacements.set(n) -- number or 'reset' +function replacements.set(n) if n == v_reset then n = unsetvalue else @@ -182,8 +282,14 @@ end -- interface -commands.setreplacements = replacements.set -commands.addreplacements = replacements.add +implement { + name = "setreplacements", + actions = replacements.set, + arguments = "string" +} -nodes.tasks.prependaction("processors","words","languages.replacements.handler") -nodes.tasks.disableaction("processors","languages.replacements.handler") +implement { + name = "addreplacements", + actions = replacements.add, + arguments = { "string", "string", "string" } +} |