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 +4 -4
- data/lib/tasks/gem.rake +3 -1
- data/lib/yap/shell/parser/grammar.y +39 -34
- data/lib/yap/shell/parser/lexer.rb +65 -4
- data/lib/yap/shell/parser/nodes.rb +57 -0
- data/lib/yap/shell/parser/version.rb +1 -1
- data/lib/yap/shell/parser_impl.rb +162 -118
- data/spec/yap/shell/lexer_spec.rb +175 -20
- data/spec/yap/shell/parser_spec.rb +57 -0
- data/yap-shell-parser.gemspec +1 -0
- metadata +19 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f0e319c332283c42adde5fcba98b7ee4c8f8e8bb
|
4
|
+
data.tar.gz: 9d1e3d51c8df81d6e3a50cca393bff86f3edbaeb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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 =
|
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
|
-
|
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
|
105
|
-
# "
|
106
|
-
# "
|
107
|
-
# "echo
|
108
|
-
|
109
|
-
# "
|
110
|
-
# "echo
|
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::
|
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
|
-
|
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
|
-
|
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
|
@@ -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',
|
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
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
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,
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
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,
|
49
|
-
nil, nil,
|
50
|
-
|
51
|
-
|
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
|
-
-
|
55
|
-
-14, -
|
56
|
-
-
|
57
|
-
-
|
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,
|
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,
|
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,
|
67
|
-
-
|
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
|
-
|
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, :
|
81
|
-
1, 19, :
|
82
|
-
3, 20, :
|
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
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
1,
|
95
|
-
1,
|
96
|
-
2,
|
97
|
-
|
98
|
-
2,
|
99
|
-
1,
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
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
|
-
|
122
|
-
|
155
|
+
:BeginCommandSubstitution => 13,
|
156
|
+
:EndCommandSubstitution => 14,
|
157
|
+
"(" => 15,
|
158
|
+
")" => 16 }
|
123
159
|
|
124
|
-
racc_nt_base =
|
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
|
-
|
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
|
-
|
227
|
-
|
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
|
298
|
+
# reduce 17 omitted
|
234
299
|
|
235
|
-
module_eval(<<'.,.,', 'grammar.y',
|
236
|
-
def
|
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
|
307
|
+
# reduce 19 omitted
|
243
308
|
|
244
|
-
# reduce
|
309
|
+
# reduce 20 omitted
|
245
310
|
|
246
|
-
# reduce
|
311
|
+
# reduce 21 omitted
|
247
312
|
|
248
|
-
module_eval(<<'.,.,', 'grammar.y',
|
249
|
-
def
|
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',
|
256
|
-
def
|
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',
|
263
|
-
def
|
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
|
334
|
+
# reduce 25 omitted
|
270
335
|
|
271
|
-
module_eval(<<'.,.,', 'grammar.y',
|
272
|
-
def
|
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',
|
279
|
-
def
|
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',
|
286
|
-
def
|
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',
|
293
|
-
def
|
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',
|
300
|
-
def
|
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',
|
307
|
-
def
|
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',
|
314
|
-
def
|
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
|
335
|
-
# "
|
336
|
-
# "
|
337
|
-
# "echo
|
338
|
-
|
339
|
-
# "
|
340
|
-
# "echo
|
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::
|
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(:
|
711
|
+
t(:BeginCommandSubstitution, "`", lineno: 0),
|
699
712
|
t(:Command, "pwd", lineno: 0),
|
700
|
-
t(:
|
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
|
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(:
|
815
|
+
t(:BeginCommandSubstitution, '`', lineno: 0),
|
710
816
|
t(:Command, 'git', lineno: 0),
|
711
817
|
t(:Argument, 'cbranch', lineno: 0),
|
712
|
-
t(:
|
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(:
|
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(:
|
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(:
|
841
|
+
t(:BeginCommandSubstitution, "`", lineno: 0),
|
737
842
|
t(:Command,'pwd', lineno: 0),
|
738
|
-
t(:
|
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(:
|
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(:
|
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(:
|
869
|
+
t(:BeginCommandSubstitution, "$(", lineno: 0),
|
765
870
|
t(:Command, "pwd", lineno: 0),
|
766
|
-
t(:
|
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(:
|
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(:
|
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(:
|
894
|
+
t(:BeginCommandSubstitution, "$(", lineno: 0),
|
790
895
|
t(:Command,'pwd', lineno: 0),
|
791
|
-
t(:
|
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(:
|
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(:
|
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
|
data/yap-shell-parser.gemspec
CHANGED
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
|
+
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-
|
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:
|