From 194673386286de2e9ecf092ff5888427c39a5e0f Mon Sep 17 00:00:00 2001 From: Andy Chu Date: Sat, 13 Apr 2024 18:24:05 -0400 Subject: [PATCH] [ysh breaking] ARGV is a normal List variable It's not an alias for the "argv stack". Why? - There was a bug where mutating ARGV was ignored. You got a new value.List every time. - Removing the argv stack simplifies a pure YSH runtime (see Zulip) - Removing the argv stack removes special semantics from the language. - ARGV is just a list. - We are going to get rid of "$@", so there's no reason to be constrained by its odd semantics. Now these are 2 different "worlds": - shell functions and "$@ - procs and @ARGV --- core/state.py | 26 ++++++++------------ spec/ysh-builtin-module.test.sh | 4 +-- spec/ysh-proc.test.sh | 43 +++++++++++++++++++++------------ spec/ysh-scope.test.sh | 8 +++--- spec/ysh-xtrace.test.sh | 6 ++--- 5 files changed, 46 insertions(+), 41 deletions(-) diff --git a/core/state.py b/core/state.py index 91934c15ff..5e8030fcf0 100644 --- a/core/state.py +++ b/core/state.py @@ -961,15 +961,14 @@ def __init__(self, mem, mutable_opts, proc, argv): frame = NewDict() # type: Dict[str, Cell] assert argv is not None - if True: + if proc.sh_compat: # shell function mem.argv_stack.append(_ArgFrame(argv)) else: - # TODO: procs get argv - # - open: the args - # - closed: always empty - #frame['ARGV'] = _MakeArgvCell(argv) - pass + # procs + # - open: is equivalent to ...ARGV + # - closed: ARGV is empty list + frame['ARGV'] = _MakeArgvCell(argv) mem.var_stack.append(frame) @@ -982,6 +981,7 @@ def __init__(self, mem, mutable_opts, proc, argv): # 'if p' should be allowed. self.mem = mem self.mutable_opts = mutable_opts + self.sh_compat = proc.sh_compat def __enter__(self): # type: () -> None @@ -993,8 +993,8 @@ def __exit__(self, type, value, traceback): self.mem.PopCall() self.mem.var_stack.pop() - # TODO: do this conditionally - self.mem.argv_stack.pop() + if self.sh_compat: + self.mem.argv_stack.pop() class ctx_Temp(object): @@ -1112,7 +1112,7 @@ def __init__(self, dollar0, argv, arena, debug_stack): frame = NewDict() # type: Dict[str, Cell] - #frame['ARGV'] = _MakeArgvCell(argv) + frame['ARGV'] = _MakeArgvCell(argv) self.var_stack = [frame] @@ -1879,14 +1879,8 @@ def GetValue(self, name, which_scopes=scope_e.Shopt): # if name not in COMPUTED_VARS: ... with str_switch(name) as case: - if case('ARGV'): - # TODO: ARGV can be a normal mutable variable in YSH - items = [value.Str(s) - for s in self.GetArgv()] # type: List[value_t] - return value.List(items) - # "Registers" - elif case('_status'): + if case('_status'): return num.ToBig(self.TryStatus()) elif case('_error'): diff --git a/spec/ysh-builtin-module.test.sh b/spec/ysh-builtin-module.test.sh index a4d7a77bca..8ac6c54f9c 100644 --- a/spec/ysh-builtin-module.test.sh +++ b/spec/ysh-builtin-module.test.sh @@ -38,13 +38,13 @@ status=0 ## END #### runproc -shopt --set parse_proc +shopt --set parse_proc parse_at f() { write -- f "$@" } proc p { - write -- p "$@" + write -- p @ARGV } runproc f 1 2 echo status=$? diff --git a/spec/ysh-proc.test.sh b/spec/ysh-proc.test.sh index 92b1235378..57b7a08f5e 100644 --- a/spec/ysh-proc.test.sh +++ b/spec/ysh-proc.test.sh @@ -1,4 +1,4 @@ -## oils_failures_allowed: 1 +## oils_failures_allowed: 0 #### Open proc (any number of args) shopt --set parse_proc @@ -42,7 +42,7 @@ builtin set -- a b c foo x y z ## STDOUT: ARGV x y z -dollar-at x y z +dollar-at a b c ## END #### Closed proc has empty "$@" or ARGV @@ -60,7 +60,7 @@ params x y z -['dollar-at'] +['dollar-at', 'a', 'b', 'c'] ['ARGV'] ## END @@ -157,10 +157,10 @@ p x (:| a b |, {bob: 42}, a = 5) ## END #### Proc name-with-hyphen -shopt --set parse_proc +shopt --set parse_proc parse_at proc name-with-hyphen { - echo "$@" + echo @ARGV } name-with-hyphen x y z ## STDOUT: @@ -369,24 +369,24 @@ argv.py @ARGV set -- 'a b' c argv.py "$@" -argv.py @ARGV +argv.py @ARGV # separate from the argv stack f() { argv.py "$@" - argv.py @ARGV + argv.py @ARGV # separate from the argv stack } f 1 '2 3' ## STDOUT: [] [] ['a b', 'c'] -['a b', 'c'] -['1', '2 3'] +[] ['1', '2 3'] +[] ## END -#### Mutating global and local ARGV +#### Mutating global ARGV $SH -c ' shopt -s ysh:upgrade @@ -396,12 +396,24 @@ argv.py global @ARGV # should not be ignored call ARGV->append("GG") +argv.py global @ARGV +' +## STDOUT: +['global'] +['global', 'GG'] +## END + +#### Mutating local ARGV + +$SH -c ' +shopt -s ysh:upgrade + argv.py global @ARGV proc p { - argv.py local @ARGV + argv.py @ARGV call ARGV->append("LL") - argv.py local @ARGV + argv.py @ARGV } p local @ARGV @@ -412,9 +424,8 @@ argv.py global @ARGV ## STDOUT: ['global', 'a b', 'c'] -['global', 'a b', 'c', 'GG'] -['local', 'a b', 'c', 'GG'] -['local', 'a b', 'c', 'GG', 'LL'] -['global', 'a b', 'c', 'GG', 'LL'] +['local', 'a b', 'c'] +['local', 'a b', 'c', 'LL'] +['global', 'a b', 'c'] ## END diff --git a/spec/ysh-scope.test.sh b/spec/ysh-scope.test.sh index 209c22b9c5..b7acd9f3dc 100644 --- a/spec/ysh-scope.test.sh +++ b/spec/ysh-scope.test.sh @@ -429,12 +429,12 @@ p2 x= ## END #### unset composes when you turn on dynamic scope -shopt -s oil:all +shopt -s ysh:all -proc unset-two { +proc unset-two (v, w) { shopt --set dynamic_scope { - unset $1 - unset $2 + unset $v + unset $w } } diff --git a/spec/ysh-xtrace.test.sh b/spec/ysh-xtrace.test.sh index eb500d439e..6801922ba6 100644 --- a/spec/ysh-xtrace.test.sh +++ b/spec/ysh-xtrace.test.sh @@ -56,15 +56,15 @@ sed --regexp-extended 's/[[:digit:]]{2,}/12345/g' err.txt >&2 ## END #### proc and shell function -shopt --set oil:upgrade +shopt --set ysh:upgrade set -x shfunc() { : $1 } -proc p { - : $1 +proc p (x) { + : $x } shfunc 1