asciimath 1.0.0.preview.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 79e61b8558a971dd887b6e48246a1ee34be6098e
4
+ data.tar.gz: c7f13147dbb2460cf4ab5564249a0391fdb57095
5
+ SHA512:
6
+ metadata.gz: 6c7a2f710248ecf71df6a124aecf8e96e0e626b7448831166212572a24cccf615ef85c2f3749531499e3dc2155122c5b219dba012a6cc264de668532ab869962
7
+ data.tar.gz: 8350a5355cdf93da68d7c9224c8cc820a2fd01b2edee38cd758f6911c75e577f63d5a216b9e2d114c308bb8bd07af95ea1d717b55fd097afedb7b565de8284a4
@@ -0,0 +1,16 @@
1
+ /.idea/
2
+ /.bundle/
3
+ /.yardoc
4
+ /Gemfile.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ *.bundle
12
+ *.so
13
+ *.o
14
+ *.a
15
+ *.iml
16
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in asciimath.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Pepijn Van Eeckhoudt
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,31 @@
1
+ # Asciimath
2
+
3
+ An [asciimath](http://asciimath.org) parser and MathML generator written in pure Ruby.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'asciimath'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install asciimath
20
+
21
+ ## Usage
22
+
23
+ TODO: Write usage instructions here
24
+
25
+ ## Contributing
26
+
27
+ 1. Fork it ( https://github.com/[my-github-username]/asciimath/fork )
28
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
29
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
30
+ 4. Push to the branch (`git push origin my-new-feature`)
31
+ 5. Create a new Pull Request
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'asciimath/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "asciimath"
8
+ spec.version = Asciimath::VERSION
9
+ spec.authors = ["Pepijn Van Eeckhoudt"]
10
+ spec.email = ["pepijn@vaneeckhoudt.net"]
11
+ spec.summary = %q{Asciimath parser and converter}
12
+ spec.description = %q{A pure Ruby Asciimath parsing and conversion library.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
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_development_dependency "bundler", "~> 1.6"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec", "~> 3.1.0"
24
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'asciimath/cli'
4
+ ::Asciimath::CLI.run(ARGV)
@@ -0,0 +1,5 @@
1
+ require_relative 'asciimath/version'
2
+
3
+ require_relative 'asciimath/parser'
4
+
5
+ require_relative 'asciimath/mathml'
@@ -0,0 +1,12 @@
1
+ require_relative 'parser'
2
+ require_relative 'mathml'
3
+
4
+ module Asciimath
5
+ module CLI
6
+ def self.run(args)
7
+ asciimath = args.last
8
+ mathml = Asciimath.parse(asciimath).to_mathml
9
+ puts mathml
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,126 @@
1
+ module Asciimath
2
+ class MathMLBuilder
3
+ def initialize(prefix)
4
+ @prefix = prefix
5
+ @mathml = ''
6
+ end
7
+
8
+ def to_s
9
+ @mathml
10
+ end
11
+
12
+ def append_expression(expression, attrs = {})
13
+ math('', attrs) do
14
+ append(expression, :single_child => true)
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def append(expression, opts = {})
21
+ case expression
22
+ when Array
23
+ if expression.length <= 1 || opts[:single_child]
24
+ expression.each { |e| append(e) }
25
+ else
26
+ mrow do
27
+ expression.each { |e| append(e) }
28
+ end
29
+ end
30
+ when Hash
31
+ case expression[:type]
32
+ when :operator
33
+ mo(expression[:c])
34
+ when :identifier
35
+ mi(expression[:c])
36
+ when :number
37
+ mn(expression[:c])
38
+ when :text
39
+ mtext(expression[:c])
40
+ when :paren
41
+ paren = !opts[:strip_paren]
42
+ if paren
43
+ if opts[:single_child]
44
+ mo(expression[:lparen]) if expression[:lparen]
45
+ append(expression[:e], :single_child => true)
46
+ mo(expression[:rparen]) if expression[:rparen]
47
+ else
48
+ mrow do
49
+ mo(expression[:lparen]) if expression[:lparen]
50
+ append(expression[:e], :single_child => true)
51
+ mo(expression[:rparen]) if expression[:rparen]
52
+ end
53
+ end
54
+ else
55
+ append(expression[:e])
56
+ end
57
+ when :unary
58
+ operator = expression[:operator]
59
+ tag("m#{operator}") do
60
+ append(expression[:s], :single_child => true, :strip_paren => true)
61
+ end
62
+ when :binary
63
+ operator = expression[:operator]
64
+ tag("m#{operator}") do
65
+ append(expression[:s1], :strip_paren => (operator != :sub && operator != :sup))
66
+ append(expression[:s2], :strip_paren => true)
67
+ end
68
+ when :ternary
69
+ operator = expression[:operator]
70
+ tag("m#{operator}") do
71
+ append(expression[:s1])
72
+ append(expression[:s2], :strip_paren => true)
73
+ append(expression[:s3], :strip_paren => true)
74
+ end
75
+ when :matrix
76
+ mrow do
77
+ mo(expression[:lparen]) if expression[:lparen]
78
+ mtable do
79
+ expression[:rows].each do |row|
80
+ mtr do
81
+ row.each do |col|
82
+ mtd do
83
+ append(col)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ mo(expression[:rparen]) if expression[:rparen]
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ def method_missing(meth, *args, &block)
96
+ tag(meth, *args, &block)
97
+ end
98
+
99
+ def tag(tag, *args)
100
+ attrs = args.last.is_a?(Hash) ? args.pop : {}
101
+ text = args.last.is_a?(String) ? args.pop : ''
102
+
103
+ @mathml << '<' << @prefix << tag.to_s
104
+
105
+ attrs.each_pair do |key, value|
106
+ @mathml << ' ' << key << '="' << value << '"'
107
+ end
108
+
109
+
110
+ if block_given? || text
111
+ @mathml << '>'
112
+ @mathml << text
113
+ yield self if block_given?
114
+ @mathml << '</' << @prefix << tag.to_s << '>'
115
+ else
116
+ @mathml << '/>'
117
+ end
118
+ end
119
+ end
120
+
121
+ class Expression
122
+ def to_mathml(prefix = "", attrs = {})
123
+ MathMLBuilder.new(prefix).append_expression(@parsed_expression, attrs).to_s
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,529 @@
1
+ require 'strscan'
2
+
3
+ # Parser for ASCIIMath expressions.
4
+ #
5
+ # The syntax for ASCIIMath in EBNF style notation is
6
+ #
7
+ # expr = ( simp ( fraction | sub | super ) )+
8
+ # simp = constant | paren_expr | unary_expr | binary_expr | text
9
+ # fraction = '/' simp
10
+ # super = '^' simp
11
+ # sub = '_' simp super?
12
+ # paren_expr = lparen expr rparen
13
+ # lparen = '(' | '[' | '{' | '(:' | '{:'
14
+ # rparen = ')' | ']' | '}' | ':)' | ':}'
15
+ # unary_expr = unary_op simp
16
+ # unary_op = 'sqrt' | 'text'
17
+ # binary_expr = binary_op simp simp
18
+ # binary_op = 'frac' | 'root' | 'stackrel'
19
+ # text = '"' [^"]* '"'
20
+ # constant = number | symbol | identifier
21
+ # number = '-'? [0-9]+ ( '.' [0-9]+ )?
22
+ # symbol = /* any string in the symbol table */
23
+ # identifier = [A-z]
24
+ #
25
+ # ASCIIMath is parsed left to right without any form of operator precedence.
26
+ # When parsing the 'constant' the parser will try to find the longest matching string in the symbol
27
+ # table starting at the current position of the parser. If no matching string can be found the
28
+ # character at the current position of the parser is interpreted as an identifier instead.
29
+ module Asciimath
30
+ # Internal: Splits an ASCIIMath expression into a sequence of tokens.
31
+ # Each token is represented as a Hash containing the keys :value and :type.
32
+ # The :value key is used to store the text associated with each token.
33
+ # The :type key indicates the semantics of the token. The value for :type will be one
34
+ # of the following symbols:
35
+ #
36
+ # - :identifier a symbolic name or a bit of text without any further semantics
37
+ # - :text a bit of arbitrary text
38
+ # - :number a number
39
+ # - :operator a mathematical operator symbol
40
+ # - :unary a unary operator (e.g., sqrt, text, ...)
41
+ # - :infix an infix operator (e.g, /, _, ^, ...)
42
+ # - :binary a binary operator (e.g., frac, root, ...)
43
+ # - :accent an accent character
44
+ # - :eof indicates no more tokens are available
45
+ #
46
+ # Each token type may also have an :underover modifier. When present and set to true
47
+ # sub- and superscript expressions associated with the token will be rendered as
48
+ # under- and overscriptabove and below rather than as sub- or superscript.
49
+ #
50
+ # :accent tokens additionally have a :postion value which is set to either :over or :under.
51
+ # This determines if the accent should be rendered over or under the expression to which
52
+ # it applies.
53
+ #
54
+ class Tokenizer
55
+ WHITESPACE = /^\s+/
56
+ NUMBER_START = /[-0-9]/
57
+ NUMBER = /-?[0-9]+(?:\.[0-9]+)?/
58
+ TEXT = /"[^"]+"/
59
+
60
+ # Public: Initializes an ASCIIMath tokenizer.
61
+ #
62
+ # string - The ASCIIMath expression to tokenize
63
+ # symbols - The symbol table to use while tokenizing
64
+ def initialize(string, symbols)
65
+ @string = StringScanner.new(string)
66
+ @symbols = symbols
67
+ lookahead = @symbols.keys.map { |k| k.length }.max
68
+ @symbol_regexp = /([^\s0-9]{1,#{lookahead}})/
69
+ end
70
+
71
+ # Public: Read the next token from the ASCIIMath expression and move the tokenizer
72
+ # ahead by one token.
73
+ #
74
+ # Returns the next token as a Hash
75
+ def next_token
76
+ if @push_back
77
+ t = @push_back
78
+ @push_back = nil
79
+ return t
80
+ end
81
+
82
+ @string.scan(WHITESPACE)
83
+
84
+ return {:value => nil, :type => :eof} if @string.eos?
85
+
86
+ case @string.peek(1)
87
+ when '"'
88
+ read_text
89
+ when NUMBER_START
90
+ read_number() || read_symbol
91
+ else
92
+ read_symbol()
93
+ end
94
+ end
95
+
96
+ # Public: Pushes the given token back to the tokenizer. A subsequent call to next_token
97
+ # will return the given token rather than generating a new one. At most one
98
+ # token can be pushed back.
99
+ #
100
+ # token - The token to push back
101
+ def push_back(token)
102
+ @push_back = token unless token[:type] == :eof
103
+ end
104
+
105
+ private
106
+
107
+ # Private: Reads a text token from the input string
108
+ #
109
+ # Returns the text token or nil if a text token could not be matched at
110
+ # the current position
111
+ def read_text
112
+ read_value(TEXT) do |text|
113
+ {:value => text[1..-2], :type => :text}
114
+ end
115
+ end
116
+
117
+ # Private: Reads a number token from the input string
118
+ #
119
+ # Returns the number token or nil if a number token could not be matched at
120
+ # the current position
121
+ def read_number
122
+ read_value(NUMBER) do |number|
123
+ {:value => number, :type => :number}
124
+ end
125
+ end
126
+
127
+ # Private: Reads a symbol token from the input string. This method first creates
128
+ # a String from the input String starting from the current position with a length
129
+ # that matches that of the longest key in the symbol table. It then looks up that
130
+ # substring in the symbol table. If the substring is present in the symbol table, the
131
+ # associated value is returned and the position is moved ahead by the length of the
132
+ # substring. Otherwise this method chops one character off the end of the substring
133
+ # and repeats the symbol lookup. This continues until a single character is left.
134
+ # If that character can still not be found in the symbol table, then an identifier
135
+ # token is returned whose value is the remaining single character string.
136
+ #
137
+ # Returns the token that was read or nil if a token could not be matched at
138
+ # the current position
139
+ def read_symbol
140
+ position = @string.pos
141
+ read_value(@symbol_regexp) do |s|
142
+ until s.length == 1 || @symbols.include?(s)
143
+ s.chop!
144
+ end
145
+ @string.pos = position + s.length
146
+ @symbols[s] || {:value => s, :type => :identifier}
147
+ end
148
+ end
149
+
150
+ # Private: Reads a String from the input String that matches the given RegExp
151
+ #
152
+ # regexp - a RegExp that will be used to match the token
153
+ # block - if a block is provided the matched token will be passed to the block
154
+ #
155
+ # Returns the matched String or the value returned by the block if one was given
156
+ def read_value(regexp)
157
+ s = @string.scan(regexp)
158
+ if s
159
+ yield s
160
+ else
161
+ s
162
+ end
163
+ end
164
+ end
165
+
166
+ class Parser
167
+ SYMBOLS = {
168
+ # Operation symbols
169
+ '+' => {:value => '+', :type => :operator},
170
+ '-' => {:value => '&#x2212;', :type => :operator},
171
+ '*' => {:value => '&#x22C5;', :type => :operator},
172
+ '**' => {:value => '&#x22C6;', :type => :operator},
173
+ '//' => {:value => '/', :type => :operator},
174
+ '\\\\' => {:value => '\\', :type => :operator},
175
+ 'xx' => {:value => '&#x00D7;', :type => :operator},
176
+ '-:' => {:value => '&#x00F7;', :type => :operator},
177
+ '@' => {:value => '&#x26AC;', :type => :operator},
178
+ 'o+' => {:value => '&#x2295;', :type => :operator},
179
+ 'ox' => {:value => '&#x2297;', :type => :operator},
180
+ 'o.' => {:value => '&#x2299;', :type => :operator},
181
+ 'sum' => {:value => '&#x2211;', :type => :operator, :underover => true},
182
+ 'prod' => {:value => '&#x220F;', :type => :operator, :underover => true},
183
+ '^^' => {:value => '&#x2227;', :type => :operator},
184
+ '^^^' => {:value => '&#x22C0;', :type => :operator, :underover => true},
185
+ 'vv' => {:value => '&#x2228;', :type => :operator},
186
+ 'vvv' => {:value => '&#x22C1;', :type => :operator, :underover => true},
187
+ 'nn' => {:value => '&#x2229;', :type => :operator},
188
+ 'nnn' => {:value => '&#x22C2;', :type => :operator, :underover => true},
189
+ 'uu' => {:value => '&#x222A;', :type => :operator},
190
+ 'uuu' => {:value => '&#x22C3;', :type => :operator, :underover => true},
191
+
192
+ # Relation symbols
193
+ '=' => {:value => '=', :type => :operator},
194
+ '!=' => {:value => '&#x2260;', :type => :operator},
195
+ ':=' => {:value => ':=', :type => :operator},
196
+ '<' => {:value => '&#x003C;', :type => :operator},
197
+ 'lt' => {:value => '&#x003C;', :type => :operator},
198
+ '>' => {:value => '&#x003E;', :type => :operator},
199
+ 'gt' => {:value => '&#x003E;', :type => :operator},
200
+ '<=' => {:value => '&#x2264;', :type => :operator},
201
+ 'lt=' => {:value => '&#x2264;', :type => :operator},
202
+ '>=' => {:value => '&#x2265;', :type => :operator},
203
+ 'geq' => {:value => '&#x2265;', :type => :operator},
204
+ '-<' => {:value => '&#x227A;', :type => :operator},
205
+ '-lt' => {:value => '&#x227A;', :type => :operator},
206
+ '>-' => {:value => '&#x227B;', :type => :operator},
207
+ '-<=' => {:value => '&#x2AAF;', :type => :operator},
208
+ '>-=' => {:value => '&#x2AB0;', :type => :operator},
209
+ 'in' => {:value => '&#x2208;', :type => :operator},
210
+ '!in' => {:value => '&#x2209;', :type => :operator},
211
+ 'sub' => {:value => '&#x2282;', :type => :operator},
212
+ 'sup' => {:value => '&#x2283;', :type => :operator},
213
+ 'sube' => {:value => '&#x2286;', :type => :operator},
214
+ 'supe' => {:value => '&#x2287;', :type => :operator},
215
+ '-=' => {:value => '&#x2261;', :type => :operator},
216
+ '~=' => {:value => '&#x2245;', :type => :operator},
217
+ '~~' => {:value => '&#x2248;', :type => :operator},
218
+ 'prop' => {:value => '&#x221D;', :type => :operator},
219
+
220
+ # Logical symbols
221
+ 'and' => {:value => 'and', :type => :text},
222
+ 'or' => {:value => 'or', :type => :text},
223
+ 'not' => {:value => '&#x00AC;', :type => :operator},
224
+ '=>' => {:value => '&#x21D2;', :type => :operator},
225
+ 'if' => {:value => 'if', :type => :operator},
226
+ '<=>' => {:value => '&#x21D4;', :type => :operator},
227
+ 'AA' => {:value => '&#x2200;', :type => :operator},
228
+ 'EE' => {:value => '&#x2203;', :type => :operator},
229
+ '_|_' => {:value => '&#x22A5;', :type => :operator},
230
+ 'TT' => {:value => '&#x22A4;', :type => :operator},
231
+ '|--' => {:value => '&#x22A2;', :type => :operator},
232
+ '|==' => {:value => '&#x22A8;', :type => :operator},
233
+
234
+ # Grouping brackets
235
+ '(' => {:value => '(', :type => :lparen},
236
+ ')' => {:value => ')', :type => :rparen},
237
+ '[' => {:value => '[', :type => :lparen},
238
+ ']' => {:value => ']', :type => :rparen},
239
+ '{' => {:value => '{', :type => :lparen},
240
+ '}' => {:value => '}', :type => :rparen},
241
+ '|' => {:value => '|', :type => :lrparen},
242
+ '||' => {:value => '||', :type => :lrparen},
243
+ '(:' => {:value => '&#x2329;', :type => :lparen},
244
+ ':)' => {:value => '&#x232A;', :type => :rparen},
245
+ '<<' => {:value => '&#x2329;', :type => :lparen},
246
+ '>>' => {:value => '&#x232A;', :type => :rparen},
247
+ '{:' => {:value => nil, :type => :lparen},
248
+ ':}' => {:value => nil, :type => :rparen},
249
+
250
+ # Miscellaneous symbols
251
+ 'int' => {:value => '&#x222B;', :type => :operator},
252
+ 'dx' => {:value => 'dx', :type => :identifier},
253
+ 'dy' => {:value => 'dy', :type => :identifier},
254
+ 'dz' => {:value => 'dz', :type => :identifier},
255
+ 'dt' => {:value => 'dt', :type => :identifier},
256
+ 'oint' => {:value => '&#x222E;', :type => :operator},
257
+ 'del' => {:value => '&#x2202;', :type => :operator},
258
+ 'grad' => {:value => '&#x2207;', :type => :operator},
259
+ '+-' => {:value => '&#x00B1;', :type => :operator},
260
+ 'O/' => {:value => '&#x2205;', :type => :operator},
261
+ 'oo' => {:value => '&#x221E;', :type => :operator},
262
+ 'aleph' => {:value => '&#x2135;', :type => :operator},
263
+ '...' => {:value => '...', :type => :operator},
264
+ ':.' => {:value => '&#x2234;', :type => :operator},
265
+ '/_' => {:value => '&#x2220;', :type => :operator},
266
+ '\\ ' => {:value => '&#x00A0;', :type => :operator},
267
+ 'quad' => {:value => '\u00A0\u00A0', :type => :operator},
268
+ 'qquad' => {:value => '\u00A0\u00A0\u00A0\u00A0', :type => :operator},
269
+ 'cdots' => {:value => '&#x22EF;', :type => :operator},
270
+ 'vdots' => {:value => '&#x22EE;', :type => :operator},
271
+ 'ddots' => {:value => '&#x22F1;', :type => :operator},
272
+ 'diamond' => {:value => '&#x22C4;', :type => :operator},
273
+ 'square' => {:value => '&#x25A1;', :type => :operator},
274
+ '|__' => {:value => '&#x230A;', :type => :operator},
275
+ '__|' => {:value => '&#x230B;', :type => :operator},
276
+ '|~' => {:value => '&#x2308;', :type => :operator},
277
+ '~|' => {:value => '&#x2309;', :type => :operator},
278
+ 'CC' => {:value => '&#x2102;', :type => :operator},
279
+ 'NN' => {:value => '&#x2115;', :type => :operator},
280
+ 'QQ' => {:value => '&#x211A;', :type => :operator},
281
+ 'RR' => {:value => '&#x211D;', :type => :operator},
282
+ 'ZZ' => {:value => '&#x2124;', :type => :operator},
283
+ 'f' => {:value => 'f', :type => :identifier},
284
+ 'g' => {:value => 'g', :type => :identifier},
285
+
286
+ # Standard functions
287
+ 'lim' => {:value => 'lim', :type => :operator, :underover => true},
288
+ 'Lim' => {:value => 'Lim', :type => :operator, :underover => true},
289
+ 'sin' => {:value => 'sin', :type => :operator},
290
+ 'cos' => {:value => 'cos', :type => :operator},
291
+ 'tan' => {:value => 'tan', :type => :operator},
292
+ 'sinh' => {:value => 'sinh', :type => :operator},
293
+ 'cosh' => {:value => 'cosh', :type => :operator},
294
+ 'tanh' => {:value => 'tanh', :type => :operator},
295
+ 'cot' => {:value => 'cot', :type => :operator},
296
+ 'sec' => {:value => 'sec', :type => :operator},
297
+ 'csc' => {:value => 'csc', :type => :operator},
298
+ 'log' => {:value => 'log', :type => :operator},
299
+ 'ln' => {:value => 'ln', :type => :operator},
300
+ 'det' => {:value => 'det', :type => :operator},
301
+ 'dim' => {:value => 'dim', :type => :operator},
302
+ 'mod' => {:value => 'mod', :type => :operator},
303
+ 'gcd' => {:value => 'gcd', :type => :operator},
304
+ 'lcm' => {:value => 'lcm', :type => :operator},
305
+ 'lub' => {:value => 'lub', :type => :operator},
306
+ 'glb' => {:value => 'glb', :type => :operator},
307
+ 'min' => {:value => 'min', :type => :operator, :underover => true},
308
+ 'max' => {:value => 'max', :type => :operator, :underover => true},
309
+
310
+ # Accents
311
+ 'hat' => {:value => '&#x005E;', :type => :accent, :position => :over},
312
+ 'bar' => {:value => '&#x00AF;', :type => :accent, :position => :over},
313
+ 'ul' => {:value => '_', :type => :accent, :position => :under},
314
+ 'vec' => {:value => '&#x2192;', :type => :accent, :position => :over},
315
+ 'dot' => {:value => '.', :type => :accent, :position => :over},
316
+ 'ddot' => {:value => '..', :type => :accent, :position => :over},
317
+
318
+ # Arrows
319
+ 'uarr' => {:value => '&#x2191;', :type => :operator},
320
+ 'darr' => {:value => '&#x2193;', :type => :operator},
321
+ 'rarr' => {:value => '&#x2192;', :type => :operator},
322
+ '->' => {:value => '&#x2192;', :type => :operator},
323
+ '>->' => {:value => '&#x21A3;', :type => :operator},
324
+ '->>' => {:value => '&#x21A0;', :type => :operator},
325
+ '>->>' => {:value => '&#x2916;', :type => :operator},
326
+ '|->' => {:value => '&#x21A6;', :type => :operator},
327
+ 'larr' => {:value => '&#x2190;', :type => :operator},
328
+ 'harr' => {:value => '&#x2194;', :type => :operator},
329
+ 'rArr' => {:value => '&#x21D2;', :type => :operator},
330
+ 'lArr' => {:value => '&#x21D0;', :type => :operator},
331
+ 'hArr' => {:value => '&#x21D4;', :type => :operator},
332
+
333
+ # Other
334
+ 'sqrt' => {:value => :sqrt, :type => :unary},
335
+ 'text' => {:value => :text, :type => :unary},
336
+ 'frac' => {:value => :frac, :type => :binary},
337
+ 'root' => {:value => :root, :type => :binary},
338
+ 'stackrel' => {:value => :over, :type => :binary},
339
+ '/' => {:value => :frac, :type => :infix},
340
+ '_' => {:value => :sub, :type => :infix},
341
+ '^' => {:value => :sup, :type => :infix},
342
+
343
+ # Greek letters
344
+ 'alpha' => {:value => '&#x03b1;', :type => :identifier},
345
+ 'Alpha' => {:value => '&#x0391;', :type => :identifier},
346
+ 'beta' => {:value => '&#x03b2;', :type => :identifier},
347
+ 'Beta' => {:value => '&#x0392;', :type => :identifier},
348
+ 'gamma' => {:value => '&#x03b3;', :type => :identifier},
349
+ 'Gamma' => {:value => '&#x0393;', :type => :operator},
350
+ 'delta' => {:value => '&#x03b4;', :type => :identifier},
351
+ 'Delta' => {:value => '&#x0394;', :type => :operator},
352
+ 'epsilon' => {:value => '&#x03b5;', :type => :identifier},
353
+ 'Epsilon' => {:value => '&#x0395;', :type => :identifier},
354
+ 'varepsilon' => {:value => '&#x025b;', :type => :identifier},
355
+ 'zeta' => {:value => '&#x03b6;', :type => :identifier},
356
+ 'Zeta' => {:value => '&#x0396;', :type => :identifier},
357
+ 'eta' => {:value => '&#x03b7;', :type => :identifier},
358
+ 'Eta' => {:value => '&#x0397;', :type => :identifier},
359
+ 'theta' => {:value => '&#x03b8;', :type => :identifier},
360
+ 'Theta' => {:value => '&#x0398;', :type => :operator},
361
+ 'vartheta' => {:value => '&#x03d1;', :type => :identifier},
362
+ 'iota' => {:value => '&#x03b9;', :type => :identifier},
363
+ 'Iota' => {:value => '&#x0399;', :type => :identifier},
364
+ 'kappa' => {:value => '&#x03ba;', :type => :identifier},
365
+ 'Kappa' => {:value => '&#x039a;', :type => :identifier},
366
+ 'lambda' => {:value => '&#x03bb;', :type => :identifier},
367
+ 'Lambda' => {:value => '&#x039b;', :type => :operator},
368
+ 'mu' => {:value => '&#x03bc;', :type => :identifier},
369
+ 'Mu' => {:value => '&#x039c;', :type => :identifier},
370
+ 'nu' => {:value => '&#x03bd;', :type => :identifier},
371
+ 'Nu' => {:value => '&#x039d;', :type => :identifier},
372
+ 'xi' => {:value => '&#x03be;', :type => :identifier},
373
+ 'Xi' => {:value => '&#x039e;', :type => :operator},
374
+ 'omicron' => {:value => '&#x03bf;', :type => :identifier},
375
+ 'Omicron' => {:value => '&#x039f;', :type => :identifier},
376
+ 'pi' => {:value => '&#x03c0;', :type => :identifier},
377
+ 'Pi' => {:value => '&#x03a0;', :type => :operator},
378
+ 'rho' => {:value => '&#x03c1;', :type => :identifier},
379
+ 'Rho' => {:value => '&#x03a1;', :type => :identifier},
380
+ 'sigma' => {:value => '&#x03c3;', :type => :identifier},
381
+ 'Sigma' => {:value => '&#x03a3;', :type => :operator},
382
+ 'tau' => {:value => '&#x03c4;', :type => :identifier},
383
+ 'Tau' => {:value => '&#x03a4;', :type => :identifier},
384
+ 'upsilon' => {:value => '&#x03c5;', :type => :identifier},
385
+ 'Upsilon' => {:value => '&#x03a5;', :type => :identifier},
386
+ 'phi' => {:value => '&#x03c6;', :type => :identifier},
387
+ 'Phi' => {:value => '&#x03a6;', :type => :identifier},
388
+ 'varphi' => {:value => '&#x03d5;', :type => :identifier},
389
+ 'chi' => {:value => '\u03b3c7', :type => :identifier},
390
+ 'Chi' => {:value => '\u0393a7', :type => :identifier},
391
+ 'psi' => {:value => '&#x03c8;', :type => :identifier},
392
+ 'Psi' => {:value => '&#x03a8;', :type => :identifier},
393
+ 'omega' => {:value => '&#x03c9;', :type => :identifier},
394
+ 'Omega' => {:value => '&#x03a9;', :type => :operator},
395
+ }
396
+
397
+ def parse(input)
398
+ Expression.new(
399
+ input,
400
+ parse_expression(Tokenizer.new(input, SYMBOLS), 0)
401
+ )
402
+ end
403
+
404
+ private
405
+ def parse_expression(tok, depth)
406
+ e = []
407
+
408
+ while (s1 = parse_simple_expression(tok, depth))
409
+ t1 = tok.next_token
410
+
411
+ if t1[:type] == :infix
412
+ s2 = parse_simple_expression(tok, depth)
413
+ t2 = tok.next_token
414
+ if t1[:value] == :sub && t2[:value] == :sup
415
+ s3 = parse_simple_expression(tok, depth)
416
+ operator = s1[:underover] ? :underover : :subsup
417
+ e << {:type => :ternary, :operator => operator, :s1 => s1, :s2 => s2, :s3 => s3}
418
+ else
419
+ operator = s1[:underover] ? (t1[:value] == :sub ? :under : :over) : t1[:value]
420
+ e << {:type => :binary, :operator => operator, :s1 => s1, :s2 => s2}
421
+ tok.push_back(t2)
422
+ if (t2[:type] == :lrparen || t2[:type] == :rparen) && depth > 0
423
+ break
424
+ end
425
+ end
426
+ elsif t1[:type] == :eof
427
+ e << s1
428
+ break
429
+ else
430
+ e << s1
431
+ tok.push_back(t1)
432
+ if (t1[:type] == :lrparen || t1[:type] == :rparen) && depth > 0
433
+ break
434
+ end
435
+ end
436
+ end
437
+
438
+ e
439
+ end
440
+
441
+ def parse_simple_expression(tok, depth)
442
+ t1 = tok.next_token
443
+
444
+ case t1[:type]
445
+ when :lparen, :lrparen
446
+ t2 = tok.next_token
447
+ case t2[:type]
448
+ when :rparen, :lrparen
449
+ {:type => :paren, :e => e, :lparen => t1[:value], :rparen => t2[:value]}
450
+ else
451
+ tok.push_back(t2)
452
+
453
+ e = parse_expression(tok, depth + 1)
454
+
455
+ t2 = tok.next_token
456
+ case t2[:type]
457
+ when :rparen, :lrparen
458
+ convert_to_matrix({:type => :paren, :e => e, :lparen => t1[:value], :rparen => t2[:value]})
459
+ else
460
+ tok.push_back(t2)
461
+ {:type => :paren, :e => e, :lparen => t1[:value]}
462
+ end
463
+ end
464
+ when :accent
465
+ s = parse_simple_expression(tok, depth)
466
+ {:type => :binary, :s1 => s, :s2 => {:type => :operator, :c => t1[:value]}, :operator => t1[:position]}
467
+ when :unary
468
+ s = parse_simple_expression(tok, depth)
469
+ {:type => :unary, :s => s, :operator => t1[:value]}
470
+ when :binary
471
+ s1 = parse_simple_expression(tok, depth)
472
+ s2 = parse_simple_expression(tok, depth)
473
+ {:type => :binary, :s1 => s1, :s2 => s2, :operator => t1[:value]}
474
+ when :eof
475
+ nil
476
+ else
477
+ {:type => t1[:type], :c => t1[:value], :underover => t1[:underover]}
478
+ end
479
+ end
480
+
481
+ def convert_to_matrix(expression)
482
+ return expression unless matrix? expression
483
+
484
+ rows = expression[:e].select.with_index { |obj, i| i.even? }.map do |row|
485
+ row[:e].select.with_index { |obj, i| i.even? }
486
+ end
487
+
488
+ {:type => :matrix, :rows => rows, :lparen => expression[:lparen], :rparen => expression[:rparen]}
489
+ end
490
+
491
+ def matrix?(expression)
492
+ return false unless expression.is_a?(Hash) && expression[:type] == :paren
493
+
494
+ rows, separators = expression[:e].partition.with_index { |obj, i| i.even? }
495
+
496
+ rows.length > 1 &&
497
+ rows.length > separators.length &&
498
+ separators.all? { |item| item[:type] == :identifier && item[:c] == ',' } &&
499
+ (rows.all? { |item| item[:type] == :paren && item[:lparen] == '(' && item[:rparen] == ')' } ||
500
+ rows.all? { |item| item[:type] == :paren && item[:lparen] == '[' && item[:rparen] == ']' }) &&
501
+ rows.all? { |item| item[:e].length == rows[0][:e].length } &&
502
+ rows.all? { |item| matrix_cols?(item[:e]) }
503
+ end
504
+
505
+ def matrix_cols?(expression)
506
+ return false unless expression.is_a?(Array)
507
+
508
+ cols, separators = expression.partition.with_index { |obj, i| i.even? }
509
+
510
+ cols.all? { |item| item[:type] != :identifier || item[:c] != ',' } &&
511
+ separators.all? { |item| item[:type] == :identifier && item[:c] == ',' }
512
+ end
513
+ end
514
+
515
+ class Expression
516
+ def initialize(asciimath, parsed_expression)
517
+ @asciimath = asciimath
518
+ @parsed_expression = parsed_expression
519
+ end
520
+
521
+ def to_s
522
+ @asciimath
523
+ end
524
+ end
525
+
526
+ def self.parse(asciimath)
527
+ Parser.new.parse(asciimath)
528
+ end
529
+ end
@@ -0,0 +1,3 @@
1
+ module Asciimath
2
+ VERSION = "1.0.0.preview.1"
3
+ end
@@ -0,0 +1,67 @@
1
+ require 'rspec'
2
+ require 'asciimath'
3
+
4
+ TEST_CASES = {
5
+ 'x+b/(2a)<+-sqrt((b^2)/(4a^2)-c/a)' =>
6
+ '<math><mi>x</mi><mo>+</mo><mfrac><mi>b</mi><mrow><mn>2</mn><mi>a</mi></mrow></mfrac><mo>&#x003C;</mo><mo>&#x00B1;</mo><msqrt><mrow><mfrac><msup><mi>b</mi><mn>2</mn></msup><mrow><mn>4</mn><msup><mi>a</mi><mn>2</mn></msup></mrow></mfrac><mo>&#x2212;</mo><mfrac><mi>c</mi><mi>a</mi></mfrac></mrow></msqrt></math>',
7
+ 'a^2 + b^2 = c^2' =>
8
+ '<math><msup><mi>a</mi><mn>2</mn></msup><mo>+</mo><msup><mi>b</mi><mn>2</mn></msup><mo>=</mo><msup><mi>c</mi><mn>2</mn></msup></math>',
9
+ 'x = (-b+-sqrt(b^2-4ac))/(2a)' =>
10
+ '<math><mi>x</mi><mo>=</mo><mfrac><mrow><mo>&#x2212;</mo><mi>b</mi><mo>&#x00B1;</mo><msqrt><mrow><msup><mi>b</mi><mn>2</mn></msup><mn>-4</mn><mi>a</mi><mi>c</mi></mrow></msqrt></mrow><mrow><mn>2</mn><mi>a</mi></mrow></mfrac></math>',
11
+ 'm = (y_2 - y_1)/(x_2 - x_1) = (Deltay)/(Deltax)' =>
12
+ '<math><mi>m</mi><mo>=</mo><mfrac><mrow><msub><mi>y</mi><mn>2</mn></msub><mo>&#x2212;</mo><msub><mi>y</mi><mn>1</mn></msub></mrow><mrow><msub><mi>x</mi><mn>2</mn></msub><mo>&#x2212;</mo><msub><mi>x</mi><mn>1</mn></msub></mrow></mfrac><mo>=</mo><mfrac><mrow><mo>&#x0394;</mo><mi>y</mi></mrow><mrow><mo>&#x0394;</mo><mi>x</mi></mrow></mfrac></math>',
13
+ 'f\'(x) = lim_(Deltax->0)(f(x+Deltax)-f(x))/(Deltax)' =>
14
+ '<math><mi>f</mi><mi>\'</mi><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow><mo>=</mo><munder><mo>lim</mo><mrow><mo>&#x0394;</mo><mi>x</mi><mo>&#x2192;</mo><mn>0</mn></mrow></munder><mfrac><mrow><mi>f</mi><mrow><mo>(</mo><mi>x</mi><mo>+</mo><mo>&#x0394;</mo><mi>x</mi><mo>)</mo></mrow><mo>&#x2212;</mo><mi>f</mi><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow></mrow><mrow><mo>&#x0394;</mo><mi>x</mi></mrow></mfrac></math>',
15
+ 'd/dx [x^n] = nx^(n - 1)' =>
16
+ '<math><mfrac><mi>d</mi><mi>dx</mi></mfrac><mrow><mo>[</mo><msup><mi>x</mi><mi>n</mi></msup><mo>]</mo></mrow><mo>=</mo><mi>n</mi><msup><mi>x</mi><mrow><mi>n</mi><mo>&#x2212;</mo><mn>1</mn></mrow></msup></math>',
17
+ 'int_a^b f(x) dx = [F(x)]_a^b = F(b) - F(a)' =>
18
+ '<math><msubsup><mo>&#x222B;</mo><mi>a</mi><mi>b</mi></msubsup><mi>f</mi><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow><mi>dx</mi><mo>=</mo><msubsup><mrow><mo>[</mo><mi>F</mi><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow><mo>]</mo></mrow><mi>a</mi><mi>b</mi></msubsup><mo>=</mo><mi>F</mi><mrow><mo>(</mo><mi>b</mi><mo>)</mo></mrow><mo>&#x2212;</mo><mi>F</mi><mrow><mo>(</mo><mi>a</mi><mo>)</mo></mrow></math>',
19
+ 'int_a^b f(x) dx = f(c)(b - a)' =>
20
+ '<math><msubsup><mo>&#x222B;</mo><mi>a</mi><mi>b</mi></msubsup><mi>f</mi><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow><mi>dx</mi><mo>=</mo><mi>f</mi><mrow><mo>(</mo><mi>c</mi><mo>)</mo></mrow><mrow><mo>(</mo><mi>b</mi><mo>&#x2212;</mo><mi>a</mi><mo>)</mo></mrow></math>',
21
+ 'ax^2 + bx + c = 0' =>
22
+ '<math><mi>a</mi><msup><mi>x</mi><mn>2</mn></msup><mo>+</mo><mi>b</mi><mi>x</mi><mo>+</mo><mi>c</mi><mo>=</mo><mn>0</mn></math>',
23
+ '"average value"=1/(b-a) int_a^b f(x) dx' =>
24
+ '<math><mtext>average value</mtext><mo>=</mo><mfrac><mn>1</mn><mrow><mi>b</mi><mo>&#x2212;</mo><mi>a</mi></mrow></mfrac><msubsup><mo>&#x222B;</mo><mi>a</mi><mi>b</mi></msubsup><mi>f</mi><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow><mi>dx</mi></math>',
25
+ 'd/dx[int_a^x f(t) dt] = f(x)' =>
26
+ '<math><mfrac><mi>d</mi><mi>dx</mi></mfrac><mrow><mo>[</mo><msubsup><mo>&#x222B;</mo><mi>a</mi><mi>x</mi></msubsup><mi>f</mi><mrow><mo>(</mo><mi>t</mi><mo>)</mo></mrow><mi>dt</mi><mo>]</mo></mrow><mo>=</mo><mi>f</mi><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow></math>',
27
+ 'hat(ab) bar(xy) ul(A) vec(v)' =>
28
+ '<math><mover><mrow><mi>a</mi><mi>b</mi></mrow><mo>&#x005E;</mo></mover><mover><mrow><mi>x</mi><mi>y</mi></mrow><mo>&#x00AF;</mo></mover><munder><mi>A</mi><mo>_</mo></munder><mover><mi>v</mi><mo>&#x2192;</mo></mover></math>',
29
+ 'z_12^34' =>
30
+ '<math><msubsup><mi>z</mi><mn>12</mn><mn>34</mn></msubsup></math>',
31
+ 'lim_(x->c)(f(x)-f(c))/(x-c)' =>
32
+ '<math><munder><mo>lim</mo><mrow><mi>x</mi><mo>&#x2192;</mo><mi>c</mi></mrow></munder><mfrac><mrow><mi>f</mi><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow><mo>&#x2212;</mo><mi>f</mi><mrow><mo>(</mo><mi>c</mi><mo>)</mo></mrow></mrow><mrow><mi>x</mi><mo>&#x2212;</mo><mi>c</mi></mrow></mfrac></math>',
33
+ 'int_0^(pi/2) g(x) dx' =>
34
+ '<math><msubsup><mo>&#x222B;</mo><mn>0</mn><mfrac><mi>&#x03c0;</mi><mn>2</mn></mfrac></msubsup><mi>g</mi><mrow><mo>(</mo><mi>x</mi><mo>)</mo></mrow><mi>dx</mi></math>',
35
+ 'sum_(n=0)^oo a_n' =>
36
+ '<math><munderover><mo>&#x2211;</mo><mrow><mi>n</mi><mo>=</mo><mn>0</mn></mrow><mo>&#x221E;</mo></munderover><msub><mi>a</mi><mi>n</mi></msub></math>',
37
+ '((1,2,3),(4,5,6),(7,8,9))' =>
38
+ '<math><mrow><mo>(</mo><mtable><mtr><mtd><mn>1</mn></mtd><mtd><mn>2</mn></mtd><mtd><mn>3</mn></mtd></mtr><mtr><mtd><mn>4</mn></mtd><mtd><mn>5</mn></mtd><mtd><mn>6</mn></mtd></mtr><mtr><mtd><mn>7</mn></mtd><mtd><mn>8</mn></mtd><mtd><mn>9</mn></mtd></mtr></mtable><mo>)</mo></mrow></math>',
39
+ '|(a,b),(c,d)|=ad-bc' =>
40
+ '<math><mrow><mo>|</mo><mtable><mtr><mtd><mi>a</mi></mtd><mtd><mi>b</mi></mtd></mtr><mtr><mtd><mi>c</mi></mtd><mtd><mi>d</mi></mtd></mtr></mtable><mo>|</mo></mrow><mo>=</mo><mi>a</mi><mi>d</mi><mo>&#x2212;</mo><mi>b</mi><mi>c</mi></math>',
41
+ '((a_(11), cdots , a_(1n)),(vdots, ddots, vdots),(a_(m1), cdots , a_(mn)))' =>
42
+ '<math><mrow><mo>(</mo><mtable><mtr><mtd><msub><mi>a</mi><mn>11</mn></msub></mtd><mtd><mo>&#x22EF;</mo></mtd><mtd><msub><mi>a</mi><mrow><mn>1</mn><mi>n</mi></mrow></msub></mtd></mtr><mtr><mtd><mo>&#x22EE;</mo></mtd><mtd><mo>&#x22F1;</mo></mtd><mtd><mo>&#x22EE;</mo></mtd></mtr><mtr><mtd><msub><mi>a</mi><mrow><mi>m</mi><mn>1</mn></mrow></msub></mtd><mtd><mo>&#x22EF;</mo></mtd><mtd><msub><mi>a</mi><mrow><mi>m</mi><mi>n</mi></mrow></msub></mtd></mtr></mtable><mo>)</mo></mrow></math>',
43
+ 'sum_(k=1)^n k = 1+2+ cdots +n=(n(n+1))/2' =>
44
+ '<math><munderover><mo>&#x2211;</mo><mrow><mi>k</mi><mo>=</mo><mn>1</mn></mrow><mi>n</mi></munderover><mi>k</mi><mo>=</mo><mn>1</mn><mo>+</mo><mn>2</mn><mo>+</mo><mo>&#x22EF;</mo><mo>+</mo><mi>n</mi><mo>=</mo><mfrac><mrow><mi>n</mi><mrow><mo>(</mo><mi>n</mi><mo>+</mo><mn>1</mn><mo>)</mo></mrow></mrow><mn>2</mn></mfrac></math>',
45
+ }
46
+
47
+ module AsciimathHelper
48
+ def expect_mathml(asciimath, mathml)
49
+ expect(Asciimath.parse(asciimath).to_mathml).to eq(mathml)
50
+ end
51
+ end
52
+
53
+ RSpec.configure do |c|
54
+ c.include AsciimathHelper
55
+ end
56
+
57
+ describe "Asciimath::MathMLBuilder" do
58
+ TEST_CASES.each_pair do |asciimath, mathml|
59
+ it "should produce identical output to asciimathml.js for '#{asciimath}'" do
60
+ expect_mathml(asciimath, mathml)
61
+ end
62
+ end
63
+
64
+ it 'should not generate mo elements for {: and :}' do
65
+ expect_mathml '{:(a,b),(c,d):}', '<math><mrow><mtable><mtr><mtd><mi>a</mi></mtd><mtd><mi>b</mi></mtd></mtr><mtr><mtd><mi>c</mi></mtd><mtd><mi>d</mi></mtd></mtr></mtable></mrow></math>'
66
+ end
67
+ end
metadata ADDED
@@ -0,0 +1,101 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: asciimath
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.preview.1
5
+ platform: ruby
6
+ authors:
7
+ - Pepijn Van Eeckhoudt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-12-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 3.1.0
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 3.1.0
55
+ description: A pure Ruby Asciimath parsing and conversion library.
56
+ email:
57
+ - pepijn@vaneeckhoudt.net
58
+ executables:
59
+ - asciimath
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - LICENSE.txt
66
+ - README.md
67
+ - Rakefile
68
+ - asciimath.gemspec
69
+ - bin/asciimath
70
+ - lib/asciimath.rb
71
+ - lib/asciimath/cli.rb
72
+ - lib/asciimath/mathml.rb
73
+ - lib/asciimath/parser.rb
74
+ - lib/asciimath/version.rb
75
+ - test/parser_spec.rb
76
+ homepage: ''
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">"
92
+ - !ruby/object:Gem::Version
93
+ version: 1.3.1
94
+ requirements: []
95
+ rubyforge_project:
96
+ rubygems_version: 2.2.2
97
+ signing_key:
98
+ specification_version: 4
99
+ summary: Asciimath parser and converter
100
+ test_files:
101
+ - test/parser_spec.rb