re_duxml 0.1.0 → 0.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 996c30dfa1b40d7aa2d27de06d5bb770e4858f23
4
- data.tar.gz: 20462c1ca90131bff9c05fbe9826001fd46f0cec
3
+ metadata.gz: 7c7362dbfed6bd6c78ddbbd9660a55a639e802a6
4
+ data.tar.gz: c73453fe830475b19e4c34f60724b79abfdb2a59
5
5
  SHA512:
6
- metadata.gz: 46717577e85998db3f56ca0e26fd87b28f6220908fac32002683190b85d550340d3ca9b324163474f47a9ca5888b9f122fd22ed47af48a5c8abd528a071418e0
7
- data.tar.gz: 2a04ed33b59e2209fb77c240d305233547af83973cb85e67c1d44cb9c14a9a96025f54d68b0b0b3f0e18c6ba5cc29d87e7a3dd369079182cc319450a5c9e284c
6
+ metadata.gz: a4f44cdfc1bd794195f904a99dda9545e04e4ae35da83f8972492193ce8ea98094fe1c9abc188195df25a13324c864d08bd81e62a1c83ee630f6df4a65288678
7
+ data.tar.gz: d16dec0c48f1f7332781741871cbe90b56d17da1b89783e402c1a941b383c6d02c2b1f92c64e69aa7c62cb484ae8e2b25a983e14af8227eaf3e4277393a12656
data/lib/ast_ext/node.rb CHANGED
@@ -1,53 +1,53 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
- require 'ast'
3
-
4
- module AST
5
- # redefining in order to allow type itself to be any type e.g. String, Symbol, Fixnum, etc.
6
- class Node
7
- include AST
8
-
9
- # The `properties` hash is passed to {#assign_properties}.
10
- def initialize(type, children=[], properties={})
11
- @type, @children = type, children.to_a.freeze
12
-
13
- assign_properties(properties)
14
-
15
- @hash = [@type.object_id, @children, self.class].hash
16
-
17
- freeze
18
- end
19
-
20
- # @param logic [Hash] hash of operators allowed in this AST containing each operator's print properties
21
- # @return [String] string reconstituted from polish-notation into notation normally required by each operator
22
- def print(logic=nil)
23
- return type.to_s if children.empty?
24
- str = ''
25
- op = type.respond_to?(:text) ? type : logic[type.to_s]
26
- return str unless op
27
- case op.position
28
- when :prefix
29
- str << op.symbol
30
- children.each do |c| str << c.print(logic) end
31
- when :postfix
32
- children.each do |c| str << c.print(logic) end
33
- str << op.symbol
34
- when :infix
35
- if op.arity > 2
36
- str << children.first.print(logic) << op.symbol << children[1].print(logic) << op.pair.to_s << children.last.print
37
- else
38
- str << (children.first.respond_to?(:print) ? children.first.print(logic) : children.first.to_s) << op.symbol << children.last.print
39
- end
40
- else # should not happen
41
- end
42
- str
43
- end # to_s
44
- end # class Node
45
-
46
- def new_ast(op, *obj)
47
- args = obj.collect do |o| o.is_a?(Node) ? o : Node.new(o) end
48
- args.unshift self if is_a?(Node)
49
- args.unshift Node.new(self) if is_a?(Fixnum)
50
- args.unshift Node.new(self) if self.is_a?(Symbolic::Variable)
51
- Node.new(op, args)
52
- end
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require 'ast'
3
+
4
+ module AST
5
+ # redefining in order to allow type itself to be any type e.g. String, Symbol, Fixnum, etc.
6
+ class Node
7
+ include AST
8
+
9
+ # The `properties` hash is passed to {#assign_properties}.
10
+ def initialize(type, children=[], properties={})
11
+ @type, @children = type, children.to_a.freeze
12
+
13
+ assign_properties(properties)
14
+
15
+ @hash = [@type.object_id, @children, self.class].hash
16
+
17
+ freeze
18
+ end
19
+
20
+ # @param logic [Hash] hash of operators allowed in this AST containing each operator's print properties
21
+ # @return [String] string reconstituted from polish-notation into notation normally required by each operator
22
+ def print(logic=nil)
23
+ return type.to_s if children.empty?
24
+ str = ''
25
+ op = type.respond_to?(:text) ? type : logic[type.to_s]
26
+ return str unless op
27
+ case op.position
28
+ when :prefix
29
+ str << op.symbol
30
+ children.each do |c| str << c.print(logic) end
31
+ when :postfix
32
+ children.each do |c| str << c.print(logic) end
33
+ str << op.symbol
34
+ when :infix
35
+ if op.arity > 2
36
+ str << children.first.print(logic) << op.symbol << children[1].print(logic) << op.pair.symbol << children.last.print
37
+ else
38
+ str << (children.first.respond_to?(:print) ? children.first.print(logic) : children.first.symbol) << op.symbol << children.last.print
39
+ end
40
+ else # should not happen
41
+ end
42
+ str
43
+ end # to_s
44
+ end # class Node
45
+
46
+ def new_ast(op, *obj)
47
+ args = obj.collect do |o| o.is_a?(Node) ? o : Node.new(o) end
48
+ args.unshift self if is_a?(Node)
49
+ args.unshift Node.new(self) if is_a?(Fixnum)
50
+ args.unshift Node.new(self) if self.is_a?(Symbolic::Variable)
51
+ Node.new(op, args)
52
+ end
53
53
  end
