yap-shell-parser 0.5.0 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/yap/shell/parser/lexer.rb +95 -22
- data/lib/yap/shell/parser/version.rb +1 -1
- data/lib/yap/shell/parser.rb +20 -7
- data/yap-shell-parser.gemspec +2 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4cbb2bd60f57e8a7ec75043614f19ca42b6a5b87
|
4
|
+
data.tar.gz: 91050d9396ad122987af76764bca6810ac99546c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a9d8a4feabef744b97369765d8dda04f92964f92ec7f8f47c159dca53d76dbeee25c4d7635911c99eb709fa9d44739e0d7994ec34cff78ee5492b4db616254ca
|
7
|
+
data.tar.gz: 1037679abed76641c88652aa503064093f9a77ed7420c9fd31c7b62c27df8b0fdbfe1254a7bb295b45197939863a62ef8c3f057c63fe93a62c0cf6223c8d495d
|
@@ -103,6 +103,7 @@ module Yap::Shell
|
|
103
103
|
end
|
104
104
|
|
105
105
|
def tokenize(str)
|
106
|
+
debug_log "##{__callee__} string: #{str.inspect}"
|
106
107
|
@chunk = str
|
107
108
|
@tokens = []
|
108
109
|
@lineno = 0
|
@@ -119,6 +120,7 @@ module Yap::Shell
|
|
119
120
|
line_continuation_token
|
120
121
|
|
121
122
|
while process_next_chunk.call
|
123
|
+
debug_log "##{__callee__} looking for next token in: #{@chunk.inspect}"
|
122
124
|
result =
|
123
125
|
comment_token ||
|
124
126
|
block_token ||
|
@@ -156,7 +158,9 @@ module Yap::Shell
|
|
156
158
|
private
|
157
159
|
|
158
160
|
def token(tag, value, attrs:{})
|
159
|
-
|
161
|
+
token = [tag, Token.new(tag, value, lineno:@lineno, attrs:attrs)]
|
162
|
+
debug_log "#{__callee__} found token: #{token.inspect}"
|
163
|
+
@tokens.push token
|
160
164
|
end
|
161
165
|
|
162
166
|
def block_token
|
@@ -172,6 +176,8 @@ module Yap::Shell
|
|
172
176
|
@looking_for_args = false
|
173
177
|
token :BlockEnd, md[1]
|
174
178
|
md[0].length
|
179
|
+
else
|
180
|
+
did_not_match
|
175
181
|
end
|
176
182
|
end
|
177
183
|
|
@@ -179,11 +185,16 @@ module Yap::Shell
|
|
179
185
|
if md=@chunk.match(COMMENT)
|
180
186
|
token :Comment, md[0]
|
181
187
|
md[0].length
|
188
|
+
else
|
189
|
+
did_not_match
|
182
190
|
end
|
183
191
|
end
|
184
192
|
|
185
193
|
def numerical_range_token
|
186
|
-
|
194
|
+
if @looking_for_args
|
195
|
+
skipping
|
196
|
+
return
|
197
|
+
end
|
187
198
|
|
188
199
|
if md=@chunk.match(NUMERIC_RANGE_W_CALL)
|
189
200
|
start, stop = md[2].to_i, md[3].to_i
|
@@ -227,31 +238,47 @@ module Yap::Shell
|
|
227
238
|
token :Range, (start..stop)
|
228
239
|
md[0].length
|
229
240
|
|
241
|
+
else
|
242
|
+
did_not_match
|
230
243
|
end
|
231
|
-
|
232
244
|
end
|
233
245
|
|
234
246
|
def command_token
|
235
|
-
if
|
247
|
+
if @looking_for_args
|
248
|
+
skipping
|
249
|
+
return
|
250
|
+
elsif md=@chunk.match(COMMAND)
|
236
251
|
@looking_for_args = true
|
237
252
|
token :Command, md[1]
|
238
253
|
md[0].length
|
254
|
+
else
|
255
|
+
did_not_match
|
239
256
|
end
|
240
257
|
end
|
241
258
|
|
242
259
|
def literal_command_token
|
243
|
-
if
|
260
|
+
if @looking_for_args
|
261
|
+
skipping
|
262
|
+
return
|
263
|
+
elsif md=@chunk.match(LITERAL_COMMAND)
|
244
264
|
@looking_for_args = true
|
245
265
|
token :LiteralCommand, md[1]
|
246
266
|
md[0].length
|
267
|
+
else
|
268
|
+
did_not_match
|
247
269
|
end
|
248
270
|
end
|
249
271
|
|
250
272
|
def numeric_expr_token
|
251
|
-
if
|
273
|
+
if @looking_for_args
|
274
|
+
skipping
|
275
|
+
return
|
276
|
+
elsif md=@chunk.match(NUMERIC_EXPR)
|
252
277
|
@looking_for_args = true
|
253
278
|
token :NumericExpr, md[1]
|
254
279
|
md[0].length
|
280
|
+
else
|
281
|
+
did_not_match
|
255
282
|
end
|
256
283
|
end
|
257
284
|
|
@@ -281,6 +308,8 @@ module Yap::Shell
|
|
281
308
|
|
282
309
|
token :Heredoc, contents
|
283
310
|
consumed_length
|
311
|
+
else
|
312
|
+
did_not_match
|
284
313
|
end
|
285
314
|
end
|
286
315
|
|
@@ -296,6 +325,8 @@ module Yap::Shell
|
|
296
325
|
result = process_internal_eval substr, consumed: consumed
|
297
326
|
token :InternalEval, result.str
|
298
327
|
return result.consumed_length
|
328
|
+
else
|
329
|
+
did_not_match
|
299
330
|
end
|
300
331
|
end
|
301
332
|
|
@@ -308,6 +339,8 @@ module Yap::Shell
|
|
308
339
|
elsif md=@chunk.match(REDIRECTION2)
|
309
340
|
token :Redirection, md[2], attrs: { target: md[3] }
|
310
341
|
md[0].length
|
342
|
+
else
|
343
|
+
did_not_match
|
311
344
|
end
|
312
345
|
end
|
313
346
|
|
@@ -315,14 +348,19 @@ module Yap::Shell
|
|
315
348
|
if md=@chunk.match(SUBGROUP)
|
316
349
|
token md[0], md[0]
|
317
350
|
return md[0].length
|
351
|
+
else
|
352
|
+
did_not_match
|
318
353
|
end
|
319
354
|
end
|
320
355
|
|
321
356
|
# Matches and consumes non-meaningful whitespace.
|
322
357
|
def whitespace_token
|
323
|
-
|
324
|
-
|
325
|
-
|
358
|
+
if md=WHITESPACE.match(@chunk)
|
359
|
+
input = md.to_a[0]
|
360
|
+
input.length
|
361
|
+
else
|
362
|
+
did_not_match
|
363
|
+
end
|
326
364
|
end
|
327
365
|
|
328
366
|
def argument_token
|
@@ -361,13 +399,18 @@ module Yap::Shell
|
|
361
399
|
token :Argument, str, attrs: attrs
|
362
400
|
characters_read
|
363
401
|
else
|
364
|
-
|
402
|
+
did_not_match
|
365
403
|
end
|
404
|
+
else
|
405
|
+
skipping
|
366
406
|
end
|
367
407
|
end
|
368
408
|
|
369
409
|
def assignment_token
|
370
|
-
if
|
410
|
+
if @looking_for_args
|
411
|
+
skipping
|
412
|
+
return
|
413
|
+
elsif md=@chunk.match(LH_ASSIGNMENT)
|
371
414
|
token :LValue, md[2]
|
372
415
|
consumed_length = md[1].length
|
373
416
|
i = consumed_length
|
@@ -382,6 +425,8 @@ module Yap::Shell
|
|
382
425
|
consumed_length += md[0].length
|
383
426
|
end
|
384
427
|
consumed_length
|
428
|
+
else
|
429
|
+
did_not_match
|
385
430
|
end
|
386
431
|
end
|
387
432
|
|
@@ -391,6 +436,8 @@ module Yap::Shell
|
|
391
436
|
LineContinuationFound,
|
392
437
|
"Expected more input, line continutation found"
|
393
438
|
)
|
439
|
+
else
|
440
|
+
did_not_match
|
394
441
|
end
|
395
442
|
end
|
396
443
|
|
@@ -411,6 +458,8 @@ module Yap::Shell
|
|
411
458
|
@looking_for_args = false
|
412
459
|
token :Pipe, md[0]
|
413
460
|
md[0].length
|
461
|
+
else
|
462
|
+
did_not_match
|
414
463
|
end
|
415
464
|
end
|
416
465
|
|
@@ -423,7 +472,9 @@ module Yap::Shell
|
|
423
472
|
else
|
424
473
|
token :Command, result.str
|
425
474
|
end
|
426
|
-
|
475
|
+
result.consumed_length
|
476
|
+
else
|
477
|
+
did_not_match
|
427
478
|
end
|
428
479
|
end
|
429
480
|
|
@@ -446,7 +497,9 @@ module Yap::Shell
|
|
446
497
|
token :EndCommandSubstitution, delimiter
|
447
498
|
end
|
448
499
|
|
449
|
-
|
500
|
+
consumed_length_so_far + append_result.consumed_length
|
501
|
+
else
|
502
|
+
did_not_match
|
450
503
|
end
|
451
504
|
end
|
452
505
|
|
@@ -512,37 +565,57 @@ module Yap::Shell
|
|
512
565
|
i = delimiter.length # start string matching after our delimiter
|
513
566
|
result_str = ''
|
514
567
|
|
568
|
+
if indent == 0
|
569
|
+
debug_log "##{__callee__} input_str=#{input_str.inspect} delimiter=#{delimiter.inspect} start_position=#{i}"
|
570
|
+
indent += 1
|
571
|
+
else
|
572
|
+
debug_log "#{' ' * indent}##{__callee__} input_str=#{input_str.inspect} delimiter=#{delimiter.inspect} start_position=#{i}"
|
573
|
+
indent += 1
|
574
|
+
end
|
575
|
+
indent_str = ' ' * indent
|
576
|
+
|
515
577
|
loop do
|
516
578
|
chunk = input_str[i..-1]
|
517
579
|
|
518
|
-
puts "#{' '*indent}I: #{i}" if ENV["DEBUG"]
|
519
|
-
|
520
580
|
if i >= input_str.length
|
521
|
-
|
581
|
+
debug_log "#{indent_str}found unterminated string, raising error"
|
522
582
|
raise NonterminatedString, "Expected to find #{delimiter} in:\n #{input_str}"
|
523
|
-
return OpenStruct.new(str:result_str, consumed_length: input_str.length)
|
524
583
|
end
|
525
584
|
|
526
585
|
if chunk.start_with?(nested_delimiter) # we found a nested escaped string
|
527
|
-
|
586
|
+
debug_log "#{indent_str}nested string found in #{chunk.inspect}"
|
528
587
|
result = process_string(chunk[0..-1], nested_delimiter, indent+2)
|
529
588
|
result_str << [delimiter, result.str, delimiter].join
|
530
|
-
|
531
|
-
|
589
|
+
debug_log "#{indent_str}nested string found was #{result.inspect}"
|
532
590
|
i += result.consumed_length
|
533
591
|
|
534
592
|
elsif chunk.start_with?(delimiter) # we found the end of our current nested escaped string
|
535
|
-
|
593
|
+
debug_log "#{indent_str}found the end of the string making string: #{result_str.inspect}"
|
536
594
|
return OpenStruct.new(str:result_str, consumed_length: i+delimiter.length)
|
537
595
|
|
538
596
|
else
|
539
597
|
char = input_str[i]
|
540
598
|
result_str << char
|
541
|
-
|
599
|
+
debug_log "#{indent_str}consuming character #{char} at position #{i}"
|
542
600
|
i += 1
|
543
601
|
end
|
544
602
|
end
|
545
603
|
end
|
546
604
|
|
605
|
+
def debug_log(message)
|
606
|
+
Treefell['lexer'].puts message
|
607
|
+
end
|
608
|
+
|
609
|
+
def did_not_match
|
610
|
+
calling_method = caller[0][/`.*'/][1..-2]
|
611
|
+
debug_log "##{calling_method} didn't match: #{@chunk.inspect}"
|
612
|
+
nil
|
613
|
+
end
|
614
|
+
|
615
|
+
def skipping
|
616
|
+
calling_method = caller[0][/`.*'/][1..-2]
|
617
|
+
debug_log "##{calling_method} is being skipped"
|
618
|
+
nil
|
619
|
+
end
|
547
620
|
end
|
548
621
|
end
|
data/lib/yap/shell/parser.rb
CHANGED
@@ -5,20 +5,33 @@ module Yap
|
|
5
5
|
module Parser
|
6
6
|
class ParseError < ::StandardError ; end
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
class << self
|
9
|
+
def parse(input)
|
10
|
+
debug_log "#parse entry with: #{input.inspect}"
|
11
|
+
Yap::Shell::ParserImpl.new.parse(input).tap do |results|
|
12
|
+
debug_log "#parse exit returning: #{results.inspect}"
|
13
|
+
end
|
14
|
+
rescue Racc::ParseError => ex
|
15
|
+
raise ParseError, "Message: #{ex.message}\nInput: #{input}"
|
16
|
+
end
|
17
|
+
|
18
|
+
def each_command_substitution_for(input, &blk)
|
19
|
+
Yap::Shell::Parser::Lexer.new.each_command_substitution_for(input, &blk)
|
20
|
+
end
|
13
21
|
|
14
|
-
|
15
|
-
|
22
|
+
private
|
23
|
+
|
24
|
+
def debug_log(message)
|
25
|
+
Treefell['parser'].puts message
|
26
|
+
end
|
16
27
|
end
|
28
|
+
|
17
29
|
end
|
18
30
|
end
|
19
31
|
end
|
20
32
|
|
21
33
|
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../../"
|
34
|
+
require 'treefell'
|
22
35
|
require 'yap/shell/parser/lexer'
|
23
36
|
require 'yap/shell/parser/nodes'
|
24
37
|
require 'yap/shell/parser_impl'
|
data/yap-shell-parser.gemspec
CHANGED
@@ -19,6 +19,8 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
20
20
|
spec.require_paths = ["lib"]
|
21
21
|
|
22
|
+
spec.add_dependency "treefell", "~> 0.2.3"
|
23
|
+
|
22
24
|
spec.add_development_dependency "bundler", "~> 1.7"
|
23
25
|
spec.add_development_dependency "rake", "~> 10.0"
|
24
26
|
spec.add_development_dependency "rspec", "~> 3.2"
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yap-shell-parser
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.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: 2016-05-
|
11
|
+
date: 2016-05-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: treefell
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.2.3
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.2.3
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: bundler
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|