diff options
author | Chris Larson <chris_larson@mentor.com> | 2010-12-31 11:00:27 +0000 |
---|---|---|
committer | Richard Purdie <rpurdie@linux.intel.com> | 2011-01-04 14:46:39 +0000 |
commit | 4addbd191dec44d01e8d9961e645948c0ebd04c8 (patch) | |
tree | 733ca121d5f57fec9634fd047c26749aced188de /bitbake/lib/bb/pysh/pyshyacc.py | |
parent | 489d17596d2c532f2d8db3ef8c0f122ca49bb466 (diff) | |
download | poky-4addbd191dec44d01e8d9961e645948c0ebd04c8.tar.gz |
Move the pysh package into the bb package
The pysh we're using is modified, and we don't want to risk it conflicting
with one from elsewhere.
(Bitbake rev: 1cbf8a9403b4b60d59bfd90a51c3e4246ab834d6)
Signed-off-by: Chris Larson <chris_larson@mentor.com>
Signed-off-by: Richard Purdie <rpurdie@linux.intel.com>
Diffstat (limited to 'bitbake/lib/bb/pysh/pyshyacc.py')
-rw-r--r-- | bitbake/lib/bb/pysh/pyshyacc.py | 772 |
1 files changed, 772 insertions, 0 deletions
diff --git a/bitbake/lib/bb/pysh/pyshyacc.py b/bitbake/lib/bb/pysh/pyshyacc.py new file mode 100644 index 0000000000..3d9510c0c3 --- /dev/null +++ b/bitbake/lib/bb/pysh/pyshyacc.py | |||
@@ -0,0 +1,772 @@ | |||
1 | # pyshyacc.py - PLY grammar definition for pysh | ||
2 | # | ||
3 | # Copyright 2007 Patrick Mezard | ||
4 | # | ||
5 | # This software may be used and distributed according to the terms | ||
6 | # of the GNU General Public License, incorporated herein by reference. | ||
7 | |||
8 | """PLY grammar file. | ||
9 | """ | ||
10 | import sys | ||
11 | |||
12 | import pyshlex | ||
13 | tokens = pyshlex.tokens | ||
14 | |||
15 | from ply import yacc | ||
16 | import sherrors | ||
17 | |||
18 | class IORedirect: | ||
19 | def __init__(self, op, filename, io_number=None): | ||
20 | self.op = op | ||
21 | self.filename = filename | ||
22 | self.io_number = io_number | ||
23 | |||
24 | class HereDocument: | ||
25 | def __init__(self, op, name, content, io_number=None): | ||
26 | self.op = op | ||
27 | self.name = name | ||
28 | self.content = content | ||
29 | self.io_number = io_number | ||
30 | |||
31 | def make_io_redirect(p): | ||
32 | """Make an IORedirect instance from the input 'io_redirect' production.""" | ||
33 | name, io_number, io_target = p | ||
34 | assert name=='io_redirect' | ||
35 | |||
36 | if io_target[0]=='io_file': | ||
37 | io_type, io_op, io_file = io_target | ||
38 | return IORedirect(io_op, io_file, io_number) | ||
39 | elif io_target[0]=='io_here': | ||
40 | io_type, io_op, io_name, io_content = io_target | ||
41 | return HereDocument(io_op, io_name, io_content, io_number) | ||
42 | else: | ||
43 | assert False, "Invalid IO redirection token %s" % repr(io_type) | ||
44 | |||
45 | class SimpleCommand: | ||
46 | """ | ||
47 | assigns contains (name, value) pairs. | ||
48 | """ | ||
49 | def __init__(self, words, redirs, assigns): | ||
50 | self.words = list(words) | ||
51 | self.redirs = list(redirs) | ||
52 | self.assigns = list(assigns) | ||
53 | |||
54 | class Pipeline: | ||
55 | def __init__(self, commands, reverse_status=False): | ||
56 | self.commands = list(commands) | ||
57 | assert self.commands #Grammar forbids this | ||
58 | self.reverse_status = reverse_status | ||
59 | |||
60 | class AndOr: | ||
61 | def __init__(self, op, left, right): | ||
62 | self.op = str(op) | ||
63 | self.left = left | ||
64 | self.right = right | ||
65 | |||
66 | class ForLoop: | ||
67 | def __init__(self, name, items, cmds): | ||
68 | self.name = str(name) | ||
69 | self.items = list(items) | ||
70 | self.cmds = list(cmds) | ||
71 | |||
72 | class WhileLoop: | ||
73 | def __init__(self, condition, cmds): | ||
74 | self.condition = list(condition) | ||
75 | self.cmds = list(cmds) | ||
76 | |||
77 | class UntilLoop: | ||
78 | def __init__(self, condition, cmds): | ||
79 | self.condition = list(condition) | ||
80 | self.cmds = list(cmds) | ||
81 | |||
82 | class FunDef: | ||
83 | def __init__(self, name, body): | ||
84 | self.name = str(name) | ||
85 | self.body = body | ||
86 | |||
87 | class BraceGroup: | ||
88 | def __init__(self, cmds): | ||
89 | self.cmds = list(cmds) | ||
90 | |||
91 | class IfCond: | ||
92 | def __init__(self, cond, if_cmds, else_cmds): | ||
93 | self.cond = list(cond) | ||
94 | self.if_cmds = if_cmds | ||
95 | self.else_cmds = else_cmds | ||
96 | |||
97 | class Case: | ||
98 | def __init__(self, name, items): | ||
99 | self.name = name | ||
100 | self.items = items | ||
101 | |||
102 | class SubShell: | ||
103 | def __init__(self, cmds): | ||
104 | self.cmds = cmds | ||
105 | |||
106 | class RedirectList: | ||
107 | def __init__(self, cmd, redirs): | ||
108 | self.cmd = cmd | ||
109 | self.redirs = list(redirs) | ||
110 | |||
111 | def get_production(productions, ptype): | ||
112 | """productions must be a list of production tuples like (name, obj) where | ||
113 | name is the production string identifier. | ||
114 | Return the first production named 'ptype'. Raise KeyError if None can be | ||
115 | found. | ||
116 | """ | ||
117 | for production in productions: | ||
118 | if production is not None and production[0]==ptype: | ||
119 | return production | ||
120 | raise KeyError(ptype) | ||
121 | |||
122 | #------------------------------------------------------------------------------- | ||
123 | # PLY grammar definition | ||
124 | #------------------------------------------------------------------------------- | ||
125 | |||
126 | def p_multiple_commands(p): | ||
127 | """multiple_commands : newline_sequence | ||
128 | | complete_command | ||
129 | | multiple_commands complete_command""" | ||
130 | if len(p)==2: | ||
131 | if p[1] is not None: | ||
132 | p[0] = [p[1]] | ||
133 | else: | ||
134 | p[0] = [] | ||
135 | else: | ||
136 | p[0] = p[1] + [p[2]] | ||
137 | |||
138 | def p_complete_command(p): | ||
139 | """complete_command : list separator | ||
140 | | list""" | ||
141 | if len(p)==3 and p[2] and p[2][1] == '&': | ||
142 | p[0] = ('async', p[1]) | ||
143 | else: | ||
144 | p[0] = p[1] | ||
145 | |||
146 | def p_list(p): | ||
147 | """list : list separator_op and_or | ||
148 | | and_or""" | ||
149 | if len(p)==2: | ||
150 | p[0] = [p[1]] | ||
151 | else: | ||
152 | #if p[2]!=';': | ||
153 | # raise NotImplementedError('AND-OR list asynchronous execution is not implemented') | ||
154 | p[0] = p[1] + [p[3]] | ||
155 | |||
156 | def p_and_or(p): | ||
157 | """and_or : pipeline | ||
158 | | and_or AND_IF linebreak pipeline | ||
159 | | and_or OR_IF linebreak pipeline""" | ||
160 | if len(p)==2: | ||
161 | p[0] = p[1] | ||
162 | else: | ||
163 | p[0] = ('and_or', AndOr(p[2], p[1], p[4])) | ||
164 | |||
165 | def p_maybe_bang_word(p): | ||
166 | """maybe_bang_word : Bang""" | ||
167 | p[0] = ('maybe_bang_word', p[1]) | ||
168 | |||
169 | def p_pipeline(p): | ||
170 | """pipeline : pipe_sequence | ||
171 | | bang_word pipe_sequence""" | ||
172 | if len(p)==3: | ||
173 | p[0] = ('pipeline', Pipeline(p[2][1:], True)) | ||
174 | else: | ||
175 | p[0] = ('pipeline', Pipeline(p[1][1:])) | ||
176 | |||
177 | def p_pipe_sequence(p): | ||
178 | """pipe_sequence : command | ||
179 | | pipe_sequence PIPE linebreak command""" | ||
180 | if len(p)==2: | ||
181 | p[0] = ['pipe_sequence', p[1]] | ||
182 | else: | ||
183 | p[0] = p[1] + [p[4]] | ||
184 | |||
185 | def p_command(p): | ||
186 | """command : simple_command | ||
187 | | compound_command | ||
188 | | compound_command redirect_list | ||
189 | | function_definition""" | ||
190 | |||
191 | if p[1][0] in ( 'simple_command', | ||
192 | 'for_clause', | ||
193 | 'while_clause', | ||
194 | 'until_clause', | ||
195 | 'case_clause', | ||
196 | 'if_clause', | ||
197 | 'function_definition', | ||
198 | 'subshell', | ||
199 | 'brace_group',): | ||
200 | if len(p) == 2: | ||
201 | p[0] = p[1] | ||
202 | else: | ||
203 | p[0] = ('redirect_list', RedirectList(p[1], p[2][1:])) | ||
204 | else: | ||
205 | raise NotImplementedError('%s command is not implemented' % repr(p[1][0])) | ||
206 | |||
207 | def p_compound_command(p): | ||
208 | """compound_command : brace_group | ||
209 | | subshell | ||
210 | | for_clause | ||
211 | | case_clause | ||
212 | | if_clause | ||
213 | | while_clause | ||
214 | | until_clause""" | ||
215 | p[0] = p[1] | ||
216 | |||
217 | def p_subshell(p): | ||
218 | """subshell : LPARENS compound_list RPARENS""" | ||
219 | p[0] = ('subshell', SubShell(p[2][1:])) | ||
220 | |||
221 | def p_compound_list(p): | ||
222 | """compound_list : term | ||
223 | | newline_list term | ||
224 | | term separator | ||
225 | | newline_list term separator""" | ||
226 | productions = p[1:] | ||
227 | try: | ||
228 | sep = get_production(productions, 'separator') | ||
229 | if sep[1]!=';': | ||
230 | raise NotImplementedError() | ||
231 | except KeyError: | ||
232 | pass | ||
233 | term = get_production(productions, 'term') | ||
234 | p[0] = ['compound_list'] + term[1:] | ||
235 | |||
236 | def p_term(p): | ||
237 | """term : term separator and_or | ||
238 | | and_or""" | ||
239 | if len(p)==2: | ||
240 | p[0] = ['term', p[1]] | ||
241 | else: | ||
242 | if p[2] is not None and p[2][1] == '&': | ||
243 | p[0] = ['term', ('async', p[1][1:])] + [p[3]] | ||
244 | else: | ||
245 | p[0] = p[1] + [p[3]] | ||
246 | |||
247 | def p_maybe_for_word(p): | ||
248 | # Rearrange 'For' priority wrt TOKEN. See p_for_word | ||
249 | """maybe_for_word : For""" | ||
250 | p[0] = ('maybe_for_word', p[1]) | ||
251 | |||
252 | def p_for_clause(p): | ||
253 | """for_clause : for_word name linebreak do_group | ||
254 | | for_word name linebreak in sequential_sep do_group | ||
255 | | for_word name linebreak in wordlist sequential_sep do_group""" | ||
256 | productions = p[1:] | ||
257 | do_group = get_production(productions, 'do_group') | ||
258 | try: | ||
259 | items = get_production(productions, 'in')[1:] | ||
260 | except KeyError: | ||
261 | raise NotImplementedError('"in" omission is not implemented') | ||
262 | |||
263 | try: | ||
264 | items = get_production(productions, 'wordlist')[1:] | ||
265 | except KeyError: | ||
266 | items = [] | ||
267 | |||
268 | name = p[2] | ||
269 | p[0] = ('for_clause', ForLoop(name, items, do_group[1:])) | ||
270 | |||
271 | def p_name(p): | ||
272 | """name : token""" #Was NAME instead of token | ||
273 | p[0] = p[1] | ||
274 | |||
275 | def p_in(p): | ||
276 | """in : In""" | ||
277 | p[0] = ('in', p[1]) | ||
278 | |||
279 | def p_wordlist(p): | ||
280 | """wordlist : wordlist token | ||
281 | | token""" | ||
282 | if len(p)==2: | ||
283 | p[0] = ['wordlist', ('TOKEN', p[1])] | ||
284 | else: | ||
285 | p[0] = p[1] + [('TOKEN', p[2])] | ||
286 | |||
287 | def p_case_clause(p): | ||
288 | """case_clause : Case token linebreak in linebreak case_list Esac | ||
289 | | Case token linebreak in linebreak case_list_ns Esac | ||
290 | | Case token linebreak in linebreak Esac""" | ||
291 | if len(p) < 8: | ||
292 | items = [] | ||
293 | else: | ||
294 | items = p[6][1:] | ||
295 | name = p[2] | ||
296 | p[0] = ('case_clause', Case(name, [c[1] for c in items])) | ||
297 | |||
298 | def p_case_list_ns(p): | ||
299 | """case_list_ns : case_list case_item_ns | ||
300 | | case_item_ns""" | ||
301 | p_case_list(p) | ||
302 | |||
303 | def p_case_list(p): | ||
304 | """case_list : case_list case_item | ||
305 | | case_item""" | ||
306 | if len(p)==2: | ||
307 | p[0] = ['case_list', p[1]] | ||
308 | else: | ||
309 | p[0] = p[1] + [p[2]] | ||
310 | |||
311 | def p_case_item_ns(p): | ||
312 | """case_item_ns : pattern RPARENS linebreak | ||
313 | | pattern RPARENS compound_list linebreak | ||
314 | | LPARENS pattern RPARENS linebreak | ||
315 | | LPARENS pattern RPARENS compound_list linebreak""" | ||
316 | p_case_item(p) | ||
317 | |||
318 | def p_case_item(p): | ||
319 | """case_item : pattern RPARENS linebreak DSEMI linebreak | ||
320 | | pattern RPARENS compound_list DSEMI linebreak | ||
321 | | LPARENS pattern RPARENS linebreak DSEMI linebreak | ||
322 | | LPARENS pattern RPARENS compound_list DSEMI linebreak""" | ||
323 | if len(p) < 7: | ||
324 | name = p[1][1:] | ||
325 | else: | ||
326 | name = p[2][1:] | ||
327 | |||
328 | try: | ||
329 | cmds = get_production(p[1:], "compound_list")[1:] | ||
330 | except KeyError: | ||
331 | cmds = [] | ||
332 | |||
333 | p[0] = ('case_item', (name, cmds)) | ||
334 | |||
335 | def p_pattern(p): | ||
336 | """pattern : token | ||
337 | | pattern PIPE token""" | ||
338 | if len(p)==2: | ||
339 | p[0] = ['pattern', ('TOKEN', p[1])] | ||
340 | else: | ||
341 | p[0] = p[1] + [('TOKEN', p[2])] | ||
342 | |||
343 | def p_maybe_if_word(p): | ||
344 | # Rearrange 'If' priority wrt TOKEN. See p_if_word | ||
345 | """maybe_if_word : If""" | ||
346 | p[0] = ('maybe_if_word', p[1]) | ||
347 | |||
348 | def p_maybe_then_word(p): | ||
349 | # Rearrange 'Then' priority wrt TOKEN. See p_then_word | ||
350 | """maybe_then_word : Then""" | ||
351 | p[0] = ('maybe_then_word', p[1]) | ||
352 | |||
353 | def p_if_clause(p): | ||
354 | """if_clause : if_word compound_list then_word compound_list else_part Fi | ||
355 | | if_word compound_list then_word compound_list Fi""" | ||
356 | else_part = [] | ||
357 | if len(p)==7: | ||
358 | else_part = p[5] | ||
359 | p[0] = ('if_clause', IfCond(p[2][1:], p[4][1:], else_part)) | ||
360 | |||
361 | def p_else_part(p): | ||
362 | """else_part : Elif compound_list then_word compound_list else_part | ||
363 | | Elif compound_list then_word compound_list | ||
364 | | Else compound_list""" | ||
365 | if len(p)==3: | ||
366 | p[0] = p[2][1:] | ||
367 | else: | ||
368 | else_part = [] | ||
369 | if len(p)==6: | ||
370 | else_part = p[5] | ||
371 | p[0] = ('elif', IfCond(p[2][1:], p[4][1:], else_part)) | ||
372 | |||
373 | def p_while_clause(p): | ||
374 | """while_clause : While compound_list do_group""" | ||
375 | p[0] = ('while_clause', WhileLoop(p[2][1:], p[3][1:])) | ||
376 | |||
377 | def p_maybe_until_word(p): | ||
378 | # Rearrange 'Until' priority wrt TOKEN. See p_until_word | ||
379 | """maybe_until_word : Until""" | ||
380 | p[0] = ('maybe_until_word', p[1]) | ||
381 | |||
382 | def p_until_clause(p): | ||
383 | """until_clause : until_word compound_list do_group""" | ||
384 | p[0] = ('until_clause', UntilLoop(p[2][1:], p[3][1:])) | ||
385 | |||
386 | def p_function_definition(p): | ||
387 | """function_definition : fname LPARENS RPARENS linebreak function_body""" | ||
388 | p[0] = ('function_definition', FunDef(p[1], p[5])) | ||
389 | |||
390 | def p_function_body(p): | ||
391 | """function_body : compound_command | ||
392 | | compound_command redirect_list""" | ||
393 | if len(p)!=2: | ||
394 | raise NotImplementedError('functions redirections lists are not implemented') | ||
395 | p[0] = p[1] | ||
396 | |||
397 | def p_fname(p): | ||
398 | """fname : TOKEN""" #Was NAME instead of token | ||
399 | p[0] = p[1] | ||
400 | |||
401 | def p_brace_group(p): | ||
402 | """brace_group : Lbrace compound_list Rbrace""" | ||
403 | p[0] = ('brace_group', BraceGroup(p[2][1:])) | ||
404 | |||
405 | def p_maybe_done_word(p): | ||
406 | #See p_assignment_word for details. | ||
407 | """maybe_done_word : Done""" | ||
408 | p[0] = ('maybe_done_word', p[1]) | ||
409 | |||
410 | def p_maybe_do_word(p): | ||
411 | """maybe_do_word : Do""" | ||
412 | p[0] = ('maybe_do_word', p[1]) | ||
413 | |||
414 | def p_do_group(p): | ||
415 | """do_group : do_word compound_list done_word""" | ||
416 | #Do group contains a list of AndOr | ||
417 | p[0] = ['do_group'] + p[2][1:] | ||
418 | |||
419 | def p_simple_command(p): | ||
420 | """simple_command : cmd_prefix cmd_word cmd_suffix | ||
421 | | cmd_prefix cmd_word | ||
422 | | cmd_prefix | ||
423 | | cmd_name cmd_suffix | ||
424 | | cmd_name""" | ||
425 | words, redirs, assigns = [], [], [] | ||
426 | for e in p[1:]: | ||
427 | name = e[0] | ||
428 | if name in ('cmd_prefix', 'cmd_suffix'): | ||
429 | for sube in e[1:]: | ||
430 | subname = sube[0] | ||
431 | if subname=='io_redirect': | ||
432 | redirs.append(make_io_redirect(sube)) | ||
433 | elif subname=='ASSIGNMENT_WORD': | ||
434 | assigns.append(sube) | ||
435 | else: | ||
436 | words.append(sube) | ||
437 | elif name in ('cmd_word', 'cmd_name'): | ||
438 | words.append(e) | ||
439 | |||
440 | cmd = SimpleCommand(words, redirs, assigns) | ||
441 | p[0] = ('simple_command', cmd) | ||
442 | |||
443 | def p_cmd_name(p): | ||
444 | """cmd_name : TOKEN""" | ||
445 | p[0] = ('cmd_name', p[1]) | ||
446 | |||
447 | def p_cmd_word(p): | ||
448 | """cmd_word : token""" | ||
449 | p[0] = ('cmd_word', p[1]) | ||
450 | |||
451 | def p_maybe_assignment_word(p): | ||
452 | #See p_assignment_word for details. | ||
453 | """maybe_assignment_word : ASSIGNMENT_WORD""" | ||
454 | p[0] = ('maybe_assignment_word', p[1]) | ||
455 | |||
456 | def p_cmd_prefix(p): | ||
457 | """cmd_prefix : io_redirect | ||
458 | | cmd_prefix io_redirect | ||
459 | | assignment_word | ||
460 | | cmd_prefix assignment_word""" | ||
461 | try: | ||
462 | prefix = get_production(p[1:], 'cmd_prefix') | ||
463 | except KeyError: | ||
464 | prefix = ['cmd_prefix'] | ||
465 | |||
466 | try: | ||
467 | value = get_production(p[1:], 'assignment_word')[1] | ||
468 | value = ('ASSIGNMENT_WORD', value.split('=', 1)) | ||
469 | except KeyError: | ||
470 | value = get_production(p[1:], 'io_redirect') | ||
471 | p[0] = prefix + [value] | ||
472 | |||
473 | def p_cmd_suffix(p): | ||
474 | """cmd_suffix : io_redirect | ||
475 | | cmd_suffix io_redirect | ||
476 | | token | ||
477 | | cmd_suffix token | ||
478 | | maybe_for_word | ||
479 | | cmd_suffix maybe_for_word | ||
480 | | maybe_done_word | ||
481 | | cmd_suffix maybe_done_word | ||
482 | | maybe_do_word | ||
483 | | cmd_suffix maybe_do_word | ||
484 | | maybe_until_word | ||
485 | | cmd_suffix maybe_until_word | ||
486 | | maybe_assignment_word | ||
487 | | cmd_suffix maybe_assignment_word | ||
488 | | maybe_if_word | ||
489 | | cmd_suffix maybe_if_word | ||
490 | | maybe_then_word | ||
491 | | cmd_suffix maybe_then_word | ||
492 | | maybe_bang_word | ||
493 | | cmd_suffix maybe_bang_word""" | ||
494 | try: | ||
495 | suffix = get_production(p[1:], 'cmd_suffix') | ||
496 | token = p[2] | ||
497 | except KeyError: | ||
498 | suffix = ['cmd_suffix'] | ||
499 | token = p[1] | ||
500 | |||
501 | if isinstance(token, tuple): | ||
502 | if token[0]=='io_redirect': | ||
503 | p[0] = suffix + [token] | ||
504 | else: | ||
505 | #Convert maybe_* to TOKEN if necessary | ||
506 | p[0] = suffix + [('TOKEN', token[1])] | ||
507 | else: | ||
508 | p[0] = suffix + [('TOKEN', token)] | ||
509 | |||
510 | def p_redirect_list(p): | ||
511 | """redirect_list : io_redirect | ||
512 | | redirect_list io_redirect""" | ||
513 | if len(p) == 2: | ||
514 | p[0] = ['redirect_list', make_io_redirect(p[1])] | ||
515 | else: | ||
516 | p[0] = p[1] + [make_io_redirect(p[2])] | ||
517 | |||
518 | def p_io_redirect(p): | ||
519 | """io_redirect : io_file | ||
520 | | IO_NUMBER io_file | ||
521 | | io_here | ||
522 | | IO_NUMBER io_here""" | ||
523 | if len(p)==3: | ||
524 | p[0] = ('io_redirect', p[1], p[2]) | ||
525 | else: | ||
526 | p[0] = ('io_redirect', None, p[1]) | ||
527 | |||
528 | def p_io_file(p): | ||
529 | #Return the tuple (operator, filename) | ||
530 | """io_file : LESS filename | ||
531 | | LESSAND filename | ||
532 | | GREATER filename | ||
533 | | GREATAND filename | ||
534 | | DGREAT filename | ||
535 | | LESSGREAT filename | ||
536 | | CLOBBER filename""" | ||
537 | #Extract the filename from the file | ||
538 | p[0] = ('io_file', p[1], p[2][1]) | ||
539 | |||
540 | def p_filename(p): | ||
541 | #Return the filename | ||
542 | """filename : TOKEN""" | ||
543 | p[0] = ('filename', p[1]) | ||
544 | |||
545 | def p_io_here(p): | ||
546 | """io_here : DLESS here_end | ||
547 | | DLESSDASH here_end""" | ||
548 | p[0] = ('io_here', p[1], p[2][1], p[2][2]) | ||
549 | |||
550 | def p_here_end(p): | ||
551 | """here_end : HERENAME TOKEN""" | ||
552 | p[0] = ('here_document', p[1], p[2]) | ||
553 | |||
554 | def p_newline_sequence(p): | ||
555 | # Nothing in the grammar can handle leading NEWLINE productions, so add | ||
556 | # this one with the lowest possible priority relatively to newline_list. | ||
557 | """newline_sequence : newline_list""" | ||
558 | p[0] = None | ||
559 | |||
560 | def p_newline_list(p): | ||
561 | """newline_list : NEWLINE | ||
562 | | newline_list NEWLINE""" | ||
563 | p[0] = None | ||
564 | |||
565 | def p_linebreak(p): | ||
566 | """linebreak : newline_list | ||
567 | | empty""" | ||
568 | p[0] = None | ||
569 | |||
570 | def p_separator_op(p): | ||
571 | """separator_op : COMMA | ||
572 | | AMP""" | ||
573 | p[0] = p[1] | ||
574 | |||
575 | def p_separator(p): | ||
576 | """separator : separator_op linebreak | ||
577 | | newline_list""" | ||
578 | if len(p)==2: | ||
579 | #Ignore newlines | ||
580 | p[0] = None | ||
581 | else: | ||
582 | #Keep the separator operator | ||
583 | p[0] = ('separator', p[1]) | ||
584 | |||
585 | def p_sequential_sep(p): | ||
586 | """sequential_sep : COMMA linebreak | ||
587 | | newline_list""" | ||
588 | p[0] = None | ||
589 | |||
590 | # Low priority TOKEN => for_word conversion. | ||
591 | # Let maybe_for_word be used as a token when necessary in higher priority | ||
592 | # rules. | ||
593 | def p_for_word(p): | ||
594 | """for_word : maybe_for_word""" | ||
595 | p[0] = p[1] | ||
596 | |||
597 | def p_if_word(p): | ||
598 | """if_word : maybe_if_word""" | ||
599 | p[0] = p[1] | ||
600 | |||
601 | def p_then_word(p): | ||
602 | """then_word : maybe_then_word""" | ||
603 | p[0] = p[1] | ||
604 | |||
605 | def p_done_word(p): | ||
606 | """done_word : maybe_done_word""" | ||
607 | p[0] = p[1] | ||
608 | |||
609 | def p_do_word(p): | ||
610 | """do_word : maybe_do_word""" | ||
611 | p[0] = p[1] | ||
612 | |||
613 | def p_until_word(p): | ||
614 | """until_word : maybe_until_word""" | ||
615 | p[0] = p[1] | ||
616 | |||
617 | def p_assignment_word(p): | ||
618 | """assignment_word : maybe_assignment_word""" | ||
619 | p[0] = ('assignment_word', p[1][1]) | ||
620 | |||
621 | def p_bang_word(p): | ||
622 | """bang_word : maybe_bang_word""" | ||
623 | p[0] = ('bang_word', p[1][1]) | ||
624 | |||
625 | def p_token(p): | ||
626 | """token : TOKEN | ||
627 | | Fi""" | ||
628 | p[0] = p[1] | ||
629 | |||
630 | def p_empty(p): | ||
631 | 'empty :' | ||
632 | p[0] = None | ||
633 | |||
634 | # Error rule for syntax errors | ||
635 | def p_error(p): | ||
636 | msg = [] | ||
637 | w = msg.append | ||
638 | w('%r\n' % p) | ||
639 | w('followed by:\n') | ||
640 | for i in range(5): | ||
641 | n = yacc.token() | ||
642 | if not n: | ||
643 | break | ||
644 | w(' %r\n' % n) | ||
645 | raise sherrors.ShellSyntaxError(''.join(msg)) | ||
646 | |||
647 | # Build the parser | ||
648 | try: | ||
649 | import pyshtables | ||
650 | except ImportError: | ||
651 | yacc.yacc(tabmodule = 'pyshtables') | ||
652 | else: | ||
653 | yacc.yacc(tabmodule = 'pysh.pyshtables', write_tables = 0, debug = 0) | ||
654 | |||
655 | |||
656 | def parse(input, eof=False, debug=False): | ||
657 | """Parse a whole script at once and return the generated AST and unconsumed | ||
658 | data in a tuple. | ||
659 | |||
660 | NOTE: eof is probably meaningless for now, the parser being unable to work | ||
661 | in pull mode. It should be set to True. | ||
662 | """ | ||
663 | lexer = pyshlex.PLYLexer() | ||
664 | remaining = lexer.add(input, eof) | ||
665 | if lexer.is_empty(): | ||
666 | return [], remaining | ||
667 | if debug: | ||
668 | debug = 2 | ||
669 | return yacc.parse(lexer=lexer, debug=debug), remaining | ||
670 | |||
671 | #------------------------------------------------------------------------------- | ||
672 | # AST rendering helpers | ||
673 | #------------------------------------------------------------------------------- | ||
674 | |||
675 | def format_commands(v): | ||
676 | """Return a tree made of strings and lists. Make command trees easier to | ||
677 | display. | ||
678 | """ | ||
679 | if isinstance(v, list): | ||
680 | return [format_commands(c) for c in v] | ||
681 | if isinstance(v, tuple): | ||
682 | if len(v)==2 and isinstance(v[0], str) and not isinstance(v[1], str): | ||
683 | if v[0] == 'async': | ||
684 | return ['AsyncList', map(format_commands, v[1])] | ||
685 | else: | ||
686 | #Avoid decomposing tuples like ('pipeline', Pipeline(...)) | ||
687 | return format_commands(v[1]) | ||
688 | return format_commands(list(v)) | ||
689 | elif isinstance(v, IfCond): | ||
690 | name = ['IfCond'] | ||
691 | name += ['if', map(format_commands, v.cond)] | ||
692 | name += ['then', map(format_commands, v.if_cmds)] | ||
693 | name += ['else', map(format_commands, v.else_cmds)] | ||
694 | return name | ||
695 | elif isinstance(v, ForLoop): | ||
696 | name = ['ForLoop'] | ||
697 | name += [repr(v.name)+' in ', map(str, v.items)] | ||
698 | name += ['commands', map(format_commands, v.cmds)] | ||
699 | return name | ||
700 | elif isinstance(v, AndOr): | ||
701 | return [v.op, format_commands(v.left), format_commands(v.right)] | ||
702 | elif isinstance(v, Pipeline): | ||
703 | name = 'Pipeline' | ||
704 | if v.reverse_status: | ||
705 | name = '!' + name | ||
706 | return [name, format_commands(v.commands)] | ||
707 | elif isinstance(v, SimpleCommand): | ||
708 | name = ['SimpleCommand'] | ||
709 | if v.words: | ||
710 | name += ['words', map(str, v.words)] | ||
711 | if v.assigns: | ||
712 | assigns = [tuple(a[1]) for a in v.assigns] | ||
713 | name += ['assigns', map(str, assigns)] | ||
714 | if v.redirs: | ||
715 | name += ['redirs', map(format_commands, v.redirs)] | ||
716 | return name | ||
717 | elif isinstance(v, RedirectList): | ||
718 | name = ['RedirectList'] | ||
719 | if v.redirs: | ||
720 | name += ['redirs', map(format_commands, v.redirs)] | ||
721 | name += ['command', format_commands(v.cmd)] | ||
722 | return name | ||
723 | elif isinstance(v, IORedirect): | ||
724 | return ' '.join(map(str, (v.io_number, v.op, v.filename))) | ||
725 | elif isinstance(v, HereDocument): | ||
726 | return ' '.join(map(str, (v.io_number, v.op, repr(v.name), repr(v.content)))) | ||
727 | elif isinstance(v, SubShell): | ||
728 | return ['SubShell', map(format_commands, v.cmds)] | ||
729 | else: | ||
730 | return repr(v) | ||
731 | |||
732 | def print_commands(cmds, output=sys.stdout): | ||
733 | """Pretty print a command tree.""" | ||
734 | def print_tree(cmd, spaces, output): | ||
735 | if isinstance(cmd, list): | ||
736 | for c in cmd: | ||
737 | print_tree(c, spaces + 3, output) | ||
738 | else: | ||
739 | print >>output, ' '*spaces + str(cmd) | ||
740 | |||
741 | formatted = format_commands(cmds) | ||
742 | print_tree(formatted, 0, output) | ||
743 | |||
744 | |||
745 | def stringify_commands(cmds): | ||
746 | """Serialize a command tree as a string. | ||
747 | |||
748 | Returned string is not pretty and is currently used for unit tests only. | ||
749 | """ | ||
750 | def stringify(value): | ||
751 | output = [] | ||
752 | if isinstance(value, list): | ||
753 | formatted = [] | ||
754 | for v in value: | ||
755 | formatted.append(stringify(v)) | ||
756 | formatted = ' '.join(formatted) | ||
757 | output.append(''.join(['<', formatted, '>'])) | ||
758 | else: | ||
759 | output.append(value) | ||
760 | return ' '.join(output) | ||
761 | |||
762 | return stringify(format_commands(cmds)) | ||
763 | |||
764 | |||
765 | def visit_commands(cmds, callable): | ||
766 | """Visit the command tree and execute callable on every Pipeline and | ||
767 | SimpleCommand instances. | ||
768 | """ | ||
769 | if isinstance(cmds, (tuple, list)): | ||
770 | map(lambda c: visit_commands(c,callable), cmds) | ||
771 | elif isinstance(cmds, (Pipeline, SimpleCommand)): | ||
772 | callable(cmds) | ||