diff options
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) | ||
