yap-shell-parser 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e0f5112fefe27736d9fe13a853af8d68fe0ef467
4
- data.tar.gz: b878cc0fab5a610f2d9c301bc298ce57ef446b29
3
+ metadata.gz: f0e319c332283c42adde5fcba98b7ee4c8f8e8bb
4
+ data.tar.gz: 9d1e3d51c8df81d6e3a50cca393bff86f3edbaeb
5
5
  SHA512:
6
- metadata.gz: 4ece41ddbb734944259ea26c50c8b3aa1d5ccdfa9592dc348c210ad75af0ac9918bbe872a13dbe2f369a3a5fd7a7ac124f189226c3d369b0cd35588804bc46a5
7
- data.tar.gz: 15e8978edc514cd0d81a1521b5872d6766e59b710057bfd5e42699b151e742c39d9d38500335658dfbfdc676b5fa84c241b4c5f282d7cef4d1572181294e5258
6
+ metadata.gz: eaaa6d7f6082dbb8915253dc41eb62295b98b7170398c6e4918396f19c3c714a2a5c968cc9e34f865fbe13c082d2e5ee688212952107a10fc6d41b1d0c6dbe97
7
+ data.tar.gz: 2a8f4c7efa8af4a3b14c5b275da68cd8071231afb68a653f374af6f888e9bbce6b2690e053ddc3735be0810a8f86dd4966f66a91ecf5aadf832d35228bc01f92
data/lib/tasks/gem.rake CHANGED
@@ -15,11 +15,13 @@ namespace :bump do
15
15
  _major = major.call($1) if major
16
16
  _minor = minor.call($2) if minor
17
17
  _patch = patch.call($3) if patch
18
- version = %|VERSION = "#{_major}.#{_minor}.#{_patch}"|
18
+ version = "#{_major}.#{_minor}.#{_patch}"
19
+ results = %|VERSION = "#{version}"|
19
20
  end
20
21
  File.write(@file, contents)
21
22
  system "bundle"
22
23
  system "git add #{ProjectVersion::FILE} && git commit -m 'Bumping version to #{version}'"
24
+ system "git tag v#{version}"
23
25
  end
24
26
 
25
27
  private
@@ -3,7 +3,7 @@
3
3
  # convert Array-like string into Ruby's Array.
4
4
 
5
5
  class Yap::Shell::ParserImpl
6
- token Command LiteralCommand Argument Heredoc InternalEval Separator Conditional Pipe Redirection LValue RValue
6
+ token Command LiteralCommand Argument Heredoc InternalEval Separator Conditional Pipe Redirection LValue RValue BeginCommandSubstitution EndCommandSubstitution
7
7
  #
8
8
  # prechigh
9
9
  # # left '**' '*' '/' '%'
@@ -35,9 +35,19 @@ pipeline : pipeline Pipe stmts2
35
35
 
36
36
  stmts2 : '(' stmts ')'
37
37
  { result = val[1] }
38
+ | stmts2 stmt_w_substitutions
39
+ { result = ConcatenationNode.new(val[0], val[1]) }
40
+ | stmt_w_substitutions
38
41
  | command_w_heredoc
39
42
  | internal_eval
40
43
 
44
+ stmt_w_substitutions : stmt_w_substitutions2 args
45
+ { result = val[0] ; val[0].tail = val[1] }
46
+ | stmt_w_substitutions2
47
+
48
+ stmt_w_substitutions2 : BeginCommandSubstitution stmts EndCommandSubstitution
49
+ { result = CommandSubstitutionNode.new(val[1]) }
50
+
41
51
  command_w_heredoc : command_w_redirects Heredoc
42
52
  { val[0].heredoc = val[1] ; result = val[0] }
43
53
  | command_w_redirects
@@ -68,26 +78,42 @@ command2: Command
68
78
  { result = CommandNode.new(val[0].value, val[1].flatten, literal:true) }
69
79
 
70
80
  args : Argument
71
- { result = [val[0].value] }
81
+ { result = [ArgumentNode.new(val[0].value)] }
72
82
  | args Argument
73
- { result = [val[0], val[1].value] }
83
+ { result = [val[0], ArgumentNode.new(val[1].value)].flatten }
74
84
 
75
85
  internal_eval : InternalEval
76
86
  { result = InternalEvalNode.new(val[0].value) }
77
87
 
88
+ ---- header
89
+ if $0 ==__FILE__
90
+ $LOAD_PATH.unshift File.dirname(__FILE__) + "/../../"
91
+ module Yap
92
+ module Shell
93
+ module Parser
94
+ end
95
+ end
96
+ end
97
+ require 'yap/shell/parser/nodes'
98
+ end
78
99
 
79
100
  ---- inner
80
101
  include Yap::Shell::Parser::Nodes
102
+
103
+ def each_command_substitution_for(input, &blk)
104
+ Yap::Shell::Parser::Lexer.new.each_command_substitution_for(input, &blk)
105
+ end
106
+
81
107
  #=end
82
108
  def parse(str)
83
- # @yydebug = true
109
+ @yydebug = true
84
110
 
85
111
  @q = Yap::Shell::Parser::Lexer.new.tokenize(str)
86
112
  # @q.push [false, '$'] # is optional from Racc 1.3.7
87
113
  # puts @q.inspect
88
114
  # puts "---- parse tree follows ----"
89
115
  __send__(Racc_Main_Parsing_Routine, _racc_setup(), false)
90
- #do_parse
116
+ # do_parse
91
117
  end
92
118
 
93
119
  def next_token
@@ -101,41 +127,20 @@ if $0 == __FILE__
101
127
  require 'yap/shell/parser/lexer'
102
128
  require 'yap/shell/parser/nodes'
