mysh 0.6.9 → 0.6.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/mysh.rb +5 -4
- data/lib/mysh/expression.rb +11 -31
- data/lib/mysh/external.rb +1 -1
- data/lib/mysh/globalize.rb +13 -0
- data/lib/mysh/handlebars.rb +9 -10
- data/lib/mysh/handlebars/string.rb +26 -12
- data/lib/mysh/internal/actions/help/hbar.txt +11 -0
- data/lib/mysh/internal/actions/load.rb +1 -1
- data/lib/mysh/internal/actions/show/env.rb +2 -4
- data/lib/mysh/internal/actions/show/gem.rb +3 -16
- data/lib/mysh/internal/actions/show/term.rb +5 -7
- data/lib/mysh/internal/actions/type.rb +1 -1
- data/lib/mysh/pre_processor.rb +2 -2
- data/lib/mysh/quick.rb +7 -1
- data/lib/mysh/version.rb +1 -1
- data/samples/show.txt +2 -0
- data/tests/my_shell_tests.rb +5 -6
- metadata +2 -3
- data/lib/mysh/binding_wrapper.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1aaea546a90b6d110dd8910757083c385a7e72d5
|
4
|
+
data.tar.gz: 4f82d70adf3479acf12ee7a670c6d3a247326777
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b6d81b4a6cce7307a7b1262ad4125afb1cc1c0083a2c128543ff43211bd3be70bf4c3372f43a01b1225b4668d077b3b29cac51c29741313c0ae757510398e3a
|
7
|
+
data.tar.gz: 1aff82db1052b83caf85b0e09f006eebebf80a0b0782da66660b09586fabe1527bdd72f4760c47eaa2a4387a5f16447acc6ce588f364101d3a692736c628bad1
|
data/README.md
CHANGED
@@ -26,7 +26,7 @@ See the original article at:
|
|
26
26
|
(http://www.blackbytes.info/2016/07/writing-a-shell-in-ruby/)
|
27
27
|
|
28
28
|
Oh, and one other little thing. A survey of the mysh reveals that it currently
|
29
|
-
contains
|
29
|
+
contains 2188 lines of code. It seems that there has been some growth beyond
|
30
30
|
the 25 lines in the original article.
|
31
31
|
|
32
32
|
## Installation
|
@@ -907,7 +907,7 @@ on the optional parms.
|
|
907
907
|
###### "string".preprocess(context=mysh_default_context)
|
908
908
|
Process the string for embedded variables and handlebars. By default,
|
909
909
|
any embedded ruby is evaluated in the mysh global expression binding. However,
|
910
|
-
another
|
910
|
+
another binding may be passed to access an alternative execution environment.
|
911
911
|
|
912
912
|
###### "string".to_host_spec
|
913
913
|
Given a string with a file spec, to_host_spec adjusts that string so that it is
|
data/lib/mysh.rb
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
# mysh -- MY SHell -- a Ruby
|
3
|
+
# mysh -- MY SHell -- a Ruby inspired command line shell.
|
4
4
|
|
5
|
+
require 'pp'
|
5
6
|
require 'English'
|
6
7
|
require 'in_array'
|
7
8
|
require 'pause_output'
|
8
9
|
require 'format_output'
|
9
10
|
|
10
11
|
require_relative 'mysh/exceptions'
|
11
|
-
require_relative 'mysh/binding_wrapper'
|
12
12
|
require_relative 'mysh/input_wrapper'
|
13
13
|
require_relative 'mysh/user_input'
|
14
|
-
require_relative 'mysh/expression'
|
15
14
|
require_relative 'mysh/internal'
|
16
15
|
require_relative 'mysh/quick'
|
17
16
|
require_relative 'mysh/external'
|
@@ -22,7 +21,7 @@ require_relative 'mysh/pre_processor'
|
|
22
21
|
require_relative 'mysh/process'
|
23
22
|
require_relative 'mysh/globalize'
|
24
23
|
require_relative 'mysh/init'
|
25
|
-
|
24
|
+
require_relative 'mysh/expression'
|
26
25
|
require_relative 'mysh/version'
|
27
26
|
|
28
27
|
#The Mysh (MY SHell) module. A container for mysh and its functionality.
|
@@ -43,3 +42,5 @@ end
|
|
43
42
|
if __FILE__ == $0
|
44
43
|
Mysh.run(ARGV) #Run a shell if this file is run directly.
|
45
44
|
end
|
45
|
+
|
46
|
+
$VERBOSE = nil
|
data/lib/mysh/expression.rb
CHANGED
@@ -1,66 +1,46 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
require 'pp'
|
4
3
|
require_relative 'expression/lineage'
|
5
4
|
|
6
|
-
|
7
|
-
#<br>Endemic Code Smells
|
8
|
-
#* :reek:ModuleInitialize -- False positive
|
5
|
+
# Endemic Code Smells :reek:ModuleInitialize -- False positive
|
9
6
|
module Mysh
|
10
7
|
|
11
|
-
#Set up some popular constants
|
8
|
+
# Set up some popular constants
|
12
9
|
E = Math::E
|
13
10
|
PI = Math::PI
|
14
11
|
|
15
|
-
|
16
|
-
|
17
|
-
#
|
12
|
+
$mysh_exec_result = nil
|
13
|
+
|
14
|
+
# Reset the state of the execution hosting environment.
|
15
|
+
# Endemic Code Smells :reek:TooManyStatements -- False positive
|
18
16
|
def self.reset_host
|
19
17
|
exec_class = Class.new do
|
20
18
|
|
21
19
|
include Math
|
22
20
|
|
23
|
-
#Set up a new execution environment
|
21
|
+
# Set up a new execution environment
|
24
22
|
def initialize
|
25
|
-
$
|
26
|
-
$mysh_exec_binding = mysh_binding
|
27
|
-
end
|
28
|
-
|
29
|
-
#Do the actual work of executing an expression.
|
30
|
-
#<br>Note:
|
31
|
-
#* The expression string always begins with an '=' character.
|
32
|
-
def execute(expression)
|
33
|
-
pp $mysh_exec_binding.eval("$mysh_exec_result" + expression)
|
34
|
-
:expression
|
23
|
+
$mysh_exec_binding = binding
|
35
24
|
end
|
36
25
|
|
37
|
-
#Return a simple message for less convoluted error messages.
|
26
|
+
# Return a simple message for less convoluted error messages.
|
38
27
|
def inspect
|
39
28
|
"exec_host"
|
40
29
|
end
|
41
30
|
|
42
|
-
#Evaluate the string in the my shell context.
|
43
|
-
def mysh_eval(str)
|
44
|
-
$mysh_exec_binding.eval(str)
|
45
|
-
end
|
46
|
-
|
47
31
|
private
|
48
32
|
|
49
|
-
#Get the previous result
|
33
|
+
# Get the previous result
|
50
34
|
def result
|
51
35
|
$mysh_exec_result
|
52
36
|
end
|
53
37
|
|
54
|
-
#Reset the state of the execution host.
|
38
|
+
# Reset the state of the execution host.
|
55
39
|
def reset
|
56
40
|
Mysh.reset_host
|
57
41
|
nil
|
58
42
|
end
|
59
43
|
|
60
|
-
#Create a binding for mysh to execute expressions in.
|
61
|
-
def mysh_binding
|
62
|
-
binding
|
63
|
-
end
|
64
44
|
end
|
65
45
|
|
66
46
|
$mysh_exec_host = exec_class.new
|
data/lib/mysh/external.rb
CHANGED
data/lib/mysh/globalize.rb
CHANGED
@@ -27,4 +27,17 @@ class Object
|
|
27
27
|
err.to_s
|
28
28
|
end
|
29
29
|
|
30
|
+
# Get the latest version for the named gem. Patched code.
|
31
|
+
def latest_version_for(name, fetcher=nil)
|
32
|
+
dependency = Gem::Dependency.new(name)
|
33
|
+
fetcher ||= Gem::SpecFetcher.new
|
34
|
+
|
35
|
+
if specs = fetcher.spec_for_dependency(dependency)[0][-1]
|
36
|
+
spec = specs[0]
|
37
|
+
spec && spec.version
|
38
|
+
else
|
39
|
+
"<Not found in repository>"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
30
43
|
end
|
data/lib/mysh/handlebars.rb
CHANGED
@@ -2,21 +2,20 @@
|
|
2
2
|
|
3
3
|
require_relative 'handlebars/string'
|
4
4
|
|
5
|
-
|
5
|
+
# Handlebar embedded ruby support.
|
6
6
|
class Object
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
#
|
11
|
-
|
8
|
+
private
|
9
|
+
|
10
|
+
# Show a file with embedded ruby handlebars.
|
11
|
+
# Note: The message receiver is the evaluation host for the handlebar code.
|
12
|
+
def show_handlebar_file(name, evaluator = $mysh_exec_binding)
|
12
13
|
puts eval_handlebar_file(name, evaluator)
|
13
14
|
end
|
14
15
|
|
15
|
-
#Expand a file with embedded ruby handlebars.
|
16
|
-
|
17
|
-
#
|
18
|
-
#<br>Endemic Code Smells
|
19
|
-
#* :reek:UtilityFunction
|
16
|
+
# Expand a file with embedded ruby handlebars.
|
17
|
+
# Note: The message receiver is the evaluation host for the handlebar code.
|
18
|
+
# Endemic Code Smells :reek:UtilityFunction
|
20
19
|
def eval_handlebar_file(name, evaluator)
|
21
20
|
IO.read(name).preprocess(evaluator)
|
22
21
|
end
|
@@ -1,22 +1,36 @@
|
|
1
1
|
# coding: utf-8
|
2
2
|
|
3
|
-
#Monkey patches for Mysh handlebars
|
3
|
+
# Monkey patches for Mysh handlebars
|
4
4
|
class String
|
5
5
|
|
6
|
-
#Evaluate any variable substitutions in the input.
|
7
|
-
def eval_handlebars(evaluator=$
|
8
|
-
|
9
|
-
code = match[2...-2]
|
10
|
-
silent = code.end_with?("#")
|
11
|
-
result = evaluator.mysh_eval(code)
|
6
|
+
# Evaluate any variable substitutions in the input.
|
7
|
+
def eval_handlebars(evaluator=$mysh_exec_binding)
|
8
|
+
string, text, buffer = self, "", []
|
12
9
|
|
13
|
-
|
10
|
+
until string.empty?
|
11
|
+
text, code, string = string.partition(/{{.*?}}/m)
|
12
|
+
|
13
|
+
if not text.empty?
|
14
|
+
text = text.gsub(/\\[{}]/) {|found| found[1]}
|
15
|
+
buffer << "_m_<<#{text.inspect};"
|
16
|
+
elsif buffer.empty?
|
17
|
+
buffer << ""
|
18
|
+
end
|
19
|
+
|
20
|
+
unless code.empty?
|
21
|
+
if code[-3] == "#"
|
22
|
+
buffer << "#{code[2...-3]};"
|
23
|
+
else
|
24
|
+
buffer << "_m_<<(#{code[2...-2]}).to_s;"
|
25
|
+
end
|
26
|
+
end
|
14
27
|
end
|
15
|
-
end
|
16
28
|
|
17
|
-
|
18
|
-
|
19
|
-
|
29
|
+
if buffer.length > 1
|
30
|
+
evaluator.eval("_m_ = '';" + buffer.join + "_m_")
|
31
|
+
else
|
32
|
+
text
|
33
|
+
end
|
20
34
|
end
|
21
35
|
|
22
36
|
end
|
@@ -30,6 +30,17 @@ those cases, simply end the expression with a '#' character. For example:
|
|
30
30
|
mysh>echo \{\{ "not embedded" #\}\} \{\{ "embedded" \}\}
|
31
31
|
embedded
|
32
32
|
|
33
|
+
A common use for not embedded code snippets is to act as the control
|
34
|
+
structures. For example:
|
35
|
+
|
36
|
+
\{\{ 3.times do |i| #\}\} The count is: \{\{ i+1 \}\}
|
37
|
+
\{\{ end #\}\}
|
38
|
+
|
39
|
+
Yields the following output:
|
40
|
+
|
41
|
+
{{ 3.times do |i| #}} The count is: {{ i+1 }}
|
42
|
+
{{ end #}}
|
43
|
+
|
33
44
|
Finally, it may be that it is desired to embed braces into a text file or
|
34
45
|
the command line. In that case precede the brace with a backslash character
|
35
46
|
like: \\{ or \\}
|
@@ -8,14 +8,12 @@ module Mysh
|
|
8
8
|
|
9
9
|
# Execute the @env shell command.
|
10
10
|
def process_command(_args)
|
11
|
-
print WORKING
|
11
|
+
print WORKING
|
12
12
|
Gem.refresh
|
13
13
|
|
14
14
|
puts "Key mysh environment information.", "",
|
15
15
|
info.format_output_bullets, "",
|
16
16
|
path.format_output_bullets, ""
|
17
|
-
|
18
|
-
@ran_once = true
|
19
17
|
end
|
20
18
|
|
21
19
|
private
|
@@ -28,7 +26,7 @@ module Mysh
|
|
28
26
|
["installed", Gem::Specification.find_all_by_name("mysh")
|
29
27
|
.map{|s| s.version.to_s}
|
30
28
|
.join(", ")],
|
31
|
-
["latest", insouciant {
|
29
|
+
["latest", insouciant {latest_version_for("mysh").to_s}],
|
32
30
|
["init file", $mysh_init_file.to_host_spec],
|
33
31
|
["user", ENV['USER']],
|
34
32
|
["home", (ENV['HOME'] || "").to_host_spec],
|
@@ -8,7 +8,7 @@ module Mysh
|
|
8
8
|
|
9
9
|
#Execute the @gem shell command.
|
10
10
|
def process_command(input)
|
11
|
-
print WORKING
|
11
|
+
print WORKING
|
12
12
|
Gem.refresh
|
13
13
|
|
14
14
|
args = input.cooked_body.split(" ")[1..-1]
|
@@ -19,7 +19,6 @@ module Mysh
|
|
19
19
|
specific(args)
|
20
20
|
end
|
21
21
|
|
22
|
-
@ran_once = true
|
23
22
|
end
|
24
23
|
|
25
24
|
private
|
@@ -61,7 +60,7 @@ module Mysh
|
|
61
60
|
|
62
61
|
# Get gem info on the specified gems
|
63
62
|
def specific(args)
|
64
|
-
details = []
|
63
|
+
fetcher, details = Gem::SpecFetcher.new, []
|
65
64
|
|
66
65
|
args.each do |gem_name|
|
67
66
|
version_list = Gem::Specification.find_all_by_name(gem_name)
|
@@ -69,7 +68,7 @@ module Mysh
|
|
69
68
|
.join(", ")
|
70
69
|
details << [gem_name, version_list]
|
71
70
|
|
72
|
-
latest = insouciant {latest_version_for(gem_name).to_s}
|
71
|
+
latest = insouciant {latest_version_for(gem_name, fetcher).to_s}
|
73
72
|
details << ["latest", latest]
|
74
73
|
details << [" ", " "]
|
75
74
|
end
|
@@ -78,18 +77,6 @@ module Mysh
|
|
78
77
|
details.format_output_bullets
|
79
78
|
end
|
80
79
|
|
81
|
-
# Get the latest version for the named gem. Patched code.
|
82
|
-
def latest_version_for(name)
|
83
|
-
dependency = Gem::Dependency.new(name)
|
84
|
-
fetcher = Gem::SpecFetcher.fetcher
|
85
|
-
if specs = fetcher.spec_for_dependency(dependency)[0][-1]
|
86
|
-
spec = specs[0]
|
87
|
-
spec && spec.version
|
88
|
-
else
|
89
|
-
"<Not found in repository>"
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
80
|
end
|
94
81
|
|
95
82
|
desc = 'Get information about the current gem support or ' +
|
@@ -8,13 +8,11 @@ module Mysh
|
|
8
8
|
|
9
9
|
#Execute the @term shell command.
|
10
10
|
def process_command(_args)
|
11
|
-
print WORKING
|
11
|
+
print WORKING
|
12
12
|
Gem.refresh
|
13
13
|
|
14
14
|
puts "Key term information.", "",
|
15
15
|
info.format_output_bullets, ""
|
16
|
-
|
17
|
-
@ran_once = true
|
18
16
|
end
|
19
17
|
|
20
18
|
private
|
@@ -22,18 +20,20 @@ module Mysh
|
|
22
20
|
# Get the info
|
23
21
|
# Endemic Code Smells :reek:UtilityFunction
|
24
22
|
def info
|
23
|
+
fetcher = Gem::SpecFetcher.new
|
24
|
+
|
25
25
|
[["about", MiniReadline::DESCRIPTION],
|
26
26
|
["version", MiniReadline::VERSION],
|
27
27
|
["installed", Gem::Specification.find_all_by_name("mini_readline")
|
28
28
|
.map{|s| s.version.to_s}
|
29
29
|
.join(", ")],
|
30
|
-
["latest", insouciant {
|
30
|
+
["latest", insouciant {latest_version_for("mini_readline", fetcher).to_s}],
|
31
31
|
["about", MiniTerm::DESCRIPTION],
|
32
32
|
["version", MiniTerm::VERSION],
|
33
33
|
["installed", Gem::Specification.find_all_by_name("mini_term")
|
34
34
|
.map{|s| s.version.to_s}
|
35
35
|
.join(", ")],
|
36
|
-
["latest", insouciant {
|
36
|
+
["latest", insouciant {latest_version_for("mini_term", fetcher).to_s}],
|
37
37
|
["platform", MiniTerm::TERM_PLATFORM.inspect],
|
38
38
|
["term type", MiniTerm::TERM_TYPE.inspect],
|
39
39
|
["columns", MiniTerm.width.to_s],
|
@@ -45,8 +45,6 @@ module Mysh
|
|
45
45
|
]
|
46
46
|
end
|
47
47
|
|
48
|
-
|
49
|
-
|
50
48
|
end
|
51
49
|
|
52
50
|
desc = 'Get information about the console. See ?term for more.'
|
data/lib/mysh/pre_processor.rb
CHANGED
@@ -4,8 +4,8 @@
|
|
4
4
|
class String
|
5
5
|
|
6
6
|
#The mysh string pre-processor stack.
|
7
|
-
def preprocess(evaluator=$
|
8
|
-
self.eval_variables.eval_handlebars(evaluator)
|
7
|
+
def preprocess(evaluator=$mysh_exec_binding)
|
8
|
+
self.eval_variables.eval_handlebars(evaluator)
|
9
9
|
end
|
10
10
|
|
11
11
|
end
|
data/lib/mysh/quick.rb
CHANGED
@@ -9,10 +9,16 @@ module Mysh
|
|
9
9
|
QUICK['!'] = lambda {|input| HISTORY_COMMAND.process_quick_command(input)}
|
10
10
|
QUICK['#'] = lambda {|input| MYSH_COMMENT.process_command(input)}
|
11
11
|
QUICK['%'] = lambda {|input| TIMED_COMMAND.process_command(input)}
|
12
|
-
QUICK['='] = lambda {|input| $mysh_exec_host.execute(input.raw.preprocess)}
|
13
12
|
QUICK['?'] = lambda {|input| HELP_COMMAND.process_quick_command(input)}
|
14
13
|
QUICK['@'] = lambda {|input| SHOW_COMMAND.process_quick_command(input)}
|
15
14
|
|
15
|
+
QUICK['='] = lambda do |input|
|
16
|
+
cmd = "$mysh_exec_result=(" + input.raw[1..-1].preprocess + ")"
|
17
|
+
puts cmd if MNV[:debug].extract_boolean
|
18
|
+
pp $mysh_exec_binding.eval(cmd)
|
19
|
+
:expression
|
20
|
+
end
|
21
|
+
|
16
22
|
#Try to execute the inputing as a quick command.
|
17
23
|
def self.try_execute_quick(input)
|
18
24
|
QUICK[input.quick_command].call(input)
|
data/lib/mysh/version.rb
CHANGED
data/samples/show.txt
CHANGED
data/tests/my_shell_tests.rb
CHANGED
@@ -19,7 +19,6 @@ class MyShellTester < Minitest::Test
|
|
19
19
|
assert_equal(Class, Mysh::ActionPool.class)
|
20
20
|
assert_equal(Module, Mysh::MNV.class)
|
21
21
|
assert_equal(Class, Mysh::Keeper.class)
|
22
|
-
assert_equal(Class, Mysh::BindingWrapper.class)
|
23
22
|
assert_equal(Class, Mysh::InputWrapper.class)
|
24
23
|
|
25
24
|
assert_equal(Mysh::ActionPool, Mysh::COMMANDS.class)
|
@@ -44,13 +43,13 @@ class MyShellTester < Minitest::Test
|
|
44
43
|
|
45
44
|
def test_handlebars
|
46
45
|
assert_equal("ABC 123 DEF",
|
47
|
-
"ABC {{ (1..3).to_a.join }} DEF".
|
46
|
+
"ABC {{ (1..3).to_a.join }} DEF".preprocess)
|
48
47
|
|
49
|
-
assert_equal("ABC", "{{ 'ABC' }}".
|
50
|
-
assert_equal("", "{{ 'ABC' #}}".
|
48
|
+
assert_equal("ABC", "{{ 'ABC' }}".preprocess)
|
49
|
+
assert_equal("", "{{ 'ABC' #}}".preprocess)
|
51
50
|
|
52
|
-
assert_equal("{{ 'ABC' }}", "\\{\\{ 'ABC' \\}\\}".
|
53
|
-
assert_equal("{{A}}", "{{ '{'+'{A}'+'}' }}".
|
51
|
+
assert_equal("{{ 'ABC' }}", "\\{\\{ 'ABC' \\}\\}".preprocess)
|
52
|
+
assert_equal("{{A}}", "{{ '{'+'{A}'+'}' }}".preprocess)
|
54
53
|
end
|
55
54
|
|
56
55
|
def test_command_parsing
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mysh
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.11
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Peter Camilleri
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-12-
|
11
|
+
date: 2018-12-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -193,7 +193,6 @@ files:
|
|
193
193
|
- README.md
|
194
194
|
- bin/mysh
|
195
195
|
- lib/mysh.rb
|
196
|
-
- lib/mysh/binding_wrapper.rb
|
197
196
|
- lib/mysh/exceptions.rb
|
198
197
|
- lib/mysh/expression.rb
|
199
198
|
- lib/mysh/expression/lineage.rb
|
data/lib/mysh/binding_wrapper.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
|
3
|
-
#* mysh/internal/binding_wrapper.rb -- An action compatible wrapper for a binding.
|
4
|
-
module Mysh
|
5
|
-
|
6
|
-
#* mysh/internal/binding_wrapper.rb -- An action compatible wrapper for a binding.
|
7
|
-
class BindingWrapper
|
8
|
-
|
9
|
-
#Setup a binding wrapper
|
10
|
-
def initialize(binding)
|
11
|
-
@exec_binding = binding
|
12
|
-
end
|
13
|
-
|
14
|
-
#Evaluate the string in the wrapped context.
|
15
|
-
def mysh_eval(str)
|
16
|
-
@exec_binding.eval(str)
|
17
|
-
end
|
18
|
-
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|