def _MakeParser(code_str): # NOTE: We need the extra ]] token arena = test_lib.MakeArena('<bool_parse_test.py>') w_parser = test_lib.InitWordParser(code_str + ' ]]', arena=arena) w_parser._Next(lex_mode_e.DBracket) # for tests only p = bool_parse.BoolParser(w_parser) p._Next() return p
def _MakeParser(code_str): # NOTE: We need the extra ]] token w_parser, _ = parse_lib.MakeParserForCompletion(code_str + ' ]]') w_parser._Next(LexMode.DBRACKET) # for tests only p = bool_parse.BoolParser(w_parser) if not p._Next(): raise AssertionError return p
def _MakeParser(code_str): # NOTE: We need the extra ]] token arena = test_lib.MakeArena('<bool_parse_test.py>') w_parser, _ = parse_lib.MakeParserForCompletion(code_str + ' ]]', arena) w_parser._Next(lex_mode_e.DBRACKET) # for tests only p = bool_parse.BoolParser(w_parser) if not p._Next(): raise AssertionError return p
def _MakeParser(code_str): # NOTE: We need the extra ]] token arena = test_lib.MakeArena('<bool_parse_test.py>') parse_ctx = parse_lib.ParseContext(arena, {}) w_parser, _ = parse_ctx.MakeParserForCompletion(code_str + ' ]]', arena) w_parser._Next(lex_mode_e.DBRACKET) # for tests only p = bool_parse.BoolParser(w_parser) p._Next() return p
def ParseDBracket(self): """ Pass the underlying word parser off to the boolean expression parser. """ maybe_error_word = self.cur_word # TODO: Test interactive. Without closing ]], you should get > prompt # (PS2) self._Next() # skip [[ b_parser = bool_parse.BoolParser(self.w_parser) bnode = b_parser.Parse() # May raise return command.DBracket(bnode)
def Run(self, cmd_val): # type: (cmd_value__Argv) -> int """The test/[ builtin. The only difference between test and [ is that [ needs a matching ]. """ if self.need_right_bracket: # Preprocess right bracket if self.exec_opts.simple_test_builtin(): e_usage("should be invoked as 'test' (simple_test_builtin)") strs = cmd_val.argv if not strs or strs[-1] != ']': self.errfmt.Print_('missing closing ]', span_id=cmd_val.arg_spids[0]) return 2 # Remove the right bracket cmd_val.argv.pop() cmd_val.arg_spids.pop() w_parser = _StringWordEmitter(cmd_val) w_parser.Read() # dummy: advance past argv[0] b_parser = bool_parse.BoolParser(w_parser) # There is a fundamental ambiguity due to poor language design, in cases like: # [ -z ] # [ -z -a ] # [ -z -a ] ] # # See posixtest() in bash's test.c: # "This is an implementation of a Posix.2 proposal by David Korn." # It dispatches on expressions of length 0, 1, 2, 3, 4, and N args. We do # the same here. # # Another ambiguity: # -a is both a unary prefix operator and an infix operator. How to fix this # ambiguity? bool_node = None # type: bool_expr_t n = len(cmd_val.argv) - 1 if self.exec_opts.simple_test_builtin() and n > 3: e_usage( "should only have 3 arguments or fewer (simple_test_builtin)") try: if n == 0: return 1 # [ ] is False elif n == 1: w = w_parser.Read() bool_node = bool_expr.WordTest(w) elif n == 2: bool_node = _TwoArgs(w_parser) elif n == 3: bool_node = _ThreeArgs(w_parser) if n == 4: a0 = w_parser.Peek(0) if a0 == '!': w_parser.Read() # skip ! child = _ThreeArgs(w_parser) bool_node = bool_expr.LogicalNot(child) elif a0 == '(' and w_parser.Peek(3) == ')': w_parser.Read() # skip ')' bool_node = _TwoArgs(w_parser) else: pass # fallthrough if bool_node is None: bool_node = b_parser.ParseForBuiltin() except error.Parse as e: self.errfmt.PrettyPrintError(e, prefix='(test) ') return 2 # We technically don't need mem because we don't support BASH_REMATCH here. word_ev = _WordEvaluator() bool_ev = sh_expr_eval.BoolEvaluator(self.mem, self.exec_opts, None, self.errfmt) # We want [ a -eq a ] to always be an error, unlike [[ a -eq a ]]. This is a # weird case of [[ being less strict. bool_ev.Init_AlwaysStrict() bool_ev.word_ev = word_ev bool_ev.CheckCircularDeps() try: b = bool_ev.EvalB(bool_node) except error._ErrorWithLocation as e: # We want to catch e_die() and e_strict(). Those are both FatalRuntime # errors now, but it might not make sense later. # NOTE: This doesn't seem to happen. We have location info for all # errors that arise out of [. #if not e.HasLocation(): # raise self.errfmt.PrettyPrintError(e, prefix='(test) ') return 2 # 1 means 'false', and this usage error is like a parse error. status = 0 if b else 1 return status
def Test(argv, need_right_bracket): """The test/[ builtin. The only difference between test and [ is that [ needs a matching ]. """ if need_right_bracket: if not argv or argv[-1] != ']': util.error('[: missing closing ]') return 2 del argv[-1] w_parser = _StringWordEmitter(argv) b_parser = bool_parse.BoolParser(w_parser) # There is a fundamental ambiguity due to poor language design, in cases like: # [ -z ] # [ -z -a ] # [ -z -a ] ] # # See posixtest() in bash's test.c: # "This is an implementation of a Posix.2 proposal by David Korn." # It dispatches on expressions of length 0, 1, 2, 3, 4, and N args. We do # the same here. # # Another ambiguity: # -a is both a unary prefix operator and an infix operator. How to fix this # ambiguity? bool_node = None n = len(argv) try: if n == 0: return 1 # [ ] is False elif n == 1: bool_node = _StringWordTest(argv[0]) elif n == 2: bool_node = _TwoArgs(argv) elif n == 3: bool_node = _ThreeArgs(argv) if n == 4: a0 = argv[0] if a0 == '!': child = _ThreeArgs(argv[1:]) bool_node = ast.LogicalNot(child) elif a0 == '(' and argv[3] == ')': bool_node = _TwoArgs(argv[1:3]) else: pass # fallthrough if bool_node is None: bool_node = b_parser.ParseForBuiltin() except util.ParseError as e: # TODO: There should be a nice method to print argv. And some way to point # to the error. log("Error parsing %s", argv) util.error("test: %s", e.UserErrorString()) return 2 # parse error is 2 # mem: Don't need it for BASH_REMATCH? Or I guess you could support it # exec_opts: don't need it, but might need it later mem = None # Not necessary word_ev = _WordEvaluator() arena = None # We want [ a -eq a ] to always be an error, unlike [[ a -eq a ]]. This is a # weird case of [[ being less strict. class _DummyExecOpts(): def __init__(self): self.strict_arith = True exec_opts = _DummyExecOpts() bool_ev = expr_eval.BoolEvaluator(mem, exec_opts, word_ev, arena) try: b = bool_ev.Eval(bool_node) except util.FatalRuntimeError as e: # e.g. [ -t xxx ] # TODO: Printing the location would be nice. util.error('test: %s', e.UserErrorString()) return 2 # because this is more like a parser error. status = 0 if b else 1 return status
def __call__(self, cmd_val): # type: (cmd_value__Argv) -> int """The test/[ builtin. The only difference between test and [ is that [ needs a matching ]. """ if self.need_right_bracket: # Preprocess right bracket strs = cmd_val.argv if not strs or strs[-1] != ']': self.errfmt.Print('missing closing ]', span_id=cmd_val.arg_spids[0]) return 2 # Remove the right bracket cmd_val.argv.pop() cmd_val.arg_spids.pop() w_parser = _StringWordEmitter(cmd_val) w_parser.Read() # dummy: advance past argv[0] b_parser = bool_parse.BoolParser(w_parser) # There is a fundamental ambiguity due to poor language design, in cases like: # [ -z ] # [ -z -a ] # [ -z -a ] ] # # See posixtest() in bash's test.c: # "This is an implementation of a Posix.2 proposal by David Korn." # It dispatches on expressions of length 0, 1, 2, 3, 4, and N args. We do # the same here. # # Another ambiguity: # -a is both a unary prefix operator and an infix operator. How to fix this # ambiguity? bool_node = None # type: bool_expr_t n = len(cmd_val.argv) - 1 try: if n == 0: return 1 # [ ] is False elif n == 1: w = w_parser.Read() bool_node = bool_expr.WordTest(w) elif n == 2: bool_node = _TwoArgs(w_parser) elif n == 3: bool_node = _ThreeArgs(w_parser) if n == 4: a0 = w_parser.Peek(0) if a0 == '!': w_parser.Read() # skip ! child = _ThreeArgs(w_parser) bool_node = bool_expr.LogicalNot(child) elif a0 == '(' and w_parser.Peek(3) == ')': w_parser.Read() # skip ')' bool_node = _TwoArgs(w_parser) else: pass # fallthrough if bool_node is None: bool_node = b_parser.ParseForBuiltin() except error.Parse as e: self.errfmt.PrettyPrintError(e, prefix='(test) ') return 2 # mem: Don't need it for BASH_REMATCH? Or I guess you could support it mem = None # Not necessary word_ev = _WordEvaluator() arena = None # We want [ a -eq a ] to always be an error, unlike [[ a -eq a ]]. This is a # weird case of [[ being less strict. class _DummyExecOpts(): def __init__(self): # type: () -> None self.strict_arith = True exec_opts = _DummyExecOpts() bool_ev = expr_eval.BoolEvaluator(mem, exec_opts, word_ev, arena) try: b = bool_ev.Eval(bool_node) # type = bool except error.FatalRuntime as e: # Hack: we don't get the (test) prefix but we get location info. We # don't have access to mem.CurrentSpanId() here. if not e.HasLocation(): raise self.errfmt.PrettyPrintError(e, prefix='(test) ') return 2 # 1 means 'false', and this usage error is like a parse error. status = 0 if b else 1 return status
def Test(argv, need_right_bracket): """The test/[ builtin. The only difference between test and [ is that [ needs a matching ]. """ if need_right_bracket: if argv[-1] != ']': util.error('[: missing closing ]') return 2 del argv[-1] w_parser = _StringWordEmitter(argv) b_parser = bool_parse.BoolParser(w_parser) # There is a fundamental ambiguity due to poor language design, in cases like: # [ -z ] # [ -z -a ] # [ -z -a ] ] # # See posixtest() in bash's test.c: # "This is an implementation of a Posix.2 proposal by David Korn." # It dispatches on expressions of length 0, 1, 2, 3, 4, and N args. We do # the same here. # # Another ambiguity: # -a is both a unary prefix operator and an infix operator. How to fix this # ambiguity? bool_node = None n = len(argv) try: if n == 0: return 1 # [ ] is False elif n == 1: bool_node = _StringWordTest(argv[0]) elif n == 2: bool_node = _TwoArgs(argv) elif n == 3: bool_node = _ThreeArgs(argv) if n == 4: a0 = argv[0] if a0 == '!': child = _ThreeArgs(argv[1:]) bool_node = ast.LogicalNot(child) elif a0 == '(' and argv[3] == ')': bool_node = _TwoArgs(argv[1:3]) else: pass # fallthrough if bool_node is None: bool_node = b_parser.ParseForBuiltin() #log('Bool expr %s', bool_node) if bool_node is None: for e in b_parser.Error(): log("test: %s", e.UserErrorString()) # TODO: There should be a nice method to print argv. And some way to # point to the error. log("Error parsing test/[ expression: %s", argv) return 2 # parse error is 2 except util.ParseError as e: util.error(e.UserErrorString()) return 2 # mem: Don't need it for BASH_REMATCH? Or I guess you could support it # exec_opts: don't need it, but might need it later mem = None exec_opts = None word_ev = _WordEvaluator() bool_ev = expr_eval.BoolEvaluator(mem, exec_opts, word_ev) try: b = bool_ev.Eval(bool_node) except util.FatalRuntimeError as e: # e.g. [ -t xxx ] # TODO: Printing the location would be nice. print('test: %s' % e.UserErrorString(), file=sys.stderr) return 2 status = 0 if b else 1 return status