103
129
  [
104
- # "echo foo",
105
- # "echo foo ; echo bar baz yep",
106
- # "echo foo && echo bar baz yep",
107
- # "echo foo && echo bar && ls foo && ls bar",
108
- # "echo foo ; echo bar baz yep ; ls foo",
109
- # "echo foo && echo bar ; ls baz",
110
- # "echo foo && echo bar ; ls baz ; echo zach || echo gretchen",
111
- # "echo foo | bar",
112
- # "echo foo | bar && foo | bar",
113
- # "foo && bar ; word || baz ; yep | grep -v foo",
114
- # "( foo )",
115
- # "( foo a b && bar c d )",
116
- # "( foo a b && (bar c d | baz e f))",
117
- # "((((foo))))",
118
- # "foo -b -c ; (this ;that ;the; other ;thing) && yep",
119
- # "foo -b -c ; (this ;that && other ;thing) && yep",
120
- # "4 + 5",
121
- # "!'hello' ; 4 - 4 && 10 + 3",
122
- # "\\foo <<-EOT\nbar\nEOT",
123
- # "ls | grep md | grep WISH",
124
- # "(!upcase)",
125
- # "echo foo > bar.txt",
126
- # "ls -l > a.txt ; echo f 2> b.txt ; cat b &> c.txt ; du -sh 1>&2 1>hey.txt",
127
- # "!Dir.chdir('..')",
128
- # "FOO=123",
129
- # "FOO=123 BAR=345",
130
- # "FOO=abc bar=2314 car=14ab ls -l",
131
- "FOO=abc BAR='hello world' ls -l ; CAR=f echo foo && say hi"
130
+ # "echo `echo hi`",
131
+ # "`git cbranch`",
132
+ # "`git cbranch`.bak",
133
+ # "echo `echo hi`",
134
+ "echo `echo hi` foo bar baz",
135
+ # "`hi``bye` `what`",
136
+ # "echo && `what` && where is `that`thing | `you know`",
132
137
  ].each do |src|
133
138
  puts 'parsing:'
134
139
  print src
135
140
  puts
136
141
  puts 'result:'
137
142
  require 'pp'
138
- ast = Yap::Shell::Parser.new.parse(src)
143
+ ast = Yap::Shell::ParserImpl.new.parse(src)
139
144
  pp ast
140
145
  end
141
146
 
