Permalink
Browse files

Implement $FUNCNAME (bash-only, used by Nix.)

Also make note of a var-ref issue where $? is not dynamic.

Addresses issue #26.
  • Loading branch information...
Andy Chu
Andy Chu committed Aug 11, 2017
1 parent 439a44a commit 0b86961e74a41e2c2cc7d33dcc84438b968033ce
Showing with 47 additions and 19 deletions.
  1. +2 −2 core/cmd_exec.py
  2. +1 −0 core/completion_test.py
  3. +21 −2 core/state.py
  4. +7 −7 core/state_test.py
  5. +4 −7 spec/introspect.test.sh
  6. +11 −0 spec/var-ref.test.sh
  7. +1 −1 test/spec.sh
View
@@ -983,7 +983,7 @@ def RunFunc(self, func_node, argv):
if not self.fd_state.Push(def_redirects, self.waiter):
return 1 # error
self.mem.Push(argv[1:])
self.mem.PushCall(func_node.name, argv[1:])
# Redirects still valid for functions.
# Here doc causes a pipe and Process(SubProgramThunk).
@@ -996,7 +996,7 @@ def RunFunc(self, func_node, argv):
# break/continue used in the wrong place
e_die('Unexpected %r (in function call)', e.token.val, token=e.token)
finally:
self.mem.Pop()
self.mem.PopCall()
self.fd_state.Pop()
return status
View
@@ -83,6 +83,7 @@ def testShellFuncExecution(self):
pairs = [ast.assign_pair(ast.LhsName('COMPREPLY'), assign_op.Equal, w)]
body_node = ast.Assignment(Id.Assign_None, [], pairs)
func_node.name = 'myfunc'
func_node.body = body_node
a = completion.ShellFuncAction(ex, func_node)
View
@@ -166,6 +166,10 @@ def __init__(self, argv0, argv, environ):
self.var_stack = [top]
self.argv0 = argv0
self.argv_stack = [_ArgFrame(argv)]
# NOTE: could use deque and appendleft/popleft, but
# 1. ASDL type checking of StrArray doesn't allow it (could be fixed)
# 2. We don't otherwise depend on the collections module
self.func_name_stack = []
self.last_status = 0 # Mutable public variable
self.last_job_id = -1 # Uninitialized value mutable public variable
@@ -205,12 +209,17 @@ def _InitEnviron(self, environ):
# Stack
#
def Push(self, argv):
def PushCall(self, func_name, argv):
"""For function calls."""
# bash uses this order: top of stack first.
self.func_name_stack.append(func_name)
self.var_stack.append({})
self.argv_stack.append(_ArgFrame(argv))
def Pop(self):
def PopCall(self):
self.func_name_stack.pop()
self.var_stack.pop()
self.argv_stack.pop()
@@ -425,6 +434,16 @@ def SetVar(self, lval, value, new_flags, lookup_mode):
# NOTE: Have a default for convenience
def GetVar(self, name, lookup_mode=scope.Dynamic):
assert isinstance(name, str), name
# Do lookup of system globals before looking at user variables. Note: we
# could optimize this at compile-time like $?. That would break
# ${!varref}, but it's already broken for $?.
if name == 'FUNCNAME':
# bash wants it in reverse order. This is a little inefficient but we're
# not depending on deque().
strs = list(reversed(self.func_name_stack))
return runtime.StrArray(strs)
cell, _ = self._FindCellAndNamespace(name, lookup_mode)
if cell:
View
@@ -18,16 +18,16 @@ class MemTest(unittest.TestCase):
def testGet(self):
mem = state.Mem('', [], {})
mem.Push(['a', 'b'])
mem.PushCall('my-func', ['a', 'b'])
print(mem.GetVar('HOME'))
mem.Pop()
mem.PopCall()
print(mem.GetVar('NONEXISTENT'))
def testSetVarClearFlag(self):
mem = state.Mem('', [], {})
print(mem)
mem.Push(['ONE'])
mem.PushCall('my-func', ['ONE'])
self.assertEqual(2, len(mem.var_stack)) # internal details
# local x=y
@@ -36,7 +36,7 @@ def testSetVarClearFlag(self):
self.assertEqual('y', mem.var_stack[-1]['x'].val.s)
# New frame
mem.Push(['TWO'])
mem.PushCall('my-func', ['TWO'])
self.assertEqual(3, len(mem.var_stack)) # internal details
# x=y -- test out dynamic scope
@@ -220,10 +220,10 @@ def testUnset(self):
def testArgv(self):
mem = state.Mem('', [], {})
mem.Push(['a', 'b'])
mem.PushCall('my-func', ['a', 'b'])
self.assertEqual(['a', 'b'], mem.GetArgv())
mem.Push(['x', 'y'])
mem.PushCall('my-func', ['x', 'y'])
self.assertEqual(['x', 'y'], mem.GetArgv())
status = mem.Shift(1)
@@ -238,7 +238,7 @@ def testArgv(self):
self.assertEqual([], mem.GetArgv())
self.assertEqual(1, status) # error
mem.Pop()
mem.PopCall()
self.assertEqual(['a', 'b'], mem.GetArgv())
def testArgv2(self):
View
@@ -5,15 +5,12 @@
### ${FUNCNAME[@]} array
f() {
echo "begin: ${FUNCNAME[@]}"
argv.py "${FUNCNAME[@]}"
g
echo "end: ${FUNCNAME[@]}"
argv.py "${FUNCNAME[@]}"
}
g() {
echo "func: ${FUNCNAME[@]}"
argv.py "${FUNCNAME[@]}"
}
f
# stdout-json: "begin: f\nfunc: g f\nend: f\n"
# N-I mksh stdout-json: "begin: \nfunc: \nend: \n"
# N-I dash stdout-json: ""
# N-I dash status: 2
# stdout-json: "['f']\n['g', 'f']\n['f']\n"
View
@@ -13,6 +13,16 @@ echo ref ${!a} ${a}
# BUG mksh stdout: ref a b
# N-I dash/zsh stdout-json: ""
### var ref with special vars
myfunc() {
local ref=$1
echo ${!ref}
}
myfunc FUNCNAME
myfunc '?' # osh doesn't do this dynamically
# stdout-json: "myfunc\n0\n"
# N-I mksh stdout-json: "ref\nref\n"
### declare -n and ${!a}
declare -n a
a=b
@@ -66,3 +76,4 @@ caller
# BUG mksh stdout-json: ""
# BUG mksh status: 1
View
@@ -457,7 +457,7 @@ extended-glob() {
# ${!var} syntax -- oil should replace this with associative arrays.
var-ref() {
sh-spec spec/var-ref.test.sh --osh-failures-allowed 4 \
sh-spec spec/var-ref.test.sh --osh-failures-allowed 5 \
$BASH $MKSH $OSH "$@"
}

0 comments on commit 0b86961

Please sign in to comment.