Last active
February 3, 2021 14:08
-
-
Save zr-tex8r/c7901658a866adfcd3cd66b6dfa86997 to your computer and use it in GitHub Desktop.
LaTeX:「verbatimな文字列」を任意の場所で使う(bxrawstrパッケージ)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- | |
-- This is file 'bxrawstr.lua'. | |
-- | |
-- Copyright (c) 2018 Takayuki YATO (aka. "ZR") | |
-- GitHub: https://github.com/zr-tex8r | |
-- Twitter: @zr_tex8r | |
-- | |
-- This package is distributed under the MIT License. | |
-- | |
luatexbase.provides_module{ | |
name = 'bxrawstr', | |
date = '2018/12/22', | |
version = '0.2', | |
} | |
local name = 'bxrawstr' | |
local M = {} | |
---------------------------- helpers | |
local ltb = luatexbase | |
local getcount, setcount, gettoks, getcatcode = | |
tex.getcount, tex.setcount, tex.gettoks, tex.getcatcode | |
local unpack = unpack or table.unpack | |
local concat, insert = table.concat, table.insert | |
local format, utfvalues = string.format, string.utfvalues | |
local utfbyte, utfchar = unicode.utf8.byte, unicode.utf8.char | |
local function info(...) ltb.module_info(name, format(...)) end | |
local function alert(...) ltb.module_warning(name, format(...)) end | |
local function abort(...) ltb.module_error(name, format(...)) end | |
---------------------------- interfaces to TeX | |
local c_enabled = ltb.registernumber('bxlrs@enabled') | |
local c_newline = ltb.registernumber('bxlrs@newline') | |
local c_rescan = ltb.registernumber('bxlrs@rescan') | |
local c_errorcode = ltb.registernumber('bxlrs@errorcode') | |
local t_delimiters = ltb.registernumber('bxlrs@delimiters') | |
local cc_latex = assert(ltb.catcodetables.latex) | |
local cc_other = assert(ltb.catcodetables.other) | |
local cmd_init, cmd_term, cmd_do, cmd_to = | |
'\\bxlrsS*', '\\bxlrsE*', '\\bxlrsDo', '\\bxlrsTo' | |
local E_good, E_baddelim, E_badopt, E_inproc = 0, 1, 2, -1 | |
local N_space, N_delete, N_keep = 0, 1, 2 | |
local R_none, R_rescan, R_verbish = 0, 1, 2 | |
local function set_error(code) | |
setcount(c_errorcode, code) | |
end | |
---------------------------- delimiters | |
do | |
local pspec, pini, ptrm | |
function M.get_delimiters() | |
local spec = gettoks(t_delimiters) | |
if pspec == spec then | |
return pini, ptrm | |
end | |
local ini, trm = spec:match('^{(.*)}{(.*)}$') | |
ini = (ini or ''):gsub('[ \\\t\n\r]', '') | |
trm = (trm or ''):gsub('[ \\\t\n\r]', '') | |
if ini:match('^[^*0-9A-Za-z]') and trm:match('[^*0-9A-Za-z]$') then | |
pspec, pini, ptrm = spec, ini, trm | |
info("The delimiters are changed:\n '%s','%s'\n", pini, ptrm) | |
return ini, trm | |
end | |
end | |
function M.check_delimiters() | |
if M.is_in_process() then | |
return set_error(E_inproc) | |
elseif M.get_delimiters() then | |
return set_error(E_good) | |
else | |
return set_error(E_baddelim) | |
end | |
end | |
end | |
---------------------------- option parser | |
do | |
function M.parse_option(optstr) | |
local ok, newline, rescan = true, N_space, R_none | |
if type(optstr) == 'string' then | |
for c in optstr:gmatch('%S') do | |
if c == 's' then newline = N_space | |
elseif c == 'd' then newline = N_delete | |
elseif c == 'k' then newline = N_keep | |
elseif c == 'R' then rescan = R_none | |
elseif c == 'r' then rescan = R_rescan | |
elseif c == 'v' then rescan = R_verbish | |
else ok = false | |
end | |
end | |
else ok = false | |
end | |
if ok then | |
setcount(c_newline, newline) | |
setcount(c_rescan, rescan) | |
end | |
set_error(ok and E_good or E_badopt) | |
end | |
end | |
---------------------------- rs-encoding | |
do | |
local ltjflg = utfchar(0xFFFFF)..'\n$' | |
local f1, f2, f3 = '*%02X', '*u%04X', '*U%06X' | |
function M.encode(str) | |
return (str:gsub('[^0-9A-Za-z]+', function (ss) | |
local t = {} | |
for b in utfvalues(ss) do | |
t[#t+1] = format( | |
(b < 0x100) and f1 or (b < 0x10000) and f2 or f3, b) | |
end | |
return concat(t) | |
end)) | |
end | |
function M.encode_tail(str) | |
return M.encode((str..'\n'):gsub(ltjflg, '')) | |
end | |
function M.decode(rstr) | |
local chex = function(s) return utfchar(tonumber(s, 16)) end | |
return (rstr:gsub(' ', ''):gsub('%*U(......)', chex) | |
:gsub('%*u(....)', chex):gsub('%*(..)', chex)) | |
end | |
end | |
---------------------------- line processor | |
do | |
local in_process = false | |
function M.is_in_process() | |
return in_process | |
end | |
local function append(t, s) | |
if t then t[#t+1] = s else t = {s} end | |
return t | |
end | |
local function encode_part(line, ini, trm) | |
local pos, t = 1, nil | |
while pos do | |
if in_process then | |
local npos = line:find(trm, pos, true) | |
if npos then | |
t = append(t, M.encode(line:sub(pos, npos-1))..cmd_term) | |
in_process, pos = false, npos + #ini | |
elseif t then | |
t = append(t, M.encode_tail(line:sub(pos))) | |
pos = nil | |
else | |
return M.encode_tail(line) | |
end | |
else | |
local npos = line:find(ini, pos, true) | |
if npos then | |
t = append(t, line:sub(pos, npos-1)..cmd_init) | |
in_process, pos = true, npos + #ini | |
elseif t then | |
t = append(t, line:sub(pos)) | |
pos = nil | |
else | |
return line | |
end | |
end | |
end | |
return concat(t) | |
end | |
function M.process_line(buf) | |
-- NB: callback may be called even when disabled | |
if getcount(c_enabled) == 0 then | |
return nil | |
elseif getcatcode(0x5C) ~= 0 then | |
return nil | |
end | |
-- | |
local ini, trm = M.get_delimiters() | |
if ini and trm then | |
return encode_part(buf, ini, trm) | |
end | |
return nil | |
end | |
end | |
---------------------------- rawstring processors | |
do | |
local function sanitize(rstr) | |
rstr = rstr:gsub('[ \t\r\n]', '') | |
local p = rstr:find('[^*0-9A-Za-z]') | |
if p then | |
alert("Bad character (U+%04X)\nfound in rs-encoded sections", | |
utfbyte(rstr:sub(p))) | |
return '*2ARSERROR*2A' | |
end | |
return rstr | |
end | |
local function subst_newline(nl, txt) | |
if nl == N_space then return txt:gsub('\n', ' ') | |
elseif nl == N_delete then return txt:gsub('\n', '') | |
else return txt | |
end | |
end | |
function M.direct(rstr) | |
local txt = subst_newline(E_space, M.decode(sanitize(rstr))) | |
tex.sprint(-2, txt) | |
end | |
function M.indirect(nl, rstr) | |
local txt = subst_newline(nl, M.decode(sanitize(rstr))) | |
print("-----"..tostring(nl)..txt.."-----") | |
tex.sprint(cc_latex, cmd_to..'{') | |
tex.sprint(cc_other, txt) | |
tex.sprint(cc_latex, '}') | |
end | |
local mark = utfchar(0xFDD8) | |
function M.rescan(nl, resc, nocmd, rstr) | |
local txt, gtxt = subst_newline(nl, M.decode(sanitize(rstr))) | |
if nocmd == 1 then gtxt = txt | |
elseif resc == R_rescan then gtxt = '{'..txt..'}' | |
elseif resc == R_verbish then gtxt = mark..txt..mark | |
end | |
tex.sprint(cc_latex, '\\expandafter'..cmd_do..'\\scantextokens{') | |
for _, lin in ipairs(gtxt:explode('\n')) do | |
tex.print(cc_other, lin) | |
end | |
tex.sprint(cc_latex, '}') | |
end | |
end | |
---------------------------- register callbacks | |
do | |
local desc = name..'.process_input_buffer' | |
function M.enable(val) | |
if M.is_in_process() then | |
return set_error(E_inproc) | |
end | |
if val then | |
if not ltb.in_callback('process_input_buffer', desc) then | |
ltb.add_to_callback('process_input_buffer', M.process_line, desc) | |
end | |
elseif tex.currentgrouplevel == 0 then | |
if ltb.in_callback('process_input_buffer', desc) then | |
ltb.remove_from_callback('process_input_buffer', desc) | |
end | |
end | |
return set_error(E_good) | |
end | |
end | |
---------------------------- done | |
return M | |
-- EOF |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
%% | |
%% This is file 'bxrawstr.sty'. | |
%% | |
%% Copyright (c) 2018 Takayuki YATO (aka. "ZR") | |
%% GitHub: https://github.com/zr-tex8r | |
%% Twitter: @zr_tex8r | |
%% | |
%% This package is distributed under the MIT License. | |
%% | |
%% package declaration | |
\NeedsTeXFormat{LaTeX2e} | |
\ProvidesPackage{bxrawstr}[2018/12/22 v0.2] | |
%% preparation | |
\def\bxlrs@pkgname{bxrawstr} | |
\providecommand\bxDebug[1]{} | |
%--------------------------------------- environment check | |
%% check engine | |
\RequirePackage{ifluatex}[2010/03/01]% v1.3 | |
\ifluatex\else | |
\PackageError\bxlrs@pkgname | |
{This package requires LuaTeX} | |
{Package loading is aborted.} | |
\expandafter\endinput\fi\relax | |
\directlua{tex.enableprimitives("", | |
tex.extraprimitives("omega", "aleph", "luatex"))} | |
\ifnum\luatexversion<75 | |
\PackageError\bxlrs@pkgname | |
{LuaTeX v0.75 or later is required} | |
{Package loading is aborted.} | |
\expandafter\endinput\fi\relax | |
%% employ luatexbase to use multi-callback | |
\RequirePackage{luatexbase}[] | |
%--------------------------------------- general | |
%% packages | |
\RequirePackage{etoolbox}[2011/01/03]% v2.1 | |
%% variables | |
\newcount\bxlrs@enabled | |
\newcount\bxlrs@newline | |
\newcount\bxlrs@rescan | |
\newcount\bxlrs@errorcode | |
\newtoks\bxlrs@delimiters | |
%% \bxlrs@cond\ifXXX...\fi{<yes>}{<no>} | |
\@gobbletwo\if\if \def\bxlrs@cond#1\fi{% | |
#1\expandafter\@firstoftwo \else \expandafter\@secondoftwo \fi} | |
%% \bxlrs@get@option{<cont>}... | |
\def\bxlrs@get@option#1#2{% | |
\bxlrs@cond\ifx[#2\fi{\bxlrs@get@option@a{#1}}{#1{}#2}} | |
\def\bxlrs@get@option@a#1#2]{#1{#2}} | |
%% \bxlrs@str{<string>} | |
\def\bxlrs@str#1{% | |
'\luaescapestring{#1}'} | |
%% \bxlrs@check@error | |
\def\bxlrs@check@error{% | |
\ifcase\bxlrs@errorcode % E_good | |
\or % E_baddelim | |
\PackageError\bxlrs@pkgname | |
{Bad delimiter string}{\@eha} | |
\or % E_badopt | |
\PackageError\bxlrs@pkgname | |
{Bad option character}{\@eha} | |
\else | |
\PackageError\bxlrs@pkgname | |
{INTERNAL ERROR (\the\bxlrs@errorcode)}{\@ehc} | |
\fi} | |
%--------------------------------------- Lua modules | |
\ifdefined\bxUseDebug | |
\directlua{ bxlrs = require "bxrawstr" } | |
\else % supress errors | |
\directlua{pcall(function() bxlrs = require "bxrawstr" end)} | |
\fi | |
\ifnum % check if module is successfully loaded | |
\directlua{tex.sprint(bxlrs and '1' or '0')}=\z@ | |
\PackageError\bxlrs@pkgname | |
{Failure in loading Lua module} | |
{Package loading is aborted.} | |
\expandafter\endinput\fi\relax | |
%--------------------------------------- rawstr processors | |
%% \bxlrsTo{<arg>}{<command>} | |
\long\def\bxlrsTo#1#2{% | |
\ifstrempty{#2}{#1}{#2{#1}}} | |
%% \bxlrsS*<rs-section>\bxlrsE* | |
% Prints the string. | |
\def\bxlrsS*#1\bxlrsE*{% | |
\directlua{bxlrs.direct(\bxlrs@str{#1})}} | |
%% \rawstr[<option>]\CS...\bxlrsS*<rs-section>\bxlrsE* | |
\newcommand*\rawstr{% | |
\bxlrs@get@option\bxlrs@rawstr@a} | |
\def\bxlrs@rawstr@a#1{% | |
\directlua{bxlrs.parse_option(\bxlrs@str{#1})}% | |
\bxlrs@check@error | |
\bxlrs@cond\ifnum\bxlrs@rescan=\z@\fi{% | |
\bxlrs@rawstr@indirect}{%else | |
\bxlrs@rawstr@rescan}} | |
\long\def\bxlrs@rawstr@indirect#1\bxlrsS*#2\bxlrsE*{% | |
\directlua{bxlrs.indirect(\the\bxlrs@newline,\bxlrs@str{#2})}% | |
{#1}} | |
\protected\long\def\bxlrs@rawstr@rescan#1\bxlrsS*#2\bxlrsE*{% | |
\def\bxlrsDo{#1}% | |
\directlua{bxlrs.rescan(\the\bxlrs@newline,\the\bxlrs@rescan,% | |
\ifx\bxlrsDo\@empty 1\else0\fi,% | |
\bxlrs@str{#2})}} | |
%--------------------------------------- other user interface | |
%%<*> \rawstrenable | |
\newrobustcmd\rawstrenable{% | |
\bxlrs@enabled\@ne | |
\directlua{bxlrs.enable(true)}% | |
\bxlrs@check@error} | |
%%<*> \rawstrdisable | |
\newrobustcmd\rawstrdisable{% | |
\bxlrs@enabled\z@ | |
\directlua{bxlrs.enable(false)}% | |
\bxlrs@check@error} | |
%%<+> \rawstrSetDelimiters | |
\newrobustcmd\rawstrSetDelimiters[2]{% | |
\bxlrs@delimiters{{#1}{#2}}% | |
\directlua{bxlrs.check_delimiters()}% | |
\bxlrs@check@error} | |
%--------------------------------------- initial setup | |
%% set delimiters | |
\rawstrSetDelimiters{[[|}{|]]} | |
%--------------------------------------- all done | |
\endinput | |
%% EOF |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
前提環境
インストール方法
bxrawstr.sty
,bxrawstr.lua
→$TEXMF/tex/lualatex/bxrawstr/
ライセンス
MITライセンスが適用される。