yap-shell-parser 0.2.2 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8e9067d2b8034ad2ca424fbdb61b5c939be1470e
4
- data.tar.gz: 1ae4cf2f162e5c19bd8419619034759e069836d5
3
+ metadata.gz: b1f871f25358079c9bb5bfda0d59b77504b36b88
4
+ data.tar.gz: ae41ab9e03b55318f48c9fd739448600c30e545c
5
5
  SHA512:
6
- metadata.gz: 7bfc31b24ff9925175ded8555a3a8462c4d88fca804f035432c572c2a623069fe370437fa8e14097494fc53e40eccfe97396034763ec7c476a8326d8a99c455d
7
- data.tar.gz: 8534df6ecc65ec92307d491e3dd760ac3129cc3808df6e2f07b2d8a733cafc1bfce0ecb56148c6ea4fa40d5233095af6fb28740109d24b7eb9fee43429ebf1ba
6
+ metadata.gz: 2b6dc9b5ebfee853ef445c1f8a60ed9e9ca8d81365497f2ae5fda0e64721ffa215c451ebd1cd95c8485759fa918d0085c7b8dfc75a1cbf7be823d2969e6f8356
7
+ data.tar.gz: fd2670a3b93ec221a664a71e0280ef79b5cf25f7a968abde4d2655188f89c5bbcf2de784f574f3a8ecd4355ab007ea1fcadc7d7253477b12e02962d0e37a4c34
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.2.3
@@ -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 BeginCommandSubstitution EndCommandSubstitution
6
+ token Command LiteralCommand Argument Heredoc InternalEval Separator Conditional Pipe Redirection LValue RValue BeginCommandSubstitution EndCommandSubstitution Range BlockBegin BlockEnd BlockParams
7
7
  #
8
8
  # prechigh
9
9
  # # left '**' '*' '/' '%'
@@ -24,10 +24,24 @@ stmts : stmts Separator stmt
24
24
  { result = StatementsNode.new(val[0], val[2]) }
25
25
  | stmt
26
26
  { result = StatementsNode.new(val[0]) }
27
+ | range_stmt
27
28
 
28
29
  stmt : stmt Conditional pipeline
29
30
  { result = ConditionalNode.new(val[1].value, val[0], val[2]) }
30
31
  | pipeline
32
+ | block_stmt
33
+
34
+ block_stmt : range_stmt BlockBegin stmts BlockEnd
35
+ { result = val[0].tap { |range_node| range_node.tail = BlockNode.new(nil, val[2]) } }
36
+ | range_stmt BlockBegin BlockParams stmts BlockEnd
37
+ { result = val[0].tap { |range_node| range_node.tail = BlockNode.new(nil, val[3], params: val[2].value) } }
38
+ | stmt BlockBegin stmts BlockEnd
39
+ { result = BlockNode.new(val[0], val[2]) }
40
+ | stmt BlockBegin BlockParams stmts BlockEnd
41
+ { result = BlockNode.new(val[0], val[3], params: val[2].value) }
42
+
43
+ range_stmt : Range
44
+ { result = RangeNode.new(val[0]) }
31
45
 
32
46
  pipeline : pipeline Pipe stmts2
33
47
  { result = PipelineNode.new(val[0], val[2]) }
@@ -105,6 +119,7 @@ end
105
119
  @yydebug = true
106
120
 
107
121
  @q = Yap::Shell::Parser::Lexer.new.tokenize(str)
122
+ pp @q if ENV["DEBUG"]
108
123
  # @q.push [false, '$'] # is optional from Racc 1.3.7
109
124
  # puts @q.inspect
110
125
  # puts "---- parse tree follows ----"
@@ -119,6 +134,7 @@ end
119
134
  ---- footer
120
135
 
121
136
  if $0 == __FILE__
137
+ require 'pry'
122
138
  $LOAD_PATH.unshift File.dirname(__FILE__) + "/lib/"
123
139
  require 'yap/shell/parser/lexer'
124
140
  require 'yap/shell/parser/nodes'
@@ -127,7 +143,9 @@ if $0 == __FILE__
127
143
  # "`git cbranch`",
128
144
  # "`git cbranch`.bak",
129
145
  # "echo `echo hi`",
130
- "echo `echo hi` foo bar baz",
146
+ # "ls *.rb te* { |f| f }",
147
+ # "f { |a, b, c| echo foo }"
148
+ "(0..3) as n : echo hi",
131
149
  # "`hi``bye` `what`",
132
150
  # "echo && `what` && where is `that`thing | `you know`",
133
151
  ].each do |src|
@@ -2,6 +2,9 @@ require 'ostruct'
2
2
 
3
3
  module Yap::Shell
4
4
  class Parser::Lexer
5
+ class Error < ::StandardError ; end
6
+ class HeredocMissingEndDelimiter < Error ; end
7
+
5
8
  class Token
6
9
  include Comparable
7
10
 
