Permalink
Browse files

Statically parse local -a A=(1 2) and declare -a A=(1 2).

Note that we accept flags but don't do anything with them yet.  The
variable is known to be an array from the RHS, not LHS.

We might have to parse these dynamically later, as with 'export', but so
far all usages are static.

Addresses issue #26.
  • Loading branch information...
Andy Chu
Andy Chu committed Aug 8, 2017
1 parent 981001a commit 4b5d75de9598f0705769af6f4a66b96bbbf66431
Showing with 59 additions and 15 deletions.
  1. +6 −0 core/cmd_exec.py
  2. +29 −13 osh/cmd_parse.py
  3. +1 −2 osh/osh.asdl
  4. +23 −0 spec/assign.test.sh
View
@@ -649,6 +649,12 @@ def _Dispatch(self, node, fork_external):
if node.keyword == Id.Assign_Local:
lookup_mode = scope.LocalOnly
flags = ()
elif node.keyword == Id.Assign_Declare:
# declare is like local, except it can also be used outside functions?
lookup_mode = scope.LocalOnly
# TODO: Respect flags. -r and -x matter, but -a and -A might be
# implicit in the RHS?
flags = ()
elif node.keyword == Id.Assign_Readonly:
lookup_mode = scope.Dynamic
flags = (var_flags.ReadOnly,)
View
@@ -439,13 +439,28 @@ def _MakeSimpleCommand(self, prefix_bindings, suffix_words, redirects):
return node
def _MakeAssignment(self, assign_kw, suffix_words):
flags = []
bindings = []
for i, w in enumerate(suffix_words):
if i == 0:
continue # skip over local, export, etc.
left_spid = word.LeftMostSpanForWord(w)
# First parse flags, e.g. -r -x -a -A. None of the flags have arguments.
n = len(suffix_words)
i = 1
while i < n:
w = suffix_words[i]
ok, static_val, quoted = word.StaticEval(w)
if not ok or quoted:
break # can't statically evaluate
if static_val.startswith('-'):
flags.append(static_val)
else:
break # not a flag, rest are args
i += 1
# Now parse bindings or variable names
while i < n:
w = suffix_words[i]
left_spid = word.LeftMostSpanForWord(w)
kv = word.LooksLikeAssignment(w)
if kv:
k, v = kv
@@ -462,19 +477,20 @@ def _MakeAssignment(self, assign_kw, suffix_words):
# Parse this differently then?
# dynamic-export?
# It sets global variables.
ok, value, quoted = word.StaticEval(w)
ok, static_val, quoted = word.StaticEval(w)
if not ok or quoted:
self.AddErrorContext(
'Variable names must be constant strings, got %s', w, word=w)
return None
pair = (value, None, left_spid) # No value is equivalent to ''
m = VAR_NAME_RE.match(value)
self.AddErrorContext(
'Variable names must be constant strings, got %s', w, word=w)
return None
pair = (static_val, None, left_spid) # No value is equivalent to ''
m = VAR_NAME_RE.match(static_val)
if not m:
self.AddErrorContext('Invalid variable name %r', value, word=w)
self.AddErrorContext('Invalid variable name %r', static_val, word=w)
return None
bindings.append(pair)
i += 1
# TODO: Also make with LhsIndexedName
pairs = []
@@ -483,7 +499,7 @@ def _MakeAssignment(self, assign_kw, suffix_words):
p.spids.append(spid)
pairs.append(p)
node = ast.Assignment(assign_kw, pairs)
node = ast.Assignment(assign_kw, flags, pairs)
return node
View
@@ -158,8 +158,7 @@ module osh
-- TODO: respect order
| SimpleCommand(word* words, redir* redirects, env_pair* more_env)
| Sentence(command child, token terminator)
-- TODO: parse flags -r -x; -a and -A aren't needed
| Assignment(id keyword, assign_pair* pairs)
| Assignment(id keyword, string* flags, assign_pair* pairs)
| ControlFlow(token token, word? arg_word)
| Pipeline(command* children, bool negated, int* stderr_indices)
-- TODO: Should be left and right
View
@@ -68,3 +68,26 @@ echo "v=$v"
# OK bash/dash/mksh stdout: v=None
# OK bash/dash/mksh status: 0
# status: 2
### local -a
# nixpkgs setup.sh uses this (issue #26)
f() {
local -a array=(x y z)
argv "${array[@]}"
}
f
# stdout: ['x', 'y', 'z']
# N-I dash stdout-json: ""
# N-I dash status: 2
# N-I mksh stdout-json: ""
# N-I mksh status: 1
### declare -a
# nixpkgs setup.sh uses this (issue #26)
declare -a array=(x y z)
argv "${array[@]}"
# stdout: ['x', 'y', 'z']
# N-I dash stdout-json: ""
# N-I dash status: 2
# N-I mksh stdout-json: ""
# N-I mksh status: 1

0 comments on commit 4b5d75d

Please sign in to comment.