@@ -1,38 +1,37 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
- require 'con_duxml/instance'
3
-
4
- module ConDuxml
5
- # XML object array
6
- # represents a pattern of copies of a this object's children or referents
7
- # differentiates between copies using iterator Parameter
8
- module Array
9
- include Instance
10
-
11
- # @return [Array[Element]] flattened array of all duplicated Elements
12
- def activate
13
- # TODO add implicit @iterator param
14
- size_expr = size.respond_to?(:to_i) ? size.to_i : size.to_s
15
- if size_expr.is_a? Fixnum
16
- new_children = []
17
- size_expr.times do
18
- source_nodes = if nodes.empty? and self[:ref]
19
- [resolve_ref.clone]
20
- else
21
- nodes.collect do |node| node.clone end
22
- end
23
- source_nodes.each do |node|
24
- new_children << node
25
- end
26
- end
27
- new_children.flatten
28
- else
29
- [self]
30
- end
31
- end # def instantiate
32
-
33
- # size can be Fixnum or a Parameter expression
34
- def size
35
- self[:size]
36
- end
37
- end # class Array
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require 'con_duxml/instance'
3
+
4
+ module ConDuxml
5
+ # XML object array
6
+ # represents a pattern of copies of a this object's children or referents
7
+ # differentiates between copies using iterator Parameter
8
+ module Array
9
+ include Instance
10
+
11
+ # @return [Array[Element]] flattened array of all duplicated Elements
12
+ def activate
13
+ size_expr = size.respond_to?(:to_i) ? size.to_i : size.to_s
14
+ if size_expr.is_a? Fixnum
15
+ new_children = []
16
+ size_expr.times do
17
+ source_nodes = if nodes.empty? and self[:ref]
18
+ [resolve_ref.clone]
19
+ else
20
+ nodes.collect do |node| node.clone end
21
+ end
22
+ source_nodes.each do |node|
23
+ new_children << node
24
+ end
25
+ end
26
+ new_children.flatten
27
+ else
28
+ [self]
29
+ end
30
+ end # def instantiate
31
+
32
+ # size can be Fixnum or a Parameter expression
33
+ def size
34
+ self[:size]
35
+ end
36
+ end # class Array
38
37
  end # module Dux