@@ -37,18 +40,29 @@ module Yap::Shell
37
40
  ARG = /[^\s;\|\(\)\{\}\[\]\&\!\\\<`][^\s;\|\(\)\{\}\[\]\&\>\<`]*/
38
41
  COMMAND = /\A(#{ARG})/
39
42
  LITERAL_COMMAND = /\A\\(#{ARG})/
40
- WHITESPACE = /\A[^\n\S]+/
43
+ WHITESPACE = /\A\s+/
41
44
  LH_ASSIGNMENT = /\A(([A-z_][\w]*)=)/
42
45
  RH_VALUE = /\A(\S+)/
43
46
  STATEMENT_TERMINATOR = /\A(;)/
44
47
  PIPE_TERMINATOR = /\A(\|)/
45
48
  CONDITIONAL_TERMINATOR = /\A(&&|\|\|)/
46
- HEREDOC = /\A<<-?([A-z0-9]+)\s*^(.*)?(^\s*\1\s*$)/m
49
+ HEREDOC_START = /\A<<-?([A-z0-9]+)\s*\n/
47
50
  INTERNAL_EVAL = /\A(?:(\!)|([0-9]+))/
48
51
  SUBGROUP = /\A(\(|\))/
49
52
  REDIRECTION = /\A(([12]?>&?[12]?)\s*(?![12]>)(#{ARG})?)/
50
53
  REDIRECTION2 = /\A((&>|<)\s*(#{ARG}))/
51
54
 
55
+ NUMERIC_RANGE = /\A\(((\d+)\.\.(\d+))\)(\.each)?/
56
+ NUMERIC_RANGE_W_CALL = /\A\(((\d+)\.\.(\d+))\)(\.each)?\s*:\s*/
57
+ NUMERIC_RANGE_W_PARAM = /\A(\((\d+)\.\.(\d+))\)\s+as\s+([A-z0-9,\s]+)\s*:\s*/
58
+ NUMERIC_REPETITION = /\A((\d+)(\.times))\s*/
59
+ NUMERIC_REPETITION_2 = /\A((\d+)(\.times))\s*:\s*/
60
+ NUMERIC_REPETITION_W_PARAM = /\A((\d+)(\.times))\s+as\s+([A-z0-9,\s]+)\s*:\s*/
61
+
62
+ BLOCK_BEGIN = /\A\s*(\{)\s*(?:\|\s*([A-z0-9,\s]+)\s*\|)?/
63
+ BLOCK_END = /\A\s*(\})\s*/
64
+
65
+ SPLIT_BLOCK_PARAMS_RGX = /\s*,\s*|\s*/
52
66
 
53
67
  # Loop over the given input and yield command substitutions. This yields
54
68
  # an object that responds to #str, and #position.
@@ -88,6 +102,7 @@ module Yap::Shell
88
102
  @tokens = []
89
103
  @lineno = 0
90
104
  @looking_for_args = false
105
+ @tokens_to_add_when_done = []
91
106
 
92
107
  max = 100
93
108
  count = 0
@@ -96,6 +111,8 @@ module Yap::Shell
96
111
 
97
112
  while process_next_chunk.call
98
113
  result =
114
+ block_token ||
115
+ numerical_range_token ||
99
116
  command_substitution_token ||
100
117
  subgroup_token ||
101
118
  assignment_token ||
@@ -115,6 +132,10 @@ module Yap::Shell
115
132
  @current_position += result.to_i
116
133
  end
117
134
 
135
+ @tokens_to_add_when_done.each do |args|
136
+ token *args
137
+ end
138
+
118
139
  @tokens
119
140
  end
120
141
 
@@ -124,6 +145,71 @@ module Yap::Shell
124
145
  @tokens.push [tag, Token.new(tag, value, lineno:@lineno, attrs:attrs)]
125
146
  end
126
147
 
148
+ def block_token
149
+ if md=@chunk.match(BLOCK_BEGIN)
150
+ @looking_for_args = false
151
+ token :BlockBegin, md[1]
152
+ if md[2]
153
+ params = md[2].split(SPLIT_BLOCK_PARAMS_RGX)
154
+ token :BlockParams, params
155
+ end
156
+ md[0].length
157
+ elsif md=@chunk.match(BLOCK_END)
158
+ @looking_for_args = false
159
+ token :BlockEnd, md[1]
160
+ md[0].length
161
+ end
162
+ end
163
+
164
+ def numerical_range_token
165
+ return if @looking_for_args
166
+
167
+ if md=@chunk.match(NUMERIC_RANGE_W_CALL)
168
+ start, stop = md[2].to_i, md[3].to_i
169
+ token :Range, (start..stop)
170
+ token :BlockBegin, '{'
171
+ @tokens_to_add_when_done << [:BlockEnd, '}']
172
+ md[0].length
173
+
174
+ elsif md=@chunk.match(NUMERIC_RANGE_W_PARAM)
175
+ start, stop = md[2].to_i, md[3].to_i
176
+ token :Range, (start..stop)
177
+ token :BlockBegin, '{'
178
+ params = md[4].split(SPLIT_BLOCK_PARAMS_RGX)
179
+ token :BlockParams, params
180
+ @tokens_to_add_when_done << [:BlockEnd, '}']
181
+ md[0].length
182
+
183
+ elsif md=@chunk.match(NUMERIC_REPETITION_2)
184
+ start, stop = 1, md[2].to_i
185
+ token :Range, (start..stop)
186
+ token :BlockBegin, '{'
187
+ @tokens_to_add_when_done << [:BlockEnd, '}']
188
+ md[0].length
189
+
190
+ elsif md=@chunk.match(NUMERIC_REPETITION_W_PARAM)
191
+ start, stop = 1, md[2].to_i
192
+ token :Range, (start..stop)
193
+ token :BlockBegin, '{'
194
+ params = md[4].split(SPLIT_BLOCK_PARAMS_RGX)
195
+ token :BlockParams, params
196
+ @tokens_to_add_when_done << [:BlockEnd, '}']
197
+ md[0].length
198
+
199
+ elsif md=@chunk.match(NUMERIC_REPETITION)
200
+ start, stop = 1, md[2].to_i
201
+ token :Range, (start..stop)
202
+ md[0].length
203
+
204
+ elsif md=@chunk.match(NUMERIC_RANGE)
205
+ start, stop = md[2].to_i, md[3].to_i
206
+ token :Range, (start..stop)
207
+ md[0].length
208
+
209
+ end
210
+
211
+ end
212
+
127
213
  def command_token
128
214
  if !@looking_for_args && md=@chunk.match(COMMAND)
129
215
  @looking_for_args = true
@@ -149,9 +235,31 @@ module Yap::Shell
149
235
  end
150
236
 
151
237
  def heredoc_token
152
- if md=@chunk.match(HEREDOC)
153
- token :Heredoc, md[2]
154
- md[0].length
238
+ if md=@chunk.match(HEREDOC_START)
239
+ delimiter = md[1]
240
+ str = @chunk[md[0].length..-1]
241
+ consumed_length = md[0].length
242
+
243
+ delimeter_regex = Regexp.escape(delimiter)
244
+
245
+ contents = ""
246
+ found_ending_delimiter = false
247
+ str.lines.each do |line|
248
+ if md=line.match(/^(.*?)\s*#{delimeter_regex}\s*$/m)
249
+ contents << $1
250
+ found_ending_delimiter = true
251
+ else
252
+ contents << line
253
+ end
254
+ consumed_length += line.length
255
+ end
256
+
257
+ unless found_ending_delimiter
258
+ raise HeredocMissingEndDelimiter, "Missing end delimiter on #{@chunk}"
259
+ end
260
+
261
+ token :Heredoc, contents
262
+ consumed_length
155
263
  end
156
264
  end
157
265
 
@@ -207,7 +315,7 @@ module Yap::Shell
207
315
  result = process_string @chunk[characters_read..-1], ch
208
316
  str << result.str
209
317
  characters_read += result.consumed_length
210
- elsif prev_char != '\\' && ch =~ /[\s\|;&\)]/
318
+ elsif prev_char != '\\' && ch =~ /[\s\|;&\)\}]/
211
319
  break
212
320
  else
213
321
  str << ch
@@ -258,5 +258,47 @@ module Yap::Shell
258
258
  end
259
259
  end
260
260
 
261
+ class BlockNode
262
+ include Visitor
263
+
264
+ attr_accessor :head, :tail, :params
265
+
266
+ def initialize(head, tail, params: [])
267
+ @head = head
268
+ @tail = tail
269
+ @params = params
270
+ end
271
+
272
+ def to_s(indent:0)
273
+ if @counter_reference
274
+ "BlockNode(#{@head.inspect}, tail: #{@tail.inspect} params: #{@params.inspect})"
275
+ else
276
+ "BlockNode(#{@head.inspect}, tail: #{@tail.inspect} params: #{@params.inspect})"
277
+ end
278
+ end
279
+
280
+ def inspect
281
+ to_s
282
+ end
283
+ end
284
+
285
+ class RangeNode
286
+ include Visitor
287
+
288
+ attr_accessor :head, :tail
289
+
290
+ def initialize(head, tail=nil)
291
+ @head = head
292
+ @tail = tail
293
+ end
294
+
295
+ def to_s(indent:0)
296
+ "(#{@head.inspect}, tail: #{tail.inspect})"
297
+ end
298
+
299
+ def inspect
300
+ to_s
301
+ end
302
+ end
261
303
  end
262
304
  end
@@ -3,7 +3,7 @@ require 'yap/shell/parser'
3
3
  module Yap
4
4
  module Shell
5
5
  module Parser
6
- VERSION = "0.2.2"
6
+ VERSION = "0.3.1"
7
7
  end
8
8
  end
9
9
  end