Skip to content

Commit

Permalink
[osh/arith_parse] Add parse_unimplemented option
Browse files Browse the repository at this point in the history
Addresses #640 for ble.sh.  But this option could be used in other
cases where we're thinking about expanding the Oil language.
  • Loading branch information
Andy Chu committed Mar 12, 2020
1 parent 97dcc53 commit 7ef4741
Show file tree
Hide file tree
Showing 5 changed files with 32 additions and 14 deletions.
4 changes: 4 additions & 0 deletions frontend/option_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,10 @@ def _Init(opt_def):
opt_def.Add('parse_ignored', groups=['strict:all', 'oil:basic', 'oil:all'],
default=True)

# Undocumented option to parse things that won't run. For ble.sh's dynamic
# LHS arithmetic, but can be used for other things too.
opt_def.Add('parse_unimplemented', default=False)

for name in _AGGRESSIVE_PARSE_OPTIONS:
opt_def.Add(name, groups=['oil:all'])

Expand Down
2 changes: 1 addition & 1 deletion frontend/parse_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ def MakeArithParser(self, code_str):
lx = self._MakeLexer(line_reader)
w_parser = word_parse.WordParser(self, lx, line_reader)
w_parser.Init(lex_mode_e.Arith) # Special initialization
a_parser = tdop.TdopParser(arith_parse.Spec(), w_parser)
a_parser = tdop.TdopParser(arith_parse.Spec(), w_parser, self.parse_opts)
return a_parser

def MakeParserForCommandSub(self, line_reader, lexer, eof_id):
Expand Down
4 changes: 2 additions & 2 deletions osh/arith_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def NullIncDec(p, w, bp):
# type: (TdopParser, word_t, int) -> arith_expr_t
""" ++x or ++x[1] """
right = p.ParseUntil(bp)
child = tdop.ToLValue(right)
child = tdop.ToLValue(right, p.parse_opts.parse_unimplemented())
if child is None:
p_die("This value can't be assigned to", word=w)
return arith_expr.UnaryAssign(word_.ArithId(w), child)
Expand Down Expand Up @@ -50,7 +50,7 @@ def LeftIncDec(p, w, left, rbp):
else:
raise AssertionError()

child = tdop.ToLValue(left)
child = tdop.ToLValue(left, p.parse_opts.parse_unimplemented())
if child is None:
p_die("This value can't be assigned to", word=w)
return arith_expr.UnaryAssign(op_id, child)
Expand Down
34 changes: 24 additions & 10 deletions osh/tdop.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from _devbuild.gen.id_kind_asdl import Id, Id_t
from _devbuild.gen.syntax_asdl import (
arith_expr, arith_expr_e, arith_expr_t,
arith_expr__VarRef, arith_expr__Binary,
arith_expr__VarRef, arith_expr__Binary, arith_expr__ArithWord,
sh_lhs_expr, sh_lhs_expr_t,
word_t,
)
Expand All @@ -22,6 +22,7 @@

if TYPE_CHECKING: # break circular dep
from osh.word_parse import WordParser
from core import optview
LeftFunc = Callable[['TdopParser', word_t, arith_expr_t, int], arith_expr_t]
NullFunc = Callable[['TdopParser', word_t, int], arith_expr_t]

Expand All @@ -37,8 +38,8 @@ def IsIndexable(node):
#return node.tag_() in (arith_expr_e.VarRef, arith_expr_e.ArithWord)


def ToLValue(node):
# type: (arith_expr_t) -> sh_lhs_expr_t
def ToLValue(node, parse_unimplemented):
# type: (arith_expr_t, bool) -> sh_lhs_expr_t
"""Determine if a node is a valid L-value by whitelisting tags.
Valid:
Expand All @@ -58,12 +59,23 @@ def ToLValue(node):
n.spids.append(node.token.span_id)
return n

elif case(arith_expr_e.ArithWord):
if parse_unimplemented:
node = cast(arith_expr__ArithWord, UP_node)
return sh_lhs_expr.Name('DUMMY_parse_unimplemented')

elif case(arith_expr_e.Binary):
node = cast(arith_expr__Binary, UP_node)
if (node.op_id == Id.Arith_LBracket and
node.left.tag_() == arith_expr_e.VarRef):
left = cast(arith_expr__VarRef, node.left)
return sh_lhs_expr.IndexedName(left.token.val, node.right)
if node.op_id == Id.Arith_LBracket:
UP_left = node.left
if node.left.tag_() == arith_expr_e.VarRef:
left = cast(arith_expr__VarRef, UP_left)
return sh_lhs_expr.IndexedName(left.token.val, node.right)

if parse_unimplemented and node.left.tag_() == arith_expr_e.ArithWord:
return sh_lhs_expr.IndexedName(
'DUMMY_parse_unimplemented', node.right)

# But a[0][0] = 1 is NOT valid.

return None
Expand Down Expand Up @@ -134,7 +146,7 @@ def LeftAssign(p, w, left, rbp):
# type: (TdopParser, word_t, arith_expr_t, int) -> arith_expr_t
""" Normal binary operator like 1+2 or 2*3, etc. """
# x += 1, or a[i] += 1
lhs = ToLValue(left)
lhs = ToLValue(left, p.parse_opts.parse_unimplemented())
if lhs is None:
# TODO: It would be nice to point at 'left', but osh/word.py doesn't
# support arbitrary arith_expr_t.
Expand Down Expand Up @@ -257,10 +269,12 @@ class TdopParser(object):
"""
Parser state. Current token and lookup stack.
"""
def __init__(self, spec, w_parser):
# type: (ParserSpec, WordParser) -> None
def __init__(self, spec, w_parser, parse_opts):
# type: (ParserSpec, WordParser, optview.Parse) -> None
self.spec = spec
self.w_parser = w_parser
self.parse_opts = parse_opts

self.cur_word = None # type: word_t # current token
self.op_id = Id.Undefined_Tok

Expand Down
2 changes: 1 addition & 1 deletion osh/word_parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -943,7 +943,7 @@ def _ReadArithExpr(self):
See the assertion in ArithParser.Parse() -- unexpected extra input.
"""
# calls self.ReadWord(lex_mode_e.Arith)
a_parser = tdop.TdopParser(arith_parse.Spec(), self)
a_parser = tdop.TdopParser(arith_parse.Spec(), self, self.parse_opts)
anode = a_parser.Parse()
return anode

Expand Down

0 comments on commit 7ef4741

Please sign in to comment.