@@ -1,18 +1,18 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
- require File.expand_path(File.dirname(__FILE__) + '/../../ruby_ext/string')
3
-
4
- # methods to extend Dux::Object with methods needed to process parameterized XML content
5
- module Parameterization
6
- # returns true if self[:if] is true or indeterminate (because condition is currently parameterized)
7
- # returns false otherwise i.e. this node does not exist in this design build
8
- def if?
9
- return true unless (if_str = self[:if])
10
- if_str.parameterized? || if_str == 'true' ? true : false
11
- end
12
-
13
- # changes condition of this object's existence
14
- def if=(condition)
15
- # check for valid conditional
16
- change_attr_value :if, condition
17
- end
18
- end # module Parameterization
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require File.expand_path(File.dirname(__FILE__) + '/../../ruby_ext/string')
3
+
4
+ # methods to extend Dux::Object with methods needed to process parameterized XML content
5
+ module Parameterization
6
+ # returns true if self[:if] is true or indeterminate (because condition is currently parameterized)
7
+ # returns false otherwise i.e. this node does not exist in this design build
8
+ def if?
9
+ return true unless (if_str = self[:if])
10
+ if_str.parameterized? || if_str == 'true' ? true : false
11
+ end
12
+
13
+ # changes condition of this object's existence
14
+ def if=(condition)
15
+ # check for valid conditional
16
+ change_attr_value :if, condition
17
+ end
18
+ end # module Parameterization
@@ -1,9 +1,9 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
- require_relative 'element/parameterization'
3
- require 'duxml'
4
-
5
- module Duxml
6
- class Element
7
- include Parameterization
8
- end
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require_relative 'element/parameterization'
3
+ require 'duxml'
4
+
5
+ module Duxml
6
+ class Element
7
+ include Parameterization
8
+ end
9
9
  end
@@ -81,7 +81,8 @@ module ReDuxml
81
81
  op_stack << token.value
82
82
  when :grouping
83
83
  op_prop = token.value
84
- case op_prop.to_s
84
+ op_str = op_prop.respond_to?(:nodes) ? op_prop.symbol : op_prop.to_s
85
+ case op_str
85
86
  when '('
86
87
  if input.any? && input.first.type == :grouping && input.first.value.to_s == '('
87
88
  input.shift
@@ -90,7 +91,7 @@ module ReDuxml
90
91
  op_stack << op_prop
91
92
  end
92
93
  when ')'
93
- while op_stack.any? && op_stack.last.symbol != op_prop.pair.to_s
94
+ while op_stack.any? && op_stack.last.symbol != op_prop.pair.symbol
94
95
  consume(arities.pop || op_stack.last.arity)
95
96
  end
96
97
  lparen = op_stack.pop
