Як ви пишете 25м3? Ув маркдауні це виглядає рівно так, як можна очікувати:
25м<sup>3</sup>
Деякі редактори (імакс) дозволяють друкувати superscript цифри;
наприклад для надрядкового 2 треба віртуозно натиснути C-x 8 ^ 2
. Надрядкову a так надрукувати не вийде--замість неї з'явиться
літера â.
Юнікод мав би мати надрядкові та підрядкові симболи для кожної абетки, але цього не сталося. Присутній сабсет--для, наприклад, латини та безнадійної кирилиці--розмазан по випадкових місцях. Якщо підрядкові цифри можна отримати знаючи офсета, то для абеток це працювати не буде: e.g., підрядкова літера j сидить ув секції Latin Extended-C, втиснута поміж загадковою фінно-угорською ∃ (яка дуже нагадує мотематичний квантор існування) та надрядковою великою V.
Юнікод ніби має спеціяльну секцію Superscript and Subscripts, але вона виглядає як поганий жарт:
(Пусті світлі клітинки це не помилка рендерінґу симбола, а "not assigned" місця.)
Врешті-решт, якщо ретельно пошукати, знайти локацію більшості (не всіх) над/підрядкових літер можна, і тоді написавши елементарний скрипт що робить заміну звичайних літер на над/підрядкові, будь-який пристойний редактор дозволить замінити виділений текста на результат роботи скрипта.
Це є звичайно, цікаво, але було би цікавіше виділяти маркапний
варіянт де трансформувалися би лише елементи <sup>
чи <sub>
,
наприклад для хформули y = x2a:
Працює це так. 1й скрипт робить заміну відповідних симболів:
$ lsb_release -d | ./supsub sub
dₑₛ𞁞ᵣᵢₚₜᵢₒₙ: fₑdₒᵣₐ ᵣₑₗₑₐₛₑ ₃₉ ₍ₜₕᵢᵣₜᵧ ₙᵢₙₑ₎
$ lsb_release -d | ./supsub sub | ./supsub invert
ᵈ𞀵ˢ𞀿ʳⁱ𞀾ᵗⁱ𞀼ⁿ: ᶠ𞀵ᵈ𞀼ʳ𞀰 ʳ𞀵ˡ𞀵𞀰ˢ𞀵 ³⁹ ⁽ᵗʰⁱʳᵗʸ ⁿⁱⁿ𞀵⁾
$ lsb_release -d | ./supsub sub | ./supsub invert | ./supsub restore
dеsсrірtіоn: fеdоrа rеlеаsе 39 (thіrty nіnе)
(Рендерінґ бовзерами бажає залишати кращого.)
$ cat supsub
#!/usr/bin/env -S ruby --disable-gems
db = DATA.read.split(/\s+/).filter {|v| v}
$supa = db.map {|v| [v[0], v[1]] }.to_h
$sub = db.map {|v| [v[0], v[2]] }.to_h
mode = 'sup|sub|invert|restore'
abort "Usage: supsub #{mode} < file.txt" unless ARGV[0] =~ /^#{mode}$/
def tr mode, chars
case mode
when 'sup'
chars.map { |ch| $supa[ch.downcase] || ch }.join
when 'sub'
chars.map { |ch| $sub[ch.downcase] || ch }.join
when 'invert'
supa_v = $supa.invert
sub_v = $sub.invert
chars.map do |ch|
if supa_v[ch]
$sub[supa_v[ch]] || ch
elsif sub_v[ch]
$supa[sub_v[ch]] || ch
else
ch
end
end.join
else # restore
supa_v = $supa.invert
sub_v = $sub.invert
chars.map { |ch| supa_v[ch] || sub_v[ch] || ch}.join
end
end
while (line = STDIN.gets)
print tr(ARGV[0], line.chars)
end
__END__
0⁰₀ 1¹₁ 2²₂ 3³₃ 4⁴₄ 5⁵₅ 6⁶₆ 7⁷₇ 8⁸₈ 9⁹₉ +⁺₊ -⁻₋ =⁼₌ (⁽₍ )⁾₎
aᵃₐ bᵇb cᶜ𞁞 dᵈd eᵉₑ fᶠf gᵍg hʰₕ iⁱᵢ jʲⱼ kᵏₖ lˡₗ mᵐₘ nⁿₙ oᵒₒ
pᵖₚ q𐞥q rʳᵣ sˢₛ tᵗₜ uᵘᵤ vᵛᵥ wʷw xˣₓ yʸᵧ zᶻz
а𞀰ₐ б𞀱𞁒 в𞀲𞁓 г𞀳𞁔 ґґ𞁧 д𞀴𞁕 е𞀵ₑ ж𞀶𞁗 з𞀷𞁘 и𞀸𞁙 іⁱ𞁨 їїї ййй
к𞀹𞁚 л𞀺𞁛 м𞀻ₘ нᵸн о𞀼ₒ п𞀽𞁝 р𞀾ₚ с𞀿𞁞 т𞁀т у𞁁𞁟 ф𞁂𞁠 х𞁃𞁡 ц𞁄𞁢
ч𞁅𞁣 ш𞁆𞁤 щщщ ьꚝь ю𞁉ю яяя
Замінювати воно може лише цифри, симболи латини та української абетки. Сили на пошук голок ув юнікоді у мене закінчилися.
2й скрипта є трохи цікавіший:
$ echo '<sub>test</sub> <i>lol</i> <sup>haha</sup> but <sup>it works</sup>!' | ./supsub-xml
ₜₑₛₜ <i>lol</i> ʰᵃʰᵃ but ⁱᵗ ʷᵒʳᵏˢ!
За допомогою 鋸 парситься фрагменти xml та викликається попередній
скрипта (supsub) коли зустрічається елемент <sup>
чи <sub>
. Версія
1 скрипта supsub-xml може бути такою примітивною як
#!/usr/bin/env ruby
require 'nokogiri'
cmd = ARGV[0] || File.join(__dir__, 'supsub')
doc = Nokogiri::HTML.fragment STDIN.read
doc.css('sup,sub').each do |node|
IO.popen("#{cmd} #{node.name}", 'w+') do |t|
t.write node.text
t.close_write
node.replace t.gets
end
end
print doc.to_s
Воно працює, і, для більшості випадків його можна залишити як є, але форкання на кожну трансформацію виглядає варварським.
Стівенс в своїй APUE розповідає про coprocesses, які він описує як процесси які живуть одночасно з головною програмою (parent), і до яких parent під'єднується через 2 пайпи, які він сам створює:
Bash підтримує коупроцеси, але використовують їх ~ніхто, тому що ⓐ нарід ув більшості не гребе шо то за коупроцеси такі, ⓑ пише майкросервіси на расті для калькуляції 6*8, ⓒ це є забута стародавня технологія предків.
Найпростіший приклад виглядає так: parent запускає звичайну утіліту tr(1), яка має робити upcase для всього, шо надходить на її stdin:
$ cat coprocess
#!/usr/bin/env ruby
parent_in, parent_out = IO.pipe
child_in, child_out = IO.pipe
spawn "tr '[a-z]' '[A-Z]'", in: child_in, out: parent_out
parent_out.close
child_in.close
child_out.puts "lol"
print parent_in.gets
child_out.puts "haha"
print parent_in.gets
На жаль, як запустити ./coprocess
, ми не отримаємо LOL та HAHA:
скрипта зависне ув дедлоку на рядку print parent_in.gets
: tr з'їсть
свій stdin і напише результат ув свій stdout, використовуючи fwrite(2)
з libc (принаймі лайнаксна версія з coreutils), яка буферує результат,
тому parent буде чекати на байти з пайпу вічно.
Зарадити цьому можна пустивши tr через утіліту stdbuf(1):
spawn "stdbuf -i0 -o0 tr '[a-z]' '[A-Z]'", in: child_in, out: parent_out
тоді дейта почне рухатися пайпами:
$ ./coprocess
LOL
HAHA
Цей хфінт вухами працює лише для хфункцій з libc. Рубі (як і інші мови) має свій IO механізм, тому stdbuf йому допоможе як дикобразу вогнемета.
Але вихід є: якщо змусити коупроцесс думати що той є підключений до терміналу, тоді спрощена схема роботи supsub-xml що форкає supsub буде виглядати на кшталт (NSFW):
Рубі має чудове вбудоване ікстеншона pty, яке дозволяє замість IO.pipe для parent писати PTY.open, не чіпаючи все інше.
$ cat supsub-xml
#!/usr/bin/env ruby
require 'nokogiri'
require 'pty'
cmd = ARGV[0] || File.join(__dir__, 'supsub')
class Coprocess
def initialize cmd
@master, slave = PTY.open
read, @write = IO.pipe
spawn cmd, in: read, out: slave
read.close
slave.close
end
def puts str; @write.puts str; end
def gets; @master.gets; end
end
transforms = {
"sup" => Coprocess.new("#{cmd} sup"),
"sub" => Coprocess.new("#{cmd} sub")
}
doc = Nokogiri::HTML.fragment STDIN.read
doc.css('sup,sub').each do |node|
tr = transforms[node.name]
tr.puts node.text
node.replace tr.gets.chomp
rescue
warn "transforming `#{node.text}` failed: #{$!}"
end
print doc.to_s
Ув версії 2 ми створюємо 2 коупроцесси для елементів <sup>
та
<sub>
заздалегідь і використовуємо їх як локальні
майкросервіси. Якщо додати sleep ув кінець скрипта, можна подивитися
як це виглядає фізично:
$ $$
bash: 42396: command not found
$ echo '<sub>test</sub> <i>lol</i> <sup>haha</sup>!' | ./supsub-xml
ₜₑₛₜ <i>lol</i> ʰᵃʰᵃ!
поки воно спить, з іншого терміналу:
$ pstree -p 42396 -al
bash,42396
└─ruby,100032 ./supsub-xml
├─ruby,100033 --disable-gems ... sup
└─ruby,100034 --disable-gems ... sub
$ file /proc/100032/fd/* # supsub-xml
...
$ file /proc/100033/fd/* # supsub sup
...