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