yap-shell-parser 0.0.4 → 0.1.0

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: 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: