hierogloss 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.travis.yml +3 -0
- data/GARDINER-LICENSE.txt +6 -0
- data/Gemfile +4 -0
- data/KRAMDOWN-LICENSE.txt +29 -0
- data/LICENSE.txt +15 -0
- data/README.md +72 -0
- data/Rakefile +8 -0
- data/UNICODE-DATA-LICENSE.txt +15 -0
- data/UNLICENSE.txt +30 -0
- data/assets/fonts/Gardiner.eot +0 -0
- data/assets/fonts/Gardiner.otf +0 -0
- data/assets/fonts/Gardiner.svg +13593 -0
- data/assets/fonts/Gardiner.ttf +0 -0
- data/assets/fonts/Gardiner.woff +0 -0
- data/bin/hierogloss +25 -0
- data/data/Unicode-MdC-Mapping-v1.utf8 +1001 -0
- data/data/hierogloss.html.erb +55 -0
- data/examples/disjunction.md +13 -0
- data/examples/disjunction.png +0 -0
- data/examples/liverpool.md +11 -0
- data/hierogloss.gemspec +26 -0
- data/lib/hierogloss/dictionary.rb +39 -0
- data/lib/hierogloss/gloss.rb +174 -0
- data/lib/hierogloss/version.rb +3 -0
- data/lib/hierogloss.rb +24 -0
- data/lib/kramdown/converter/bbcode.rb +113 -0
- data/lib/kramdown/converter/htlal.rb +34 -0
- data/lib/kramdown/parser/hierogloss.rb +37 -0
- data/src/Gardiner.ttf +0 -0
- data/test/minitest_helper.rb +4 -0
- data/test/test_dictionary.rb +50 -0
- data/test/test_gloss.rb +43 -0
- data/test/test_hierogloss.rb +7 -0
- data/test/test_kramdown_extensions.rb +72 -0
- metadata +157 -0
@@ -0,0 +1,55 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta charset="utf-8">
|
5
|
+
<%
|
6
|
+
# This code from Kramdown's default template.
|
7
|
+
extend ::Kramdown::Utils::Html
|
8
|
+
title = 'Hierogloss'
|
9
|
+
h = @converter.root.children.find {|c| c.type == :header}
|
10
|
+
if h
|
11
|
+
collector = lambda {|c| c.children.collect {|cc| cc.type == :text ? escape_html(cc.value, :text) : collector.call(cc)}.join('')}
|
12
|
+
title = collector.call(h)
|
13
|
+
end
|
14
|
+
%>
|
15
|
+
<title><%= title %></title>
|
16
|
+
<meta name="generator" content="hierogloss kramdown <%= ::Kramdown::VERSION %>" />
|
17
|
+
|
18
|
+
<style type="text/css">
|
19
|
+
@font-face {
|
20
|
+
font-family: 'WebGardiner';
|
21
|
+
src: url('fonts/Gardiner.eot');
|
22
|
+
src: local('βΊ'), url('fonts/Gardiner.woff') format('woff'), url('fonts/Gardiner.ttf') format('truetype'), url('fonts/Gardiner.svg') format('svg');
|
23
|
+
font-weight: normal;
|
24
|
+
font-style: normal;
|
25
|
+
}
|
26
|
+
|
27
|
+
.hgls-h, .hgls-l {
|
28
|
+
font-family: "WebGardiner", "Aegyptus", "Gardiner", serif;
|
29
|
+
}
|
30
|
+
.hgls-l { font-style: normal; font-size: 18pt; }
|
31
|
+
.hgls-h { font-size: 24px; }
|
32
|
+
|
33
|
+
.hgls-gloss td { padding-right: 18px; }
|
34
|
+
.hgls-gloss {
|
35
|
+
margin-left: 18px;
|
36
|
+
page-break-inside: avoid;
|
37
|
+
}
|
38
|
+
p.hgls-gloss {
|
39
|
+
margin-top: 0;
|
40
|
+
}
|
41
|
+
table.hgls-gloss a:link, table.hgls-gloss a:visited {
|
42
|
+
color: black;
|
43
|
+
text-decoration: none;
|
44
|
+
}
|
45
|
+
table.hgls-gloss a:hover {
|
46
|
+
text-decoration: underline;
|
47
|
+
}
|
48
|
+
</style>
|
49
|
+
</head>
|
50
|
+
<body>
|
51
|
+
|
52
|
+
<%= @body %>
|
53
|
+
|
54
|
+
</body>
|
55
|
+
</html>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# Disjunction in Middle Egyptian
|
2
|
+
|
3
|
+
This example is based on one in Allen's excellent [Middle Egyptian: An
|
4
|
+
Introduction to the Language and Culture of Hieroglyphs][allen].
|
5
|
+
|
6
|
+
H: πππ€ | πππ | ππ€πͺπ
±
|
7
|
+
L: s | s.t | r-pw
|
8
|
+
G: man | woman | whichever
|
9
|
+
T: either [a] man or [a] woman
|
10
|
+
|
11
|
+
You can emphasize an "or" by following the options with {r-pw}.
|
12
|
+
|
13
|
+
[allen]: http://www.amazon.com/Middle-Egyptian-Introduction-Language-Hieroglyphs/dp/0521741440
|
Binary file
|
@@ -0,0 +1,11 @@
|
|
1
|
+
H: π | π ππΏπ
±π΅ππͺ | πΉπ | πππ
|
2
|
+
L: wab | mnTw-Htp | ir(w)~n | Hnw.t
|
3
|
+
G: prΓͺtre | Mentouhotep | nΓ© de | Henout
|
4
|
+
|
5
|
+
H: π | ππππ΄πππ
± | ππ»
|
6
|
+
L: wab | rnfsnbw | mAa(-xrw)
|
7
|
+
G: prΓͺtre | Renef-senebou | juste de voix
|
8
|
+
|
9
|
+
H: π
ππ | πππ | πΉπ | πππ₯πππ | ππ»
|
10
|
+
L: sA.t=f | Hnw.t | ir(w) n | nbw(.t)-aat | mAa(.t-xrw)
|
11
|
+
G: fille=sa | Henout | nΓ©e de | Neboutaat | juste de voix
|
data/hierogloss.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'hierogloss/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "hierogloss"
|
8
|
+
spec.version = Hierogloss::VERSION
|
9
|
+
spec.authors = ["Eric Kidd"]
|
10
|
+
spec.email = ["git@randomhacks.net"]
|
11
|
+
spec.description = %q{Extends the Markdown parser Kramdown to support hieroglyphs, inline multi-column glosses, and output to BBCode for use on forums. Includes an executable for processing files and a webfont version of the Gardiner signs.}
|
12
|
+
spec.summary = %q{Markdown extensions for hieroglyphic glosses and BBCode}
|
13
|
+
spec.homepage = "https://github.com/emk/hierogloss"
|
14
|
+
spec.license = "Public domain + other open source licenses"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "kramdown", "~> 1.3"
|
22
|
+
spec.add_development_dependency "prawn", "~> 0.14.0"
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "minitest"
|
26
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
module Hierogloss
|
4
|
+
#:nodoc: Internal dictionary-related utilities. APIs are not stable.
|
5
|
+
module Dictionary
|
6
|
+
DATA_DIR = File.join(File.dirname(__FILE__), '..', '..', 'data')
|
7
|
+
MDC_MAPPING_PATH = File.join(DATA_DIR, "Unicode-MdC-Mapping-v1.utf8")
|
8
|
+
|
9
|
+
GARDINER = {}
|
10
|
+
File.open(MDC_MAPPING_PATH, "r:bom|utf-8") do |f|
|
11
|
+
f.each_line do |l|
|
12
|
+
l.chomp!
|
13
|
+
sign, hex, codes, remarks = l.split(/\t/, 4)
|
14
|
+
for code in codes.split(/ /)
|
15
|
+
next unless code =~ /\A[A-Z][0-9]+\z/
|
16
|
+
GARDINER[sign] = code
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
"πΏππππ
±π²ππͺππ
ππππππ‘ππ΄πππ‘πΌππΏπ§π".each_char do |c|
|
21
|
+
GARDINER.delete(c)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Try to kick things into shape for hierogl.ch.
|
25
|
+
def self.headword(word)
|
26
|
+
hw = word
|
27
|
+
hw.gsub!(/[()]/, '')
|
28
|
+
hw.sub!(/=.*\z/, '')
|
29
|
+
hw.sub!(/\.w?t\z/, 't')
|
30
|
+
hw.sub!(/\..*\z/, '')
|
31
|
+
hw
|
32
|
+
end
|
33
|
+
|
34
|
+
# Given a Unicode hieroglyph, get the corresponding Gardiner sign.
|
35
|
+
def self.gardiner(sign)
|
36
|
+
GARDINER[sign]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
|
3
|
+
|
4
|
+
require 'hierogloss/dictionary'
|
5
|
+
|
6
|
+
module Hierogloss
|
7
|
+
#:nodoc:
|
8
|
+
class Row
|
9
|
+
attr_reader :raw_cells
|
10
|
+
|
11
|
+
def initialize(row_text)
|
12
|
+
@raw_cells = row_text.split(/\|/).map {|c| c.strip }
|
13
|
+
end
|
14
|
+
|
15
|
+
def span?
|
16
|
+
false
|
17
|
+
end
|
18
|
+
|
19
|
+
def attributes
|
20
|
+
attrs = {}
|
21
|
+
attrs['class'] = class_attr if class_attr
|
22
|
+
attrs
|
23
|
+
end
|
24
|
+
|
25
|
+
def class_attr
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_kramdown
|
30
|
+
attrs = attributes
|
31
|
+
tr = Kramdown::Element.new(:tr, nil, attrs)
|
32
|
+
raw_cells.each do |c|
|
33
|
+
td = Kramdown::Element.new(:td)
|
34
|
+
children = cell_to_kramdown(c)
|
35
|
+
if children.kind_of?(Array)
|
36
|
+
td.children.concat(children)
|
37
|
+
else
|
38
|
+
td.children << children
|
39
|
+
end
|
40
|
+
tr.children << td
|
41
|
+
end
|
42
|
+
tr
|
43
|
+
end
|
44
|
+
|
45
|
+
def cell_to_kramdown(cell)
|
46
|
+
Kramdown::Element.new(:text, cell)
|
47
|
+
end
|
48
|
+
|
49
|
+
def search_link(query, text)
|
50
|
+
base_url = "http://www.hierogl.ch/hiero/Sp%C3%A9cial:Recherche"
|
51
|
+
escaped_query = CGI.escape(query)
|
52
|
+
url = "#{base_url}?search=#{escaped_query}&go=Lire"
|
53
|
+
|
54
|
+
link = Kramdown::Element.new(:a, nil, {'href' => url})
|
55
|
+
link.children << Kramdown::Element.new(:text, text)
|
56
|
+
link
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
#:nodoc:
|
61
|
+
class HieroglyphRow < Row
|
62
|
+
def class_attr
|
63
|
+
'hgls-h'
|
64
|
+
end
|
65
|
+
|
66
|
+
def cell_to_kramdown(cell)
|
67
|
+
cell.chars.map do |c|
|
68
|
+
gardiner = Dictionary.gardiner(c)
|
69
|
+
if !gardiner.nil?
|
70
|
+
search_link("Signe:#{gardiner}", c)
|
71
|
+
else
|
72
|
+
Kramdown::Element.new(:text, c)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
#:nodoc:
|
79
|
+
class TransliterationRow < Row
|
80
|
+
JR_TRANSLITERATION = {
|
81
|
+
"A" => "κ£",
|
82
|
+
"i" => "j",
|
83
|
+
"a" => "κ₯",
|
84
|
+
"H" => "αΈ₯",
|
85
|
+
"x" => "αΈ«",
|
86
|
+
"X" => "αΊ",
|
87
|
+
"S" => "Ε‘",
|
88
|
+
"q" => "αΈ³",
|
89
|
+
"K" => "αΈ³",
|
90
|
+
"T" => "αΉ―",
|
91
|
+
"D" => "αΈ"
|
92
|
+
}
|
93
|
+
|
94
|
+
def self.fancy(tl)
|
95
|
+
tl.chars.map {|c| JR_TRANSLITERATION[c] || c }.join
|
96
|
+
end
|
97
|
+
|
98
|
+
def class_attr
|
99
|
+
'hgls-l'
|
100
|
+
end
|
101
|
+
|
102
|
+
def cell_to_kramdown(cell)
|
103
|
+
fancy = self.class.fancy(cell)
|
104
|
+
search_link(Dictionary.headword(cell), fancy)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
#:nodoc:
|
109
|
+
class TranslationRow
|
110
|
+
attr_reader :text
|
111
|
+
|
112
|
+
def initialize(row_text)
|
113
|
+
@text = row_text.strip
|
114
|
+
end
|
115
|
+
|
116
|
+
def span?
|
117
|
+
true
|
118
|
+
end
|
119
|
+
|
120
|
+
def class_attr
|
121
|
+
'hgls-t'
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_kramdown
|
125
|
+
em = Kramdown::Element.new(:em, nil)
|
126
|
+
em.children << Kramdown::Element.new(:text, text)
|
127
|
+
em
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
#:nodoc:
|
132
|
+
class Gloss
|
133
|
+
attr_reader :rows
|
134
|
+
|
135
|
+
def initialize(text)
|
136
|
+
@rows = text.lines.map {|l| l.chomp }.map do |row|
|
137
|
+
row =~ /\A(\w+):(.*)\z/
|
138
|
+
raise "C'est quoi, [[#{row}]]\?" unless $1 && $2
|
139
|
+
type =
|
140
|
+
case $1
|
141
|
+
when "H" then HieroglyphRow
|
142
|
+
when "L" then TransliterationRow
|
143
|
+
when "T" then TranslationRow
|
144
|
+
else Row
|
145
|
+
end
|
146
|
+
type.new($2)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def to_kramdown
|
151
|
+
result = []
|
152
|
+
# Neither Kramdown nor BBCode support rowspans, so we'll just cheat
|
153
|
+
# for now.
|
154
|
+
rows.chunk {|r| r.span? }.each do |spans, rows|
|
155
|
+
if spans
|
156
|
+
rows.each do |r|
|
157
|
+
class_attr = "hgls-gloss #{r.class_attr}"
|
158
|
+
p = Kramdown::Element.new(:p, nil, 'class' => class_attr)
|
159
|
+
p.children << r.to_kramdown
|
160
|
+
result << p
|
161
|
+
end
|
162
|
+
else
|
163
|
+
table = Kramdown::Element.new(:table, nil, { 'class' => 'hgls-gloss' },
|
164
|
+
alignment: [])
|
165
|
+
tbody = Kramdown::Element.new(:tbody)
|
166
|
+
table.children << tbody
|
167
|
+
rows.each {|r| tbody.children << r.to_kramdown }
|
168
|
+
result << table
|
169
|
+
end
|
170
|
+
end
|
171
|
+
result
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
data/lib/hierogloss.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'kramdown'
|
2
|
+
require "hierogloss/version"
|
3
|
+
require "hierogloss/dictionary"
|
4
|
+
require "hierogloss/gloss"
|
5
|
+
require "kramdown/parser/hierogloss"
|
6
|
+
require "kramdown/converter/bbcode"
|
7
|
+
# No guarantess of backwards compatibility for this one.
|
8
|
+
require "kramdown/converter/htlal"
|
9
|
+
|
10
|
+
# Most of our internal APIs are undocumented at this point, but you
|
11
|
+
# can use this gem via Kramdown's APIs.
|
12
|
+
#
|
13
|
+
# Kramdown::Document.new(ARGF.read, input: 'hierogloss').to_html
|
14
|
+
#
|
15
|
+
# Or if you want to post on an online forum, try:
|
16
|
+
#
|
17
|
+
# Kramdown::Document.new(ARGF.read, input: 'hierogloss').to_bbcode
|
18
|
+
#
|
19
|
+
# Note that the BBCode converter does not yet support all available
|
20
|
+
# Markdown constructs. You're welcome to try other kramdown backends;
|
21
|
+
# some of them may more-or-less work.
|
22
|
+
module Hierogloss
|
23
|
+
# Nothing to do here yet.
|
24
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
module Kramdown
|
2
|
+
module Converter
|
3
|
+
# Outputs some of the most common Markdown elements as BBCode.
|
4
|
+
# Everything in this class is internal.
|
5
|
+
class Bbcode < Base
|
6
|
+
include ::Kramdown::Utils::Html
|
7
|
+
|
8
|
+
DISPATCHER = Hash.new {|h,k| h[k] = "convert_#{k}"} #:nodoc:
|
9
|
+
|
10
|
+
def initialize(root, options)
|
11
|
+
super
|
12
|
+
@stack = []
|
13
|
+
end
|
14
|
+
|
15
|
+
def convert(el, opts = {})
|
16
|
+
send(DISPATCHER[el.type], el, opts)
|
17
|
+
end
|
18
|
+
|
19
|
+
def inner(el, opts)
|
20
|
+
@stack.push([el, opts])
|
21
|
+
result = el.children.map do |inner_el|
|
22
|
+
convert(inner_el, options)
|
23
|
+
end
|
24
|
+
@stack.pop
|
25
|
+
result
|
26
|
+
end
|
27
|
+
|
28
|
+
def convert_header(el, opts)
|
29
|
+
tag("b", nil, inner(el, opts)) + ["\n"]
|
30
|
+
end
|
31
|
+
|
32
|
+
def convert_p(el, opts)
|
33
|
+
inner(el, opts) + ["\n"]
|
34
|
+
end
|
35
|
+
|
36
|
+
def convert_blank(el, opts)
|
37
|
+
"\n"
|
38
|
+
end
|
39
|
+
|
40
|
+
def convert_text(el, opts)
|
41
|
+
# Wouldn't it be nice if we could escape BBCode?
|
42
|
+
el.value.gsub(/\s+/, ' ') # Ignore newlines in raw text.
|
43
|
+
end
|
44
|
+
|
45
|
+
def convert_em(el, opts)
|
46
|
+
tag("i", nil, inner(el, opts))
|
47
|
+
end
|
48
|
+
|
49
|
+
def convert_strong(el, opts)
|
50
|
+
tag("b", nil, inner(el, opts))
|
51
|
+
end
|
52
|
+
|
53
|
+
def convert_a(el, opts)
|
54
|
+
tag("url", el.attr['href'], inner(el, opts))
|
55
|
+
end
|
56
|
+
|
57
|
+
def convert_smart_quote(el, opts)
|
58
|
+
entity_to_str(smart_quote_entity(el))
|
59
|
+
end
|
60
|
+
|
61
|
+
def convert_table(el, opts)
|
62
|
+
tag("table", nil, ["\n"] + inner(el, opts))
|
63
|
+
end
|
64
|
+
|
65
|
+
def convert_tbody(el, opts)
|
66
|
+
inner(el, opts)
|
67
|
+
end
|
68
|
+
|
69
|
+
def convert_tr(el, opts)
|
70
|
+
tag("tr", nil, inner(el, opts)) + ["\n"]
|
71
|
+
end
|
72
|
+
|
73
|
+
def convert_td(el, opts)
|
74
|
+
if @stack.last.first.attr['class'] == 'hgls-h'
|
75
|
+
tag("td", nil, tag("size", "24", inner(el, opts)))
|
76
|
+
else
|
77
|
+
tag("td", nil, inner(el, opts))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def convert_blockquote(el, opts)
|
82
|
+
# No newline because BBCode will add one itself. We also run
|
83
|
+
# results_to_text so we can clean up trailing newlines.
|
84
|
+
tag("quote", nil, results_to_text(inner(el, opts)))
|
85
|
+
end
|
86
|
+
|
87
|
+
def convert_codeblock(el, opts)
|
88
|
+
# No newline because BBCode will add one itself.
|
89
|
+
tag("code", nil, el.value.sub(/\n\z/, ''))
|
90
|
+
end
|
91
|
+
|
92
|
+
def convert_img(el, opts)
|
93
|
+
tag("img", nil, el.attr['src'])
|
94
|
+
end
|
95
|
+
|
96
|
+
def convert_root(el, opts)
|
97
|
+
results_to_text(inner(el, opts))
|
98
|
+
end
|
99
|
+
|
100
|
+
def tag(name, arg, content)
|
101
|
+
if arg
|
102
|
+
["[#{name}=#{arg}]", content, "[/#{name}]"]
|
103
|
+
else
|
104
|
+
["[#{name}]", content, "[/#{name}]"]
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def results_to_text(results)
|
109
|
+
results.flatten.compact.join.sub(/\n+\z/, '')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Kramdown
|
2
|
+
module Converter
|
3
|
+
# Outputs some of the most common Markdown elements in a stripped down
|
4
|
+
# BBCode dialect without table support or reliable font size control.
|
5
|
+
# Everything in this class is internal.
|
6
|
+
class Htlal < Bbcode
|
7
|
+
include ::Kramdown::Utils::Html
|
8
|
+
|
9
|
+
def convert_table(el, opts)
|
10
|
+
inner(el, opts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def convert_tbody(el, opts)
|
14
|
+
inner(el, opts)
|
15
|
+
end
|
16
|
+
|
17
|
+
def convert_tr(el, opts)
|
18
|
+
spaced = []
|
19
|
+
inner(el, opts).each do |td|
|
20
|
+
spaced << td << " | "
|
21
|
+
end
|
22
|
+
spaced.pop
|
23
|
+
spaced << "\n"
|
24
|
+
spaced
|
25
|
+
end
|
26
|
+
|
27
|
+
def convert_td(el, opts)
|
28
|
+
# We'd like to make hieroglyph cells bigger, but that doesn't play
|
29
|
+
# nicely with links.
|
30
|
+
inner(el, opts)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Kramdown
|
2
|
+
module Parser
|
3
|
+
# Parses an extended Kramdown syntax with support for inline glosses.
|
4
|
+
# Everything in this class is internal.
|
5
|
+
class Hierogloss < ::Kramdown::Parser::Kramdown
|
6
|
+
def initialize(source, options)
|
7
|
+
super
|
8
|
+
@span_parsers.unshift(:translit)
|
9
|
+
@block_parsers.unshift(:gloss)
|
10
|
+
end
|
11
|
+
|
12
|
+
TRANSLIT_START = /{.*?}/
|
13
|
+
|
14
|
+
def parse_translit
|
15
|
+
@src.pos += @src.matched_size
|
16
|
+
mdc = @src.matched[1..-2]
|
17
|
+
em = Element.new(:em, nil, 'class' => 'hgls-l')
|
18
|
+
em.children <<
|
19
|
+
Element.new(:text, ::Hierogloss::TransliterationRow.fancy(mdc))
|
20
|
+
@tree.children << em
|
21
|
+
end
|
22
|
+
define_parser(:translit, TRANSLIT_START, '{')
|
23
|
+
|
24
|
+
GLOSS_START = /^(H|L|G|T):/
|
25
|
+
GLOSS_MATCH = /((^(H|L|G|T):.*)\r?\n)*/
|
26
|
+
|
27
|
+
def parse_gloss
|
28
|
+
start_line_number = @src.current_line_number
|
29
|
+
data = @src.scan(self.class::GLOSS_MATCH)
|
30
|
+
@tree.children.concat(::Hierogloss::Gloss.new(data).to_kramdown)
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
define_parser(:gloss, GLOSS_START)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/src/Gardiner.ttf
ADDED
Binary file
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'minitest_helper'
|
3
|
+
|
4
|
+
class TestDictionary < Minitest::Test
|
5
|
+
def assert_hw(headword, word)
|
6
|
+
assert_equal(headword, Hierogloss::Dictionary.headword(word))
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_should_leave_simple_headwords_alone
|
10
|
+
assert_hw("xr", "xr")
|
11
|
+
assert_hw("Hr", "Hr")
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_should_strip_parens
|
15
|
+
assert_hw("ny", "n(y)")
|
16
|
+
assert_hw("mAa-Hrw", "mAa(-Hrw)")
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_should_strip_clitics
|
20
|
+
assert_hw("ir", "ir=n")
|
21
|
+
assert_hw("im", "im=s")
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_should_remove_dot_before_t
|
25
|
+
assert_hw("Sspt", "Ssp.t")
|
26
|
+
assert_hw("Hmt", "Hm.t")
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_should_strip_plurals
|
30
|
+
assert_hw("Sspt", "Ssp.wt")
|
31
|
+
assert_hw("hrw", "hrw.w")
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_should_strip_verb_endings
|
35
|
+
assert_hw("ir", "ir.n=f")
|
36
|
+
assert_hw("Dd", "Dd.n")
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_should_provide_gardiner_signs_for_most_signs
|
40
|
+
assert_equal("A1", Hierogloss::Dictionary.gardiner("π"))
|
41
|
+
assert_equal("D4", Hierogloss::Dictionary.gardiner("πΉ"))
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_should_not_provide_gardiner_signs_for_uniliterals
|
45
|
+
# Let's not link these common characters.
|
46
|
+
"πΏππππ
±π²ππͺππ
ππππππ‘ππ΄πππ‘πΌππΏπ§π".each_char do |c|
|
47
|
+
assert_nil(Hierogloss::Dictionary.gardiner(c), "should not translate #{c}")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
data/test/test_gloss.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'minitest_helper'
|
3
|
+
|
4
|
+
class TestGloss < MiniTest::Test
|
5
|
+
include Hierogloss
|
6
|
+
|
7
|
+
def setup
|
8
|
+
input = <<EOD
|
9
|
+
H: πππ€ | πππ
|
10
|
+
L: s | s.t
|
11
|
+
G: homme | femme
|
12
|
+
T: l'homme et la femme
|
13
|
+
EOD
|
14
|
+
@gloss = Hierogloss::Gloss.new(input)
|
15
|
+
end
|
16
|
+
|
17
|
+
def assert_row(type, raw_cells, row)
|
18
|
+
assert_instance_of(type, row)
|
19
|
+
refute(row.span?)
|
20
|
+
assert_equal(raw_cells, row.raw_cells)
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_should_parse_gloss_into_appropriate_rows
|
24
|
+
assert_equal(4, @gloss.rows.length)
|
25
|
+
assert_row(HieroglyphRow, ["πππ€", "πππ"], @gloss.rows[0])
|
26
|
+
assert_row(TransliterationRow, ["s", "s.t"], @gloss.rows[1])
|
27
|
+
assert_row(Row, ["homme", "femme"], @gloss.rows[2])
|
28
|
+
|
29
|
+
# Translations don't have cells.
|
30
|
+
assert_instance_of(TranslationRow, @gloss.rows[3])
|
31
|
+
assert(@gloss.rows[3].span?)
|
32
|
+
assert_equal("l'homme et la femme", @gloss.rows[3].text)
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_should_be_convertible_to_a_list_of_kramdown_elements
|
36
|
+
# We don't actually care what's in there; we just want to render
|
37
|
+
# something plausible.
|
38
|
+
kramdown = @gloss.to_kramdown
|
39
|
+
assert_instance_of(Array, kramdown)
|
40
|
+
assert(kramdown.length > 0)
|
41
|
+
kramdown.each {|k| assert_instance_of(Kramdown::Element, k) }
|
42
|
+
end
|
43
|
+
end
|