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