@@ -49,6 +49,40 @@ module Yap::Shell
49
49
  REDIRECTION = /\A(([12]?>&?[12]?)\s*(?![12]>)(#{ARG})?)/
50
50
  REDIRECTION2 = /\A((&>|<)\s*(#{ARG}))/
51
51
 
52
+
53
+ # Loop over the given input and yield command substitutions. This yields
54
+ # an object that responds to #str, and #position.
55
+ #
56
+ # * The #str will be the contents of the command substitution, e.g. foo in `foo` or $(foo)
57
+ # * The #position will be range denoting where the command substitution started and stops in the string
58
+ #
59
+ # This will yield a result for every command substitution found.
60
+ #
61
+ # == Note
62
+ #
63
+ # This will not yield nested command substitutions. The caller is responsible
64
+ # for that.
65
+ def each_command_substitution_for(input, &blk)
66
+ return unless input
67
+
68
+ i = 0
69
+ loop do
70
+ break if i >= input.length
71
+
72
+ @chunk = input[i..-1]
73
+ if md=@chunk.match(COMMAND_SUBSTITUTION)
74
+ start = i
75
+ delimiter = md[1] == "$(" ? ")" : md[1]
76
+ result = process_string @chunk[md[0].length-1..-1], delimiter
77
+ consumed_length_so_far = result.consumed_length + (md[0].length - 1)
78
+ i += consumed_length_so_far
79
+ yield OpenStruct.new(str:result.str, position:(start..i))
80
+ else
81
+ i += 1
82
+ end
83
+ end
84
+ end
85
+
52
86
  def tokenize(str)
53
87
  @str = str
54
88
  @tokens = []
@@ -172,7 +206,7 @@ module Yap::Shell
172
206
  result = process_string @chunk[i..-1], ch
173
207
  str << result.str
174
208
  i += result.consumed_length
175
- elsif ch =~ /[\s\|;&]/
209
+ elsif ch =~ /[\s\|;&\)]/
176
210
  break
177
211
  else
178
212
  str << ch
@@ -233,14 +267,41 @@ module Yap::Shell
233
267
 
234
268
  def command_substitution_token
235
269
  if md=@chunk.match(COMMAND_SUBSTITUTION)
270
+ @looking_for_args = true
271
+
236
272
  delimiter = md[1] == "$(" ? ")" : md[1]
237
273
  result = process_string @chunk[md[0].length-1..-1], delimiter
238
- token :BeginSubcommand, md[1]
274
+
275
+ consumed_length_so_far = result.consumed_length + (md[0].length - 1)
276
+ append_result = process_until_separator(@chunk[consumed_length_so_far..-1])
277
+
278
+ token :BeginCommandSubstitution, md[1]
239
279
  @tokens.push *self.class.new.tokenize(result.str)
240
- token :EndSubcommand, delimiter
241
280
 
242
- result.consumed_length + (md[0].length - 1)
281
+ if append_result.consumed_length > 0
282
+ token :EndCommandSubstitution, delimiter, attrs:{concat_with: append_result.str}
283
+ else
284
+ token :EndCommandSubstitution, delimiter
285
+ end
286
+
287
+ return consumed_length_so_far + append_result.consumed_length
288
+ end
289
+ end
290
+
291
+ def process_until_separator(input_str)
292
+ str = ""
293
+ i = 0
294
+ loop do
295
+ ch = input_str[i]
296
+
297
+ if ch && ch !~ /[\s;\|&>\$<`]/
298
+ str << ch
299
+ i+=1
300
+ else
301
+ break
302
+ end
243
303
  end
304
+ OpenStruct.new(str:str, consumed_length: str.length)
244
305
  end
245
306
 
246
307
  def process_internal_eval(input_str, consumed:0)
@@ -6,6 +6,24 @@ module Yap::Shell
6
6
  end
7
7
  end
8
8
 
9
+ class ArgumentNode
10
+ include Visitor
11
+
12
+ attr_reader :lvalue
13
+
14
+ def initialize(lvalue)
15
+ @lvalue = lvalue
16
+ end
17
+
18
+ def inspect
19
+ to_s
20
+ end
21
+
22
+ def to_s
23
+ "ArgumentNode(#{lvalue.inspect})"
24
+ end
25
+ end
26
+
9
27
  class AssignmentNode
10
28
  include Visitor
11
29
 
@@ -201,5 +219,44 @@ module Yap::Shell
201
219
  end
202
220
  end
203
221
 
222
+ class ConcatenationNode
223
+ include Visitor
224
+
225
+ attr_reader :left, :right
226
+
227
+ def initialize(left, right)
228
+ @left = left
229
+ @right = right
230
+ end
231
+
232
+ def to_s(indent:0)
233
+ "ConcatenationNode(left: #{left.to_s}, right: #{right.to_s})"
234
+ end
235
+
236
+ def inspect
237
+ to_s
238
+ end
239
+
240
+ end
241
+
242
+ class CommandSubstitutionNode
243
+ include Visitor
244
+
245
+ attr_accessor :tail
246
+ attr_reader :node
247
+
248
+ def initialize(node)
249
+ @node = node
250
+ end
251
+
252
+ def to_s(indent:0)
253
+ "CommandSubstitutionNode(#{@node.to_s}, tail: #{tail.inspect})"
254
+ end
255
+
256
+ def inspect
257
+ to_s
258
+ end
259
+ end
260
+
204
261
  end
205
262
  end
@@ -3,7 +3,7 @@ require 'yap/shell/parser'
3
3
  module Yap
4
4
  module Shell
5
5
  module Parser
6
- VERSION = "0.0.4"
6
+ VERSION = "0.1.0"
7
7
  end
8
8
  end
9
9
  end
@@ -5,22 +5,39 @@
5
5
  #
6
6
 
7
7
  require 'racc/parser.rb'
8
+
9
+ if $0 ==__FILE__
10
+ $LOAD_PATH.unshift File.dirname(__FILE__) + "/../../"
11
+ module Yap
12
+ module Shell
13
+ module Parser
14
+ end
15
+ end
16
+ end
17
+ require 'yap/shell/parser/nodes'
18
+ end
19
+
8
20
  module Yap
9
21
  module Shell
10
22
  class ParserImpl < Racc::Parser
11
23
 
12
- module_eval(<<'...end grammar.y/module_eval...', 'grammar.y', 80)
24
+ module_eval(<<'...end grammar.y/module_eval...', 'grammar.y', 101)
13
25
  include Yap::Shell::Parser::Nodes
26
+
27
+ def each_command_substitution_for(input, &blk)
28
+ Yap::Shell::Parser::Lexer.new.each_command_substitution_for(input, &blk)
29
+ end
30
+
14
31
  #=end
15
32
  def parse(str)
16
- # @yydebug = true
33
+ @yydebug = true
17
34
 
18
35
  @q = Yap::Shell::Parser::Lexer.new.tokenize(str)
19
36
  # @q.push [false, '$'] # is optional from Racc 1.3.7
20
37
  # puts @q.inspect
21
38
  # puts "---- parse tree follows ----"
22
39
  __send__(Racc_Main_Parsing_Routine, _racc_setup(), false)
23
- #do_parse
40
+ # do_parse
24
41
  end
25
42
 
26
43
  def next_token
@@ -31,78 +48,95 @@ module_eval(<<'...end grammar.y/module_eval...', 'grammar.y', 80)
31
48
  ##### State transition tables begin ###
32
49
 
33
50
  racc_action_table = [
34
- 15, 16, 29, 23, 17, 29, 15, 16, 24, 13,
35
- 17, 6, 15, 16, 27, 13, 17, 6, 15, 16,
36
- 31, 13, 17, 6, 15, 16, 21, 13, 17, 6,
37
- 15, 16, 20, 13, 19, 6, 19, 18, 36, 26,
38
- 37, 35, 37, 20, 21 ]
51
+ 18, 19, 28, 22, 20, 34, 18, 19, 28, 16,
52
+ 20, 11, 41, 6, 28, 16, 22, 11, 37, 6,
53
+ 18, 19, 11, 43, 20, 24, 18, 19, 23, 16,
54
+ 20, 11, 22, 6, 42, 16, 21, 11, 30, 6,
55
+ 18, 19, 44, 31, 20, 42, 18, 19, 42, 16,
56
+ 20, 11, 23, 6, 24, 16, 11, 11, nil, 6,
57
+ 18, 19, nil, nil, nil, nil, nil, nil, nil, 33 ]
39
58
 
40
59
  racc_action_check = [
41
- 0, 0, 16, 9, 0, 15, 6, 6, 9, 0,
42
- 6, 0, 21, 21, 13, 6, 21, 6, 20, 20,
43
- 18, 21, 20, 21, 19, 19, 4, 20, 19, 20,
44
- 12, 12, 3, 19, 22, 19, 2, 1, 26, 12,
45
- 28, 22, 30, 32, 33 ]
60
+ 0, 0, 19, 26, 0, 16, 11, 11, 18, 0,
61
+ 11, 0, 26, 0, 10, 11, 29, 11, 21, 11,
62
+ 6, 6, 5, 29, 6, 4, 24, 24, 3, 6,
63
+ 24, 6, 2, 6, 27, 24, 1, 24, 12, 24,
64
+ 23, 23, 33, 12, 23, 35, 22, 22, 36, 23,
65
+ 22, 23, 38, 23, 39, 22, 40, 22, nil, 22,
66
+ 15, 15, nil, nil, nil, nil, nil, nil, nil, 15 ]
46
67
 
47
68
  racc_action_pointer = [
48
- -2, 37, 29, 24, 17, nil, 4, nil, nil, -2,
49
- nil, nil, 28, 2, nil, 1, -2, nil, 20, 22,
50
- 16, 10, 27, nil, nil, nil, 26, nil, 36, nil,
51
- 38, nil, 35, 35, nil, nil, nil, nil ]
69
+ -2, 36, 25, 20, 16, 9, 18, nil, nil, nil,
70
+ 10, 4, 33, nil, nil, 58, -7, nil, 4, -2,
71
+ nil, 18, 44, 38, 24, nil, -4, 30, nil, 9,
72
+ nil, nil, nil, 30, nil, 41, 44, nil, 44, 45,
73
+ 43, nil, nil, nil, nil ]
52
74
 
53
75
  racc_action_default = [
54
- -28, -28, -1, -3, -5, -7, -28, -9, -10, -12,
55
- -14, -15, -16, -28, -20, -21, -23, -27, -28, -28,
56
- -28, -28, -28, -11, -13, -17, -28, -19, -22, -25,
57
- -24, 38, -2, -4, -6, -8, -18, -26 ]
76
+ -33, -33, -1, -3, -5, -7, -33, -10, -11, -12,
77
+ -14, -33, -17, -19, -20, -21, -33, -25, -26, -28,
78
+ -32, -33, -33, -33, -33, -9, -33, -13, -30, -33,
79
+ -16, -18, -22, -33, -24, -27, -29, 45, -2, -4,
80
+ -6, -8, -31, -15, -23 ]
58
81
 
59
82
  racc_goto_table = [
60
- 2, 28, 30, 33, 32, 34, 22, 25, 1 ]
83
+ 25, 2, 27, 40, 39, 38, 1, 26, 32, nil,
84
+ 35, 36, 29, nil, nil, nil, nil, nil, nil, nil,
85
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
86
+ nil, nil, nil, nil, nil, 25 ]
61
87
 
62
88
  racc_goto_check = [
63
- 2, 13, 13, 4, 3, 5, 2, 10, 1 ]
89
+ 6, 2, 10, 5, 4, 3, 1, 2, 13, nil,
90
+ 10, 10, 2, nil, nil, nil, nil, nil, nil, nil,
91
+ nil, nil, nil, nil, nil, nil, nil, nil, nil, nil,
92
+ nil, nil, nil, nil, nil, 6 ]
64
93
 
65
94
  racc_goto_pointer = [
66
- nil, 8, 0, -15, -17, -16, nil, nil, nil, nil,
67
- -5, nil, nil, -14 ]
95
+ nil, 6, 1, -17, -19, -21, -5, nil, nil, nil,
96
+ -8, nil, nil, -7, nil, nil ]
68
97
 
69
98
  racc_goto_default = [
70
99
  nil, nil, nil, 3, 4, 5, 7, 8, 9, 10,
71
- 11, 12, 14, nil ]
100
+ nil, 12, 13, 14, 15, 17 ]
72
101
 
73
102
  racc_reduce_table = [
74
103
  0, 0, :racc_error,
75
- 1, 16, :_reduce_none,
76
- 3, 17, :_reduce_2,
77
- 1, 17, :_reduce_3,
78
- 3, 18, :_reduce_4,
79
104
  1, 18, :_reduce_none,
80
- 3, 19, :_reduce_6,
81
- 1, 19, :_reduce_none,
82
- 3, 20, :_reduce_8,
83
- 1, 20, :_reduce_none,
105
+ 3, 19, :_reduce_2,
106
+ 1, 19, :_reduce_3,
107
+ 3, 20, :_reduce_4,
84
108
  1, 20, :_reduce_none,
85
- 2, 21, :_reduce_11,
109
+ 3, 21, :_reduce_6,
86
110
  1, 21, :_reduce_none,
111
+ 3, 22, :_reduce_8,
112
+ 2, 22, :_reduce_9,
113
+ 1, 22, :_reduce_none,
114
+ 1, 22, :_reduce_none,
115
+ 1, 22, :_reduce_none,
87
116
  2, 23, :_reduce_13,
88
117
  1, 23, :_reduce_none,
89
- 1, 23, :_reduce_none,
90
- 1, 23, :_reduce_none,
91
- 2, 24, :_reduce_17,
92
- 3, 26, :_reduce_18,
93
- 2, 26, :_reduce_19,
94
- 1, 25, :_reduce_none,
95
- 1, 27, :_reduce_21,
96
- 2, 27, :_reduce_22,
97
- 1, 27, :_reduce_23,
98
- 2, 27, :_reduce_24,
99
- 1, 28, :_reduce_25,
100
- 2, 28, :_reduce_26,
101
- 1, 22, :_reduce_27 ]
102
-
103
- racc_reduce_n = 28
104
-
105
- racc_shift_n = 38
118
+ 3, 26, :_reduce_15,
119
+ 2, 24, :_reduce_16,
120
+ 1, 24, :_reduce_none,
121
+ 2, 28, :_reduce_18,
122
+ 1, 28, :_reduce_none,
123
+ 1, 28, :_reduce_none,
124
+ 1, 28, :_reduce_none,
125
+ 2, 29, :_reduce_22,
126
+ 3, 31, :_reduce_23,
127
+ 2, 31, :_reduce_24,
128
+ 1, 30, :_reduce_none,
129
+ 1, 32, :_reduce_26,
130
+ 2, 32, :_reduce_27,
131
+ 1, 32, :_reduce_28,
132
+ 2, 32, :_reduce_29,
133
+ 1, 27, :_reduce_30,
134
+ 2, 27, :_reduce_31,
135
+ 1, 25, :_reduce_32 ]
136
+
137
+ racc_reduce_n = 33
138
+
139
+ racc_shift_n = 45
106
140
 
107
141
  racc_token_table = {
108
142
  false => 0,
@@ -118,10 +152,12 @@ racc_token_table = {
118
152
  :Redirection => 10,
119
153
  :LValue => 11,
120
154
  :RValue => 12,
121
- "(" => 13,
122
- ")" => 14 }
155
+ :BeginCommandSubstitution => 13,
156
+ :EndCommandSubstitution => 14,
157
+ "(" => 15,
158
+ ")" => 16 }
123
159
 
124
- racc_nt_base = 15
160
+ racc_nt_base = 17
125
161
 
126
162
  racc_use_result_var = true
127
163
 
@@ -155,6 +191,8 @@ Racc_token_to_s_table = [
155
191
  "Redirection",
156
192
  "LValue",
157
193
  "RValue",
194
+ "BeginCommandSubstitution",
195
+ "EndCommandSubstitution",
158
196
  "\"(\"",
159
197
  "\")\"",
160
198
  "$start",
@@ -163,14 +201,16 @@ Racc_token_to_s_table = [
163
201
  "stmt",
164
202
  "pipeline",
165
203
  "stmts2",
204
+ "stmt_w_substitutions",
166
205
  "command_w_heredoc",
167
206
  "internal_eval",
207
+ "stmt_w_substitutions2",
208
+ "args",
168
209
  "command_w_redirects",
169
210
  "command_w_vars",
170
211
  "command",
171
212
  "vars",
172
- "command2",
173
- "args" ]
213
+ "command2" ]
174
214
 
175
215
  Racc_debug_parser = false
176
216
 
@@ -219,99 +259,124 @@ module_eval(<<'.,.,', 'grammar.y', 36)
219
259
  end
220
260
  .,.,
221
261
 
222
- # reduce 9 omitted
262
+ module_eval(<<'.,.,', 'grammar.y', 38)
263
+ def _reduce_9(val, _values, result)
264
+ result = ConcatenationNode.new(val[0], val[1])
265
+ result
266
+ end
267
+ .,.,
223
268
 
224
269
  # reduce 10 omitted
225
270
 
226
- module_eval(<<'.,.,', 'grammar.y', 41)
227
- def _reduce_11(val, _values, result)
271
+ # reduce 11 omitted
272
+
273
+ # reduce 12 omitted
274
+
275
+ module_eval(<<'.,.,', 'grammar.y', 44)
276
+ def _reduce_13(val, _values, result)
277
+ result = val[0] ; val[0].tail = val[1]
278
+ result
279
+ end
280
+ .,.,
281
+
282
+ # reduce 14 omitted
283
+
284
+ module_eval(<<'.,.,', 'grammar.y', 48)
285
+ def _reduce_15(val, _values, result)
286
+ result = CommandSubstitutionNode.new(val[1])
287
+ result
288
+ end
289
+ .,.,
290
+
291
+ module_eval(<<'.,.,', 'grammar.y', 51)
292
+ def _reduce_16(val, _values, result)
228
293
  val[0].heredoc = val[1] ; result = val[0]
229
294
  result
230
295
  end
231
296
  .,.,
232
297
 
233
- # reduce 12 omitted
298
+ # reduce 17 omitted
234
299
 
235
- module_eval(<<'.,.,', 'grammar.y', 45)
236
- def _reduce_13(val, _values, result)
300
+ module_eval(<<'.,.,', 'grammar.y', 55)
301
+ def _reduce_18(val, _values, result)
237
302
  val[0].redirects << RedirectionNode.new(val[1].value, val[1].attrs[:target]) ; result = val[0]
238
303
  result
239
304
  end
240
305
  .,.,
241
306
 
242
- # reduce 14 omitted
307
+ # reduce 19 omitted
243
308
 
244
- # reduce 15 omitted
309
+ # reduce 20 omitted
245
310
 
246
- # reduce 16 omitted
311
+ # reduce 21 omitted
247
312
 
248
- module_eval(<<'.,.,', 'grammar.y', 51)
249
- def _reduce_17(val, _values, result)
313
+ module_eval(<<'.,.,', 'grammar.y', 61)
314
+ def _reduce_22(val, _values, result)
250
315
  result = EnvWrapperNode.new(val[0], val[1])
251
316
  result
252
317
  end
253
318
  .,.,
254
319
 
255
- module_eval(<<'.,.,', 'grammar.y', 54)
256
- def _reduce_18(val, _values, result)
320
+ module_eval(<<'.,.,', 'grammar.y', 64)
321
+ def _reduce_23(val, _values, result)
257
322
  val[0].add_var(val[1].value, val[2].value) ; result = val[0]
258
323
  result
259
324
  end
260
325
  .,.,
261
326
 
262
- module_eval(<<'.,.,', 'grammar.y', 56)
263
- def _reduce_19(val, _values, result)
327
+ module_eval(<<'.,.,', 'grammar.y', 66)
328
+ def _reduce_24(val, _values, result)
264
329
  result = EnvNode.new(val[0].value, val[1].value)
265
330
  result
266
331
  end
267
332
  .,.,
268
333
 
269
- # reduce 20 omitted
334
+ # reduce 25 omitted
270
335
 
271
- module_eval(<<'.,.,', 'grammar.y', 61)
272
- def _reduce_21(val, _values, result)
336
+ module_eval(<<'.,.,', 'grammar.y', 71)
337
+ def _reduce_26(val, _values, result)
273
338
  result = CommandNode.new(val[0].value)
274
339
  result
275
340
  end
276
341
  .,.,
277
342
 
278
- module_eval(<<'.,.,', 'grammar.y', 63)
279
- def _reduce_22(val, _values, result)
343
+ module_eval(<<'.,.,', 'grammar.y', 73)
344
+ def _reduce_27(val, _values, result)
280
345
  result = CommandNode.new(val[0].value, val[1].flatten)
281
346
  result
282
347
  end
283
348
  .,.,
284
349
 
285
- module_eval(<<'.,.,', 'grammar.y', 65)
286
- def _reduce_23(val, _values, result)
350
+ module_eval(<<'.,.,', 'grammar.y', 75)
351
+ def _reduce_28(val, _values, result)
287
352
  result = CommandNode.new(val[0].value, literal:true)
288
353
  result
289
354
  end
290
355
  .,.,
291
356
 
292
- module_eval(<<'.,.,', 'grammar.y', 67)
293
- def _reduce_24(val, _values, result)
357
+ module_eval(<<'.,.,', 'grammar.y', 77)
358
+ def _reduce_29(val, _values, result)
294
359
  result = CommandNode.new(val[0].value, val[1].flatten, literal:true)
295
360
  result
296
361
  end
297
362
  .,.,
298
363
 
299
- module_eval(<<'.,.,', 'grammar.y', 70)
300
- def _reduce_25(val, _values, result)
301
- result = [val[0].value]
364
+ module_eval(<<'.,.,', 'grammar.y', 80)
365
+ def _reduce_30(val, _values, result)
366
+ result = [ArgumentNode.new(val[0].value)]
302
367
  result
303
368
  end
304
369
  .,.,
305
370
 
306
- module_eval(<<'.,.,', 'grammar.y', 72)
307
- def _reduce_26(val, _values, result)
308
- result = [val[0], val[1].value]
371
+ module_eval(<<'.,.,', 'grammar.y', 82)
372
+ def _reduce_31(val, _values, result)
373
+ result = [val[0], ArgumentNode.new(val[1].value)].flatten
309
374
  result
310
375
  end
311
376
  .,.,
312
377
 
313
- module_eval(<<'.,.,', 'grammar.y', 75)
314
- def _reduce_27(val, _values, result)
378
+ module_eval(<<'.,.,', 'grammar.y', 85)
379
+ def _reduce_32(val, _values, result)
315
380
  result = InternalEvalNode.new(val[0].value)
316
381
  result
317
382
  end
@@ -331,41 +396,20 @@ if $0 == __FILE__
331
396
  require 'yap/shell/parser/lexer'
332
397
  require 'yap/shell/parser/nodes'
333
398
  [
334
- # "echo foo",
335
- # "echo foo ; echo bar baz yep",
336
- # "echo foo && echo bar baz yep",
337
- # "echo foo && echo bar && ls foo && ls bar",
338
- # "echo foo ; echo bar baz yep ; ls foo",
339
- # "echo foo && echo bar ; ls baz",
340
- # "echo foo && echo bar ; ls baz ; echo zach || echo gretchen",
341
- # "echo foo | bar",
342
- # "echo foo | bar && foo | bar",
343
- # "foo && bar ; word || baz ; yep | grep -v foo",
344
- # "( foo )",
345
- # "( foo a b && bar c d )",
346
- # "( foo a b && (bar c d | baz e f))",
347
- # "((((foo))))",
348
- # "foo -b -c ; (this ;that ;the; other ;thing) && yep",
349
- # "foo -b -c ; (this ;that && other ;thing) && yep",
350
- # "4 + 5",
351
- # "!'hello' ; 4 - 4 && 10 + 3",
352
- # "\\foo <<-EOT\nbar\nEOT",
353
- # "ls | grep md | grep WISH",
354
- # "(!upcase)",
355
- # "echo foo > bar.txt",
356
- # "ls -l > a.txt ; echo f 2> b.txt ; cat b &> c.txt ; du -sh 1>&2 1>hey.txt",
357
- # "!Dir.chdir('..')",
358
- # "FOO=123",
359
- # "FOO=123 BAR=345",
360
- # "FOO=abc bar=2314 car=14ab ls -l",
361
- "FOO=abc BAR='hello world' ls -l ; CAR=f echo foo && say hi"
399
+ # "echo `echo hi`",
400
+ # "`git cbranch`",
401
+ # "`git cbranch`.bak",
402
+ # "echo `echo hi`",
403
+ "echo `echo hi` foo bar baz",
404
+ # "`hi``bye` `what`",
405
+ # "echo && `what` && where is `that`thing | `you know`",
362
406
  ].each do |src|
363
407
  puts 'parsing:'
364
408
  print src
365
409
  puts
366
410
  puts 'result:'
367
411
  require 'pp'
368
- ast = Yap::Shell::Parser.new.parse(src)
412
+ ast = Yap::Shell::ParserImpl.new.parse(src)
369
413
  pp ast
370
414
  end
371
415
 
@@ -444,6 +444,19 @@ describe Yap::Shell::Parser::Lexer do
444
444
  ]}
445
445
  end
446
446
 
447
+ describe 'simple command with arguments: (foo a b)' do
448
+ let(:str){ "(foo a (b))" }
449
+ it { should eq [
450
+ t('(', '(', lineno:0),
451
+ t(:Command, 'foo', lineno:0),
452
+ t(:Argument, 'a', lineno:0),
453
+ t('(', '(', lineno:0),
454
+ t(:Argument, 'b', lineno:0),
455
+ t(')', ')', lineno:0),
456
+ t(')', ')', lineno:0)
457
+ ]}
458
+ end
459
+
447
460
  describe 'simple interval eval: (!bar)' do
448
461
  let(:str){ "(!bar)" }
449
462
  it { should eq [
@@ -695,29 +708,121 @@ describe Yap::Shell::Parser::Lexer do
695
708
  describe "backticks can wrap simple commands: `pwd`" do
696
709
  let(:str){ "`pwd`" }
697
710
  it { should eq [
698
- t(:BeginSubcommand, "`", lineno: 0),
711
+ t(:BeginCommandSubstitution, "`", lineno: 0),
699
712
  t(:Command, "pwd", lineno: 0),
700
- t(:EndSubcommand, "`", lineno: 0)
713
+ t(:EndCommandSubstitution, "`", lineno: 0)
714
+ ]}
715
+ end
716
+
717
+ describe "backticks can be used with additional arguments: git branch `git cbranch` bak" do
718
+ let(:str){ "git branch `git cbranch` bak" }
719
+ it { should eq [
720
+ t(:Command, "git", lineno: 0),
721
+ t(:Argument, "branch", lineno: 0),
722
+ t(:BeginCommandSubstitution, '`', lineno: 0),
723
+ t(:Command, 'git', lineno: 0),
724
+ t(:Argument, 'cbranch', lineno: 0),
725
+ t(:EndCommandSubstitution, '`', lineno: 0),
726
+ t(:Argument, 'bak', lineno: 0)
701
727
  ]}
702
728
  end
703
729
 
704
- describe "backticks can be used as part of an argument: git branch `git cbranch`.bak" do
730
+ describe "backticks back to back: `hi``bye`" do
731
+ let(:str){ "`hi``bye`" }
732
+ it { should eq [
733
+ t(:BeginCommandSubstitution, '`', lineno: 0),
734
+ t(:Command, 'hi', lineno: 0),
735
+ t(:EndCommandSubstitution, '`', lineno: 0),
736
+ t(:BeginCommandSubstitution, '`', lineno: 0),
737
+ t(:Command, 'bye', lineno: 0),
738
+ t(:EndCommandSubstitution, '`', lineno: 0),
739
+ ]}
740
+ end
741
+
742
+ describe "backticks and following arguments can be separated: `ls`<SEPERATOR>bak" do
743
+ context "separated with a space" do
744
+ let(:str){ "`ls` bak" }
745
+ it { should eq [
746
+ t(:BeginCommandSubstitution, '`', lineno: 0),
747
+ t(:Command, 'ls', lineno: 0),
748
+ t(:EndCommandSubstitution, '`', lineno: 0),
749
+ t(:Argument, 'bak', lineno: 0)
750
+ ]}
751
+ end
752
+
753
+ context "separated with a semi-colon" do
754
+ let(:str){ "`ls`;bak" }
755
+ it { should eq [
756
+ t(:BeginCommandSubstitution, '`', lineno: 0),
757
+ t(:Command, 'ls', lineno: 0),
758
+ t(:EndCommandSubstitution, '`', lineno: 0),
759
+ t(:Separator, ';', lineno: 0),
760
+ t(:Command, 'bak', lineno: 0)
761
+ ]}
762
+ end
763
+
764
+ context "separated with a pipe" do
765
+ let(:str){ "`ls`|bak" }
766
+ it { should eq [
767
+ t(:BeginCommandSubstitution, '`', lineno: 0),
768
+ t(:Command, 'ls', lineno: 0),
769
+ t(:EndCommandSubstitution, '`', lineno: 0),
770
+ t(:Pipe, '|', lineno: 0),
771
+ t(:Command, 'bak', lineno: 0)
772
+ ]}
773
+ end
774
+
775
+ context "separated with &&" do
776
+ let(:str){ "`ls`&&bak" }
777
+ it { should eq [
778
+ t(:BeginCommandSubstitution, '`', lineno: 0),
779
+ t(:Command, 'ls', lineno: 0),
780
+ t(:EndCommandSubstitution, '`', lineno: 0),
781
+ t(:Conditional, '&&', lineno: 0),
782
+ t(:Command, 'bak', lineno: 0)
783
+ ]}
784
+ end
785
+
786
+ context "separated with ||" do
787
+ let(:str){ "`ls`||bak" }
788
+ it { should eq [
789
+ t(:BeginCommandSubstitution, '`', lineno: 0),
790
+ t(:Command, 'ls', lineno: 0),
791
+ t(:EndCommandSubstitution, '`', lineno: 0),
792
+ t(:Conditional, '||', lineno: 0),
793
+ t(:Command, 'bak', lineno: 0)
794
+ ]}
795
+ end
796
+
797
+ context "separated with &" do
798
+ let(:str){ "`ls`&bak" }
799
+ pending "background syntax not supported yet"
800
+ # it { should eq [
801
+ # t(:BeginCommandSubstitution, '`', lineno: 0),
802
+ # t(:Command, 'ls', lineno: 0),
803
+ # t(:EndCommandSubstitution, '`', lineno: 0),
804
+ # t(:Separator, '&', lineno: 0),
805
+ # t(:Command, 'bak', lineno: 0)
806
+ # ]}
807
+ end
808
+ end
809
+
810
+ describe "backticks can be instructed to concatenate with arguments that aren't separated: git branch `git cbranch`.bak" do
705
811
  let(:str){ "git branch `git cbranch`.bak" }
706
812
  it { should eq [
707
813
  t(:Command, "git", lineno: 0),
708
814
  t(:Argument, "branch", lineno: 0),
709
- t(:BeginSubcommand, '`', lineno: 0),
815
+ t(:BeginCommandSubstitution, '`', lineno: 0),
710
816
  t(:Command, 'git', lineno: 0),
711
817
  t(:Argument, 'cbranch', lineno: 0),
712
- t(:EndSubcommand, '`', lineno: 0),
713
- t(:Argument, '.bak', lineno: 0)
818
+ t(:EndCommandSubstitution, '`', lineno: 0, attrs:{concat_with:'.bak'})
714
819
  ]}
715
820
  end
716
821
 
717
822
  describe "backticks can wrap complex statements: `ls -al && foo bar || baz`" do
718
823
  let(:str){ "`ls -al && foo bar || baz`" }
719
824
  it { should eq [
720
- t(:BeginSubcommand, "`", lineno: 0),
825
+ t(:BeginCommandSubstitution, "`", lineno: 0),
721
826
  t(:Command,'ls', lineno: 0),
722
827
  t(:Argument, '-al', lineno: 0),
723
828
  t(:Conditional, '&&', lineno: 0),
@@ -725,7 +830,7 @@ describe Yap::Shell::Parser::Lexer do
725
830
  t(:Argument, 'bar', lineno: 0),
726
831
  t(:Conditional, '||', lineno: 0),
727
832
  t(:Command, 'baz', lineno: 0),
728
- t(:EndSubcommand, "`", lineno: 0)
833
+ t(:EndCommandSubstitution, "`", lineno: 0)
729
834
  ]}
730
835
  end
731
836
 
@@ -733,9 +838,9 @@ describe Yap::Shell::Parser::Lexer do
733
838
  let(:str){ "echo `pwd`" }
734
839
  it { should eq [
735
840
  t(:Command,'echo', lineno: 0),
736
- t(:BeginSubcommand, "`", lineno: 0),
841
+ t(:BeginCommandSubstitution, "`", lineno: 0),
737
842
  t(:Command,'pwd', lineno: 0),
738
- t(:EndSubcommand, "`", lineno: 0)
843
+ t(:EndCommandSubstitution, "`", lineno: 0)
739
844
  ]}
740
845
  end
741
846
 
@@ -743,7 +848,7 @@ describe Yap::Shell::Parser::Lexer do
743
848
  let(:str){ "echo `pwd && foo bar || baz ; yep` ; hello" }
744
849
  it { should eq [
745
850
  t(:Command,'echo', lineno: 0),
746
- t(:BeginSubcommand, "`", lineno: 0),
851
+ t(:BeginCommandSubstitution, "`", lineno: 0),
747
852
  t(:Command,'pwd', lineno: 0),
748
853
  t(:Conditional, '&&', lineno: 0),
749
854
  t(:Command, 'foo', lineno: 0),
@@ -752,7 +857,7 @@ describe Yap::Shell::Parser::Lexer do
752
857
  t(:Command, 'baz', lineno: 0),
753
858
  t(:Separator, ";", lineno: 0),
754
859
  t(:Command, 'yep', lineno: 0),
755
- t(:EndSubcommand, "`", lineno: 0),
860
+ t(:EndCommandSubstitution, "`", lineno: 0),
756
861
  t(:Separator, ";", lineno: 0),
757
862
  t(:Command, "hello", lineno: 0)
758
863
  ]}
@@ -761,16 +866,16 @@ describe Yap::Shell::Parser::Lexer do
761
866
  describe "dollar-sign paren can wrap simple commands: $(pwd)" do
762
867
  let(:str){ "$(pwd)" }
763
868
  it { should eq [
764
- t(:BeginSubcommand, "$(", lineno: 0),
869
+ t(:BeginCommandSubstitution, "$(", lineno: 0),
765
870
  t(:Command, "pwd", lineno: 0),
766
- t(:EndSubcommand, ")", lineno: 0)
871
+ t(:EndCommandSubstitution, ")", lineno: 0)
767
872
  ]}
768
873
  end
769
874
 
770
875
  describe "dollar-sign paren can wrap complex statements: $(ls -al && foo bar || baz)" do
771
876
  let(:str){ "$(ls -al && foo bar || baz)" }
772
877
  it { should eq [
773
- t(:BeginSubcommand, "$(", lineno: 0),
878
+ t(:BeginCommandSubstitution, "$(", lineno: 0),
774
879
  t(:Command,'ls', lineno: 0),
775
880
  t(:Argument, '-al', lineno: 0),
776
881
  t(:Conditional, '&&', lineno: 0),
@@ -778,7 +883,7 @@ describe Yap::Shell::Parser::Lexer do
778
883
  t(:Argument, 'bar', lineno: 0),
779
884
  t(:Conditional, '||', lineno: 0),
780
885
  t(:Command, 'baz', lineno: 0),
781
- t(:EndSubcommand, ")", lineno: 0)
886
+ t(:EndCommandSubstitution, ")", lineno: 0)
782
887
  ]}
783
888
  end
784
889
 
@@ -786,9 +891,9 @@ describe Yap::Shell::Parser::Lexer do
786
891
  let(:str){ "echo $(pwd)" }
787
892
  it { should eq [
788
893
  t(:Command,'echo', lineno: 0),
789
- t(:BeginSubcommand, "$(", lineno: 0),
894
+ t(:BeginCommandSubstitution, "$(", lineno: 0),
790
895
  t(:Command,'pwd', lineno: 0),
791
- t(:EndSubcommand, ")", lineno: 0)
896
+ t(:EndCommandSubstitution, ")", lineno: 0)
792
897
  ]}
793
898
  end
794
899
 
@@ -796,7 +901,7 @@ describe Yap::Shell::Parser::Lexer do
796
901
  let(:str){ "echo $(pwd && foo bar || baz ; yep) ; hello" }
797
902
  it { should eq [
798
903
  t(:Command,'echo', lineno: 0),
799
- t(:BeginSubcommand, "$(", lineno: 0),
904
+ t(:BeginCommandSubstitution, "$(", lineno: 0),
800
905
  t(:Command,'pwd', lineno: 0),
801
906
  t(:Conditional, '&&', lineno: 0),
802
907
  t(:Command, 'foo', lineno: 0),
@@ -805,7 +910,7 @@ describe Yap::Shell::Parser::Lexer do
805
910
  t(:Command, 'baz', lineno: 0),
806
911
  t(:Separator, ";", lineno: 0),
807
912
  t(:Command, 'yep', lineno: 0),
808
- t(:EndSubcommand, ")", lineno: 0),
913
+ t(:EndCommandSubstitution, ")", lineno: 0),
809
914
  t(:Separator, ";", lineno: 0),
810
915
  t(:Command, "hello", lineno: 0)
811
916
  ]}
@@ -813,4 +918,54 @@ describe Yap::Shell::Parser::Lexer do
813
918
 
814
919
  end
815
920
 
921
+ describe '#each_command_substitution_for' do
922
+ context 'when there are no substitutions' do
923
+ let(:str){ "echo " }
924
+
925
+ it "doesn't yield" do
926
+ expect { |b|
927
+ described_class.new.each_command_substitution_for(str, &b)
928
+ }.to_not yield_control
929
+ end
930
+ end
931
+
932
+ context 'when there is one substitution string' do
933
+ let(:str){ "echo `echo hi`" }
934
+
935
+ it "yields the command substitution string" do
936
+ expect { |b|
937
+ described_class.new.each_command_substitution_for(str, &b)
938
+ }.to yield_with_args OpenStruct.new(str:"echo hi", position:5..14)
939
+ end
940
+ end
941
+
942
+ context 'when there are multiple substitution strings' do
943
+ let(:str){ "echo `hi` foo $(world) `bye` $(world) foo" }
944
+
945
+ it "yields the command substitution string" do
946
+ expect { |b|
947
+ described_class.new.each_command_substitution_for(str, &b)
948
+ }.to yield_successive_args(
949
+ OpenStruct.new(str:"hi", position:5..9),
950
+ OpenStruct.new(str:"world", position:14..22),
951
+ OpenStruct.new(str:"bye", position:23..28),
952
+ OpenStruct.new(str:"world", position:29..37)
953
+ )
954
+ end
955
+ end
956
+
957
+ context 'when there are nested substitution strings' do
958
+ let(:str){ "echo `hi $(bye)`" }
959
+
960
+ it "only yields the top-level substitutions" do
961
+ expect { |b|
962
+ described_class.new.each_command_substitution_for(str, &b)
963
+ }.to yield_successive_args(
964
+ OpenStruct.new(str:"hi $(bye)", position:5..16),
965
+ )
966
+ end
967
+ end
968
+
969
+ end
970
+
816
971
  end
@@ -0,0 +1,57 @@
1
+ require 'spec_helper'
2
+ require 'yap/shell/parser'
3
+ require 'pry'
4
+
5
+ describe Yap::Shell::Parser do
6
+ subject(:parser){ Yap::Shell::Parser.new }
7
+
8
+ def self.it_parses(str)
9
+ context "#{str.inspect}" do
10
+ it "parses" do
11
+ expect do
12
+ begin
13
+ parser.parse(str)
14
+ # rescue => ex
15
+ # raise "Parser error:\n\t#{str}\n\n#{ex.message}\n#{ex.backtrace.join("\n")}"
16
+ end
17
+ end.to_not raise_error
18
+ end
19
+ end
20
+ end
21
+
22
+ it_parses "ls"
23
+ it_parses "echo foo"
24
+ it_parses "echo foo ; echo bar baz yep"
25
+ it_parses "echo foo && echo bar baz yep"
26
+ it_parses "echo foo && echo bar && ls foo && ls bar"
27
+ it_parses "echo foo ; echo bar baz yep ; ls foo"
28
+ it_parses "echo foo && echo bar ; ls baz"
29
+ it_parses "echo foo && echo bar ; ls baz ; echo zach || echo gretchen"
30
+ it_parses "echo foo | bar"
31
+ it_parses "echo foo | bar && foo | bar"
32
+ it_parses "foo && bar ; word || baz ; yep | grep -v foo"
33
+ it_parses "( foo )"
34
+ it_parses "( foo a b && bar c d )"
35
+ it_parses "( foo a b && (bar c d | baz e f))"
36
+ it_parses "((((foo))))"
37
+ it_parses "foo -b -c ; (this ;that ;the; other ;thing) && yep"
38
+ it_parses "foo -b -c ; (this ;that && other ;thing) && yep"
39
+ it_parses "4 + 5"
40
+ it_parses "!'hello' ; 4 - 4 && 10 + 3"
41
+ it_parses "\\foo <<-EOT\nbar\nEOT"
42
+ it_parses "ls | grep md | grep WISH"
43
+ it_parses "(!upcase)"
44
+ it_parses "echo foo > bar.txt"
45
+ it_parses "ls -l > a.txt ; echo f 2> b.txt ; cat b &> c.txt ; du -sh 1>&2 1>hey.txt"
46
+ it_parses "!Dir.chdir('..')"
47
+ it_parses "FOO=123"
48
+ it_parses "FOO=123 BAR=345"
49
+ it_parses "FOO=abc bar=2314 car=14ab ls -l"
50
+ it_parses "FOO=abc BAR='hello world' ls -l ; CAR=f echo foo && say hi"
51
+ it_parses "`git cbranch`"
52
+ it_parses "`git cbranch`.bak" # TODO THIS NEEDS TO MAYBE NOT WORK?
53
+ it_parses "echo `echo hi`"
54
+ it_parses "echo `echo hi` foo"
55
+ it_parses "`hi``bye` `what`"
56
+ it_parses "echo && `what` && where is `that`thing | `you know`"
57
+ end
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
22
22
  spec.add_development_dependency "bundler", "~> 1.7"
23
23
  spec.add_development_dependency "rake", "~> 10.0"
24
24
  spec.add_development_dependency "rspec", "~> 3.2"
25
+ spec.add_development_dependency "pry"
25
26
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yap-shell-parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Zach Dennis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-10 00:00:00.000000000 Z
11
+ date: 2015-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '3.2'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  description: The parser for the yap shell
56
70
  email:
57
71
  - zach.dennis@gmail.com
@@ -77,6 +91,7 @@ files:
77
91
  - lib/yap/shell/parser_impl.rb
78
92
  - spec/spec_helper.rb
79
93
  - spec/yap/shell/lexer_spec.rb
94
+ - spec/yap/shell/parser_spec.rb
80
95
  - yap-shell-parser.gemspec
81
96
  homepage: https://github.com/zdennis/yap-shell-parser
82
97
  licenses:
@@ -105,3 +120,5 @@ summary: The parser for the yap shell
105
120
  test_files:
106
121
  - spec/spec_helper.rb
107
122
  - spec/yap/shell/lexer_spec.rb
123
+ - spec/yap/shell/parser_spec.rb
124
+ has_rdoc: