rsyntaxtree 0.9.3 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.tags +179 -0
  4. data/Gemfile +2 -0
  5. data/README.md +35 -29
  6. data/Rakefile +7 -0
  7. data/bin/rsyntaxtree +42 -31
  8. data/fonts/OpenMoji-Black.ttf +0 -0
  9. data/fonts/OpenMoji-Color.ttf +0 -0
  10. data/img/elements/circle.png +0 -0
  11. data/img/elements/circle_abc.png +0 -0
  12. data/img/elements/circle_bold.png +0 -0
  13. data/img/elements/circle_hatched.png +0 -0
  14. data/img/elements/circle_one.png +0 -0
  15. data/img/elements/connector.png +0 -0
  16. data/img/elements/connector_bold.png +0 -0
  17. data/img/elements/square.png +0 -0
  18. data/img/elements/square_abc.png +0 -0
  19. data/img/elements/square_bold.png +0 -0
  20. data/img/elements/square_hatched.png +0 -0
  21. data/img/elements/square_one.png +0 -0
  22. data/img/rsyntaxtree.png +0 -0
  23. data/img/sample.png +0 -0
  24. data/img/sample.svg +38 -0
  25. data/lib/rsyntaxtree/base_graph.rb +262 -0
  26. data/lib/rsyntaxtree/element.rb +156 -25
  27. data/lib/rsyntaxtree/elementlist.rb +16 -13
  28. data/lib/rsyntaxtree/markup_parser.rb +208 -0
  29. data/lib/rsyntaxtree/string_parser.rb +187 -204
  30. data/lib/rsyntaxtree/svg_graph.rb +471 -298
  31. data/lib/rsyntaxtree/utils.rb +49 -6
  32. data/lib/rsyntaxtree/version.rb +1 -1
  33. data/lib/rsyntaxtree.rb +144 -161
  34. data/rsyntaxtree.gemspec +2 -0
  35. data/test/markup_parser_test.rb +207 -0
  36. metadata +52 -11
  37. data/fonts/latinmodern-math.otf +0 -0
  38. data/fonts/lmroman10-bold.otf +0 -0
  39. data/fonts/lmroman10-bolditalic.otf +0 -0
  40. data/fonts/lmroman10-italic.otf +0 -0
  41. data/fonts/lmroman10-regular.otf +0 -0
  42. data/lib/rsyntaxtree/error_message.rb +0 -68
  43. data/lib/rsyntaxtree/graph.rb +0 -312
  44. data/lib/rsyntaxtree/tree_graph.rb +0 -327
@@ -6,15 +6,58 @@
6
6
  #==========================
7
7
  #
8
8
  # Image utility functions to inspect text font metrics
9
- #
10
- # This file is part of RSyntaxTree, which is a ruby port of Andre Eisenbach's
11
- # excellent program phpSyntaxTree.
12
- #
13
9
  # Copyright (c) 2007-2021 Yoichiro Hasebe <yohasebe@gmail.com>
14
- # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
10
+
11
+ require 'rmagick'
12
+ include Magick
15
13
 
16
14
  class String
17
15
  def contains_cjk?
18
- !!(self =~ /\p{Han}|\p{Katakana}|\p{Hiragana}|\p{Hangul}/)
16
+ !!(self.gsub(WHITESPACE_BLOCK, "") =~ /\p{Han}|\p{Katakana}|\p{Hiragana}|\p{Hangul}|[^\x01-\x7E]/)
17
+ end
18
+
19
+ def contains_emoji?
20
+ !!(self.gsub(WHITESPACE_BLOCK, "").gsub(/\d/, "") =~ /\p{Emoji}/)
21
+ end
22
+
23
+ def all_emoji?
24
+ !!(self.gsub(WHITESPACE_BLOCK, "").gsub(/\d/, "") =~ /\A\p{Emoji}[\p{Emoji}\s]*\z/)
19
25
  end
