latexmath 0.1.4 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +50 -0
- data/LICENSE.md +33 -0
- data/README.adoc +133 -14
- data/artifacts/latex.bindings.python.json +1 -0
- data/artifacts/latex.bindings.rs.json +1 -0
- data/artifacts/latex.bindings.ruby.json +1 -0
- data/artifacts/latex.bindings.ts.json +1 -0
- data/artifacts/latex.checksums.txt +19 -0
- data/artifacts/latex.json +1 -0
- data/data/latex-symbols.json +3301 -0
- data/docs/model-contract.adoc +125 -0
- data/docs/parser-contract.adoc +349 -0
- data/docs/testsuite-pin.adoc +76 -0
- data/grammars/latex.wrappers.json +17 -0
- data/lib/latexmath/model/nodes.rb +918 -0
- data/lib/latexmath/model.rb +44 -0
- data/lib/latexmath/parser/artifact.rb +80 -0
- data/lib/latexmath/parser/configuration.rb +35 -0
- data/lib/latexmath/parser/engine.rb +161 -0
- data/lib/latexmath/parser/errors.rb +51 -0
- data/lib/latexmath/parser/native_recursion_guard.rb +43 -0
- data/lib/latexmath/parser/preprocessor.rb +77 -0
- data/lib/latexmath/parser/raw_parser.rb +84 -0
- data/lib/latexmath/parser/source_map.rb +121 -0
- data/lib/latexmath/parser/unwrap.rb +91 -0
- data/lib/latexmath/parser.rb +97 -0
- data/lib/latexmath/symbols.rb +74 -0
- data/lib/latexmath/version.rb +1 -1
- data/lib/latexmath.rb +5 -87
- data/model/latex.lml.checksums.txt +36 -0
- data/model/latex.lml.json +3350 -0
- metadata +60 -71
- data/.editorconfig +0 -14
- data/.github/workflows/test.yml +0 -80
- data/.gitignore +0 -22
- data/.rspec +0 -3
- data/.rubocop.yml +0 -129
- data/CODE_OF_CONDUCT.md +0 -74
- data/Gemfile +0 -10
- data/Gemfile.lock +0 -79
- data/Rakefile +0 -95
- data/bin/console +0 -14
- data/bin/setup +0 -8
- data/exe/latexmath +0 -8
- data/latexmath.gemspec +0 -34
- data/lib/latexmath/aggregator.rb +0 -351
- data/lib/latexmath/converter.rb +0 -424
- data/lib/latexmath/equation.rb +0 -13
- data/lib/latexmath/ext.rb +0 -9
- data/lib/latexmath/symbol.rb +0 -21
- data/lib/latexmath/tokenizer.rb +0 -82
- data/lib/latexmath/xml/builder.rb +0 -4
- data/lib/latexmath/xml/element.rb +0 -25
- data/lib/unimathsymbols.js.erb +0 -4
- data/lib/unimathsymbols.txt +0 -2864
- data/opal/latexmath-opal.rb +0 -40
- data/opal/ox.rb +0 -64
- data/opal/pseudoenumerator.rb +0 -19
data/Rakefile
DELETED
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
require 'bundler/setup'
|
|
2
|
-
require 'bundler/gem_tasks'
|
|
3
|
-
require 'rspec/core/rake_task'
|
|
4
|
-
RSpec::Core::RakeTask.new(:spec)
|
|
5
|
-
|
|
6
|
-
task 'build-opal' do
|
|
7
|
-
require 'opal'
|
|
8
|
-
require 'erb'
|
|
9
|
-
require 'json'
|
|
10
|
-
|
|
11
|
-
b = Opal::Builder.new
|
|
12
|
-
|
|
13
|
-
b.append_paths __dir__ + '/lib'
|
|
14
|
-
b.append_paths __dir__ + '/opal'
|
|
15
|
-
b.use_gem 'htmlentities'
|
|
16
|
-
|
|
17
|
-
build = b.build('latexmath-opal').to_s
|
|
18
|
-
# This is a hack intended for ExecJS to work properly.
|
|
19
|
-
build = build.gsub('Object freezing is not supported by Opal', '')
|
|
20
|
-
|
|
21
|
-
FileUtils.mkdir_p 'dist'
|
|
22
|
-
File.write('dist/latexmath.js', build)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
task 'spec-opal' => 'build-opal' do
|
|
26
|
-
ENV['TEST_OPAL'] = '1'
|
|
27
|
-
Rake::Task['spec'].execute
|
|
28
|
-
ENV.delete 'TEST_OPAL'
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def read_symbols(input)
|
|
32
|
-
@symbols = {}
|
|
33
|
-
File.readlines(input).each do |line|
|
|
34
|
-
next if line.start_with?('#')
|
|
35
|
-
|
|
36
|
-
columns = line.chop.split('^')
|
|
37
|
-
_unicode = columns[0]
|
|
38
|
-
latex = columns[2]
|
|
39
|
-
unicode_math = columns[3]
|
|
40
|
-
|
|
41
|
-
@symbols[latex] = _unicode if latex && !@symbols.include?(latex)
|
|
42
|
-
@symbols[unicode_math] = _unicode if unicode_math && !@symbols.include?(unicode_math)
|
|
43
|
-
|
|
44
|
-
matches = columns[-1].match(/=\s+(\\[^,^ ]+),?/)
|
|
45
|
-
if matches
|
|
46
|
-
@symbols[matches[1]] = _unicode unless @symbols[matches[1]]
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
@symbols
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
directory 'lib/latexmath/constants'
|
|
53
|
-
file 'lib/latexmath/constants/symbols.rb' => [
|
|
54
|
-
'lib/latexmath/constants',
|
|
55
|
-
'lib/unimathsymbols.txt'
|
|
56
|
-
] do |file|
|
|
57
|
-
symbols = read_symbols(file.prerequisites.last)
|
|
58
|
-
|
|
59
|
-
File.open(file.name, "w+") do |f|
|
|
60
|
-
f.puts <<~HERE
|
|
61
|
-
module Latexmath
|
|
62
|
-
module Constants
|
|
63
|
-
SYMBOLS = {
|
|
64
|
-
HERE
|
|
65
|
-
|
|
66
|
-
@symbols.each_pair do |k,v|
|
|
67
|
-
f.puts(" '#{k.to_s}' => '#{v}',")
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
f.puts <<~HERE
|
|
71
|
-
}.freeze
|
|
72
|
-
end
|
|
73
|
-
end
|
|
74
|
-
HERE
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
Rake::Task['build'].enhance ['lib/latexmath/constants/symbols.rb']
|
|
79
|
-
Rake::Task[:spec].enhance ['lib/latexmath/constants/symbols.rb']
|
|
80
|
-
|
|
81
|
-
desc 'Generate and write MathML fixtures'
|
|
82
|
-
task 'create-mathml-fixtures' do
|
|
83
|
-
require_relative 'lib/latexmath'
|
|
84
|
-
records = Dir.glob('spec/fixtures/*/*.tex')
|
|
85
|
-
|
|
86
|
-
records.each do |file|
|
|
87
|
-
tex = File.read("./#{file}")
|
|
88
|
-
tokens = Latexmath::Tokenizer.new(tex).tokenize
|
|
89
|
-
aggr = Latexmath::Aggregator.new(tokens).aggregate
|
|
90
|
-
|
|
91
|
-
File.write("./#{file.chomp('.tex')}.html", Latexmath::Converter.new(aggr).convert)
|
|
92
|
-
end
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
task default: [:spec, 'spec-opal']
|
data/bin/console
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
require 'bundler/setup'
|
|
4
|
-
require 'latexmath'
|
|
5
|
-
|
|
6
|
-
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
-
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
-
|
|
9
|
-
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
-
# require "pry"
|
|
11
|
-
# Pry.start
|
|
12
|
-
|
|
13
|
-
require 'irb'
|
|
14
|
-
IRB.start(__FILE__)
|
data/bin/setup
DELETED
data/exe/latexmath
DELETED
data/latexmath.gemspec
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
require_relative 'lib/latexmath/version'
|
|
2
|
-
|
|
3
|
-
Gem::Specification.new do |spec|
|
|
4
|
-
spec.name = 'latexmath'
|
|
5
|
-
spec.version = Latexmath::VERSION
|
|
6
|
-
spec.authors = ['Ribose Inc.']
|
|
7
|
-
spec.email = ['open.source@ribose.com']
|
|
8
|
-
|
|
9
|
-
spec.summary = 'Converts LaTeX math into MathML.'
|
|
10
|
-
spec.description = 'Converts LaTeX math into MathML.'
|
|
11
|
-
spec.homepage = 'https://github.com/plurimath/latexmath'
|
|
12
|
-
spec.license = 'BSD-2-Clause'
|
|
13
|
-
|
|
14
|
-
spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
|
|
15
|
-
|
|
16
|
-
spec.metadata['homepage_uri'] = spec.homepage
|
|
17
|
-
spec.metadata['source_code_uri'] = 'https://github.com/plurimath/latexmath'
|
|
18
|
-
spec.metadata['changelog_uri'] = 'https://github.com/plurimath/latexmath/blob/master/CHANGELOG.md.'
|
|
19
|
-
|
|
20
|
-
# Specify which files should be added to the gem when it is released.
|
|
21
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
22
|
-
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
23
|
-
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
24
|
-
end
|
|
25
|
-
spec.bindir = 'exe'
|
|
26
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
27
|
-
spec.require_paths = ['lib']
|
|
28
|
-
|
|
29
|
-
spec.add_dependency 'htmlentities', '~> 4.3'
|
|
30
|
-
spec.add_dependency 'ox', '~> 2.13'
|
|
31
|
-
spec.add_development_dependency 'equivalent-xml'
|
|
32
|
-
spec.add_development_dependency 'execjs'
|
|
33
|
-
spec.add_development_dependency 'opal'
|
|
34
|
-
end
|
data/lib/latexmath/aggregator.rb
DELETED
|
@@ -1,351 +0,0 @@
|
|
|
1
|
-
module Latexmath
|
|
2
|
-
class Aggregator
|
|
3
|
-
OPERATORS = '+-*/=[]_^{}()'.freeze
|
|
4
|
-
|
|
5
|
-
OPENING_BRACES = '{'.freeze
|
|
6
|
-
CLOSING_BRACES = '}'.freeze
|
|
7
|
-
OPENING_BRACKET = '['.freeze
|
|
8
|
-
CLOSING_BRACKET = ']'.freeze
|
|
9
|
-
OPENING_PARENTHESIS = '('.freeze
|
|
10
|
-
CLOSING_PARENTHESIS = ')'.freeze
|
|
11
|
-
|
|
12
|
-
BACKSLASH = '\\\\'.freeze
|
|
13
|
-
AMPERSAND = '&'.freeze
|
|
14
|
-
DASH = '-'.freeze
|
|
15
|
-
|
|
16
|
-
SUB_SUP = '_^'.freeze
|
|
17
|
-
SUBSCRIPT = '_'.freeze
|
|
18
|
-
SUPERSCRIPT = '^'.freeze
|
|
19
|
-
|
|
20
|
-
# Added prefix LATEX_ to avoid ruby reserved words
|
|
21
|
-
LATEX_LEFT = '\\left'.freeze
|
|
22
|
-
LATEX_RIGHT = '\\right'.freeze
|
|
23
|
-
LATEX_OVER = '\\over'.freeze
|
|
24
|
-
LATEX_HLINE = '\\hline'.freeze
|
|
25
|
-
LATEX_BEGIN = '\\begin'.freeze
|
|
26
|
-
LATEX_FRAC = '\\frac'.freeze
|
|
27
|
-
LATEX_ROOT = '\\root'.freeze
|
|
28
|
-
LATEX_SQRT = '\\sqrt'.freeze
|
|
29
|
-
|
|
30
|
-
def initialize(tokens)
|
|
31
|
-
@tokens = tokens
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def aggregate(tokens = @tokens)
|
|
35
|
-
aggregated = []
|
|
36
|
-
|
|
37
|
-
loop do
|
|
38
|
-
begin
|
|
39
|
-
token = next_item_or_group(tokens)
|
|
40
|
-
raise StopIteration if token.nil?
|
|
41
|
-
|
|
42
|
-
if token.is_a?(Array)
|
|
43
|
-
aggregated << token
|
|
44
|
-
elsif token == OPENING_BRACKET
|
|
45
|
-
previous = nil
|
|
46
|
-
previous = aggregated[-1] if aggregated.any?
|
|
47
|
-
begin
|
|
48
|
-
g = group(tokens, opening: OPENING_BRACKET, closing: CLOSING_BRACKET)
|
|
49
|
-
if previous == LATEX_SQRT
|
|
50
|
-
root = tokens.shift
|
|
51
|
-
raise StopIteration if root.nil?
|
|
52
|
-
|
|
53
|
-
if root == OPENING_BRACES
|
|
54
|
-
begin
|
|
55
|
-
root = group(tokens)
|
|
56
|
-
rescue EmptyGroupError
|
|
57
|
-
root = ''
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
aggregated[-1] = LATEX_ROOT
|
|
61
|
-
aggregated << root
|
|
62
|
-
end
|
|
63
|
-
aggregated << g
|
|
64
|
-
rescue EmptyGroupError
|
|
65
|
-
next if previous == LATEX_SQRT
|
|
66
|
-
|
|
67
|
-
aggregated += [OPENING_BRACKET, CLOSING_BRACKET]
|
|
68
|
-
end
|
|
69
|
-
elsif LIMITS.include?(token)
|
|
70
|
-
raise StopIteration if tokens.shift.nil?
|
|
71
|
-
|
|
72
|
-
a = next_item_or_group(tokens)
|
|
73
|
-
aggregated += [token, a]
|
|
74
|
-
elsif token == '\\limits'
|
|
75
|
-
previous = aggregated.pop
|
|
76
|
-
raise StopIteration if tokens.shift.nil?
|
|
77
|
-
|
|
78
|
-
a = next_item_or_group(tokens)
|
|
79
|
-
raise StopIteration if tokens.shift.nil?
|
|
80
|
-
|
|
81
|
-
b = next_item_or_group(tokens)
|
|
82
|
-
aggregated += [token, previous, a, b]
|
|
83
|
-
elsif token && SUB_SUP.include?(token)
|
|
84
|
-
aggregated = process_sub_sup(aggregated, token, tokens)
|
|
85
|
-
elsif token.start_with?(LATEX_BEGIN) || MATRICES.include?(token)
|
|
86
|
-
aggregated += environment(token, tokens)
|
|
87
|
-
elsif token == LATEX_OVER
|
|
88
|
-
numerator = aggregated
|
|
89
|
-
aggregated = []
|
|
90
|
-
aggregated << LATEX_FRAC
|
|
91
|
-
aggregated << numerator
|
|
92
|
-
aggregated << aggregate(tokens)
|
|
93
|
-
else
|
|
94
|
-
aggregated << token
|
|
95
|
-
end
|
|
96
|
-
rescue EmptyGroupError
|
|
97
|
-
aggregated += [OPENING_BRACES, CLOSING_BRACES]
|
|
98
|
-
next
|
|
99
|
-
rescue StopIteration
|
|
100
|
-
aggregated << token unless token.nil?
|
|
101
|
-
break
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
aggregated
|
|
106
|
-
end
|
|
107
|
-
|
|
108
|
-
def environment(token, tokens)
|
|
109
|
-
env = if token.start_with?(LATEX_BEGIN)
|
|
110
|
-
token[7..-2]
|
|
111
|
-
else
|
|
112
|
-
token[1..token.size]
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
alignment = nil
|
|
116
|
-
content = []
|
|
117
|
-
row = []
|
|
118
|
-
has_rowline = false
|
|
119
|
-
|
|
120
|
-
loop do
|
|
121
|
-
begin
|
|
122
|
-
token = next_item_or_group(tokens)
|
|
123
|
-
raise StopIteration if token.nil?
|
|
124
|
-
|
|
125
|
-
if token.is_a? Array
|
|
126
|
-
begin
|
|
127
|
-
if env == 'array' && token.all? { |x| 'lcr|'.include?(x) }
|
|
128
|
-
alignment = token
|
|
129
|
-
else
|
|
130
|
-
row << process_row(token)
|
|
131
|
-
end
|
|
132
|
-
rescue TypeError
|
|
133
|
-
row << token
|
|
134
|
-
end
|
|
135
|
-
elsif token == "\\end{#{env}}"
|
|
136
|
-
break
|
|
137
|
-
elsif token == AMPERSAND
|
|
138
|
-
row << token
|
|
139
|
-
elsif token == BACKSLASH
|
|
140
|
-
row = group_columns(row) if row.include?(AMPERSAND)
|
|
141
|
-
row.insert(0, LATEX_HLINE) if has_rowline
|
|
142
|
-
content << row
|
|
143
|
-
row = []
|
|
144
|
-
has_rowline = false
|
|
145
|
-
elsif token == LATEX_HLINE
|
|
146
|
-
has_rowline = true
|
|
147
|
-
elsif token == OPENING_BRACKET && content.empty?
|
|
148
|
-
begin
|
|
149
|
-
alignment = group(tokens, opening: OPENING_BRACKET, closing: CLOSING_BRACKET)
|
|
150
|
-
rescue EmptyGroupError
|
|
151
|
-
next
|
|
152
|
-
end
|
|
153
|
-
elsif token == DASH
|
|
154
|
-
next_token = tokens.shift
|
|
155
|
-
raise StopIteration if next_token.nil?
|
|
156
|
-
|
|
157
|
-
row << if next_token == "\\end{#{env}}"
|
|
158
|
-
token
|
|
159
|
-
else
|
|
160
|
-
[token, next_token]
|
|
161
|
-
end
|
|
162
|
-
elsif SUB_SUP.include?(token)
|
|
163
|
-
row = process_sub_sup(row, token, tokens)
|
|
164
|
-
elsif token.start_with?(LATEX_BEGIN)
|
|
165
|
-
row += environment(token, tokens)
|
|
166
|
-
else
|
|
167
|
-
row << token
|
|
168
|
-
end
|
|
169
|
-
rescue EmptyGroupError
|
|
170
|
-
row << []
|
|
171
|
-
next
|
|
172
|
-
rescue StopIteration
|
|
173
|
-
break
|
|
174
|
-
end
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
if row.any?
|
|
178
|
-
row = group_columns(row) if row.include?(AMPERSAND)
|
|
179
|
-
row.insert(0, LATEX_HLINE) if has_rowline
|
|
180
|
-
content << row
|
|
181
|
-
end
|
|
182
|
-
|
|
183
|
-
content = content.pop while content.size == 1 && content.first.is_a?(Array)
|
|
184
|
-
|
|
185
|
-
return ["\\#{env}", alignment.join, content] if alignment
|
|
186
|
-
|
|
187
|
-
["\\#{env}", content]
|
|
188
|
-
end
|
|
189
|
-
|
|
190
|
-
def group(tokens, opening: OPENING_BRACES, closing: CLOSING_BRACES, delimiter: nil)
|
|
191
|
-
g = []
|
|
192
|
-
|
|
193
|
-
if delimiter
|
|
194
|
-
g << delimiter
|
|
195
|
-
g << tokens.shift
|
|
196
|
-
end
|
|
197
|
-
|
|
198
|
-
loop do
|
|
199
|
-
begin
|
|
200
|
-
token = tokens.shift
|
|
201
|
-
raise StopIteration if token.nil?
|
|
202
|
-
|
|
203
|
-
if token == closing && delimiter.nil?
|
|
204
|
-
break if g.any?
|
|
205
|
-
|
|
206
|
-
raise EmptyGroupError
|
|
207
|
-
elsif token == opening
|
|
208
|
-
begin
|
|
209
|
-
g << group(tokens)
|
|
210
|
-
rescue EmptyGroupError
|
|
211
|
-
g += [[]]
|
|
212
|
-
end
|
|
213
|
-
elsif token == LATEX_LEFT
|
|
214
|
-
g << group(tokens, delimiter: token)
|
|
215
|
-
elsif token == LATEX_RIGHT
|
|
216
|
-
g << token
|
|
217
|
-
_token = tokens.shift
|
|
218
|
-
raise StopIteration if _token.nil?
|
|
219
|
-
|
|
220
|
-
g << _token
|
|
221
|
-
break
|
|
222
|
-
else
|
|
223
|
-
g << token
|
|
224
|
-
end
|
|
225
|
-
rescue StopIteration
|
|
226
|
-
break
|
|
227
|
-
end
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
if delimiter
|
|
231
|
-
right = g.index(LATEX_RIGHT)
|
|
232
|
-
raise ExtraLeftOrMissingRight if right.nil?
|
|
233
|
-
|
|
234
|
-
content = g[2..right - 1]
|
|
235
|
-
g_ = g
|
|
236
|
-
g_ = g[0..1] + [aggregate(content)] + g[right..g.size] if content.any?
|
|
237
|
-
|
|
238
|
-
return g_
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
aggregate(g)
|
|
242
|
-
end
|
|
243
|
-
|
|
244
|
-
def group_columns(row)
|
|
245
|
-
grouped = [[]]
|
|
246
|
-
row.each do |item|
|
|
247
|
-
if item == AMPERSAND
|
|
248
|
-
grouped << []
|
|
249
|
-
else
|
|
250
|
-
grouped[-1] << item
|
|
251
|
-
end
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
grouped.map { |item| item.size > 1 ? item : item.pop }
|
|
255
|
-
end
|
|
256
|
-
|
|
257
|
-
def next_item_or_group(tokens)
|
|
258
|
-
token = tokens.shift
|
|
259
|
-
raise StopIteration if token.nil?
|
|
260
|
-
|
|
261
|
-
return group(tokens) if token == OPENING_BRACES
|
|
262
|
-
|
|
263
|
-
return group(tokens, delimiter: token) if token == LATEX_LEFT
|
|
264
|
-
|
|
265
|
-
token
|
|
266
|
-
end
|
|
267
|
-
|
|
268
|
-
def find_opening_parenthesis(tokens)
|
|
269
|
-
closing = 0
|
|
270
|
-
|
|
271
|
-
tokens.map.with_index { |x, i| [i, x] }.reverse.each do |index, token|
|
|
272
|
-
if token == CLOSING_PARENTHESIS
|
|
273
|
-
closing += 1
|
|
274
|
-
elsif token == OPENING_PARENTHESIS
|
|
275
|
-
return index if closing == 0
|
|
276
|
-
|
|
277
|
-
closing -= 1
|
|
278
|
-
end
|
|
279
|
-
end
|
|
280
|
-
raise ExtraLeftOrMissingRight
|
|
281
|
-
end
|
|
282
|
-
|
|
283
|
-
def process_row(tokens)
|
|
284
|
-
row = []
|
|
285
|
-
content = []
|
|
286
|
-
|
|
287
|
-
tokens.each do |token|
|
|
288
|
-
if token == AMPERSAND
|
|
289
|
-
next
|
|
290
|
-
elsif token == BACKSLASH
|
|
291
|
-
content << row if row.any?
|
|
292
|
-
row = []
|
|
293
|
-
else
|
|
294
|
-
row << token
|
|
295
|
-
end
|
|
296
|
-
end
|
|
297
|
-
|
|
298
|
-
content << row if row.any?
|
|
299
|
-
|
|
300
|
-
content = content.pop while content.size == 1 && content.first.is_a?(Array)
|
|
301
|
-
|
|
302
|
-
content
|
|
303
|
-
end
|
|
304
|
-
|
|
305
|
-
def process_sub_sup(aggregated, token, tokens)
|
|
306
|
-
begin
|
|
307
|
-
previous = aggregated.pop
|
|
308
|
-
raise IndexError if previous.nil?
|
|
309
|
-
|
|
310
|
-
if previous.is_a?(String) && OPERATORS.include?(previous)
|
|
311
|
-
if (previous == CLOSING_PARENTHESIS) && aggregated.include?(OPENING_PARENTHESIS)
|
|
312
|
-
index = find_opening_parenthesis(aggregated)
|
|
313
|
-
aggregated = aggregated[0, index] + [token] + [aggregated[index..aggregated.size] + [previous]]
|
|
314
|
-
else
|
|
315
|
-
aggregated += [previous, token]
|
|
316
|
-
end
|
|
317
|
-
return aggregated
|
|
318
|
-
end
|
|
319
|
-
|
|
320
|
-
begin
|
|
321
|
-
next_token = next_item_or_group(tokens)
|
|
322
|
-
if aggregated.size >= 2
|
|
323
|
-
if aggregated[-2] == SUBSCRIPT && token == SUPERSCRIPT
|
|
324
|
-
aggregated[-2] = SUB_SUP
|
|
325
|
-
aggregated += [previous, next_token]
|
|
326
|
-
elsif (aggregated[-2] == SUPERSCRIPT) && (token == SUBSCRIPT)
|
|
327
|
-
aggregated[-2] = SUB_SUP
|
|
328
|
-
aggregated += [next_token, previous]
|
|
329
|
-
else
|
|
330
|
-
aggregated += [token, previous, next_token]
|
|
331
|
-
end
|
|
332
|
-
else
|
|
333
|
-
aggregated += [token, previous, next_token]
|
|
334
|
-
end
|
|
335
|
-
rescue EmptyGroupError
|
|
336
|
-
aggregated += [token, previous, []]
|
|
337
|
-
end
|
|
338
|
-
rescue IndexError
|
|
339
|
-
next_token = next_item_or_group(tokens)
|
|
340
|
-
aggregated += [token, '', next_token]
|
|
341
|
-
end
|
|
342
|
-
|
|
343
|
-
aggregated
|
|
344
|
-
end
|
|
345
|
-
end
|
|
346
|
-
|
|
347
|
-
class EmptyGroupError < StandardError; end
|
|
348
|
-
class ExtraLeftOrMissingRight < StandardError; end
|
|
349
|
-
class MissingSuperScriptOrSubscript < StandardError; end
|
|
350
|
-
class StopIteration < StandardError; end
|
|
351
|
-
end
|