math_ml 0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Extension of String class by MathML Library
4
+ #
5
+ # Copyright (C) 2007, KURODA Hiraku <hiraku@hinet.mydns.jp>
6
+ # You can redistribute it and/or modify it under GPL2.
7
+ #
8
+
9
+ require "math_ml"
10
+
11
+ module MathML
12
+ module String
13
+ @@mathml_latex_parser = nil
14
+ def self.mathml_latex_parser
15
+ @@mathml_latex_parser = MathML::LaTeX::Parser.new unless @@mathml_latex_parser
16
+ @@mathml_latex_parser
17
+ end
18
+
19
+ def self.mathml_latex_parser=(mlp)
20
+ raise TypeError unless mlp.is_a?(MathML::LaTeX::Parser) || mlp==nil
21
+ @@mathml_latex_parser = mlp
22
+ end
23
+
24
+ def to_mathml(displaystyle=false)
25
+ MathML::String.mathml_latex_parser.parse(self, displaystyle)
26
+ end
27
+ end
28
+ end
29
+
30
+ class String
31
+ include MathML::String
32
+ end
@@ -0,0 +1,350 @@
1
+ #!/usr/bin/ruby
2
+ #
3
+ # Utility for MathML Library
4
+ #
5
+ # Copyright (C) 2006, KURODA Hiraku <hiraku@hinet.mydns.jp>
6
+ # You can redistribute it and/or modify it under GPL2.
7
+ #
8
+
9
+ require "math_ml"
10
+
11
+ module MathML::Util
12
+ ESCAPES = {"<"=>"lt",
13
+ ">"=>"gt",
14
+ "&"=>"amp",
15
+ "\""=>"quot",
16
+ "'"=>"apos"
17
+ }
18
+ INVALID_RE = /(?!)/
19
+ EQNARRAY_RE = /\\begin\s*\{eqnarray\}(#{MathML::LaTeX::MBEC}*?)\\end\s*\{eqnarray\}/
20
+ SINGLE_COMMAND_RE = /(\\([a-zA-Z]+))[ \t]?/
21
+
22
+ def self.escapeXML(s, br=false)
23
+ r = s.gsub(/[<>&"']/){|m| "&#{ESCAPES[m]};"}
24
+ br ? r.gsub(/\n/, "<br />\n") : r
25
+ end
26
+
27
+ def escapeXML(s, br=false)
28
+ MathML::Util.escapeXML(s, br)
29
+ end
30
+
31
+ def self.collect_regexp(a)
32
+ if a
33
+ a = [a].flatten
34
+ a.size>0 ? Regexp.new(a.inject(""){|r, i| i.is_a?(Regexp) ? "#{r}#{i.to_s}|" : r}.chop) : INVALID_RE
35
+ else
36
+ INVALID_RE
37
+ end
38
+ end
39
+
40
+ def collect_regexp(a)
41
+ MathML::Util.collect_regexp(a)
42
+ end
43
+
44
+ class MathData
45
+ attr_reader :math_list, :msrc_list, :dmath_list, :dsrc_list, :escape_list, :esrc_list, :user_list, :usrc_list
46
+ def initialize
47
+ @math_list = []
48
+ @msrc_list = []
49
+ @dmath_list = []
50
+ @dsrc_list = []
51
+ @escape_list = []
52
+ @esrc_list = []
53
+ @user_list = []
54
+ @usrc_list = []
55
+ end
56
+
57
+ def update(s)
58
+ @math_list.concat(s.math_list)
59
+ @msrc_list.concat(s.msrc_list)
60
+ @dmath_list.concat(s.dmath_list)
61
+ @dsrc_list.concat(s.dsrc_list)
62
+ @escape_list.concat(s.escape_list)
63
+ @esrc_list.concat(s.esrc_list)
64
+ @user_list.concat(s.user_list)
65
+ @usrc_list.concat(s.usrc_list)
66
+ end
67
+ end
68
+
69
+ class SimpleLaTeX
70
+ include MathML::Util
71
+ @@default_latex = nil
72
+ DEFAULT = {
73
+ :delimiter=>"\001",
74
+ :math_env_list=>[
75
+ /\$((?:\\.|[^\\\$])#{MathML::LaTeX::MBEC}*?)\$/m,
76
+ /\\\((#{MathML::LaTeX::MBEC}*?)\\\)/m
77
+ ],
78
+ :dmath_env_list=>[
79
+ /\$\$(#{MathML::LaTeX::MBEC}*?)\$\$/m,
80
+ /\\\[(#{MathML::LaTeX::MBEC}*?)\\\]/m
81
+ ],
82
+ :escape_list=>[
83
+ /\\(.)/m
84
+ ],
85
+ :through_list=>[
86
+ ],
87
+ :escape_any=> false,
88
+ :without_parse=>false
89
+ }
90
+
91
+ def initialize(options = {})
92
+ @params = DEFAULT.merge(options)
93
+ @params[:parser] = MathML::LaTeX::Parser.new unless @params[:parser] || @params[:without_parse]
94
+
95
+ @params[:math_envs] = collect_regexp(@params[:math_env_list])
96
+ @params[:dmath_envs] = collect_regexp(@params[:dmath_env_list])
97
+ @params[:escapes] = collect_regexp(@params[:escape_list])
98
+ @params[:throughs] = collect_regexp(@params[:through_list])
99
+ reset_encode_proc
100
+ reset_rescue_proc
101
+ reset_decode_proc
102
+ reset_unencode_proc
103
+ end
104
+
105
+ def reset_encode_proc
106
+ @encode_proc_re = INVALID_RE
107
+ @encode_proc = nil
108
+ end
109
+
110
+ def set_encode_proc(*re, &proc)
111
+ @encode_proc_re = collect_regexp(re)
112
+ @encode_proc = proc
113
+ end
114
+
115
+ def reset_rescue_proc
116
+ @rescue_proc = nil
117
+ end
118
+
119
+ def set_rescue_proc(&proc)
120
+ @rescue_proc = proc
121
+ end
122
+
123
+ def reset_decode_proc
124
+ @decode_proc = nil
125
+ end
126
+
127
+ def set_decode_proc(&proc)
128
+ @decode_proc = proc
129
+ end
130
+
131
+ def set_unencode_proc(&proc)
132
+ @unencode_proc = proc
133
+ end
134
+
135
+ def reset_unencode_proc
136
+ @unencode_proc = nil
137
+ end
138
+
139
+ def encode(src, *proc_re, &proc)
140
+ if proc_re.size>0 && proc_re[0].is_a?(MathData)
141
+ data = proc_re.shift
142
+ else
143
+ data = MathData.new
144
+ end
145
+
146
+ proc_re = proc_re.size==0 ? @encode_proc_re : collect_regexp(proc_re)
147
+ proc = @encode_proc unless proc
148
+
149
+ s = StringScanner.new(src)
150
+ encoded = ""
151
+
152
+ until s.eos?
153
+ if s.scan(/(.*?)(((((#{@params[:throughs]})|#{@params[:dmath_envs]})|#{@params[:math_envs]})|#{proc_re})|#{@params[:escapes]})/m)
154
+ encoded << s[1]
155
+ case
156
+ when s[6]
157
+ encoded << s[6]
158
+ when s[5], s[4]
159
+ env_src = s[5] || s[4]
160
+ if @params[:dmath_envs]=~env_src
161
+ encoded << "#{@params[:delimiter]}d#{data.dsrc_list.size}#{@params[:delimiter]}"
162
+ data.dsrc_list << env_src
163
+ else
164
+ encoded << "#{@params[:delimiter]}m#{data.msrc_list.size}#{@params[:delimiter]}"
165
+ data.msrc_list << env_src
166
+ end
167
+ when s[3]
168
+ size = s[3].size
169
+ s.pos = left = s.pos-size
170
+ if r=proc.call(s)
171
+ right = s.pos
172
+ encoded << "#{@params[:delimiter]}u#{data.user_list.size}#{@params[:delimiter]}"
173
+ data.user_list << r
174
+ data.usrc_list << s.string[left...right]
175
+ else
176
+ encoded << s.peek(size)
177
+ s.pos = s.pos+size
178
+ end
179
+ when s[2]
180
+ encoded << "#{@params[:delimiter]}e#{data.escape_list.size}#{@params[:delimiter]}"
181
+ @params[:escapes]=~s[2]
182
+ data.esrc_list << s[2]
183
+ data.escape_list << escapeXML($+, true)
184
+ end
185
+ else
186
+ encoded << s.rest
187
+ s.terminate
188
+ end
189
+ end
190
+
191
+ parse(data, @params[:parser]) unless @params[:without_parse]
192
+
193
+ return encoded, data
194
+ end
195
+
196
+ def error_to_html(e)
197
+ "<br />\n#{escapeXML(e.message)}<br />\n<code>#{escapeXML(e.done).gsub(/\n/, "<br />\n")}<strong>#{escapeXML(e.rest).gsub(/\n/, "<br />\n")}</strong></code><br />"
198
+ end
199
+
200
+ def latex_parser
201
+ @params[:parser] = MathML::LaTeX::Parser.new unless @params[:parser]
202
+ @params[:parser]
203
+ end
204
+
205
+ def parse(data, parser=nil)
206
+ parser = latex_parser unless parser
207
+ (data.math_list.size...data.msrc_list.size).each do |i|
208
+ begin
209
+ @params[:math_envs]=~data.msrc_list[i]
210
+ data.math_list[i] = parser.parse($+)
211
+ rescue MathML::LaTeX::ParseError => e
212
+ if @rescue_proc
213
+ data.math_list[i] = @rescue_proc.call(e)
214
+ else
215
+ data.math_list[i] = error_to_html(e)
216
+ end
217
+ end
218
+ end
219
+ (data.dmath_list.size...data.dsrc_list.size).each do |i|
220
+ begin
221
+ @params[:dmath_envs]=~data.dsrc_list[i]
222
+ data.dmath_list[i] = parser.parse($+, true)
223
+ rescue MathML::LaTeX::ParseError => e
224
+ if @rescue_proc
225
+ data.dmath_list[i] = @rescue_proc.call(e)
226
+ else
227
+ data.dmath_list[i] = error_to_html(e)
228
+ end
229
+ end
230
+ end
231
+ end
232
+
233
+ def decode(encoded, data, without_parsed = false, &proc)
234
+ return nil if encoded==nil
235
+ proc = @decode_proc unless proc
236
+ encoded.gsub(/#{Regexp.escape(@params[:delimiter])}([demu])(\d+)#{Regexp.escape(@params[:delimiter])}/) do
237
+ i = $2.to_i
238
+ t, d, s =
239
+ case $1
240
+ when "d"
241
+ [:dmath, without_parsed ? escapeXML(data.dsrc_list[i], true) : data.dmath_list[i], data.dsrc_list[i]]
242
+ when "e"
243
+ [:escape, data.escape_list[i], data.esrc_list[i]]
244
+ when "m"
245
+ [:math, without_parsed ? escapeXML(data.msrc_list[i], true) : data.math_list[i], data.msrc_list[i]]
246
+ when "u"
247
+ [:user, data.user_list[i], data.usrc_list[i]]
248
+ end
249
+ if proc
250
+ proc.call(d, :type=>t, :index=>i, :src=>s) || d
251
+ else
252
+ d
253
+ end
254
+ end
255
+ end
256
+
257
+ def decode_partial(type, encoded, data, &proc)
258
+ return nil if encoded==nil
259
+ head =
260
+ case type
261
+ when :math
262
+ "m"
263
+ when :dmath
264
+ "d"
265
+ when :escape
266
+ "e"
267
+ when :user
268
+ "u"
269
+ else
270
+ return
271
+ end
272
+ encoded.gsub(/#{Regexp.escape(@params[:delimiter])}#{head}(\d+)#{Regexp.escape(@params[:delimiter])}/) do
273
+ i = $1.to_i
274
+ t, d, s =
275
+ case head
276
+ when "d"
277
+ [:dmath, data.dmath_list[i], data.dsrc_list[i]]
278
+ when "e"
279
+ [:escape, data.escape_list[i], data.esrc_list[i]]
280
+ when "m"
281
+ [:math, data.math_list[i], data.msrc_list[i]]
282
+ when "u"
283
+ [:user, data.user_list[i], data.usrc_list[i]]
284
+ end
285
+ if proc
286
+ proc.call(d, :type=>t, :index=>i, :src=>s) || "#{@params[:delimiter]}#{head}#{i}#{@params[:delimiter]}"
287
+ else
288
+ d
289
+ end
290
+ end
291
+ end
292
+
293
+ def unencode(encoded, data, without_escape=false, &proc)
294
+ return nil if encoded==nil
295
+ proc = @unencode_proc unless proc
296
+ encoded.gsub(/#{Regexp.escape(@params[:delimiter])}([demu])(\d+)#{Regexp.escape(@params[:delimiter])}/) do
297
+ i = $2.to_i
298
+ t, s =
299
+ case $1
300
+ when "d"
301
+ [:dmath, data.dsrc_list[i]]
302
+ when "e"
303
+ [:escape, data.esrc_list[i]]
304
+ when "m"
305
+ [:math, data.msrc_list[i]]
306
+ when "u"
307
+ [:user, data.usrc_list[i]]
308
+ end
309
+ s = escapeXML(s, true) unless without_escape
310
+ if proc
311
+ proc.call(s, :type=>t, :index=>i) || s
312
+ else
313
+ s
314
+ end
315
+ end
316
+ end
317
+
318
+ def self.encode(src)
319
+ @@default_latex = self.new unless @@default_latex
320
+ @@default_latex.encode(src)
321
+ end
322
+
323
+ def self.decode(src, data)
324
+ @@default_latex.decode(src, data)
325
+ end
326
+
327
+ def parse_eqnarray(src, parser=nil)
328
+ src = "\\begin{array}{ccc}#{src}\\end{array}"
329
+ parser = latex_parser unless parser
330
+ begin
331
+ parser.parse(src, true)
332
+ rescue MathML::LaTeX::ParseError => e
333
+ e = MathML::LaTeX::ParseError.new(e.message,
334
+ e.rest.sub(/\\end\{array\}\z/, '\end{eqnarray}'),
335
+ e.done.sub(/\A\\begin\{array\}\{ccc\}/, '\begin{eqnarray}'))
336
+ @rescue_proc ? @rescue_proc.call(e) : error_to_html(e)
337
+ end
338
+ end
339
+
340
+ def parse_single_command(src, parser=nil)
341
+ s = src[SINGLE_COMMAND_RE, 1]
342
+ parser = latex_parser unless parser
343
+ begin
344
+ parser.parse(s)
345
+ rescue MathML::LaTeX::ParseError => e
346
+ src[SINGLE_COMMAND_RE, 2]
347
+ end
348
+ end
349
+ end
350
+ end
data/lib/math_ml.rb ADDED
@@ -0,0 +1,25 @@
1
+ # MathML Library
2
+ #
3
+ # Copyright (C) 2005, KURODA Hiraku <hiraku@hinet.mydns.jp>
4
+ # You can redistribute it and/or modify it under GPL2.
5
+
6
+ require "strscan"
7
+ module MathML
8
+ require "eim_xml"
9
+
10
+ class XMLElement < EimXML::Element
11
+ def pop
12
+ @contents.pop
13
+ end
14
+ end
15
+
16
+ def self.pcstring(s, encoded=false)
17
+ EimXML::PCString.new(s, encoded)
18
+ end
19
+
20
+ class Error < StandardError; end
21
+ end
22
+
23
+ require "math_ml/element"
24
+ require "math_ml/latex"
25
+ require "math_ml/latex/builtin_commands"
@@ -0,0 +1,32 @@
1
+ require "math_ml"
2
+
3
+ describe MathML::Element do
4
+ it "#display_style and #as_display_style" do
5
+ MathML::Element.new("test").display_style.should == nil
6
+ e = MathML::Element.new("test")
7
+ r = e.as_display_style
8
+ r.should equal(e)
9
+ e.display_style.should be_true
10
+ end
11
+
12
+ it "#pop" do
13
+ e = MathML::Element.new("super")
14
+ s = MathML::Element.new("sub")
15
+
16
+ e.pop.should be_nil
17
+
18
+ e << s
19
+ e.pop.should equal(s)
20
+ e.pop.should be_nil
21
+
22
+ e << "text"
23
+ e.pop.should == "text"
24
+ e.pop.should be_nil
25
+ end
26
+
27
+ it "#to_s" do
28
+ e = MathML::Element.new("e")
29
+ e << "test<"
30
+ e.to_s.should == "<e>test&lt;</e>"
31
+ end
32
+ end
@@ -0,0 +1,122 @@
1
+ require "math_ml"
2
+ require "spec/util"
3
+
4
+ describe MathML::LaTeX::Macro do
5
+ include MathML::Spec::Util
6
+
7
+ before(:all) do
8
+ @src = <<'EOT'
9
+ \newcommand{\newcom}{test}
10
+ \newcommand{\paramcom}[2]{param2 #2, param1 #1.}
11
+ \newcommand\ALPHA\alpha
12
+ \newcommand\BETA[1]\beta
13
+ \newcommand{\nothing}{}
14
+ \newenvironment{newenv}{begin_newenv}{end_newenv}
15
+ \newenvironment{paramenv}[2]{begin 1:#1, 2:#2}{end 2:#2 1:#1}
16
+ \newenvironment{nothing}{}{}
17
+ \newenvironment{separated environment}{sep}{env}
18
+ \newenvironment ENV
19
+ EOT
20
+ end
21
+
22
+ before do
23
+ @m = MathML::LaTeX::Macro.new
24
+ @m.parse(@src)
25
+ end
26
+
27
+ it "#parse" do
28
+ @m = MathML::LaTeX::Macro.new
29
+ lambda{@m.parse(@src)}.should_not raise_error
30
+
31
+ @m = MathML::LaTeX::Macro.new
32
+ lambda{@m.parse('\newcommand{notcommand}{}')}.should raise_parse_error("Need newcommand.", '\\newcommand{', "notcommand}{}")
33
+ lambda{@m.parse('\newcommand{\separated command}{}')}.should raise_parse_error("Syntax error.", '\newcommand{\separated', " command}{}")
34
+ lambda{@m.parse('\newcommand{\nobody}')}.should raise_parse_error("Need parameter.", '\newcommand{\nobody}', "")
35
+ lambda{@m.parse('\newcommand{\noparam}{#1}')}.should raise_parse_error("Parameter \# too large.", '\newcommand{\noparam}{#', "1}")
36
+ lambda{@m.parse('\newcommand{\overopt}[1]{#1#2}')}.should raise_parse_error("Parameter \# too large.", '\newcommand{\overopt}[1]{#1#', "2}")
37
+ lambda{@m.parse('\newcommand{\strangeopt}[-1]')}.should raise_parse_error("Need positive number.", '\newcommand{\strangeopt}[', "-1]")
38
+ lambda{@m.parse('\newcommand{\strangeopt}[a]')}.should raise_parse_error("Need positive number.", '\newcommand{\strangeopt}[', "a]")
39
+
40
+ lambda{@m.parse('\newenvironment{\command}{}{}')}.should raise_parse_error("Syntax error.", '\newenvironment{', '\command}{}{}')
41
+ lambda{@m.parse('\newenvironment{nobegin}')}.should raise_parse_error("Need begin block.", '\newenvironment{nobegin}', "")
42
+ lambda{@m.parse('\newenvironment{noend}{}')}.should raise_parse_error("Need end block.", '\newenvironment{noend}{}', "")
43
+ lambda{@m.parse('\newenvironment{noparam}{#1}{}')}.should raise_parse_error("Parameter \# too large.", '\newenvironment{noparam}{#', "1}{}")
44
+ lambda{@m.parse('\newenvironment{overparam}[1]{#1#2}{}')}.should raise_parse_error("Parameter \# too large.", '\newenvironment{overparam}[1]{#1#', "2}{}")
45
+ lambda{@m.parse('\newenvironment{strangeparam}[-1]{}{}')}.should raise_parse_error("Need positive number.", '\newenvironment{strangeparam}[', "-1]{}{}")
46
+ lambda{@m.parse('\newenvironment{strangeparam}[a]{}{}')}.should raise_parse_error("Need positive number.", '\newenvironment{strangeparam}[', "a]{}{}")
47
+
48
+ lambda{@m.parse('\newcommand{\valid}{OK} \invalid{\test}{NG}')}.should raise_parse_error("Syntax error.", '\newcommand{\valid}{OK} ', '\invalid{\test}{NG}')
49
+ lambda{@m.parse('\newcommand{\valid}{OK} invalid{\test}{NG}')}.should raise_parse_error("Syntax error.", '\newcommand{\valid}{OK} ', 'invalid{\test}{NG}')
50
+
51
+ lambda{@m.parse('\newcommand{\newcom}[test')}.should raise_parse_error("Option not closed.", '\newcommand{\newcom}', '[test')
52
+ lambda{@m.parse('\newcommand{\newcom}[1][test')}.should raise_parse_error("Option not closed.", '\newcommand{\newcom}[1]', '[test')
53
+ lambda{@m.parse('\newcommand{\newcom}[1][]{#1#2}')}.should raise_parse_error("Parameter \# too large.", '\newcommand{\newcom}[1][]{#1#', '2}')
54
+ lambda{@m.parse('\newenvironment{newenv}[1][test')}.should raise_parse_error("Option not closed.", '\newenvironment{newenv}[1]', '[test')
55
+ lambda{@m.parse('\newenvironment{newenv}[1][test')}.should raise_parse_error("Option not closed.", '\newenvironment{newenv}[1]', '[test')
56
+
57
+ lambda{@m.parse('\newcommand{\newcom')}.should raise_parse_error("Block not closed.", '\newcommand', '{\newcom')
58
+ lambda{@m.parse('\newcommand{\newcom}{test1{test2}{test3')}.should raise_parse_error("Block not closed.", '\newcommand{\newcom}', '{test1{test2}{test3')
59
+
60
+ lambda{@m.parse('\newenvironment{newenv}[1][]{#1 #2}')}.should raise_parse_error("Parameter \# too large.", '\newenvironment{newenv}[1][]{#1 #', '2}')
61
+ end
62
+
63
+ it "#commands" do
64
+ @m.commands("newcom").num.should == 0
65
+ @m.commands("paramcom").num.should == 2
66
+ @m.commands("no").should == nil
67
+ end
68
+
69
+ it "#expand_command" do
70
+ @m.expand_command("not coommand", []).should == nil
71
+
72
+ @m.expand_command("newcom", []).should == "test"
73
+ @m.expand_command("newcom", ["dummy_param"]).should == "test"
74
+ @m.expand_command("paramcom", ["1", "2"]).should == "param2 2, param1 1."
75
+ @m.expand_command("paramcom", ["12", "34"]).should == "param2 34, param1 12."
76
+ lambda{@m.expand_command("paramcom", ["12"])}.should raise_parse_error("Need more parameter.", "", "")
77
+ lambda{@m.expand_command("paramcom", [])}.should raise_parse_error("Need more parameter.", "", "")
78
+ end
79
+
80
+ it "#environments" do
81
+ @m.environments("newenv").num.should == 0
82
+ @m.environments("paramenv").num.should == 2
83
+ @m.environments("not_env").should == nil
84
+ @m.environments("separated environment").num.should == 0
85
+ end
86
+
87
+ it "#expand_environment" do
88
+ @m.expand_environment('notregistered', "dummy", []).should == nil
89
+ @m.expand_environment("newenv", "body", []).should == ' begin_newenv body end_newenv '
90
+ @m.expand_environment("paramenv", "body", ["1", "2"]).should == ' begin 1:1, 2:2 body end 2:2 1:1 '
91
+ @m.expand_environment("paramenv", "body", ["12", "34"]).should == ' begin 1:12, 2:34 body end 2:34 1:12 '
92
+ lambda{@m.expand_environment("paramenv", "body", ["1"])}.should raise_parse_error("Need more parameter.", "", "")
93
+ lambda{@m.expand_environment("paramenv", "body", [])}.should raise_parse_error("Need more parameter.", "", "")
94
+ @m.expand_environment("nothing", "body", []).should == ' body '
95
+ @m.expand_environment("separated environment", "body", []).should == ' sep body env '
96
+ @m.expand_environment("E", "body", []).should == ' N body V '
97
+ end
98
+
99
+ it "#expand_with_options" do
100
+ src = <<'EOT'
101
+ \newcommand{\opt}[1][x]{#1}
102
+ \newcommand{\optparam}[2][]{#1#2}
103
+ \newenvironment{newenv}[1][x]{s:#1}{e:#1}
104
+ \newenvironment{optenv}[2][]{s:#1}{e:#2}
105
+ EOT
106
+
107
+ m = MathML::LaTeX::Macro.new
108
+ m.parse(src)
109
+
110
+ m.expand_command("opt", []).should == 'x'
111
+ m.expand_command("opt", [], "1").should == '1'
112
+
113
+ m.expand_command("optparam", ["1"]).should == '1'
114
+ m.expand_command("optparam", ["1"], "2").should == '21'
115
+
116
+ m.expand_environment("newenv", "test", []).should == " s:x test e:x "
117
+ m.expand_environment("newenv", "test", [], "1").should == " s:1 test e:1 "
118
+
119
+ m.expand_environment("optenv", "test", ["1"]).should == " s: test e:1 "
120
+ m.expand_environment("optenv", "test", ["1"], "2").should == " s:2 test e:1 "
121
+ end
122
+ end