Skip to content

Commit

Permalink
Turn more errors to exceptions in cmd_parse.py.
Browse files Browse the repository at this point in the history
Addresses issues #27 and #103.
  • Loading branch information
Andy Chu committed Aug 23, 2018
1 parent b1b046f commit 76f2395
Show file tree
Hide file tree
Showing 4 changed files with 32 additions and 28 deletions.
2 changes: 1 addition & 1 deletion core/expr_eval.py
Expand Up @@ -510,7 +510,7 @@ def Eval(self, node):
mode = os.stat(s).st_mode
except OSError:
# TODO: Signal extra debug information?
#self._AddErrorContext("Error from stat(%r): %s" % (s, e))
#log("Error from stat(%r): %s" % (s, e))
return False

if op_id in (Id.BoolUnary_e, Id.BoolUnary_a): # -a is alias for -e
Expand Down
4 changes: 0 additions & 4 deletions core/tdop.py
Expand Up @@ -223,10 +223,6 @@ def __init__(self, spec, w_parser):

self.error_stack = []

def AddErrorContext(self, msg, *args, **kwargs):
err = util.ParseError(msg, *args, **kwargs)
self.error_stack.append(err)

def Error(self):
return self.error_stack

Expand Down
39 changes: 16 additions & 23 deletions osh/cmd_parse.py
Expand Up @@ -593,8 +593,7 @@ def ParseSimpleCommand(self):

elif kind == Kind.ControlFlow:
if redirects:
self.AddErrorContext('Got redirects in control flow: %s', redirects)
return None
p_die("Control flow shouldn't have redirects", token=kw_token)

if prefix_bindings: # FOO=bar local spam=eggs not allowed
# TODO: Change location as above
Expand Down Expand Up @@ -676,8 +675,7 @@ def ParseForWords(self):
break
if self.cur_word.tag != word_e.CompoundWord:
# TODO: Can we also show a pointer to the 'for' keyword?
self.AddErrorContext('Invalid word in for loop', word=self.cur_word)
return None
p_die('Invalid word in for loop', word=self.cur_word)

words.append(self.cur_word)
self._Next()
Expand All @@ -704,10 +702,7 @@ def _ParseForExprLoop(self):
elif self.c_id == Id.KW_Do: # missing semicolon/newline allowed
pass
else:
self.AddErrorContext(
'Unexpected token after for expression: %s', self.cur_word,
word=self.cur_word)
return None
p_die('Invalid word after for expression', word=self.cur_word)

body_node = self.ParseDoGroup()
if not body_node: return None
Expand All @@ -721,13 +716,9 @@ def _ParseForEachLoop(self):

ok, iter_name, quoted = word.StaticEval(self.cur_word)
if not ok or quoted:
self.AddErrorContext(
"Invalid for loop variable", word=self.cur_word)
return None
p_die("Loop variable name should be a constant", word=self.cur_word)
if not match.IsValidVarName(iter_name):
self.AddErrorContext(
"Invalid for loop variable name", word=self.cur_word)
return None
p_die("Invalid loop variable name", word=self.cur_word)
node.iter_name = iter_name
self._Next() # skip past name

Expand Down Expand Up @@ -761,8 +752,9 @@ def _ParseForEachLoop(self):
# do not advance

else:
# Hm I think these never happens?
self.AddErrorContext("Unexpected word in for loop: %s", self.cur_word,
word=self.cur_word)
word=self.cur_word)
return None

node.spids.extend((in_spid, semi_spid))
Expand Down Expand Up @@ -859,8 +851,9 @@ def ParseCaseItem(self):
dsemi_spid = word.LeftMostSpanForWord(self.cur_word)
self._Next()
else:
# This never happens?
self.AddErrorContext('Expected DSEMI or ESAC, got %s', self.cur_word,
word=self.cur_word)
word=self.cur_word)
return None

if not self._NewlineOk(): return None
Expand Down Expand Up @@ -910,7 +903,6 @@ def ParseCase(self):

if self.c_id != Id.KW_Esac: # empty case list
if not self.ParseCaseList(case_node.arms):
self.AddErrorContext("ParseCase: error parsing case list")
return None
# TODO: should it return a list of nodes, and extend?
if not self._Peek(): return None
Expand Down Expand Up @@ -1044,6 +1036,7 @@ def ParseCompoundCommand(self):
if self.c_id == Id.Op_DLeftParen:
return self.ParseDParen()

# This never happens?
self.AddErrorContext(
"Expected a compound command (e.g. for while if case), got %s",
self.cur_word, word=self.cur_word)
Expand Down Expand Up @@ -1082,8 +1075,8 @@ def ParseFunctionDef(self):

ok, name = word.AsFuncName(self.cur_word)
if not ok:
self.AddErrorContext('Invalid function name', word=self.cur_word)
return None
p_die('Invalid function name', word=self.cur_word)

self._Next() # skip function name

# Must be true beacuse of lookahead
Expand Down Expand Up @@ -1119,8 +1112,8 @@ def ParseKshFunctionDef(self):
if not self._Peek(): return None
ok, name = word.AsFuncName(self.cur_word)
if not ok:
self.AddErrorContext("Invalid function name: %r", self.cur_word)
return None
p_die('Invalid KSH-style function name', word=self.cur_word)

after_name_spid = word.LeftMostSpanForWord(self.cur_word) + 1
self._Next() # skip past 'function name

Expand Down Expand Up @@ -1233,8 +1226,7 @@ def ParseCommand(self):

# TODO: KW_Do is also invalid here.
if self.c_id == Id.Lit_RBrace:
self.AddErrorContext('Unexpected }', word=self.cur_word)
return None
p_die('Unexpected right brace', word=self.cur_word)

if self.c_kind == Kind.Redir: # Leading redirect
return self.ParseSimpleCommand()
Expand All @@ -1249,6 +1241,7 @@ def ParseCommand(self):

return self.ParseSimpleCommand() # echo foo

# Does this ever happen?
self.AddErrorContext(
"ParseCommand: Expected to parse a command, got %s", self.cur_word,
word=self.cur_word)
Expand Down
15 changes: 15 additions & 0 deletions test/parse-errors.sh
Expand Up @@ -194,6 +194,21 @@ cmd-parse() {

_error-case 'FOO=1 break'
_error-case 'break 1 2'
_error-case 'break >out'

_error-case 'for x in &'

_error-case 'for (( i=0; i<10; i++ )) ls'

_error-case 'for $x in 1 2 3; do echo $i; done'
_error-case 'for x.y in 1 2 3; do echo $i; done'
_error-case 'for x in 1 2 3; &'

_error-case 'x"y"() { echo hi; }'

_error-case 'function x"y" { echo hi; }'

_error-case '}'
}

redirect() {
Expand Down

0 comments on commit 76f2395

Please sign in to comment.