Skip to content

Commit

Permalink
Change WordParser.ReadWord to throw an exception.
Browse files Browse the repository at this point in the history
BoolParser now consumes it like this, and is greatly simplified.

Addresses issue #103.
  • Loading branch information
Andy Chu committed Aug 22, 2018
1 parent 1644279 commit 79c4bbd
Show file tree
Hide file tree
Showing 2 changed files with 24 additions and 34 deletions.
54 changes: 21 additions & 33 deletions osh/bool_parse.py
Expand Up @@ -65,25 +65,21 @@ def __init__(self, w_parser):
self.error_stack = []

def _NextOne(self, lex_mode=lex_mode_e.DBRACKET):
#print('_Next', self.cur_word)
n = len(self.words)
if n == 2:
assert lex_mode == lex_mode_e.DBRACKET
self.words[0] = self.words[1]
self.cur_word = self.words[0]
del self.words[1]
elif n in (0, 1):
w = self.w_parser.ReadWord(lex_mode)
if not w:
err = self.w_parser.Error()
self.error_stack.extend(err)
return False
w = self.w_parser.ReadWord(lex_mode) # may raise
if n == 0:
self.words.append(w)
else:
self.words[0] = w
self.cur_word = w

assert self.cur_word is not None
self.op_id = word.BoolId(self.cur_word)
self.b_kind = LookupKind(self.op_id)
#log('--- word %s', self.cur_word)
Expand All @@ -98,9 +94,7 @@ def _Next(self, lex_mode=lex_mode_e.DBRACKET):
it's Id.WS_Newline -- we might have to unread tokens, etc.
"""
while True:
w = self._NextOne(lex_mode=lex_mode)
if not w:
return False
self._NextOne(lex_mode=lex_mode)
if self.op_id != Id.Op_Newline:
break
return True
Expand All @@ -110,12 +104,12 @@ def _LookAhead(self):
if n != 1:
raise AssertionError(self.words)

w = self.w_parser.ReadWord(lex_mode_e.DBRACKET)
w = self.w_parser.ReadWord(lex_mode_e.DBRACKET) # may raise
self.words.append(w) # Save it for _Next()
return w

def Parse(self):
if not self._Next(): return None
self._Next()

node = self.ParseExpr()
if self.op_id != Id.Lit_DRightBracket:
Expand All @@ -131,7 +125,7 @@ def _TestAtEnd(self):

def ParseForBuiltin(self):
"""For test builtin."""
if not self._Next(): return None
self._Next()

node = self.ParseExpr()
if self.op_id != Id.Eof_Real:
Expand All @@ -149,13 +143,10 @@ def ParseExpr(self):
Expr : Term (OR Expr)?
"""
left = self.ParseTerm()
if not left:
return None # TODO: An exception should handle this case.
# [[ uses || while [ uses -o
# [[ uses || but [ uses -o
if self.op_id in (Id.Op_DPipe, Id.BoolUnary_o):
if not self._Next(): return None
self._Next()
right = self.ParseExpr()
if not right: return None
return ast.LogicalOr(left, right)
else:
return left
Expand All @@ -168,13 +159,10 @@ def ParseTerm(self):
Term : Negated (AND Term)?
"""
left = self.ParseNegatedFactor()
if not left:
return None # TODO: An exception should handle this case.
# [[ uses && while [ uses -a
# [[ uses && but [ uses -a
if self.op_id in (Id.Op_DAmp, Id.BoolUnary_a):
if not self._Next(): return None
self._Next()
right = self.ParseTerm()
if not right: return None
return ast.LogicalAnd(left, right)
else:
return left
Expand All @@ -184,9 +172,8 @@ def ParseNegatedFactor(self):
Negated : '!'? Factor
"""
if self.op_id == Id.KW_Bang:
if not self._Next(): return None
self._Next()
child = self.ParseFactor()
if not child: return None
return ast.LogicalNot(child)
else:
return self.ParseFactor()
Expand All @@ -201,9 +188,9 @@ def ParseFactor(self):
if self.b_kind == Kind.BoolUnary:
# Just save the type and not the token itself?
op = self.op_id
if not self._Next(): return None
self._Next()
w = self.cur_word
if not self._Next(): return None
self._Next()
node = ast.BoolUnary(op, w)
return node

Expand All @@ -218,16 +205,16 @@ def ParseFactor(self):
if t2_b_kind in (Kind.BoolBinary, Kind.Redir):
left = self.cur_word

if not self._Next(): return None
self._Next()
op = self.op_id

# TODO: Need to change to lex_mode_e.BASH_REGEX.
# _Next(lex_mode) then?
is_regex = t2_op_id == Id.BoolBinary_EqualTilde
if is_regex:
if not self._Next(lex_mode=lex_mode_e.BASH_REGEX): return None
self._Next(lex_mode=lex_mode_e.BASH_REGEX)
else:
if not self._Next(): return None
self._Next()

right = self.cur_word
if is_regex:
Expand All @@ -238,24 +225,25 @@ def ParseFactor(self):

ok, regex_str, unused_quoted = word.StaticEval(right)

# TODO: Should raise exception with error?
# doesn't contain $foo, etc.
if ok and not libc.regex_parse(regex_str):
p_die("Invalid regex: %r" % regex_str, word=right)

if not self._Next(): return None
self._Next()
return ast.BoolBinary(op, left, right)
else:
# [[ foo ]]
w = self.cur_word
if not self._Next(): return None
self._Next()
return ast.WordTest(w)

if self.op_id == Id.Op_LParen:
if not self._Next(): return None
self._Next()
node = self.ParseExpr()
if self.op_id != Id.Op_RParen:
p_die('Expected ), got %s', self.cur_word, word=self.cur_word)
if not self._Next(): return None
self._Next()
return node

# It's not WORD, UNARY_OP, or '('
Expand Down
4 changes: 3 additions & 1 deletion osh/word_parse.py
Expand Up @@ -1215,8 +1215,10 @@ def ReadWord(self, lex_mode):
if not need_more:
break

# TODO: Just let it raise
if not w: # Assumes AddErrorContext was already called
return None
error_stack = self.Error()
p_die('ReadWord: %s', error_stack[-1])

self.cursor = w

Expand Down

0 comments on commit 79c4bbd

Please sign in to comment.