26
+
27
+ def split_by_emoji
28
+ results = []
29
+ self.split(//).each do |ch|
30
+ case ch
31
+ when /\d/, WHITESPACE_BLOCK
32
+ results << {:type => :normal, :char => ch}
33
+ when /\p{Emoji}/
34
+ results << {:type => :emoji, :char => ch}
35
+ else
36
+ results << {:type => :normal, :char => ch}
37
+ end
38
+ end
39
+ results.reject{|string| string == ""}
40
+ end
41
+ end
42
+
43
+ module FontMetrics
44
+ def get_metrics(text, font, fontsize, font_style, font_weight)
45
+ background = Image.new(1, 1)
46
+ gc = Draw.new
47
+ gc.annotate(background, 0, 0, 0, 0, text) do |gc|
48
+ gc.font = font
49
+ gc.font_style = font_style == :italic ? ItalicStyle : NormalStyle
50
+ gc.font_weight = font_weight == :bold ? BoldWeight : NormalWeight
51
+ gc.pointsize = fontsize
52
+ gc.gravity = CenterGravity
53
+ gc.stroke = 'none'
54
+ gc.kerning = 0
55
+ gc.interline_spacing = 0
56
+ gc.interword_spacing = 0
57
+ end
58
+ metrics = gc.get_multiline_type_metrics(background, text)
59
+ return metrics
60
+ end
61
+ module_function :get_metrics
20
62
  end
63
+
@@ -1,4 +1,4 @@
1
1
  module RSyntaxTree
2
- VERSION = "0.9.3"
2
+ VERSION = "1.0.6"
3
3
  end
4
4
 
data/lib/rsyntaxtree.rb CHANGED
@@ -7,186 +7,169 @@
7
7
  #
8
8
  # Facade of rsyntaxtree library. When loaded by a driver script, it does all
9
9
  # the necessary 'require' to use the library.
10
- #
11
- # This file is part of RSyntaxTree, which is originally a ruby port of Andre Eisenbach's
12
- # excellent program phpSyntaxTree.
13
10
  # Copyright (c) 2007-2021 Yoichiro Hasebe <yohasebe@gmail.com>
14
- # Copyright (c) 2003-2004 Andre Eisenbach <andre@ironcreek.net>
15
11
 
16
12
  $LOAD_PATH << File.join( File.dirname(__FILE__), 'rsyntaxtree')
17
13
 
14
+ FONT_DIR = File.expand_path(File.dirname(__FILE__) + "/../fonts")
15
+ ETYPE_NODE = 1
16
+ ETYPE_LEAF = 2
17
+ SUBSCRIPT_CONST = 0.7
18
+ FONT_SCALING = 2
19
+ WHITESPACE_BLOCK = "■"
20
+
21
+ class RSTError < StandardError
22
+ def initialize(msg="Error: something unexpected occurred")
23
+ msg.gsub!(WHITESPACE_BLOCK, "<>")
24
+ super msg
25
+ end
26
+ end
27
+
18
28
  require 'cgi'
29
+ require 'rsvg2'
19
30
  require 'utils'
20
31
  require 'element'
21
32
  require 'elementlist'
22
33
  require 'string_parser'
23
- require 'tree_graph'
24
34
  require 'svg_graph'
25
- require 'error_message'
26
35
  require 'version'
36
+ require 'rmagick'
37
+
38
+ module RSyntaxTree
39
+
40
+ class RSGenerator
41
+
42
+ def initialize(params = {})
43
+ new_params = {}
44
+ fontset = {}
45
+ params.each do |keystr, value|
46
+ key = keystr.to_sym
47
+ case key
48
+ when :data
49
+ data = value
50
+ data = data.gsub('-AMP-', '&')
51
+ .gsub('-PERCENT-', "%")
52
+ .gsub('-PRIME-', "'")
53
+ .gsub('-SCOLON-', ';')
54
+ .gsub('-OABRACKET-', '<')
55
+ .gsub('-CABRACKET-', '>')
56
+ new_params[key] = data
57
+
58
+ when :symmetrize, :color, :transparent, :polyline
59
+ new_params[key] = value && (value != "off" && value != "false") ? true : false
60
+ when :fontsize
61
+ new_params[key] = value.to_i * FONT_SCALING
62
+ when :margin
63
+ new_params[key] = value.to_i * FONT_SCALING * 5
64
+ when :vheight
65
+ new_params[key] = value.to_f
66
+ when :fontstyle
67
+ if value == "noto-sans" || value == "sans"
68
+ fontset[:normal] = FONT_DIR + "/NotoSans-Regular.ttf"
69
+ fontset[:italic] = FONT_DIR + "/NotoSans-Italic.ttf"
70
+ fontset[:bold] = FONT_DIR + "/NotoSans-Bold.ttf"
71
+ fontset[:bolditalic] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
72
+ fontset[:math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
73
+ fontset[:cjk] = FONT_DIR + "/NotoSansJP-Regular.otf"
74
+ fontset[:emoji] = FONT_DIR + "/OpenMoji-Black.ttf"
75
+ new_params[:fontstyle] = "sans"
76
+ elsif value == "noto-serif" || value == "serif"
77
+ fontset[:normal] = FONT_DIR + "/NotoSerif-Regular.ttf"
78
+ fontset[:italic] = FONT_DIR + "/NotoSerif-Italic.ttf"
79
+ fontset[:bold] = FONT_DIR + "/NotoSerif-Bold.ttf"
80
+ fontset[:bolditalic] = FONT_DIR + "/NotoSerif-BoldItalic.ttf"
81
+ fontset[:math] = FONT_DIR + "/latinmodern-math.otf"
82
+ fontset[:cjk] = FONT_DIR + "/NotoSerifJP-Regular.otf"
83
+ fontset[:emoji] = FONT_DIR + "/OpenMoji-Black.ttf"
84
+ new_params[:fontstyle] = "serif"
85
+ elsif value == "cjk zenhei" || value == "cjk"
86
+ fontset[:normal] = FONT_DIR + "/wqy-zenhei.ttf"
87
+ fontset[:italic] = FONT_DIR + "/NotoSans-Italic.ttf"
88
+ fontset[:bold] = FONT_DIR + "/NotoSans-Bold.ttf"
89
+ fontset[:bolditalic] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
90
+ fontset[:math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
91
+ fontset[:cjk] = FONT_DIR + "/wqy-zenhei.ttf"
92
+ fontset[:emoji] = FONT_DIR + "/OpenMoji-Black.ttf"
93
+ new_params[:fontstyle] = "cjk"
94
+ end
95
+ else
96
+ new_params[key] = value
97
+ end
98
+ end
27
99
 
28
- FONT_DIR = File.expand_path(File.dirname(__FILE__) + "/../fonts")
100
+ # defaults to the following
101
+ @params = {
102
+ :symmetrize => true,
103
+ :color => true,
104
+ :transparent => false,
105
+ :fontsize => 16,
106
+ :format => "png",
107
+ :leafstyle => "auto",
108
+ :filename => "syntree",
109
+ :data => "",
110
+ :margin => 0,
111
+ :vheight => 1.0,
112
+ :polyline => false,
113
+ }
114
+
115
+ @params.merge! new_params
116
+ @params[:fontsize] = @params[:fontsize] * FONT_SCALING
117
+ @params[:margin] = @params[:margin] * FONT_SCALING
118
+ @params[:fontset] = fontset
119
+
120
+ $single_X_metrics = FontMetrics.get_metrics("X", fontset[:normal], @params[:fontsize], :normal, :normal)
121
+ $height_connector_to_text = $single_X_metrics.height / 2.0
122
+ $single_line_height = $single_X_metrics.height * 2.0
123
+ $width_half_X = $single_X_metrics.width / 2.0
124
+ $height_connector = $single_X_metrics.height * 1.0 * @params[:vheight]
125
+ $h_gap_between_nodes = $single_X_metrics.width * 0.8
126
+ $box_vertical_margin = $single_X_metrics.height * 0.8
127
+ end
29
128
 
30
- ETYPE_UNDEFINED = 0
31
- ETYPE_NODE = 1
32
- ETYPE_LEAF = 2
33
- SUBSCRIPT_CONST = 0.7
34
- FONT_SCALING = 2
129
+ def self.check_data(text)
130
+ if text.to_s == ""
131
+ raise RSTError, "Error: input text is empty"
132
+ end
133
+ StringParser.valid?(text)
134
+ end
35
135
 
36
- class RSGenerator
37
- def initialize(params = {})
38
- new_params = {}
39
- params.each do |keystr, value|
40
- key = keystr.to_sym
41
- case key
42
- when :data
43
- data = value
44
- # data = CGI.unescape(value)
45
- data = data.gsub('-AMP-', '&')
46
- .gsub('-PERCENT-', "%")
47
- .gsub('-PRIME-', "'")
48
- .gsub('-SCOLON-', ';')
49
- .gsub('-OABRACKET-', '<')
50
- .gsub('-CABRACKET-', '>')
51
- new_params[key] = data
52
- new_params[:multibyte] = data.contains_cjk?
53
- when :symmetrize, :color, :autosub, :transparent
54
- new_params[key] = value && value != "off" ? true : false
55
- when :fontsize
56
- new_params[key] = value.to_i * FONT_SCALING
57
- when :margin
58
- new_params[key] = value.to_i * FONT_SCALING * 4
59
- when :vheight
60
- new_params[key] = value.to_f
61
- when :fontstyle
62
- if value == "noto-sans" || value == "sans"
63
- new_params[:font] = FONT_DIR + "/NotoSans-Regular.ttf"
64
- new_params[:font_it] = FONT_DIR + "/NotoSans-Italic.ttf"
65
- new_params[:font_bd] = FONT_DIR + "/NotoSans-Bold.ttf"
66
- new_params[:font_bdit] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
67
- new_params[:font_math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
68
- new_params[:font_cjk] = FONT_DIR + "/NotoSansJP-Regular.otf"
69
- new_params[:fontstyle] = "sans"
70
- elsif value == "noto-serif" || value == "serif"
71
- new_params[:font] = FONT_DIR + "/NotoSerif-Regular.ttf"
72
- new_params[:font_it] = FONT_DIR + "/NotoSerif-Italic.ttf"
73
- new_params[:font_bd] = FONT_DIR + "/NotoSerif-Bold.ttf"
74
- new_params[:font_bdit] = FONT_DIR + "/NotoSerif-BoldItalic.ttf"
75
- new_params[:font_math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
76
- new_params[:font_cjk] = FONT_DIR + "/NotoSerifJP-Regular.otf"
77
- new_params[:fontstyle] = "serif"
78
- elsif value == "cjk zenhei" || value == "cjk"
79
- new_params[:font] = FONT_DIR + "/wqy-zenhei.ttf"
80
- new_params[:font_it] = FONT_DIR + "/NotoSans-Italic.ttf"
81
- new_params[:font_bd] = FONT_DIR + "/NotoSans-Bold.ttf"
82
- new_params[:font_bdit] = FONT_DIR + "/NotoSans-BoldItalic.ttf"
83
- new_params[:font_math] = FONT_DIR + "/NotoSansMath-Regular.ttf"
84
- new_params[:font_cjk] = FONT_DIR + "/wqy-zenhei.ttf"
85
- new_params[:fontstyle] = "sans"
86
- elsif value == "modern-math" || value == "math"
87
- new_params[:font] = FONT_DIR + "/latinmodern-math.otf"
88
- new_params[:font_it] = FONT_DIR + "/lmroman10-italic.otf"
89
- new_params[:font_bd] = FONT_DIR + "/lmroman10-bold.otf"
90
- new_params[:font_bdit] = FONT_DIR + "/lmroman10-bolditalic.otf"
91
- new_params[:font_math] = FONT_DIR + "/latinmodern-math.otf"
92
- new_params[:font_cjk] = FONT_DIR + "/NotoSerifJP-Regular.otf"
93
- new_params[:fontstyle] = "math"
94
- end
136
+ def draw_png(binary = false)
137
+ svg = draw_svg
138
+ rsvg = RSVG::Handle.new_from_data(svg)
139
+ dim = rsvg.dimensions
140
+ surface = Cairo::ImageSurface.new(Cairo::FORMAT_ARGB32, dim.width, dim.height)
141
+ context = Cairo::Context.new(surface)
142
+ context.render_rsvg_handle(rsvg)
143
+ b = StringIO.new
144
+ surface.write_to_png(b)
145
+ if binary
146
+ return b
95
147
  else
96
- new_params[key] = value
148
+ return b.string
97
149
  end
98
150
  end
99
151
 
100
- # defaults to the following
101
- @params = {
102
- :symmetrize => true,
103
- :color => true,
104
- :autosub => false,
105
- :transparent => false,
106
- :fontsize => 18,
107
- :format => "png",
108
- :leafstyle => "auto",
109
- :filename => "syntree",
110
- :data => "",
111
- :margin => 0,
112
- :vheight => 1.0,
113
- :fontstyle => "sans",
114
- :font => "/NotoSansJP-Regular.otf",
115
- :font_it => "/NotoSans-Italic.ttf",
116
- :font_bd => "/NotoSans-Bold.ttf",
117
- :font_bdit => "/NotoSans-BoldItalic.ttf",
118
- :font_math => "/NotoSansMath-Regular.ttf"
119
- }
120
- @metrics = {
121
- :e_width => 0,
122
- :e_padd => 14,
123
- :v_space => 20,
124
- :h_space => 0,
125
- :b_side => 0,
126
- :b_topbot => 0
127
- # :e_width => 120,
128
- # :e_padd => 14,
129
- # :v_space => 20,
130
- # :h_space => 20,
131
- # :b_side => 10,
132
- # :b_topbot => 10
133
- }
134
-
135
- @params.merge! new_params
136
- @params[:fontsize] = @params[:fontsize] * FONT_SCALING
137
- @params[:margin] = @params[:margin] * FONT_SCALING
138
- @metrics[:v_space] = @metrics[:v_space] * @params[:vheight]
139
- end
140
-
141
- def self.check_data(text)
142
- sp = StringParser.new(text)
143
- sp.valid?
144
- end
145
-
146
- def draw_png
147
- @params[:format] = "png"
148
- draw_tree
149
- end
150
-
151
- def draw_jpg
152
- @params[:format] = "jpg"
153
- draw_tree
154
- end
155
-
156
- def draw_gif
157
- @params[:format] = "gif"
158
- draw_tree
159
- end
160
-
161
- def draw_pdf
162
- @params[:format] = "pdf"
163
- draw_tree
164
- end
165
-
166
- def draw_svg
167
- @params[:format] = "svg"
168
- sp = StringParser.new(@params[:data].gsub('&', '&amp;').gsub('%', '&#37;'))
169
- sp.parse
170
- sp.auto_subscript if @params[:autosub]
171
- elist = sp.get_elementlist
172
- graph = SVGGraph.new(elist, @metrics,
173
- @params[:symmetrize], @params[:color], @params[:leafstyle], @params[:multibyte],
174
- @params[:fontstyle], @params[:font], @params[:font_cjk], @params[:fontsize], @params[:margin], @params[:transparent]
175
- )
176
- graph.svg_data
177
- end
152
+ def draw_svg
153
+ sp = StringParser.new(@params[:data].gsub('&', '&amp;'), @params[:fontset], @params[:fontsize])
154
+ sp.parse
155
+ graph = SVGGraph.new(sp.get_elementlist, @params)
156
+ graph.svg_data
157
+ end
178
158
 
179
- def draw_tree
180
- sp = StringParser.new(@params[:data])
181
- sp.parse
182
- sp.auto_subscript if @params[:autosub]
183
- elist = sp.get_elementlist
184
- graph = TreeGraph.new(elist, @metrics,
185
- @params[:symmetrize], @params[:color], @params[:leafstyle], @params[:multibyte],
186
- @params[:fontstyle], @params[:font], @params[:font_it], @params[:font_bd], @params[:font_bdit], @params[:font_math],
187
- @params[:font_cjk], @params[:fontsize], @params[:margin], @params[:transparent]
188
- )
189
- graph.to_blob(@params[:format])
159
+ def draw_tree
160
+ svg = draw_svg
161
+ image, data = Magick::Image.from_blob(svg) do |im|
162
+ im.format = 'svg'
163
+ end
164
+ if @params[:format] == "png"
165
+ image = draw_png(false)
166
+ else
167
+ image.to_blob do |im|
168
+ im.format = @params[:format].upcase
169
+ end
170
+ end
171
+ image
172
+ end
190
173
  end
191
174
  end
192
175
 
data/rsyntaxtree.gemspec CHANGED
@@ -19,4 +19,6 @@ Gem::Specification.new do |s|
19
19
  # specify any dependencies here; for example:
20
20
  s.add_runtime_dependency 'rmagick', '~> 4.2', '>= 4.2.3'
21
21
  s.add_runtime_dependency 'optimist', '~> 3.0', '>= 3.0.1'
22
+ s.add_runtime_dependency 'parslet'
23
+ s.add_runtime_dependency 'rsvg2'
22
24
  end
@@ -0,0 +1,207 @@
1
+ require "minitest/autorun"
2
+ require "minitest/pride"
3
+ require "./lib/rsyntaxtree/markup_parser.rb"
4
+
5
+ class MarkupParserTest < Minitest::Test
6
+ def setup
7
+ @parser = MarkupParser.new
8
+ end
9
+
10
+ def test_rule_cr
11
+ @parser.cr.parse('\\n')
12
+ # "\\n"@0
13
+ end
14
+
15
+ def test_rule_brackets
16
+ @parser.brackets.parse('#')
17
+ # "#"@0
18
+ end
19
+
20
+ def test_rule_triangle
21
+ @parser.triangle.parse('^')
22
+ # "^"@0
23
+ end
24
+
25
+ def test_rule_path
26
+ @parser.path.parse('+12')
27
+ # {:path=>"+12"@0}
28
+ @parser.path.parse('+>34')
29
+ # {:path=>"+>34"@0}d
30
+ end
31
+
32
+ def test_rule_escaped
33
+ "<>^+*_=~|-".split(//).each do |chr|
34
+ @parser.escaped.parse('\\' + chr)
35
+ # {:chr=>"<"@1}
36
+ # {:chr=>">"@1}
37
+ # {:chr=>"^"@1}
38
+ # {:chr=>"+"@1}
39
+ # {:chr=>"*"@1}
40
+ # {:chr=>"_"@1}
41
+ # {:chr=>"="@1}
42
+ # {:chr=>"~"@1}
43
+ # {:chr=>"|"@1}
44
+ # {:chr=>"-"@1}
45
+ end
46
+ end
47
+
48
+ def test_rule_non_escaped
49
+ "abcde12345".split(//).each do |chr|
50
+ @parser.non_escaped.parse(chr)
51
+ # {:chr=>"a"@0}
52
+ # {:chr=>"b"@0}
53
+ # {:chr=>"c"@0}
54
+ # {:chr=>"d"@0}
55
+ # {:chr=>"e"@0}
56
+ # {:chr=>"1"@0}
57
+ # {:chr=>"2"@0}
58
+ # {:chr=>"3"@0}
59
+ # {:chr=>"4"@0}
60
+ # {:chr=>"5"@0}
61
+ end
62
+ end
63
+
64
+ def test_rule_text
65
+ text = "abcde\\<\\>\\^\\+\\*\\_\\=\\~\\|\\-12345"
66
+ @parser.text.parse(text)
67
+ # {:text=>
68
+ # [{:chr=>"a"@0},
69
+ # {:chr=>"b"@1},
70
+ # {:chr=>"c"@2},
71
+ # {:chr=>"d"@3},
72
+ # {:chr=>"e"@4},
73
+ # {:chr=>"<"@6},
74
+ # {:chr=>">"@8},
75
+ # {:chr=>"^"@10},
76
+ # {:chr=>"+"@12},
77
+ # {:chr=>"*"@14},
78
+ # {:chr=>"_"@16},
79
+ # {:chr=>"="@18},
80
+ # {:chr=>"~"@20},
81
+ # {:chr=>"|"@22},
82
+ # {:chr=>"-"@24},
83
+ # {:chr=>"1"@25},
84
+ # {:chr=>"2"@26},
85
+ # {:chr=>"3"@27},
86
+ # {:chr=>"4"@28},
87
+ # {:chr=>"5"@29}]}
88
+ end
89
+
90
+ def test_rule_bolditalic
91
+ text = "***~=X\\+Y\\*Z=~***"
92
+ @parser.bolditalic.parse(text)
93
+ # {:bolditalic=>{:linethrough=>{:overline=>{:text=>[{:chr=>"X"@5}, {:chr=>"+"@7}, {:chr=>"Y"@8}, {:chr=>"*"@10}, {:chr=>"Z"@11}]}}}}
94
+ end
95
+
96
+ def test_rule_bold
97
+ text = "**~=X\\+Y\\*Z=~**"
98
+ @parser.bold.parse(text)
99
+ # {:bold=>{:linethrough=>{:overline=>{:text=>[{:chr=>"X"@4}, {:chr=>"+"@6}, {:chr=>"Y"@7}, {:chr=>"*"@9}, {:chr=>"Z"@10}]}}}}
100
+ end
101
+
102
+ def test_rule_italic
103
+ text = "*~=X\\+Y\\*Z=~*"
104
+ @parser.italic.parse(text)
105
+ # {:italic=>{:linethrough=>{:overline=>{:text=>[{:chr=>"X"@3}, {:chr=>"+"@5}, {:chr=>"Y"@6}, {:chr=>"*"@8}, {:chr=>"Z"@9}]}}}}
106
+ end
107
+
108
+ def test_rule_overline
109
+ text = "=*~X\\+Y\\*Z~*="
110
+ @parser.overline.parse(text)
111
+ # {:overline=> ...}
112
+ end
113
+
114
+ def test_rule_underline
115
+ text = "-*~X\\+Y\\*Z~*-"
116
+ @parser.underline.parse(text)
117
+ # {:underline=> ...}
118
+ end
119
+
120
+ def test_rule_linethrough
121
+ text = "~*=X\\+Y\\*Z=*~"
122
+ @parser.linethrough.parse(text)
123
+ # {:linethrough=> ...}
124
+ end
125
+
126
+ def test_rule_superscript
127
+ text = "__~*=X\\+Y\\*Z=*~__"
128
+ @parser.superscript.parse(text)
129
+ # {:superscript=> ...}
130
+ end
131
+
132
+ def test_rule_subscript
133
+ text = "_~*=X\\+Y\\*Z=*~_"
134
+ @parser.subscript.parse(text)
135
+ # {:subscript=> ...}
136
+ end
137
+
138
+ def test_rule_box
139
+ text = "|~*=X\\+Y\\*Z=*~|"
140
+ @parser.box.parse(text)
141
+ # {:box=> ...}
142
+ end
143
+
144
+ def test_rule_markup
145
+ text = "|~*=X\\+Y\\*Z=*~|"
146
+ @parser.markup.parse(text)
147
+ # {:box=>{:linethrough=>{:italic=>{:overline=>{:text=>[
148
+ # {:chr=>"X"@4}, {:chr=>"+"@6}, {:chr=>"Y"@7}, {:chr=>"*"@9}, {:chr=>"Z"@10}]}}}}
149
+ end
150
+
151
+ def test_rule_border
152
+ text = "----"
153
+ @parser.border.parse(text)
154
+ # {:border=>"----"@0}
155
+ end
156
+
157
+ def test_rule_line
158
+ text1 = "----"
159
+ @parser.line.parse(text1)
160
+ # {:border=>"----"@0}
161
+ text2 = "-u-|b|n"
162
+ @parser.line.parse(text2)
163
+ # {:line=>[{:underline=>{:text=>[{:chr=>"u"@1}]}}, {:box=>{:text=>[{:chr=>"b"@4}]}}, {:text=>[{:chr=>"n"@6}]}]}
164
+ text3 = "\\n"
165
+ @parser.line.parse(text3)
166
+ # "\\n"@0
167
+ end
168
+
169
+ def test_rule_lines
170
+ text1 = "^#X_Y_Z"
171
+ @parser.lines.parse(text1)
172
+ # [{:triangle=>"^"@0, :brackets=>"#"@1}, {:line=>[{:text=>[{:chr=>"X"@2}]},
173
+ # {:subscript=>{:text=>[{:chr=>"Y"@4}]}}, {:text=>[{:chr=>"Z"@6}]}]}, {:paths=>[]}]
174
+ text2 = "^X_Y_Z+1+>2"
175
+ @parser.lines.parse(text2)
176
+ # [{:triangle=>"^"@0, :enclosure=>:none},
177
+ # {:line=>[{:text=>[{:chr=>"X"@2}]}, {:subscript=>{:text=>[{:chr=>"Y"@4}]}}, {:text=>[{:chr=>"Z"@6}]}]},
178
+ # {:paths=>[{:path=>"+1"@7}, {:path=>"+>2"@9}]}]
179
+ text3 = "^----\\n\\nX_Y_Z+1+>2"
180
+ @parser.lines.parse(text3)
181
+ # [{:triangle=>"^"@0, :enclosure=>:none},
182
+ # {:border=>"----"@1},
183
+ # {:blankline=>"\\n\\n"@5},
184
+ # {:line=>[{:text=>[{:chr=>"X"@9}]}, {:subscript=>{:text=>[{:chr=>"Y"@11}]}},
185
+ # {:text=>[{:chr=>"Z"@13}]}]},
186
+ # {:paths=>[{:path=>"+1"@14}, {:path=>"+>2"@16}]}]
187
+ end
188
+
189
+ def test_evaluator
190
+ text1 = "^#----\\n\\nX_Y_Z+1+>2"
191
+ Markup.parse text1
192
+ # {:status=>:success,
193
+ # :results=>
194
+ # {:enclosure=>:brackets,
195
+ # :triangle=>true,
196
+ # :paths=>[{:path=>"+1"@15}, {:path=>"+>2"@17}],
197
+ # :contents=>
198
+ # [{:type=>:border},
199
+ # {:type=>:blankline},
200
+ # {:type=>:text, :elements=>[{:text=>"X", :decoration=>[]},
201
+ # {:text=>"Y", :decoration=>[:subscript]}, {:text=>"Z", :decoration=>[]}]}]}}
202
+ text2 = "!^#----\\n\\nX_Y_Z+1+>2"
203
+ Markup.parse text2
204
+ # {:status=>:error, :text=>"!^#----\\n\\nX_Y_Z+1+>2"}
205
+ end
206
+
207
+ end