Skip to content

Commit

Permalink
[refactor] Rename lvalue type.
Browse files Browse the repository at this point in the history
We go from lhs_expr to lvalue.  lvalue has to know the type.
  • Loading branch information
Andy Chu committed Jul 14, 2019
1 parent d5066c5 commit df97cc3
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 44 deletions.
2 changes: 1 addition & 1 deletion oil_lang/expr_eval.py
Expand Up @@ -54,7 +54,7 @@ def EvalLHS(self, node):
print('')

if node.tag == expr_e.Var:
return lvalue.LhsName(node.name.val)
return lvalue.Named(node.name.val)
else:
# TODO:
# subscripts, tuple unpacking, starred expressions, etc.
Expand Down
4 changes: 2 additions & 2 deletions osh/builtin.py
Expand Up @@ -835,7 +835,7 @@ def __call__(self, arg_vec):

#log('%s %s', name, val)
self.mem.SetVar(
lvalue.LhsName(name), val, (var_flags_e.Exported,), scope_e.Dynamic)
lvalue.Named(name), val, (var_flags_e.Exported,), scope_e.Dynamic)

return 0

Expand Down Expand Up @@ -1006,7 +1006,7 @@ def _UnsetVar(self, name, spid):
raise args.UsageError(
'got invalid variable name %r' % name, span_id=spid)

