Skip to content

Commit

Permalink
[osh2oil] Rough pass at translation of a[x++]=foo.
Browse files Browse the repository at this point in the history
I introduced a new OSH 'compat' keyword with an 'array-assign'
subcommand for this.  We don't want to translate OSH arithmetic to Oil
expressions, at least at first.
  • Loading branch information
Andy Chu committed Jan 29, 2019
1 parent 3dee19e commit c79d9b9
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 14 deletions.
1 change: 1 addition & 0 deletions frontend/syntax.asdl
Expand Up @@ -115,6 +115,7 @@ module syntax
lhs_expr =
LhsName(string name)
| LhsIndexedName(string name, arith_expr index)
| CompatIndexedName(string name, string index) -- for translation

arith_expr =
ArithVarRef(token token) -- variable without $
Expand Down
28 changes: 24 additions & 4 deletions osh/cmd_parse.py
Expand Up @@ -134,6 +134,25 @@ def _MakeAssignPair(parse_ctx, preparsed):
lhs = lhs_expr.LhsName(var_name)
lhs.spids.append(left_token.span_id)

elif left_token.id == Id.Lit_ArrayLhsOpen and parse_ctx.one_pass_parse:
var_name = left_token.val[:-1]
if close_token.val[-2] == '+':
op = assign_op_e.PlusEqual
else:
op = assign_op_e.Equal

left_spid = left_token.span_id + 1
right_spid = close_token.span_id

left_span = parse_ctx.arena.GetLineSpan(left_spid)
right_span = parse_ctx.arena.GetLineSpan(right_spid)
assert left_span.line_id == right_span.line_id, \
'%s and %s not on same line' % (left_span, right_span)

line = parse_ctx.arena.GetLine(left_span.line_id)
index_str = line[left_span.col : right_span.col]
lhs = lhs_expr.CompatIndexedName(var_name, index_str)

elif left_token.id == Id.Lit_ArrayLhsOpen: # a[x++]=1
var_name = left_token.val[:-1]
if close_token.val[-2] == '+':
Expand All @@ -154,8 +173,9 @@ def _MakeAssignPair(parse_ctx, preparsed):
# Now reparse everything between here
code_str = ''.join(pieces)

# NOTE: It's possible that an alias expansion underlies this, not a real file!
# We have to use a SideArena since this will happen during translation.
# NOTE: It's possible that an alias expansion underlies this, not a real
# file! We have to use a SideArena since this will happen during
# translation.
line_num = 99
source_name = 'TODO'
arena = alloc.SideArena('<LHS array index at line %d of %s>' %
Expand All @@ -170,7 +190,7 @@ def _MakeAssignPair(parse_ctx, preparsed):
else:
raise AssertionError

# TODO: Should we also create a rhs_exp.ArrayLiteral here?
# TODO: Should we also create a rhs_expr.ArrayLiteral here?
n = len(w.parts)
if part_offset == n:
val = osh_word.EmptyWord()
Expand All @@ -179,7 +199,7 @@ def _MakeAssignPair(parse_ctx, preparsed):
val = word.TildeDetect(val) or val

pair = syntax_asdl.assign_pair(lhs, op, val)
pair.spids.append(left_token.span_id) # Do we need this?
pair.spids.append(left_token.span_id) # To skip to beginning of pair
return pair


Expand Down
4 changes: 2 additions & 2 deletions test/osh2oil.sh
Expand Up @@ -1039,7 +1039,7 @@ foo=bar
a[x+1]=bar
OSH
setglobal foo = 'bar'
setglobal a[x+1] = 'bar'
compat array-assign a 'x+1' 'bar'
OIL
}
Expand Down Expand Up @@ -1316,7 +1316,7 @@ readonly -a PASSING=(
# Require --one-pass-parse
backticks
#lhs-assignment
lhs-assignment
# Compound commands
brace-group
Expand Down
34 changes: 26 additions & 8 deletions tools/osh2oil.py
Expand Up @@ -464,9 +464,14 @@ def DoAssignment(self, node, at_top_level, local_symbols):
defined_locally = True
#print("CHECKING NAME", lhs0.name, defined_locally, local_symbols)

has_array = any(
pair.lhs.tag == lhs_expr_e.CompatIndexedName for pair in node.pairs)

# need semantic analysis.
# Would be nice to assume that it's a local though.
if at_top_level:
if has_array:
self.f.write('compat ') # 'compat array-assign' syntax
elif at_top_level:
self.f.write('setglobal ')
elif defined_locally:
self.f.write('set ')
Expand Down Expand Up @@ -531,23 +536,36 @@ def DoAssignment(self, node, at_top_level, local_symbols):
else:
self.DoWordAsExpr(pair.rhs, local_symbols)

if i != n - 1:
self.f.write(',')

elif pair.lhs.tag == lhs_expr_e.LhsIndexedName:
# TODO:
elif pair.lhs.tag == lhs_expr_e.CompatIndexedName:
# NOTES:
# - parse_ctx.one_pass_parse should be on, so the span invariant
# is accurate
# - Then do the following translation:
# a[x+1]="foo $bar" ->
# compat array-assign a 'x+1' "$foo $bar"
# This avoids dealing with nested arenas.
#
# This way we don't have to deal with nested arenas.
pass
# TODO: This isn't great when there are multiple assignments.
# a[x++]=1 b[y++]=2
#
# 'compat' could apply to the WHOLE statement, with multiple
# assignments.
self.f.write("array-assign %s '%s' " % (pair.lhs.name, pair.lhs.index))

# TODO: This should be translated from EmptyWord.
if pair.rhs is None:
self.f.write("''") # local i -> var i = ''
else:
rhs_spid = word.LeftMostSpanForWord(pair.rhs)
self.cursor.SkipUntil(rhs_spid)
self.DoWordAsExpr(pair.rhs, local_symbols)

else:
raise AssertionError(pair.lhs.__class__.__name__)

if i != n - 1:
self.f.write(',')

def DoCommand(self, node, local_symbols, at_top_level=False):
if node.tag == command_e.CommandList:
# TODO: How to distinguish between echo hi; echo bye; and on separate
Expand Down

0 comments on commit c79d9b9

Please sign in to comment.