Permalink
Browse files

[osh2oil] Handle the 'until' loop, so it doesn't cause a crash.

Added test cases.

Also:

- Combine parsing and tree representation of while and until loops.  The
  execution was already unified.
- Fix the translation of while and until conditions, and add a test.
  while (( x == 0 )); now properly turns into while sh-expr ' x == 0 '.
  • Loading branch information...
Andy Chu
Andy Chu committed Sep 8, 2018
1 parent 1d0bf11 commit 1400fe14b403a8ead9d68826ccf72d6fa7440fc1
Showing with 60 additions and 30 deletions.
  1. +2 −3 core/cmd_exec.py
  2. +8 −20 osh/cmd_parse.py
  3. +2 −1 osh/cmd_parse_test.py
  4. +1 −2 osh/osh.asdl
  5. +34 −0 test/osh2oil.sh
  6. +13 −4 tools/osh2oil.py
View
@@ -929,9 +929,8 @@ def _Dispatch(self, node, fork_external):
i += 1
elif node.tag in (command_e.While, command_e.Until):
# TODO: Compile this out?
if node.tag == command_e.While:
elif node.tag == command_e.WhileUntil:
if node.keyword.id == Id.KW_While:
_DonePredicate = lambda status: status != 0
else:
_DonePredicate = lambda status: status == 0
View
@@ -924,33 +924,23 @@ def ParseFor(self):
return node
def ParseWhile(self):
def ParseWhileUntil(self):
"""
while_clause : While command_list do_group ;
"""
self._Next() # skip while
cond_node = self._ParseCommandList()
assert cond_node is not None
body_node = self.ParseDoGroup()
assert body_node is not None
return ast.While(cond_node.children, body_node)
def ParseUntil(self):
"""
until_clause : Until command_list do_group ;
"""
self._Next() # skip until
keyword = self.cur_word.parts[0].token
# This is ensured by the caller
assert keyword.id in (Id.KW_While, Id.KW_Until), keyword
self._Next() # skip while
cond_node = self._ParseCommandList()
assert cond_node is not None
body_node = self.ParseDoGroup()
assert body_node is not None
return ast.Until(cond_node.children, body_node)
return ast.WhileUntil(keyword, cond_node.children, body_node)
def ParseCaseItem(self):
"""
@@ -1152,10 +1142,8 @@ def ParseCompoundCommand(self):
if self.c_id == Id.KW_For:
return self.ParseFor()
if self.c_id == Id.KW_While:
return self.ParseWhile()
if self.c_id == Id.KW_Until:
return self.ParseUntil()
if self.c_id in (Id.KW_While, Id.KW_Until):
return self.ParseWhileUntil()
if self.c_id == Id.KW_If:
return self.ParseIf()
View
@@ -1056,7 +1056,8 @@ def testChromeIfSubshell(self):
break
} done
""")
self.assertEqual(command_e.While, node.tag)
self.assertEqual(command_e.WhileUntil, node.tag)
self.assertEqual(Id.KW_While, node.keyword.id)
node = assert_ParseCommandList(self, """\
if true; then (
View
@@ -192,8 +192,7 @@ module osh
-- order.
| ForExpr(arith_expr? init, arith_expr? cond, arith_expr? update,
command? body, redir* redirects)
| While(command* cond, command body, redir* redirects)
| Until(command* cond, command body, redir* redirects)
| WhileUntil(token keyword, command* cond, command body, redir* redirects)
| If(if_arm* arms, command* else_action, redir* redirects)
| Case(word to_match, case_arm* arms, redir* redirects)
| FuncDef(string name, command body, redir* redirects)
View
@@ -783,6 +783,38 @@ while read \
OIL
}
while-expr-loop() {
osh0-oil3 << 'OSH' 3<< 'OIL'
x=0
while (( x < 3 )); do
(( x++ ))
echo $x
done
OSH
setglobal x = '0'
while sh-expr ' x < 3 ' {
sh-expr ' x++ '
echo $x
}
OIL
}
until-loop() {
osh0-oil3 << 'OSH' 3<< 'OIL'
x=0
until (( x == 3 )); do
(( x++ ))
echo $x
done
OSH
setglobal x = '0'
while not sh-expr ' x == 3 ' {
sh-expr ' x++ '
echo $x
}
OIL
}
if_() {
osh0-oil3 << 'OSH' 3<< 'OIL'
if true; then
@@ -1197,6 +1229,8 @@ readonly -a PASSING=(
brace-group
subshell
while-loop
while-expr-loop
until-loop
if_
case_
for-loop
View
@@ -764,12 +764,21 @@ def DoCommand(self, node, local_symbols, at_top_level=False):
# Change (( )) to ( ), and then _FixDoGroup
pass
elif node.tag == command_e.While:
elif node.tag == command_e.WhileUntil:
# Skip 'until', and replace it with 'while not'
if node.keyword.id == Id.KW_Until:
kw_spid = node.keyword.span_id
self.cursor.PrintUntil(kw_spid)
self.f.write('while not')
self.cursor.SkipUntil(kw_spid + 1)
cond = node.cond
# Skip the semi-colon in the condition, which is ususally a Sentence
if len(cond) == 1 and cond[0].tag == command_e.Sentence:
spid = cond[0].terminator.span_id
self.cursor.PrintUntil(spid)
self.cursor.SkipUntil(spid + 1)
self.DoCommand(cond[0].child, local_symbols)
semi_spid = cond[0].terminator.span_id
self.cursor.SkipUntil(semi_spid + 1)
self.DoCommand(node.body, local_symbols)

0 comments on commit 1400fe1

Please sign in to comment.