ok, found = self.mem.Unset(lvalue.LhsName(name), scope_e.Dynamic)
ok, found = self.mem.Unset(lvalue.Named(name), scope_e.Dynamic)
if not ok:
self.errfmt.Print("Can't unset readonly variable %r", name,
span_id=spid)
Expand Down
2 changes: 1 addition & 1 deletion osh/cmd_exec.py
Expand Up @@ -621,7 +621,7 @@ def _EvalTempEnv(self, more_env):
val = self.word_ev.EvalWordToString(env_pair.val)
# Set each var so the next one can reference it. Example:
# FOO=1 BAR=$FOO ls /
self.mem.SetVar(lvalue.LhsName(env_pair.name), val,
self.mem.SetVar(lvalue.Named(env_pair.name), val,
(var_flags_e.Exported,), scope_e.LocalOnly)

def _Dispatch(self, node, fork_external):
Expand Down
19 changes: 12 additions & 7 deletions osh/expr_eval.py
Expand Up @@ -132,17 +132,22 @@ def EvalLhs(node, arith_ev, mem, spid, lookup_mode):
assert isinstance(node, lhs_expr_t), node

if node.tag == lhs_expr_e.LhsName: # a=x
lval = lvalue.LhsName(node.name)
lval = lvalue.Named(node.name)
lval.spids.append(spid)
return lval

if node.tag == lhs_expr_e.LhsIndexedName: # a[1+2]=x
# The index of StrArray needs to be coerced to int, but not the index of
# an AssocArray.

# TODO: instead of int_coerce, return either
# - lvalue.Indexed
# - lvalue.Keyed

int_coerce = not mem.IsAssocArray(node.name, lookup_mode)
index = arith_ev.Eval(node.index, int_coerce=int_coerce)

lval = lvalue.LhsIndexedName(node.name, index)
lval = lvalue.Indexed(node.name, index)
lval.spids.append(node.spids[0]) # copy left-most token over
return lval

Expand All @@ -157,7 +162,7 @@ def _EvalLhsArith(node, mem, arith_ev):
assert isinstance(node, lhs_expr_t), node

if node.tag == lhs_expr_e.LhsName: # (( i = 42 ))
lval = lvalue.LhsName(node.name)
lval = lvalue.Named(node.name)
# TODO: location info. Use the = token?
#lval.spids.append(spid)
return lval
Expand All @@ -168,7 +173,7 @@ def _EvalLhsArith(node, mem, arith_ev):
int_coerce = not mem.IsAssocArray(node.name, scope_e.Dynamic)
index = arith_ev.Eval(node.index, int_coerce=int_coerce)

lval = lvalue.LhsIndexedName(node.name, index)
lval = lvalue.Indexed(node.name, index)
# TODO: location info. Use the = token?
#lval.spids.append(node.spids[0])
return lval
Expand Down Expand Up @@ -196,7 +201,7 @@ def EvalLhsAndLookup(node, arith_ev, mem, exec_opts,
# Problem: It can't be an array?
# a=(1 2)
# (( a++ ))
lval = lvalue.LhsName(node.name)
lval = lvalue.Named(node.name)
val = _LookupVar(node.name, mem, exec_opts)

elif node.tag == lhs_expr_e.LhsIndexedName: # a[1] = b
Expand All @@ -207,7 +212,7 @@ def EvalLhsAndLookup(node, arith_ev, mem, exec_opts,

int_coerce = not mem.IsAssocArray(node.name, lookup_mode)
index = arith_ev.Eval(node.index, int_coerce=int_coerce)
lval = lvalue.LhsIndexedName(node.name, index)
lval = lvalue.Indexed(node.name, index)

val = mem.GetVar(node.name)

Expand Down Expand Up @@ -236,7 +241,7 @@ def EvalLhsAndLookup(node, arith_ev, mem, exec_opts,

elif val.tag == value_e.AssocArray: # declare -A a; a['x']+=1
index = arith_ev.Eval(node.index, int_coerce=False)
lval = lvalue.LhsIndexedName(node.name, index)
lval = lvalue.Indexed(node.name, index)

s = val.d.get(index)
if s is None:
Expand Down
6 changes: 3 additions & 3 deletions osh/runtime.asdl
Expand Up @@ -51,9 +51,9 @@ module runtime

-- For assignment, evaluated to osh_ast.lhs_expr
lvalue =
LhsName(string name)
| LhsIndexedName(string name, int index)
| LhsStringKey(string name, string key)
Named(string name)
| Indexed(string name, int index)
| Keyed(string name, string key)

-- evaluated version of syntax.redir
redirect =
Expand Down
8 changes: 4 additions & 4 deletions osh/state.py
Expand Up @@ -891,7 +891,7 @@ def SetVar(self, lval, val, new_flags, lookup_mode):

assert new_flags is not None

if lval.tag == lvalue_e.LhsName:
if lval.tag == lvalue_e.Named:
#if lval.name == 'ldflags':
# TODO: Turn this into a tracing feature. Like osh --tracevar ldflags
# --tracevar foo. Has to respect environment variables too.
Expand Down Expand Up @@ -938,7 +938,7 @@ def SetVar(self, lval, val, new_flags, lookup_mode):
cell.exported):
e_die("Can't export array") # TODO: error context

elif lval.tag == lvalue_e.LhsIndexedName:
elif lval.tag == lvalue_e.Indexed:
# TODO: All paths should have this? We can get here by a[x]=1 or
# (( a[ x ] = 1 )). Maybe we should make them different?
if lval.spids:
Expand Down Expand Up @@ -1124,7 +1124,7 @@ def Unset(self, lval, lookup_mode):
ok is false if the cell is read-only.
found is false if the name is not there.
"""
if lval.tag == lvalue_e.LhsName: # unset x
if lval.tag == lvalue_e.Named: # unset x
cell, namespace = self._FindCellAndNamespace(lval.name, lookup_mode)
if cell:
found = True
Expand All @@ -1135,7 +1135,7 @@ def Unset(self, lval, lookup_mode):
else:
return True, False

elif lval.tag == lvalue_e.LhsIndexedName: # unset a[1]
elif lval.tag == lvalue_e.Indexed: # unset a[1]
raise NotImplementedError

else:
Expand Down
52 changes: 26 additions & 26 deletions osh/state_test.py
Expand Up @@ -41,7 +41,7 @@ def testSearchPath(self):
self.assertEqual(None, search_path.Lookup('grep'))

# Set $PATH
mem.SetVar(lvalue.LhsName('PATH'), value.Str('/bin:/usr/bin'),
mem.SetVar(lvalue.Named('PATH'), value.Str('/bin:/usr/bin'),
(), scope_e.GlobalOnly)

self.assertEqual(None, search_path.Lookup('__nonexistent__'))
Expand All @@ -55,7 +55,7 @@ def testPushTemp(self):

# x=1
mem.SetVar(
lvalue.LhsName('x'), value.Str('1'), (), scope_e.Dynamic)
lvalue.Named('x'), value.Str('1'), (), scope_e.Dynamic)
self.assertEqual('1', mem.var_stack[-1]['x'].val.s)

mem.PushTemp()
Expand All @@ -64,11 +64,11 @@ def testPushTemp(self):

# x=temp E=3 read x <<< 'line'
mem.SetVar(
lvalue.LhsName('x'), value.Str('temp'), (), scope_e.LocalOnly)
lvalue.Named('x'), value.Str('temp'), (), scope_e.LocalOnly)
mem.SetVar(
lvalue.LhsName('E'), value.Str('3'), (), scope_e.LocalOnly)
lvalue.Named('E'), value.Str('3'), (), scope_e.LocalOnly)
mem.SetVar(
lvalue.LhsName('x'), value.Str('line'), (), scope_e.LocalOnly)
lvalue.Named('x'), value.Str('line'), (), scope_e.LocalOnly)

self.assertEqual('3', mem.var_stack[-1]['E'].val.s)
self.assertEqual('line', mem.var_stack[-1]['x'].val.s)
Expand All @@ -87,7 +87,7 @@ def testSetVarClearFlag(self):

# local x=y
mem.SetVar(
lvalue.LhsName('x'), value.Str('y'), (), scope_e.LocalOnly)
lvalue.Named('x'), value.Str('y'), (), scope_e.LocalOnly)
self.assertEqual('y', mem.var_stack[-1]['x'].val.s)

# New frame
Expand All @@ -96,19 +96,19 @@ def testSetVarClearFlag(self):

# x=y -- test out dynamic scope
mem.SetVar(
lvalue.LhsName('x'), value.Str('YYY'), (), scope_e.Dynamic)
lvalue.Named('x'), value.Str('YYY'), (), scope_e.Dynamic)
self.assertEqual('YYY', mem.var_stack[-2]['x'].val.s)
self.assertEqual(None, mem.var_stack[-1].get('x'))

# myglobal=g
mem.SetVar(
lvalue.LhsName('myglobal'), value.Str('g'), (), scope_e.Dynamic)
lvalue.Named('myglobal'), value.Str('g'), (), scope_e.Dynamic)
self.assertEqual('g', mem.var_stack[0]['myglobal'].val.s)
self.assertEqual(False, mem.var_stack[0]['myglobal'].exported)

# 'export PYTHONPATH=/'
mem.SetVar(
lvalue.LhsName('PYTHONPATH'), value.Str('/'),
lvalue.Named('PYTHONPATH'), value.Str('/'),
(var_flags_e.Exported,), scope_e.Dynamic)
self.assertEqual('/', mem.var_stack[0]['PYTHONPATH'].val.s)
self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)
Expand All @@ -117,49 +117,49 @@ def testSetVarClearFlag(self):
self.assertEqual('/', ex['PYTHONPATH'])

mem.SetVar(
lvalue.LhsName('PYTHONPATH'), None, (var_flags_e.Exported,),
lvalue.Named('PYTHONPATH'), None, (var_flags_e.Exported,),
scope_e.Dynamic)
self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)

# 'export myglobal'. None means don't touch it. Undef would be confusing
# because it might mean "unset", but we have a separated API for that.
mem.SetVar(
lvalue.LhsName('myglobal'), None, (var_flags_e.Exported,),
lvalue.Named('myglobal'), None, (var_flags_e.Exported,),
scope_e.Dynamic)
self.assertEqual(True, mem.var_stack[0]['myglobal'].exported)

# export g2 -- define and export empty
mem.SetVar(
lvalue.LhsName('g2'), None, (var_flags_e.Exported,),
lvalue.Named('g2'), None, (var_flags_e.Exported,),
scope_e.Dynamic)
self.assertEqual(value_e.Undef, mem.var_stack[0]['g2'].val.tag)
self.assertEqual(True, mem.var_stack[0]['g2'].exported)

# readonly myglobal
self.assertEqual(False, mem.var_stack[0]['myglobal'].readonly)
mem.SetVar(
lvalue.LhsName('myglobal'), None, (var_flags_e.ReadOnly,),
lvalue.Named('myglobal'), None, (var_flags_e.ReadOnly,),
scope_e.Dynamic)
self.assertEqual(True, mem.var_stack[0]['myglobal'].readonly)

mem.SetVar(
lvalue.LhsName('PYTHONPATH'), value.Str('/lib'), (),
lvalue.Named('PYTHONPATH'), value.Str('/lib'), (),
scope_e.Dynamic)
self.assertEqual('/lib', mem.var_stack[0]['PYTHONPATH'].val.s)
self.assertEqual(True, mem.var_stack[0]['PYTHONPATH'].exported)

# COMPREPLY=(1 2 3)
# invariant to enforce: arrays can't be exported
mem.SetVar(
lvalue.LhsName('COMPREPLY'), value.StrArray(['1', '2', '3']),
lvalue.Named('COMPREPLY'), value.StrArray(['1', '2', '3']),
(), scope_e.GlobalOnly)
self.assertEqual(
['1', '2', '3'], mem.var_stack[0]['COMPREPLY'].val.strs)

# export COMPREPLY
try:
mem.SetVar(
lvalue.LhsName('COMPREPLY'), None, (var_flags_e.Exported,),
lvalue.Named('COMPREPLY'), None, (var_flags_e.Exported,),
scope_e.Dynamic)
except util.FatalRuntimeError as e:
pass
Expand All @@ -168,7 +168,7 @@ def testSetVarClearFlag(self):

# readonly r=1
mem.SetVar(
lvalue.LhsName('r'), value.Str('1'), (var_flags_e.ReadOnly,),
lvalue.Named('r'), value.Str('1'), (var_flags_e.ReadOnly,),
scope_e.Dynamic)
self.assertEqual('1', mem.var_stack[0]['r'].val.s)
self.assertEqual(False, mem.var_stack[0]['r'].exported)
Expand All @@ -178,15 +178,15 @@ def testSetVarClearFlag(self):
# r=newvalue
try:
mem.SetVar(
lvalue.LhsName('r'), value.Str('newvalue'), (), scope_e.Dynamic)
lvalue.Named('r'), value.Str('newvalue'), (), scope_e.Dynamic)
except util.FatalRuntimeError as e:
pass
else:
self.fail("Expected failure")

# readonly r2 -- define empty readonly
mem.SetVar(
lvalue.LhsName('r2'), None, (var_flags_e.ReadOnly,),
lvalue.Named('r2'), None, (var_flags_e.ReadOnly,),
scope_e.Dynamic)
self.assertEqual(value_e.Undef, mem.var_stack[0]['r2'].val.tag)
self.assertEqual(True, mem.var_stack[0]['r2'].readonly)
Expand All @@ -197,7 +197,7 @@ def testSetVarClearFlag(self):
mem.ClearFlag('PYTHONPATH', var_flags_e.Exported, scope_e.Dynamic)
self.assertEqual(False, mem.var_stack[0]['PYTHONPATH'].exported)

lhs = lvalue.LhsIndexedName('a', 1)
lhs = lvalue.Indexed('a', 1)
lhs.spids.append(0)
# a[1]=2
mem.SetVar(lhs, value.Str('2'), (), scope_e.Dynamic)
Expand All @@ -217,7 +217,7 @@ def testSetVarClearFlag(self):

# readonly a
mem.SetVar(
lvalue.LhsName('a'), None, (var_flags_e.ReadOnly,),
lvalue.Named('a'), None, (var_flags_e.ReadOnly,),
scope_e.Dynamic)
self.assertEqual(True, mem.var_stack[0]['a'].readonly)

Expand All @@ -234,7 +234,7 @@ def testGetVar(self):

# readonly a=x
mem.SetVar(
lvalue.LhsName('a'), value.Str('x'), (var_flags_e.ReadOnly,),
lvalue.Named('a'), value.Str('x'), (var_flags_e.ReadOnly,),
scope_e.Dynamic)

val = mem.GetVar('a', scope_e.Dynamic)
Expand All @@ -249,25 +249,25 @@ def testExportThenAssign(self):

# export U
mem.SetVar(
lvalue.LhsName('U'), None, (var_flags_e.Exported,), scope_e.Dynamic)
lvalue.Named('U'), None, (var_flags_e.Exported,), scope_e.Dynamic)
print(mem)

# U=u
mem.SetVar(
lvalue.LhsName('U'), value.Str('u'), (), scope_e.Dynamic)
lvalue.Named('U'), value.Str('u'), (), scope_e.Dynamic)
print(mem)
e = mem.GetExported()
self.assertEqual('u', e['U'])

def testUnset(self):
mem = _InitMem()
# unset a
mem.Unset(lvalue.LhsName('a'), scope_e.Dynamic)
mem.Unset(lvalue.Named('a'), scope_e.Dynamic)

return # not implemented yet

# unset a[1]
mem.Unset(lvalue.LhsIndexedName('a', 1), scope_e.Dynamic)
mem.Unset(lvalue.Indexed('a', 1), scope_e.Dynamic)

def testArgv(self):
mem = _InitMem()
Expand Down

0 comments on commit df97cc3

Please sign in to comment.