summaryrefslogtreecommitdiffstats
path: root/bitbake/lib/bb/pysh/pyshyacc.py
diff options
context:
space:
mode:
authorChris Larson <chris_larson@mentor.com>2010-12-31 11:00:27 +0000
committerRichard Purdie <rpurdie@linux.intel.com>2011-01-04 14:46:39 +0000
commit4addbd191dec44d01e8d9961e645948c0ebd04c8 (patch)
tree733ca121d5f57fec9634fd047c26749aced188de /bitbake/lib/bb/pysh/pyshyacc.py
parent489d17596d2c532f2d8db3ef8c0f122ca49bb466 (diff)
downloadpoky-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.py772
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"""
10import sys
11
12import pyshlex
13tokens = pyshlex.tokens
14
15from ply import yacc
16import sherrors
17
18class 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
24class 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
31def 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
45class 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
54class 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
60class AndOr:
61 def __init__(self, op, left, right):
62 self.op = str(op)
63 self.left = left
64 self.right = right
65
66class ForLoop:
67 def __init__(self, name, items, cmds):
68 self.name = str(name)
69 self.items = list(items)
70 self.cmds = list(cmds)
71
72class WhileLoop:
73 def __init__(self, condition, cmds):
74 self.condition = list(condition)
75 self.cmds = list(cmds)
76
77class UntilLoop:
78 def __init__(self, condition, cmds):
79 self.condition = list(condition)
80 self.cmds = list(cmds)
81
82class FunDef:
83 def __init__(self, name, body):
84 self.name = str(name)
85 self.body = body
86
87class BraceGroup:
88 def __init__(self, cmds):
89 self.cmds = list(cmds)
90
91class 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
97class Case:
98 def __init__(self, name, items):
99 self.name = name
100 self.items = items
101
102class SubShell:
103 def __init__(self, cmds):
104 self.cmds = cmds
105
106class RedirectList:
107 def __init__(self, cmd, redirs):
108 self.cmd = cmd
109 self.redirs = list(redirs)
110
111def 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
126def 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
138def 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
146def 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
156def 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
165def p_maybe_bang_word(p):
166 """maybe_bang_word : Bang"""
167 p[0] = ('maybe_bang_word', p[1])
168
169def 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
177def 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
185def 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
207def 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
217def p_subshell(p):
218 """subshell : LPARENS compound_list RPARENS"""
219 p[0] = ('subshell', SubShell(p[2][1:]))
220
221def 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
236def 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
247def 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
252def 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
271def p_name(p):
272 """name : token""" #Was NAME instead of token
273 p[0] = p[1]
274
275def p_in(p):
276 """in : In"""
277 p[0] = ('in', p[1])
278
279def 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
287def 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
298def p_case_list_ns(p):
299 """case_list_ns : case_list case_item_ns
300 | case_item_ns"""
301 p_case_list(p)
302
303def 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
311def 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
318def 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
335def 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
343def 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
348def 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
353def 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
361def 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
373def 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
377def 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
382def 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
386def p_function_definition(p):
387 """function_definition : fname LPARENS RPARENS linebreak function_body"""
388 p[0] = ('function_definition', FunDef(p[1], p[5]))
389
390def 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
397def p_fname(p):
398 """fname : TOKEN""" #Was NAME instead of token
399 p[0] = p[1]
400
401def p_brace_group(p):
402 """brace_group : Lbrace compound_list Rbrace"""
403 p[0] = ('brace_group', BraceGroup(p[2][1:]))
404
405def 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
410def p_maybe_do_word(p):
411 """maybe_do_word : Do"""
412 p[0] = ('maybe_do_word', p[1])
413
414def 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
419def 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
443def p_cmd_name(p):
444 """cmd_name : TOKEN"""
445 p[0] = ('cmd_name', p[1])
446
447def p_cmd_word(p):
448 """cmd_word : token"""
449 p[0] = ('cmd_word', p[1])
450
451def 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
456def 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
473def 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
510def 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
518def 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
528def 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
540def p_filename(p):
541 #Return the filename
542 """filename : TOKEN"""
543 p[0] = ('filename', p[1])
544
545def 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
550def p_here_end(p):
551 """here_end : HERENAME TOKEN"""
552 p[0] = ('here_document', p[1], p[2])
553
554def 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
560def p_newline_list(p):
561 """newline_list : NEWLINE
562 | newline_list NEWLINE"""
563 p[0] = None
564
565def p_linebreak(p):
566 """linebreak : newline_list
567 | empty"""
568 p[0] = None
569
570def p_separator_op(p):
571 """separator_op : COMMA
572 | AMP"""
573 p[0] = p[1]
574
575def 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
585def 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.
593def p_for_word(p):
594 """for_word : maybe_for_word"""
595 p[0] = p[1]
596
597def p_if_word(p):
598 """if_word : maybe_if_word"""
599 p[0] = p[1]
600
601def p_then_word(p):
602 """then_word : maybe_then_word"""
603 p[0] = p[1]
604
605def p_done_word(p):
606 """done_word : maybe_done_word"""
607 p[0] = p[1]
608
609def p_do_word(p):
610 """do_word : maybe_do_word"""
611 p[0] = p[1]
612
613def p_until_word(p):
614 """until_word : maybe_until_word"""
615 p[0] = p[1]
616
617def p_assignment_word(p):
618 """assignment_word : maybe_assignment_word"""
619 p[0] = ('assignment_word', p[1][1])
620
621def p_bang_word(p):
622 """bang_word : maybe_bang_word"""
623 p[0] = ('bang_word', p[1][1])
624
625def p_token(p):
626 """token : TOKEN
627 | Fi"""
628 p[0] = p[1]
629
630def p_empty(p):
631 'empty :'
632 p[0] = None
633
634# Error rule for syntax errors
635def 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
648try:
649 import pyshtables
650except ImportError:
651 yacc.yacc(tabmodule = 'pyshtables')
652else:
653 yacc.yacc(tabmodule = 'pysh.pyshtables', write_tables = 0, debug = 0)
654
655
656def 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
675def 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
732def 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
745def 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
765def 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)