asciimath 1.0.0.preview.1

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.
@@ -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