def EvalWordToString(self, word, do_fnmatch=False, do_ere=False): """ Args: word: CompoundWord Used for redirect arg, ControlFlow arg, ArithWord, BoolWord, etc. do_fnmatch is true for case $pat and RHS of [[ == ]]. pat="*.py" case $x in $pat) echo 'matches glob pattern' ;; "$pat") echo 'equal to glob string' ;; # must be glob escaped esac TODO: Raise AssertionError if it has ExtGlobPart. """ if word.tag == word_e.EmptyWord: return value.Str('') part_vals = [] for p in word.parts: self._EvalWordPart(p, part_vals, quoted=False) strs = [] for part_val in part_vals: if part_val.tag == part_value_e.String: # [[ foo == */"*".py ]] or case *.py) ... esac if do_fnmatch and not part_val.do_split_glob: s = glob_.GlobEscape(part_val.s) elif do_ere and not part_val.do_split_glob: s = glob_.ExtendedRegexEscape(part_val.s) else: s = part_val.s else: if self.exec_opts.strict_array: # Examples: echo f > "$@"; local foo="$@" # TODO: This attributes too coarsely, to the word rather than the # parts. Problem: the word is a TREE of parts, but we only have a # flat list of part_vals. The only case where we really get arrays # is "$@", "${a[@]}", "${a[@]//pat/replace}", etc. e_die( "This word should evaluate to a string, but part of it was an " "array", word=word) # TODO: Maybe add detail like this. #e_die('RHS of assignment should only have strings. ' # 'To assign arrays, use b=( "${a[@]}" )') else: # It appears to not respect IFS s = ' '.join(s for s in part_val.strs if s is not None) strs.append(s) return value.Str(''.join(strs))
def AsPosixEre(node, parts): # type: (re_t) -> List[str] """Translate an Oil regex to a POSIX ERE. Appends to a list of parts that you hvae to join. """ tag = node.tag if tag == re_e.Primitive: if node.id == Id.Re_Dot: parts.append('.') elif node.id == Id.Re_Start: parts.append('^') elif node.id == Id.Re_End: parts.append('$') else: raise AssertionError(node) return if tag == re_e.LiteralChars: # The bash [[ x =~ "." ]] construct also has to do this # TODO: What about \0 and unicode escapes? # Those won't be as LiteralChars I don't think? # Unless you put them there through \0 # Maybe DISALLOW those. # "Unprintable chars should be written as \0 or \x00 or \u0000" parts.append(glob_.ExtendedRegexEscape(node.s)) return if tag == re_e.Seq: for c in node.children: AsPosixEre(c, parts) return if tag == re_e.Alt: for i, c in enumerate(node.children): if i != 0: parts.append('|') AsPosixEre(c, parts) return if tag == re_e.Repeat: # 'foo' or "foo" or $x or ${x} evaluated to too many chars if node.child.tag == re_e.LiteralChars: if len(node.child.s) > 1: # Note: Other regex dialects have non-capturing groups since we don't # need this. e_die( "POSIX EREs don't have groups without capture, so this node " "needs () around it.", span_id=node.child.spid) AsPosixEre(node.child, parts) op = node.op op_tag = op.tag if op_tag == re_repeat_e.Op: op_id = op.op.id if op_id == Id.Arith_Plus: parts.append('+') elif op_id == Id.Arith_Star: parts.append('*') elif op_id == Id.Arith_QMark: parts.append('?') else: raise AssertionError(op_id) return if op_tag == re_repeat_e.Num: parts.append('{%s}' % op.times.val) return if op_tag == re_repeat_e.Range: lower = op.lower.val if op.lower else '' upper = op.upper.val if op.upper else '' parts.append('{%s,%s}' % (lower, upper)) return raise NotImplementedError(node.op) if tag == re_e.Group: parts.append('(') AsPosixEre(node.child, parts) parts.append(')') return if tag == re_e.PerlClass: n = node.name chars = PERL_CLASS[node.name] # looks like [:digit:] if node.negated: pat = '[^%s]' % chars else: pat = '[%s]' % chars parts.append(pat) return if tag == re_e.PosixClass: n = node.name # looks like 'digit' if node.negated: pat = '[^[:%s:]]' % n else: pat = '[[:%s:]]' % n parts.append(pat) return if tag == re_e.ClassLiteral: parts.append('[') if node.negated: parts.append('^') for term in node.terms: _ClassLiteralToPosixEre(term, parts) parts.append(']') return raise NotImplementedError(node.__class__.__name__)