@@ -1,78 +1,79 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
- require_relative 'evaluate/parser'
3
- require_relative '../../lib/ruby_ext/macro'
4
-
5
- module ReDuxml
6
- class Evaluator
7
- LOGIC_FILE = File.expand_path(File.dirname(__FILE__) + '/../../xml/logic.xml')
8
- include Math
9
- include ReDuxml
10
- include Symbolic
11
-
12
- @param_hash
13
- @parser
14
-
15
- attr_reader :param_hash, :parser
16
-
17
- def initialize(logic=nil)
18
- @parser = Parser.new(logic || LOGIC_FILE)
19
- @param_hash = {}
20
- end
21
-
22
- def to_s
23
- "#<Evaluator: param_hash: '#{param_hash.to_s}' parser_logic: '#{parser}'>"
24
- end
25
-
26
- def evaluate(_expr, _param_hash={})
27
- @param_hash = _param_hash
28
- expr = resolve_params _expr
29
- return expr if expr.parameterized?
30
- return expr if Regexp.identifier.match(expr).to_s == expr
31
- return expr.to_i if expr.to_i.to_s == expr
32
- result = reduce parser.parse expr
33
- case
34
- when result.respond_to?(:imaginary), result.class == TrueClass, result.class == FalseClass then result
35
- when result.respond_to?(:print) then result.print(parser.logic)
36
- else result.to_s
37
- end
38
- end
39
-
40
- private
41
-
42
- def resolve_params(_expr)
43
- expr = _expr.dup
44
- param_hash.each do |param, val| expr.gsub!(param, val) end
45
- expr
46
- end
47
-
48
- include Symbolic
49
-
50
- def reduce(_ast)
51
- ast = _ast.type.respond_to?(:symbol) ? _ast : new_ast(parser.logic[_ast.type.to_s], _ast.children.dup)
52
- if ast.children.any?
53
- operator = ast.type
54
- args = ast.children.collect do |c|
55
- new_child = c.children.any? ? reduce(c) : c.type
56
- if new_child.is_a?(Node) && new_child.type.is_a?(Symbol)
57
- new_ast(parser.logic[new_child.type.to_s], *new_child.children.dup)
58
- else
59
- new_child
60
- end
61
- end.flatten
62
- begin
63
- result = case operator.position
64
- when :prefix
65
- method(operator.ruby).call(*args)
66
- else
67
- args.first.send(operator.ruby, *args[1..-1])
68
- end
69
- result.nil? ? ast : result
70
- rescue
71
- ast
72
- end
73
- else
74
- ast
75
- end
76
- end # def reduce(ast)
77
- end # class Evaluator
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require_relative 'evaluate/parser'
3
+ require_relative '../../lib/ruby_ext/macro'
4
+
5
+ module ReDuxml
6
+ class Evaluator
7
+ LOGIC_FILE = File.expand_path(File.dirname(__FILE__) + '/../../xml/logic.xml')
8
+ include Math
9
+ include ReDuxml
10
+ include Symbolic
11
+
12
+ @param_hash
13
+ @parser
14
+
15
+ attr_reader :param_hash, :parser
16
+
17
+ def initialize(logic=nil)
18
+ @parser = Parser.new(logic || LOGIC_FILE)
19
+ @param_hash = {}
20
+ end
21
+
22
+ def to_s
23
+ "#<Evaluator: param_hash: '#{param_hash.to_s}' parser_logic: '#{parser}'>"
24
+ end
25
+
26
+ def evaluate(_expr, _param_hash={})
27
+ @param_hash = _param_hash
28
+ expr = resolve_params _expr
29
+ return expr if expr.parameterized?
30
+ return expr if Regexp.identifier.match(expr).to_s == expr
31
+ return expr.to_i if expr.to_i.to_s == expr
32
+ result = reduce parser.parse expr
33
+ case
34
+ when result.respond_to?(:imaginary), result.class == TrueClass, result.class == FalseClass then result
35
+ when result.respond_to?(:name) then result.name
36
+ when result.respond_to?(:to_sexp) then result.print(parser.logic)
37
+ else result.to_s
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def resolve_params(_expr)
44
+ expr = _expr.dup
45
+ param_hash.each do |param, val| expr.gsub!(param, val) end
46
+ expr
47
+ end
48
+
49
+ include Symbolic
50
+
51
+ def reduce(_ast)
52
+ ast = _ast.type.respond_to?(:symbol) ? _ast : new_ast(parser.logic[_ast.type.to_s], _ast.children.dup)
53
+ if ast.children.any?
54
+ operator = ast.type
55
+ args = ast.children.collect do |c|
56
+ new_child = c.children.any? ? reduce(c) : c.type
57
+ if new_child.is_a?(Node) && new_child.type.is_a?(Symbol)
58
+ new_ast(parser.logic[new_child.type.to_s], *new_child.children.dup)
59
+ else
60
+ new_child
61
+ end
62
+ end.flatten
63
+ begin
64
+ result = case operator.position
65
+ when :prefix
66
+ method(operator.ruby).call(*args)
67
+ else
68
+ args.first.send(operator.ruby, *args[1..-1])
69
+ end
70
+ result.nil? ? ast : result
71
+ rescue
72
+ ast
73
+ end
74
+ else
75
+ ast
76
+ end
77
+ end # def reduce(ast)
78
+ end # class Evaluator
78
79
  end # module ReDux
