View
@@ -528,7 +528,10 @@ def _ReadSingleQuotedPart(self, lex_mode):
'Unhandled token in single-quoted part %s (%d)' %
(self.cur_token, self.token_kind))
return ast.SingleQuotedPart(left, tokens)
node = ast.SingleQuotedPart(left, tokens)
node.spids.append(left.span_id) # left '
node.spids.append(self.cur_token.span_id) # right '
return node
def _ReadDoubleQuotedLeftParts(self):
"""Read substitution parts in a double quoted context."""
@@ -629,75 +632,69 @@ def _ReadExtGlobPart(self):
return part
def _ReadDoubleQuotedPart(self, eof_type=Id.Undefined_Tok, here_doc=False):
def _ReadLikeDQ(self, left_dq_token, out_parts):
"""
Args:
eof_type: for stopping at }, Id.Lit_RBrace
here_doc: Whether we are reading in a here doc context
Also ${foo%%a b c} # treat this as double quoted. until you hit
left_dq_token: A token if we are reading a double quoted part, or None if
we're reading a here doc.
out_parts: list of word_part to append to
"""
quoted_part = ast.DoubleQuotedPart()
left_spid = const.NO_INTEGER # gets set later
right_spid = const.NO_INTEGER # gets set later
# TODO: Use here doc.
if self.cur_token is not None: # None in here doc case
left_token = self.cur_token
left_spid = left_token.span_id
else:
left_token = None
done = False
while not done:
self._Next(lex_mode_e.DQ)
self._Peek()
#print(self.cur_token)
if self.token_type == eof_type: # e.g. stop at }
done = True
continue
elif self.token_kind == Kind.Lit:
if self.token_kind == Kind.Lit:
if self.token_type == Id.Lit_EscapedChar:
part = ast.EscapedLiteralPart(self.cur_token)
else:
part = ast.LiteralPart(self.cur_token)
quoted_part.parts.append(part)
out_parts.append(part)
elif self.token_kind == Kind.Left:
part = self._ReadDoubleQuotedLeftParts()
if not part:
return None
quoted_part.parts.append(part)
out_parts.append(part)
elif self.token_kind == Kind.VSub:
part = ast.SimpleVarSub(self.cur_token)
quoted_part.parts.append(part)
out_parts.append(part)
elif self.token_kind == Kind.Right:
assert self.token_type == Id.Right_DoubleQuote
if here_doc:
# Turn Id.Right_DoubleQuote into a literal part
quoted_part.parts.append(ast.LiteralPart(self.cur_token))
assert self.token_type == Id.Right_DoubleQuote, self.token_type
if left_dq_token:
done = True
else:
done = True # assume Id.Right_DoubleQuote
right_spid = self.cur_token.span_id
# In a here doc, the right quote is literal!
out_parts.append(ast.LiteralPart(self.cur_token))
elif self.token_kind == Kind.Eof:
if here_doc: # here docs will have an EOF in their token stream
done = True
else:
assert left_token is not None # See hacky condition above
if left_dq_token:
p_die('Unexpected EOF reading double-quoted string that began here',
token=left_token)
token=left_dq_token)
else: # here docs will have an EOF in their token stream
done = True
else:
raise AssertionError(self.cur_token)
# Return nothing, since we appended to 'out_parts'
def _ReadDoubleQuotedPart(self):
"""
Args:
eof_type: for stopping at }, Id.Lit_RBrace
here_doc: Whether we are reading in a here doc context
Also ${foo%%a b c} # treat this as double quoted. until you hit
"""
dq_part = ast.DoubleQuotedPart()
left_dq_token = self.cur_token
dq_part.spids.append(left_dq_token.span_id) # Left "
self._ReadLikeDQ(left_dq_token, dq_part.parts)
quoted_part.spids.extend((left_spid, right_spid))
return quoted_part
dq_part.spids.append(self.cur_token.span_id) # Right "
return dq_part
def _ReadCommandSubPart(self, token_type):
"""
@@ -1195,22 +1192,10 @@ def ReadWord(self, lex_mode):
self.cursor_was_newline = (word.CommandId(self.cursor) == Id.Op_Newline)
return self.cursor
def ReadHereDocBody(self):
def ReadHereDocBody(self, parts):
"""
Sort of like Read(), except we're in a double quoted context, but not using
double quotes.
Returns:
CompoundWord. NOTE: We could also just use a DoubleQuotedPart for both
cases?
"""
dq = self._ReadDoubleQuotedPart(here_doc=True)
assert dq is not None
return ast.CompoundWord([dq])
# TODO: _ReadDQContext(parts) should be shared between
# _ReadDoubleQuotedPart() and ReadHereDocBody()
# Call with dq_part.parts
# and here_doc_node.body
self._ReadLikeDQ(None, parts)
# Returns nothing
View
@@ -20,6 +20,7 @@ compare() {
here-doc() {
compare test/arena/here-dq.sh
compare test/arena/here-sq.sh
compare test/arena/here-multiple.sh
}
readonly -a PASSING=(
View
@@ -0,0 +1,12 @@
#!/bin/bash
# Compare osh code on stdin (fd 0) and expected oil code on fd 3.
osh0-oil3() {
bin/osh --fix "$@" | diff -u /dev/fd/3 - || { echo FAIL; exit 1; }
}
osh0-oil3 << 'OSH' 2>&1 3<< 'OIL'
x=1
OSH
x = 1
OIL
View
@@ -301,36 +301,36 @@ OIL
here-doc() {
osh0-oil3 << 'OSH' 3<< 'OIL'
cat <<ONE
cat <<ONE
echo $hi
ONE
OSH
cat << """
cat << """
echo $hi
"""
OIL
osh0-oil3 << 'OSH' 3<< 'OIL'
cat <<'ONE'
cat <<'ONE'
single quoted
ONE
OSH
cat << '''
cat << '''
single quoted
'''
OIL
# <<- is indented
osh0-oil3 << 'OSH' 3<< 'OIL'
cat <<-'ONE'
indented
body
ONE
cat <<-'ONE'
indented
body
ONE
OSH
cat << '''
indented
body
'''
cat << '''
indented
body
'''
OIL
}
View
@@ -281,6 +281,25 @@ string_to_int_bool() {
echo 'SHOULD NOT GET HERE'
}
strict_array() {
set -- 1 2
echo foo > _tmp/"$@"
set -o strict-array
echo foo > _tmp/"$@"
}
strict_array_2() {
local foo="$@"
set -o strict-array
local foo="$@"
}
strict_array_3() {
local foo=${1:- "[$@]" }
set -o strict-array
local foo=${1:- "[$@]" }
}
#
# TEST DRIVER
#
View
@@ -15,6 +15,7 @@
word_style_e = runtime_asdl.word_style_e
log = util.log
p_die = util.p_die
command_e = ast.command_e
redir_e = ast.redir_e
@@ -271,27 +272,33 @@ def DoRedirect(self, node, local_symbols):
self.DoWordInCommand(node.arg_word, local_symbols)
elif node.tag == redir_e.HereDoc:
self.cursor.PrintUntil(node.op.span_id)
ok, delimiter, delim_quoted = word.StaticEval(node.here_begin)
if not ok:
p_die('Invalid here doc delimiter', word=node.here_begin)
# Turn everything into <<. We just change the quotes
self.f.write('<<')
here_begin_spid1 = word.LeftMostSpanForWord(node.here_begin)
#here_begin_spid2 = word.RightMostSpanForWord(node.here_begin)
if node.do_expansion:
self.f.write('"""')
if delim_quoted:
self.f.write(" '''")
else:
self.f.write("'''")
self.f.write(' """')
delim_end_spid = word.RightMostSpanForWord(node.here_begin)
self.cursor.SkipUntil(delim_end_spid + 1)
#self.cursor.SkipUntil(here_begin_spid2)
self.cursor.SkipUntil(here_begin_spid1 + 1)
#self.cursor.SkipUntil(here_begin_spid + 1)
# Now print the lines
self.DoWordInCommand(node.body, local_symbols)
for part in node.stdin_parts:
self.DoWordPart(part, local_symbols)
if node.do_expansion:
self.f.write('"""')
self.cursor.SkipUntil(node.here_end_span_id + 1)
if delim_quoted:
self.f.write("'''\n")
else:
self.f.write("'''")
self.f.write('"""\n')
# Need
#self.cursor.SkipUntil(here_end_spid2)