Class: ShVars::String

Inherits:
Object
  • Object
show all
Extended by:
Forwardable
Defined in:
lib/sh_vars/string.rb

Constant Summary collapse

KEY =
%r([^\s=]+)
WORD =
%r((\\.|(?!\s|"|'|`|\${|\$\().))
SPACE =
%r(\s+)
EQUAL =
%r(=)
OPEN =
%r(\$\(|\$\{)
CLOSE =
{ '$(' => %r(\)), '${' => %r(\}) }

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(str) ⇒ String

Returns a new instance of String.



25
26
27
# File 'lib/sh_vars/string.rb', line 25

def initialize(str)
  @str = StringScanner.new(str.to_s.strip)
end

Instance Attribute Details

#strObject (readonly)

Returns the value of attribute str.



23
24
25
# File 'lib/sh_vars/string.rb', line 23

def str
  @str
end

Class Method Details

.unquote_pair(str) ⇒ Object



7
8
9
10
# File 'lib/sh_vars/string.rb', line 7

def unquote_pair(str)
  str = new(str).unquote
  new(str).pair
end

Instance Method Details

#double_quotedObject



109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/sh_vars/string.rb', line 109

def double_quoted
  pattern = /^#{'\\' * level * 4}"/
  return unless quote = scan(pattern)
  quotes << quote

  parts, part = [], nil
  parts << part while part = scan_quote(quote)
  parts << values
  parts << part while part = scan_quote(quote)

  quotes.pop
  err(quote) unless scan(pattern) == quote
  [quote, *parts, quote].join
end

#double_quoted_pairObject



39
40
41
42
# File 'lib/sh_vars/string.rb', line 39

def double_quoted_pair
  return unless var = double_quoted
  self.class.unquote_pair(var)
end

#empty_pairObject



52
53
54
55
56
57
# File 'lib/sh_vars/string.rb', line 52

def empty_pair
  return if peek(1) == '='
  key = word
  pair = [key, ''] if key
  pair
end

#err(char) ⇒ Object

Raises:



173
174
175
# File 'lib/sh_vars/string.rb', line 173

def err(char)
  raise ParseError, "expected #{char} at position #{pos} in: #{string.inspect}"
end

#levelObject



165
166
167
# File 'lib/sh_vars/string.rb', line 165

def level
  quotes.size
end

#openObject



69
70
71
# File 'lib/sh_vars/string.rb', line 69

def open
  @open ||= []
end

#pairObject



35
36
37
# File 'lib/sh_vars/string.rb', line 35

def pair
  double_quoted_pair || unquoted_pair || empty_pair
end

#paren?Boolean

Returns:

  • (Boolean)


157
158
159
# File 'lib/sh_vars/string.rb', line 157

def paren?
  open.any? && peek(open.last.size) == open.last
end

#parensObject



73
74
75
76
77
78
79
80
81
82
83
# File 'lib/sh_vars/string.rb', line 73

def parens
  return unless paren = scan(OPEN)

  open << paren
  str = yield
  open.pop

  close = CLOSE[paren] if paren
  close = scan(close)  if close
  [paren, str, close].join
end

#parseObject



29
30
31
32
33
# File 'lib/sh_vars/string.rb', line 29

def parse
  pairs = [pair]
  pairs << pair while space
  pairs.tap { err('end of string') unless eos? }
end

#quote?Boolean

Returns:

  • (Boolean)


161
162
163
# File 'lib/sh_vars/string.rb', line 161

def quote?
  quotes.any? && peek(quotes.last.size) == quotes.last
end

#quotedObject



85
86
87
# File 'lib/sh_vars/string.rb', line 85

def quoted
  double_quoted || single_quoted("'") || single_quoted("`")
end

#quotesObject



169
170
171
# File 'lib/sh_vars/string.rb', line 169

def quotes
  @quotes ||= []
end

#scan_quote(quote) ⇒ Object



124
125
126
127
128
# File 'lib/sh_vars/string.rb', line 124

def scan_quote(quote)
  str = scan(/[^#{quote}\\]+/)
  str ||= scan(/#{'\\' * level * 2}#{quote}/)
  str
end

#scan_unquote(quote) ⇒ Object



146
147
148
149
150
151
# File 'lib/sh_vars/string.rb', line 146

def scan_unquote(quote)
  str = scan(/[^#{quote}\\]+/)
  return str if str
  str = scan(/#{'\\' * level * 2}#{quote}/)
  ['\\' * (level - 1), quote].join if str
end

#single_quoted(char) ⇒ Object



99
100
101
102
103
104
105
106
107
# File 'lib/sh_vars/string.rb', line 99

def single_quoted(char)
  pattern = /#{char}/
  return unless quote = scan(pattern)
  quotes << quote
  str = scan(/[^#{char}]*/) # how about nested double quoted strs in here
  quote = quotes.pop
  err(quote) unless scan(pattern) == quote
  [quote, str, quote].join
end

#spaceObject



95
96
97
# File 'lib/sh_vars/string.rb', line 95

def space
  scan(SPACE)
end

#space?Boolean

Returns:

  • (Boolean)


153
154
155
# File 'lib/sh_vars/string.rb', line 153

def space?
  peek(1) =~ SPACE
end

#unquoteObject



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
# File 'lib/sh_vars/string.rb', line 130

def unquote
  pattern = /^#{'\\' * level * 4}"/
  return unless quote = scan(pattern)
  quotes << quote

  parts, part = [], nil
  parts << part while part = scan_unquote(quote)
  parts << unquote
  parts << part while part = scan_unquote(quote)

  quote = quotes.pop
  err(quote) unless scan(pattern) == quote
  unquote = level == 0 ? '' : %(#{'\\' * (level)}")
  [unquote, *parts, unquote].join
end

#unquoted_pairObject



44
45
46
47
48
49
50
# File 'lib/sh_vars/string.rb', line 44

def unquoted_pair
  return unless key = scan(KEY)
  return [key, ''] unless scan(EQUAL)
  parts, part = [], nil
  parts << part while part = value
  [key, parts.join]
end

#valueObject



65
66
67
# File 'lib/sh_vars/string.rb', line 65

def value
  parens { values } || quoted || word
end

#valuesObject



59
60
61
62
63
# File 'lib/sh_vars/string.rb', line 59

def values
  parts, part = [], nil
  parts << part while part = value || space
  parts.join if parts.any?
end

#wordObject



89
90
91
92
93
# File 'lib/sh_vars/string.rb', line 89

def word
  chars, char = [], nil
  chars << char while !quote? && char = scan(WORD)
  chars.join if chars.any?
end