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