data/lib/re_duxml.rb CHANGED
@@ -1,99 +1,104 @@
1
- # Copyright (c) 2016 Freescale Semiconductor Inc.
2
- require File.expand_path(File.dirname(__FILE__) + '/ruby_ext/macro')
3
- require File.expand_path(File.dirname(__FILE__) + '/re_duxml/evaluate')
4
- require File.expand_path(File.dirname(__FILE__) + '/re_duxml/element')
5
- require 'con_duxml'
6
-
7
- module ReDuxml
8
- include ConDuxml
9
-
10
- # @param doc_or_node [Doc, String] XML to load; can be Doc, String path or String XML; also XML Element when recursing
11
- # @return [Doc] instantiated XML with parameters resolved with given values
12
- def resolve(doc_or_node, parent_params={})
13
- if doc_or_node.is_a?(Element)
14
- resolved_node = doc_or_node.stub
15
- this_params = get_params(doc_or_node, parent_params)
16
- new_children = doc_or_node.nodes.collect do |child|
17
- if child.respond_to?(:nodes)
18
- new_child = child.clone
19
- new_child.attributes.each do |attr, val|
20
- new_child[attr] = resolve_str(val, this_params)
21
- end
22
- if new_child.if?
23
- new_child[:if] = nil
24
- child_params = get_params(new_child, this_params)
25
- new_child.activate.collect do |inst|
26
- resolve(inst, child_params)
27
- end
28
- end
29
- else
30
- resolve_str(child, this_params)
31
- end
32
- end.flatten.compact
33
- resolved_node << new_children
34
- else
35
- @e ||= Evaluator.new
36
- @src_doc = get_doc doc_or_node
37
- @doc = Doc.new << resolve(src_doc.root, parent_params)
38
- end
39
- end
40
-
41
- attr_reader :e, :src_doc
42
- private
43
-
44
-
45
- def get_params(node, param_hash)
46
- if node.nodes.any? and !node.text? and node.nodes[0].name == 'duxml:parameters'
47
- local_params = {}
48
- params = node[0].nodes.clone
49
- params.each do |param|
50
- new_val = resolve_str(param[:value], param_hash)
51
- node[0].delete param unless new_val.parameterized?
52
- local_params[param[:name]] = new_val
53
- end
54
- node.delete node[0] unless node[0].nodes.any?
55
- param_hash.merge local_params
56
- else
57
- param_hash
58
- end
59
- end
60
-
61
- # finds index of close parentheses corresponding to first open parentheses found in given str
62
- def find_close_parens_index(str)
63
- levels = 0
64
- index = 0
65
- str.each_char do |char|
66
- case char
67
- when '(' then
68
- levels += 1
69
- when ')' then
70
- levels -= 1
71
- else
72
- end
73
- return index if levels == 0
74
- index += 1
75
- end
76
- raise Exception, "cannot find end of parameter expression!"
77
- end
78
-
79
- # @param content_str [String] string that may contain parameterized expressions e.g. "a @(var0+var1) string @(var2)"
80
- # @param param_hash [Hash] keys are parameter names, values are parameter values e.g. {var0: 'param', var1: 'eter'}
81
- # @return [String] content_str with parameter values substituted and algebraically reduced if any unknown parameters remain e.g. 'a parameter string @(var2)'
82
- def resolve_str(content_str, param_hash)
83
- question = find_expr content_str
84
- return content_str if question.nil?
85
- reply = Macro.new e.evaluate(question, param_hash).to_s
86
- replacement_str = reply.parameterized? ? reply.macro_string : reply.demacro
87
- macro_string = Macro.new(question).macro_string
88
- content_str.gsub(macro_string, replacement_str)
89
- end
90
-
91
- # @param str [String] string that may contain parameter expression e.g. 'asdf @(param + 2) asdf'
92
- # @return [String] macro string e.g. 'asdf @(param + 2) asdf' => 'param + 2'
93
- def find_expr(str)
94
- expr_start_index = str.index('@(')
95
- return nil if expr_start_index.nil?
96
- expr_end_index = find_close_parens_index str[expr_start_index+1..-1]
97
- str[expr_start_index+2, expr_end_index-1]
98
- end
99
- end # module Dux
1
+ # Copyright (c) 2016 Freescale Semiconductor Inc.
2
+ require File.expand_path(File.dirname(__FILE__) + '/ruby_ext/macro')
3
+ require File.expand_path(File.dirname(__FILE__) + '/re_duxml/evaluate')
4
+ require File.expand_path(File.dirname(__FILE__) + '/re_duxml/element')
5
+ require File.expand_path(File.dirname(__FILE__) + '/re_duxml/array')
6
+ require 'con_duxml'
7
+
8
+ module ReDuxml
9
+ include ConDuxml
10
+
11
+ # @param doc_or_node [Doc, String] XML to load; can be Doc, String path or String XML; also XML Element when recursing
12
+ # @return [Doc] instantiated XML with parameters resolved with given values
13
+ def resolve(doc_or_node, parent_params={})
14
+ if doc_or_node.is_a?(Element)
15
+ resolved_node = doc_or_node.stub
16
+ resolved_node.attributes.each do |attr, val|
17
+ resolved_node[attr] = resolve_str(val, parent_params)
18
+ end
19
+ this_params = get_params(doc_or_node, parent_params)
20
+ new_children = doc_or_node.nodes.collect do |child|
21
+ if child.respond_to?(:nodes)
22
+ new_child = child.clone
23
+ new_child[:if] = resolve_str(new_child[:if], this_params) if new_child[:if]
24
+ if new_child.if?
25
+ new_child[:if] = nil
26
+ child_params = get_params(new_child, this_params)
27
+ new_instances = new_child.activate
28
+ i = -1
29
+ new_instances.collect do |inst|
30
+ i += 1
31
+ resolve(inst, child_params.merge({'iterator' => i.to_s}))
32
+ end
33
+ end
34
+ else
35
+ resolve_str(child, this_params)
36
+ end
37
+ end.flatten.compact
38
+ resolved_node << new_children
39
+ else
40
+ @e ||= Evaluator.new
41
+ @src_doc = get_doc doc_or_node
42
+ @doc = Doc.new << resolve(src_doc.root, parent_params)
43
+ end
44
+ end
45
+
46
+ attr_reader :e, :src_doc
47
+ private
48
+
49
+
50
+ def get_params(node, param_hash)
51
+ if node.nodes.any? and !node.text? and node.nodes[0].name == 'duxml:parameters'
52
+ local_params = {}
53
+ params = node[0].nodes.clone
54
+ params.each do |param|
55
+ new_val = resolve_str(param[:value], param_hash)
56
+ node[0].delete param unless new_val.parameterized?
57
+ local_params[param[:name]] = new_val
58
+ end
59
+ node.delete node[0] unless node[0].nodes.any?
60
+ param_hash.merge local_params
61
+ else
62
+ param_hash
63
+ end
64
+ end
65
+
66
+ # finds index of close parentheses corresponding to first open parentheses found in given str
67
+ def find_close_parens_index(str)
68
+ levels = 0
69
+ index = 0
70
+ str.each_char do |char|
71
+ case char
72
+ when '(' then
73
+ levels += 1
74
+ when ')' then
75
+ levels -= 1
76
+ else
77
+ end
78
+ return index if levels == 0
79
+ index += 1
80
+ end
81
+ raise Exception, "cannot find end of parameter expression!"
82
+ end
83
+
84
+ # @param content_str [String] string that may contain parameterized expressions e.g. "a @(var0+var1) string @(var2)"
85
+ # @param param_hash [Hash] keys are parameter names, values are parameter values e.g. {var0: 'param', var1: 'eter'}
86
+ # @return [String] content_str with parameter values substituted and algebraically reduced if any unknown parameters remain e.g. 'a parameter string @(var2)'
87
+ def resolve_str(content_str, param_hash)
88
+ question = find_expr content_str
89
+ return content_str if question.nil?
90
+ reply = Macro.new e.evaluate(question, param_hash).to_s
91
+ replacement_str = reply.parameterized? ? reply.macro_string : reply.demacro
92
+ macro_string = Macro.new(question).macro_string
93
+ content_str.gsub(macro_string, replacement_str)
94
+ end
95
+
96
+ # @param str [String] string that may contain parameter expression e.g. 'asdf @(param + 2) asdf'
97
+ # @return [String] macro string e.g. 'asdf @(param + 2) asdf' => 'param + 2'
98
+ def find_expr(str)
99
+ expr_start_index = str.index('@(')
100
+ return nil if expr_start_index.nil?
101
+ expr_end_index = find_close_parens_index str[expr_start_index+1..-1]
102
+ str[expr_start_index+2, expr_end_index-1]
103
+ end
104
+ end # module Dux