asciimath 1.0.9 → 2.0.3
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 +5 -5
- data/.github/workflows/ci.yml +20 -0
- data/AST.adoc +457 -0
- data/CHANGELOG.adoc +47 -1
- data/Gemfile +5 -0
- data/README.adoc +68 -4
- data/asciimath.gemspec +3 -6
- data/dump_symbol_table.rb +46 -0
- data/lib/asciimath.rb +5 -4
- data/lib/asciimath/ast.rb +456 -0
- data/lib/asciimath/cli.rb +4 -1
- data/lib/asciimath/color_table.rb +21 -0
- data/lib/asciimath/html.rb +128 -112
- data/lib/asciimath/latex.rb +399 -0
- data/lib/asciimath/markup.rb +509 -0
- data/lib/asciimath/mathml.rb +206 -87
- data/lib/asciimath/parser.rb +510 -350
- data/lib/asciimath/symbol_table.rb +25 -0
- data/lib/asciimath/version.rb +1 -1
- data/spec/ast.rb +144 -0
- data/spec/customisation_spec.rb +28 -0
- data/spec/parser_spec.rb +623 -165
- data/spec/schema/mathml2/common/common-attribs.xsd +41 -0
- data/spec/schema/mathml2/common/math.xsd +126 -0
- data/spec/schema/mathml2/common/xlink-href.xsd +20 -0
- data/spec/schema/mathml2/content/arith.xsd +90 -0
- data/spec/schema/mathml2/content/calculus.xsd +146 -0
- data/spec/schema/mathml2/content/common-attrib.xsd +30 -0
- data/spec/schema/mathml2/content/constants.xsd +83 -0
- data/spec/schema/mathml2/content/constructs.xsd +260 -0
- data/spec/schema/mathml2/content/elementary-functions.xsd +117 -0
- data/spec/schema/mathml2/content/functions.xsd +73 -0
- data/spec/schema/mathml2/content/linear-algebra.xsd +173 -0
- data/spec/schema/mathml2/content/logic.xsd +53 -0
- data/spec/schema/mathml2/content/relations.xsd +55 -0
- data/spec/schema/mathml2/content/semantics.xsd +85 -0
- data/spec/schema/mathml2/content/sets.xsd +236 -0
- data/spec/schema/mathml2/content/statistics.xsd +136 -0
- data/spec/schema/mathml2/content/tokens.xsd +120 -0
- data/spec/schema/mathml2/content/vector-calculus.xsd +88 -0
- data/spec/schema/mathml2/mathml2.xsd +59 -0
- data/spec/schema/mathml2/presentation/action.xsd +44 -0
- data/spec/schema/mathml2/presentation/characters.xsd +37 -0
- data/spec/schema/mathml2/presentation/common-attribs.xsd +113 -0
- data/spec/schema/mathml2/presentation/common-types.xsd +103 -0
- data/spec/schema/mathml2/presentation/error.xsd +40 -0
- data/spec/schema/mathml2/presentation/layout.xsd +195 -0
- data/spec/schema/mathml2/presentation/scripts.xsd +186 -0
- data/spec/schema/mathml2/presentation/space.xsd +52 -0
- data/spec/schema/mathml2/presentation/style.xsd +69 -0
- data/spec/schema/mathml2/presentation/table.xsd +216 -0
- data/spec/schema/mathml2/presentation/tokens.xsd +124 -0
- data/spec/schema/mathml3/mathml3-common.xsd +99 -0
- data/spec/schema/mathml3/mathml3-content.xsd +684 -0
- data/spec/schema/mathml3/mathml3-presentation.xsd +2151 -0
- data/spec/schema/mathml3/mathml3-strict-content.xsd +186 -0
- data/spec/schema/mathml3/mathml3.xsd +9 -0
- metadata +88 -48
- data/.travis.yml +0 -18
data/CHANGELOG.adoc
CHANGED
@@ -1,5 +1,51 @@
|
|
1
1
|
= Asciimath Changelog
|
2
2
|
|
3
|
+
== 2.0.3
|
4
|
+
|
5
|
+
Enhancements::
|
6
|
+
|
7
|
+
* Issue #59: make escaping of non-ASCII characters in MathML and HTML output optional.
|
8
|
+
|
9
|
+
Bug fixes::
|
10
|
+
|
11
|
+
* Issue #58: produce `<mo>` tags instead of `<mi>` tags for non-alphanumeric strings.
|
12
|
+
* Issue #62: resolve infinite recursion in LaTeX generator.
|
13
|
+
|
14
|
+
== 2.0.2
|
15
|
+
|
16
|
+
Enhancements::
|
17
|
+
|
18
|
+
* Issue #51: generate `accent` and `accentunder` attributes on `munder`, `mover`, and `munderover` tags when appropriate
|
19
|
+
* Issue #52: map `phi` to U+03D5 and `varphi` to U+03C6 by default.
|
20
|
+
* Issue #53: Add support for Roman style font command `rm`.
|
21
|
+
|
22
|
+
Bug fixes::
|
23
|
+
|
24
|
+
* Issue #50: fix parsing error when unary and binary operators are missing an operand
|
25
|
+
|
26
|
+
== 2.0.1
|
27
|
+
|
28
|
+
Enhancements::
|
29
|
+
|
30
|
+
* Issue #40: add `ker` as standard symbol (@GarkGarcia)
|
31
|
+
* Issue #46: allow customisation of the parsing and rendering symbol tables (@GarkGarcia)
|
32
|
+
|
33
|
+
Bug fixes::
|
34
|
+
|
35
|
+
* Issue #48: fix parsing of single column matrices (@ronaldtse)
|
36
|
+
|
37
|
+
== 2.0.0
|
38
|
+
|
39
|
+
Enhancements::
|
40
|
+
|
41
|
+
* The AsciiMath parser has been significantly improved and should now have reached feature parity with the original Javascript implementation.
|
42
|
+
* Issue #16: add initial LaTeX output support (@GarkGarcia)
|
43
|
+
* Issue #26: avoid generating `<mfenced>` tags in MathML output
|
44
|
+
|
45
|
+
Bug fixes::
|
46
|
+
|
47
|
+
* Issue #14: add support for additional symbols and functions that are supported by Asciimath JS
|
48
|
+
|
3
49
|
== 1.0.9
|
4
50
|
|
5
51
|
Bug fixes::
|
@@ -60,4 +106,4 @@ Enhancements::
|
|
60
106
|
|
61
107
|
Initial release::
|
62
108
|
|
63
|
-
* An AsciiMath parser and MathML converter written in Ruby
|
109
|
+
* An AsciiMath parser and MathML converter written in Ruby
|
data/Gemfile
CHANGED
data/README.adoc
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
# AsciiMath
|
2
|
+
:uri-project: https://github.com/asciidoctor/asciimath
|
2
3
|
ifndef::env-site[:status:]
|
3
4
|
|
4
|
-
An http://asciimath.org[AsciiMath] parser and MathML generator written in pure Ruby.
|
5
|
+
An http://asciimath.org[AsciiMath] parser and MathML/LaTeX generator written in pure Ruby.
|
5
6
|
|
6
7
|
ifdef::status[]
|
7
8
|
[discrete]
|
8
9
|
## Status
|
9
10
|
|
10
|
-
image:
|
11
|
+
image:{uri-project}/workflows/CI/badge.svg?branch=master["Build Status", link={uri-project}/actions?query=branch%3Amaster]
|
11
12
|
image:https://img.shields.io/gem/v/asciimath.svg?label=gem%20version[Gem Version, link=https://rubygems.org/gems/asciimath]
|
12
13
|
endif::status[]
|
13
14
|
|
@@ -48,15 +49,16 @@ parsed_expression = AsciiMath.parse(asciimath)
|
|
48
49
|
|
49
50
|
The parsed expression is a set of nested Array and Hash objects.
|
50
51
|
|
51
|
-
This expression can then be converted to MathML
|
52
|
+
This expression can then be converted to MathML, HTML (experimental) or LaTeX.
|
52
53
|
|
53
54
|
[source,ruby]
|
54
55
|
----
|
55
56
|
math_ml = parsed_expression.to_mathml
|
56
57
|
html = parsed_expression.to_html
|
58
|
+
latex = parsed_expression.to_latex
|
57
59
|
----
|
58
60
|
|
59
|
-
The MathML or
|
61
|
+
The MathML, HTML or LaTeX code is returned as a String.
|
60
62
|
|
61
63
|
### Command line
|
62
64
|
|
@@ -78,8 +80,53 @@ asciimath mathml "an asciimath string"
|
|
78
80
|
asciimath html "an asciimath string"
|
79
81
|
----
|
80
82
|
|
83
|
+
.LaTeX Generation
|
84
|
+
[source]
|
85
|
+
----
|
86
|
+
asciimath latex "an asciimath string"
|
87
|
+
----
|
88
|
+
|
81
89
|
This command will print out the generated code on stdout.
|
82
90
|
|
91
|
+
## Extentions and Customization
|
92
|
+
|
93
|
+
The parser can be extended by passing a custum tokenization table:
|
94
|
+
|
95
|
+
[source, ruby]
|
96
|
+
----
|
97
|
+
my_tokens_table = AsciiMath::SymbolTableBuilder.new
|
98
|
+
AsciiMath::Parser.add_default_parser_symbols(my_tokens_table)
|
99
|
+
my_tokens_table.add('mysymbol', :mysymbol, :symbol)
|
100
|
+
|
101
|
+
AsciiMath::parse("a + mysymbol + b", my_tokens_table.build)
|
102
|
+
----
|
103
|
+
|
104
|
+
Furthermore, the behaviour of the tokenizer be customized by altering the value
|
105
|
+
associated with a token in `AsciiMath::Tokenizer::DEFAULT_PARSE_SYMBOL_TABLE`:
|
106
|
+
|
107
|
+
[source, ruby]
|
108
|
+
----
|
109
|
+
my_tokens_table = AsciiMath::SymbolTableBuilder.new
|
110
|
+
AsciiMath::Parser.add_default_parser_symbols(my_tokens_table)
|
111
|
+
my_tokens_table.add('alpha', :beta, :symbol)
|
112
|
+
|
113
|
+
# Now "alpha + beta" is equivalent to "beta + beta"
|
114
|
+
AsciiMath::parse("alpha + beta", my_tokens_table.build)
|
115
|
+
----
|
116
|
+
|
117
|
+
The same behaviour applies to each individual render (`MathMLBuilder`,
|
118
|
+
`HTMLBuilder` and `LatexBuilder`). By adding entries to a rendere's rendering
|
119
|
+
table (or modifying exisisting entries), users can customize it's output:
|
120
|
+
|
121
|
+
[source, ruby]
|
122
|
+
----
|
123
|
+
my_rendering_table = AsciiMath::SymbolTableBuilder.new
|
124
|
+
AsciiMath::MarkupBuilder.add_default_display_symbols(my_rendering_table)
|
125
|
+
my_rendering_table.add('alpha', '\u03b2', :identifier)
|
126
|
+
|
127
|
+
# Now "alpha + beta" is equivalent to "beta + beta"
|
128
|
+
AsciiMath::parse("alpha + beta").to_mathml(my_rendering_table.build)
|
129
|
+
----
|
83
130
|
|
84
131
|
## Notes on the HTML Output
|
85
132
|
|
@@ -95,6 +142,23 @@ Known issues are as follows:
|
|
95
142
|
Rendering the HTML output correctly requires the inclusion of `style/math.css` in the html document.
|
96
143
|
There is currently no specific required font for this output, it simply selects a `serif` font family - change the `@font-family` attribute in the `.math-inline` class to select something specific.
|
97
144
|
|
145
|
+
## Notes on the LaTeX Output
|
146
|
+
|
147
|
+
All LaTeX commands and environments used in the output are coverved by
|
148
|
+
https://ctan.org/pkg/amsmath[`amsmath`] and `amssymb`, with a few exceptions:
|
149
|
+
|
150
|
+
* `\color`
|
151
|
+
* `\cancel`
|
152
|
+
* `\mathscr`
|
153
|
+
* `\twoheadrightarrowtail`
|
154
|
+
|
155
|
+
The `\color` command is supported by the
|
156
|
+
https://www.ctan.org/pkg/xcolor[`xcolor`] package, which is included in most
|
157
|
+
LaTeX distributions. The `\cancel` command is supported by the
|
158
|
+
https://www.ctan.org/pkg/cancel[cancel] package, also included in most LaTeX
|
159
|
+
distributions. The other commands are supported by the
|
160
|
+
https://ctan.org/pkg/stix[`stix`] package.
|
161
|
+
|
98
162
|
## Contributing
|
99
163
|
|
100
164
|
. Fork it (https://github.com/pepijnve/asciimath/fork)
|
data/asciimath.gemspec
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
lib = File.expand_path('../lib', __FILE__)
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
4
5
|
require 'asciimath/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
8
|
spec.name = "asciimath"
|
8
9
|
spec.version = AsciiMath::VERSION
|
9
|
-
spec.authors = ["Pepijn Van Eeckhoudt"]
|
10
|
-
spec.email = ["pepijn@vaneeckhoudt.net"]
|
10
|
+
spec.authors = ["Pepijn Van Eeckhoudt", "Gark Garcia"]
|
11
|
+
spec.email = ["pepijn@vaneeckhoudt.net", "pablo-ecobar@riseup.net"]
|
11
12
|
spec.summary = %q{AsciiMath parser and converter}
|
12
13
|
spec.description = %q{A pure Ruby AsciiMath parsing and conversion library.}
|
13
14
|
spec.homepage = ""
|
@@ -17,8 +18,4 @@ Gem::Specification.new do |spec|
|
|
17
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
20
|
spec.require_paths = ["lib"]
|
20
|
-
|
21
|
-
spec.add_development_dependency "bundler", "> 0"
|
22
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
23
|
-
spec.add_development_dependency "rspec", "~> 3.1.0"
|
24
21
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative 'lib/asciimath'
|
2
|
+
|
3
|
+
def escape_adoc(adoc)
|
4
|
+
case adoc
|
5
|
+
when nil
|
6
|
+
''
|
7
|
+
when '+'
|
8
|
+
adoc
|
9
|
+
else
|
10
|
+
"++#{adoc.gsub('|', '\\|')}++"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
puts "|==="
|
15
|
+
puts '|AsciiMath |Symbol |MathML Value |LaTeX Value'
|
16
|
+
puts
|
17
|
+
|
18
|
+
AsciiMath::Parser::DEFAULT_PARSER_SYMBOL_TABLE.each_pair do |asciimath, value|
|
19
|
+
sym = value[:value]
|
20
|
+
unless sym.is_a?(Symbol)
|
21
|
+
next
|
22
|
+
end
|
23
|
+
|
24
|
+
mathml = AsciiMath::MathMLBuilder.default_display_symbol_table[sym]
|
25
|
+
|
26
|
+
if mathml
|
27
|
+
val = mathml[:value]
|
28
|
+
else
|
29
|
+
val = "Missing!!!!!"
|
30
|
+
end
|
31
|
+
|
32
|
+
latex = AsciiMath::LatexBuilder::SYMBOLS[sym] || "\\#{sym.to_s}"
|
33
|
+
|
34
|
+
codepoint = ""
|
35
|
+
if val.is_a?(String)
|
36
|
+
codepoint = val.codepoints.map do |cp|
|
37
|
+
cpstr = sprintf('U+%04X', cp)
|
38
|
+
"https://codepoints.net/#{cpstr}[#{cpstr}]"
|
39
|
+
end.join(' ')
|
40
|
+
end
|
41
|
+
|
42
|
+
puts "|#{escape_adoc(asciimath)} |:#{sym.to_s} |#{escape_adoc(val.to_s)} (#{codepoint}) |#{escape_adoc(latex)}"
|
43
|
+
end
|
44
|
+
|
45
|
+
puts "|==="
|
46
|
+
puts
|
data/lib/asciimath.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
1
|
+
require_relative 'asciimath/version'
|
2
|
+
require_relative 'asciimath/parser'
|
3
|
+
require_relative 'asciimath/mathml'
|
4
|
+
require_relative 'asciimath/html'
|
5
|
+
require_relative 'asciimath/latex'
|
@@ -0,0 +1,456 @@
|
|
1
|
+
module AsciiMath
|
2
|
+
module AST
|
3
|
+
def expression(*e)
|
4
|
+
case e.length
|
5
|
+
when 0
|
6
|
+
nil
|
7
|
+
when 1
|
8
|
+
e[0]
|
9
|
+
else
|
10
|
+
Sequence.new(e)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def paren(lparen, e, rparen)
|
15
|
+
Paren.new(lparen, e, rparen)
|
16
|
+
end
|
17
|
+
|
18
|
+
def group(lparen, e, rparen)
|
19
|
+
Group.new(lparen, e, rparen)
|
20
|
+
end
|
21
|
+
|
22
|
+
def subsup(e, sub, sup)
|
23
|
+
SubSup.new(e, sub, sup)
|
24
|
+
end
|
25
|
+
|
26
|
+
def sub(e, sub)
|
27
|
+
SubSup.new(e, sub, nil)
|
28
|
+
end
|
29
|
+
|
30
|
+
def sup(e, sup)
|
31
|
+
SubSup.new(e, nil, sup)
|
32
|
+
end
|
33
|
+
|
34
|
+
def unary(operator, e)
|
35
|
+
UnaryOp.new(operator, e)
|
36
|
+
end
|
37
|
+
|
38
|
+
def binary(operator, e1, e2)
|
39
|
+
BinaryOp.new(operator, e1, e2)
|
40
|
+
end
|
41
|
+
|
42
|
+
def infix(e1, operator, e2)
|
43
|
+
InfixOp.new(operator, e1, e2)
|
44
|
+
end
|
45
|
+
|
46
|
+
def text(value)
|
47
|
+
Text.new(value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def number(value)
|
51
|
+
Number.new(value)
|
52
|
+
end
|
53
|
+
|
54
|
+
def symbol(symbol, text)
|
55
|
+
Symbol.new(symbol, text)
|
56
|
+
end
|
57
|
+
|
58
|
+
def identifier(value)
|
59
|
+
Identifier.new(value)
|
60
|
+
end
|
61
|
+
|
62
|
+
def matrix(lparen, rows, rparen)
|
63
|
+
Matrix.new(lparen, rows, rparen)
|
64
|
+
end
|
65
|
+
|
66
|
+
def color(r, g, b, text)
|
67
|
+
Color.new(r, g, b, text)
|
68
|
+
end
|
69
|
+
|
70
|
+
class Node
|
71
|
+
attr_reader :parent
|
72
|
+
|
73
|
+
def initialize
|
74
|
+
@parent = nil
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
attr_writer :parent
|
80
|
+
end
|
81
|
+
|
82
|
+
class InnerNode < Node
|
83
|
+
include Enumerable
|
84
|
+
|
85
|
+
def initialize
|
86
|
+
super
|
87
|
+
@children = []
|
88
|
+
end
|
89
|
+
|
90
|
+
def [](*args)
|
91
|
+
@children[*args]
|
92
|
+
end
|
93
|
+
|
94
|
+
def length
|
95
|
+
@children.length
|
96
|
+
end
|
97
|
+
|
98
|
+
def each(&block)
|
99
|
+
@children.each(&block)
|
100
|
+
end
|
101
|
+
|
102
|
+
protected
|
103
|
+
|
104
|
+
def child_nodes
|
105
|
+
@children
|
106
|
+
end
|
107
|
+
|
108
|
+
def add(node)
|
109
|
+
node.parent.remove(node) if node.parent
|
110
|
+
node.parent = self
|
111
|
+
child_nodes << node
|
112
|
+
end
|
113
|
+
|
114
|
+
def remove(node)
|
115
|
+
node.parent = nil
|
116
|
+
child_nodes.delete(node)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class Sequence < InnerNode
|
121
|
+
def initialize(nodes)
|
122
|
+
super()
|
123
|
+
nodes.each { |node| add(node) }
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_s
|
127
|
+
child_nodes.map { |node| node.to_s }.join(" ")
|
128
|
+
end
|
129
|
+
|
130
|
+
def ==(o)
|
131
|
+
o.class == self.class && o.child_nodes == child_nodes
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class Paren < InnerNode
|
136
|
+
attr_reader :lparen
|
137
|
+
attr_reader :rparen
|
138
|
+
|
139
|
+
def initialize(lparen, e, rparen)
|
140
|
+
super()
|
141
|
+
@lparen = lparen
|
142
|
+
@rparen = rparen
|
143
|
+
add(e) if e
|
144
|
+
end
|
145
|
+
|
146
|
+
def expression
|
147
|
+
child_nodes[0]
|
148
|
+
end
|
149
|
+
|
150
|
+
def to_s
|
151
|
+
"#{lparen.nil? ? '' : lparen.text}#{expression}#{rparen.nil? ? '' : rparen.text}"
|
152
|
+
end
|
153
|
+
|
154
|
+
def ==(o)
|
155
|
+
o.class == self.class && o.lparen == lparen && o.expression == expression && o.rparen == rparen
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class Group < InnerNode
|
160
|
+
attr_reader :lparen
|
161
|
+
attr_reader :rparen
|
162
|
+
|
163
|
+
def initialize(lparen, e, rparen)
|
164
|
+
super()
|
165
|
+
@lparen = lparen
|
166
|
+
@rparen = rparen
|
167
|
+
add(e) if e
|
168
|
+
end
|
169
|
+
|
170
|
+
def expression
|
171
|
+
child_nodes[0]
|
172
|
+
end
|
173
|
+
|
174
|
+
def to_s
|
175
|
+
"#{lparen.nil? ? '' : lparen.text}#{expression}#{rparen.nil? ? '' : rparen.text}"
|
176
|
+
end
|
177
|
+
|
178
|
+
def ==(o)
|
179
|
+
o.class == self.class && o.lparen == lparen && o.expression == expression && o.rparen == rparen
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
class SubSup < InnerNode
|
184
|
+
def initialize(e, sub, sup)
|
185
|
+
super()
|
186
|
+
add(e)
|
187
|
+
add(sub || Empty.new)
|
188
|
+
add(sup || Empty.new)
|
189
|
+
end
|
190
|
+
|
191
|
+
def base_expression
|
192
|
+
child_nodes[0]
|
193
|
+
end
|
194
|
+
|
195
|
+
def sub_expression
|
196
|
+
child = child_nodes[1]
|
197
|
+
child.is_a?(Empty) ? nil : child
|
198
|
+
end
|
199
|
+
|
200
|
+
def sup_expression
|
201
|
+
child = child_nodes[2]
|
202
|
+
child.is_a?(Empty) ? nil : child
|
203
|
+
end
|
204
|
+
|
205
|
+
def to_s
|
206
|
+
s = ""
|
207
|
+
s << base_expression.to_s
|
208
|
+
sub = sub_expression
|
209
|
+
if sub
|
210
|
+
s << "_" << sub.to_s
|
211
|
+
end
|
212
|
+
sup = sup_expression
|
213
|
+
if sup
|
214
|
+
s << "^" << sup.to_s
|
215
|
+
end
|
216
|
+
s
|
217
|
+
end
|
218
|
+
|
219
|
+
def ==(o)
|
220
|
+
o.class == self.class && o.base_expression == base_expression && o.sub_expression == sub_expression && o.sup_expression == sup_expression
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
class UnaryOp < InnerNode
|
225
|
+
def initialize(operator, e)
|
226
|
+
super()
|
227
|
+
add(operator)
|
228
|
+
add(e)
|
229
|
+
end
|
230
|
+
|
231
|
+
def operator
|
232
|
+
child_nodes[0]
|
233
|
+
end
|
234
|
+
|
235
|
+
def operand
|
236
|
+
child_nodes[1]
|
237
|
+
end
|
238
|
+
|
239
|
+
def to_s
|
240
|
+
"#{operator} #{operand}"
|
241
|
+
end
|
242
|
+
|
243
|
+
def ==(o)
|
244
|
+
o.class == self.class && o.operator == operator && o.operand == operand
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
class BinaryOp < InnerNode
|
249
|
+
def initialize(operator, e1, e2)
|
250
|
+
super()
|
251
|
+
add(operator)
|
252
|
+
add(e1)
|
253
|
+
add(e2)
|
254
|
+
end
|
255
|
+
|
256
|
+
|
257
|
+
def operator
|
258
|
+
child_nodes[0]
|
259
|
+
end
|
260
|
+
|
261
|
+
def operand1
|
262
|
+
child_nodes[1]
|
263
|
+
end
|
264
|
+
|
265
|
+
def operand2
|
266
|
+
child_nodes[2]
|
267
|
+
end
|
268
|
+
|
269
|
+
def to_s
|
270
|
+
"#{operator} #{operand1} #{operand2}"
|
271
|
+
end
|
272
|
+
|
273
|
+
def ==(o)
|
274
|
+
o.class == self.class && o.operator == operator && o.operand1 == operand1 && o.operand2 == operand2
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
class InfixOp < InnerNode
|
279
|
+
def initialize(operator, e1, e2)
|
280
|
+
super()
|
281
|
+
add(operator)
|
282
|
+
add(e1)
|
283
|
+
add(e2)
|
284
|
+
end
|
285
|
+
|
286
|
+
|
287
|
+
def operator
|
288
|
+
child_nodes[0]
|
289
|
+
end
|
290
|
+
|
291
|
+
def operand1
|
292
|
+
child_nodes[1]
|
293
|
+
end
|
294
|
+
|
295
|
+
def operand2
|
296
|
+
child_nodes[2]
|
297
|
+
end
|
298
|
+
|
299
|
+
def to_s
|
300
|
+
"#{operand1} #{operator} #{operand2}"
|
301
|
+
end
|
302
|
+
|
303
|
+
def ==(o)
|
304
|
+
o.class == self.class && o.operator == operator && o.operand1 == operand1 && o.operand2 == operand2
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
class ValueNode < Node
|
309
|
+
attr_reader :value
|
310
|
+
|
311
|
+
def initialize(value)
|
312
|
+
super()
|
313
|
+
@value = value
|
314
|
+
end
|
315
|
+
|
316
|
+
def to_s
|
317
|
+
value.to_s
|
318
|
+
end
|
319
|
+
|
320
|
+
def ==(o)
|
321
|
+
o.class == self.class && o.value == value
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
class Text < ValueNode
|
326
|
+
def initialize(value)
|
327
|
+
super(value.dup.freeze)
|
328
|
+
end
|
329
|
+
|
330
|
+
def to_s
|
331
|
+
'"' + super + '"'
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
class Number < ValueNode
|
336
|
+
def initialize(value)
|
337
|
+
super(value.dup.freeze)
|
338
|
+
end
|
339
|
+
end
|
340
|
+
|
341
|
+
class Symbol < ValueNode
|
342
|
+
attr_reader :text
|
343
|
+
|
344
|
+
def initialize(value, text)
|
345
|
+
super(value)
|
346
|
+
@text = text.dup.freeze
|
347
|
+
end
|
348
|
+
|
349
|
+
def ==(o)
|
350
|
+
super && o.text == text
|
351
|
+
end
|
352
|
+
|
353
|
+
def to_s
|
354
|
+
text
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
class Identifier < ValueNode
|
359
|
+
def initialize(value)
|
360
|
+
super(value.dup.freeze)
|
361
|
+
end
|
362
|
+
end
|
363
|
+
|
364
|
+
class Color < ValueNode
|
365
|
+
attr_reader :text
|
366
|
+
|
367
|
+
def initialize(r, g, b, text)
|
368
|
+
super({:r => r, :g => g, :b => b}.freeze)
|
369
|
+
@text = text.dup.freeze
|
370
|
+
end
|
371
|
+
|
372
|
+
def red
|
373
|
+
value[:r]
|
374
|
+
end
|
375
|
+
|
376
|
+
def green
|
377
|
+
value[:g]
|
378
|
+
end
|
379
|
+
|
380
|
+
def blue
|
381
|
+
value[:b]
|
382
|
+
end
|
383
|
+
|
384
|
+
def ==(o)
|
385
|
+
o.class == self.class &&
|
386
|
+
o.red == red &&
|
387
|
+
o.green == green &&
|
388
|
+
o.blue == blue &&
|
389
|
+
o.text == text
|
390
|
+
end
|
391
|
+
|
392
|
+
def to_hex_rgb
|
393
|
+
sprintf('#%02x%02x%02x', red, green, blue)
|
394
|
+
end
|
395
|
+
|
396
|
+
def to_s
|
397
|
+
text
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
class Matrix < InnerNode
|
402
|
+
attr_reader :lparen
|
403
|
+
attr_reader :rparen
|
404
|
+
|
405
|
+
def initialize(lparen, rows, rparen)
|
406
|
+
super()
|
407
|
+
@lparen = lparen
|
408
|
+
@rparen = rparen
|
409
|
+
rows.map { |row| MatrixRow.new(row) }.each { |row_seq| add(row_seq) }
|
410
|
+
end
|
411
|
+
|
412
|
+
def to_s
|
413
|
+
s = ""
|
414
|
+
s << (lparen.nil? ? '{:' : lparen.text)
|
415
|
+
s << child_nodes.map { |node| node.to_s }.join(",")
|
416
|
+
s << (rparen.nil? ? ':}' : rparen.text)
|
417
|
+
end
|
418
|
+
|
419
|
+
def ==(o)
|
420
|
+
o.class == self.class &&
|
421
|
+
o.lparen == lparen &&
|
422
|
+
o.child_nodes == child_nodes &&
|
423
|
+
o.rparen == rparen
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
class MatrixRow < InnerNode
|
428
|
+
def initialize(nodes)
|
429
|
+
super()
|
430
|
+
nodes.each { |node| add(node || Empty.new) }
|
431
|
+
end
|
432
|
+
|
433
|
+
def to_s
|
434
|
+
"(" + child_nodes.map { |node| node.to_s }.join(",") + ")"
|
435
|
+
end
|
436
|
+
|
437
|
+
def ==(o)
|
438
|
+
o.class == self.class && o.child_nodes == child_nodes
|
439
|
+
end
|
440
|
+
end
|
441
|
+
|
442
|
+
class Empty < Node
|
443
|
+
def initialize()
|
444
|
+
super
|
445
|
+
end
|
446
|
+
|
447
|
+
def to_s
|
448
|
+
''
|
449
|
+
end
|
450
|
+
|
451
|
+
def ==(o)
|
452
|
+
o.class == self.class
|
453
|
+
end
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|