Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev hoover 3 #2245

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions core/state.py
Original file line number Diff line number Diff line change
Expand Up @@ -898,11 +898,10 @@ def _MakeArgvCell(argv):

class ctx_LoopFrame(object):

def __init__(self, mem, name1):
# type: (Mem, str) -> None
def __init__(self, mem, do_new_frame):
# type: (Mem, bool) -> None
self.mem = mem
self.name1 = name1
self.do_new_frame = name1 == '__hack__'
self.do_new_frame = do_new_frame

if self.do_new_frame:
to_enclose = self.mem.var_stack[-1]
Expand Down
20 changes: 18 additions & 2 deletions demo/survey-closure.sh
Original file line number Diff line number Diff line change
Expand Up @@ -108,9 +108,25 @@ loops() {
console.assert(functions[1]() === 1, "Test 4.2 failed");
console.assert(functions[2]() === 2, "Test 4.3 failed");

console.log(functions[2]())
console.log(functions[1]())
'

# similar to proc p example
echo
echo 'JS define function'
echo

nodejs -e '
for (let i = 0; i < 5; i++) {
// this works, is it like let?
function inner() { return i; }
// let inner = function() { return i; }
}
console.log("INNER");
console.log(inner());
'

echo
echo 'LOOPS PYTHON'
echo

Expand All @@ -137,7 +153,7 @@ for i in range(3):
actual = functions[i]()
assert i == actual, "%d != %d" % (i, actual)

print(functions[2]())
print(functions[1]())
'
}

Expand Down
10 changes: 6 additions & 4 deletions demo/survey-loop.sh
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
#!/usr/bin/env bash
#
# Survey loop behavior
# Survey loop behavior - mutating the container while iterating over it
#
# List - Python and JS are consistent
# Dict - Python and JS are different - Python 3 introduced version number check
# JS and Python 2 may have "unknown" results
#
# Usage:
# demo/survey-str-api.sh <function name>
# demo/survey-loop.sh <function name>

set -o nounset
set -o pipefail
set -o errexit

source build/dev-shell.sh # python3 in $PATH

# Python and JS string and regex replacement APIs

mutate-py() {
echo ---
echo PY
Expand Down
22 changes: 18 additions & 4 deletions doc/ref/chap-cmd-lang.md
Original file line number Diff line number Diff line change
Expand Up @@ -669,17 +669,31 @@ The `io.stdin` object iterates over lines:
}
# lines are buffered, so it's much faster than `while read --raw-line`

---

(This section is based on [A Tour of YSH](../ysh-tour.html).)

#### Closing Over the Loop Variable

Each iteration of a `for` loop creates a new frame, which may be captured.

var x = 42 # outside the loop
for i in (0 ..< 3) {
var j = i + 2

var expr = ^"$x: i = $i, j = $j" # captures x, i, and j

my-task {
echo "$x: i = $i, j = $j" # also captures x, i, and j
}
}

#### Mutating Containers in a `for` Loop

- If you append or remove from a `List` while iterating over it, the loop **will** be affected.
- If you mutate a `Dict` while iterating over it, the loop will **not** be
affected.

---

(This section is based on [A Tour of YSH](../ysh-tour.html).)

### ysh-while

You can use an expression as the condition:
Expand Down
7 changes: 4 additions & 3 deletions doc/ref/chap-option.md
Original file line number Diff line number Diff line change
Expand Up @@ -205,8 +205,9 @@ Details on each option:
xtrace_rich Hierarchical and process tracing
xtrace_details (-u) Disable most tracing with +
dashglob (-u) Disabled to avoid files like -rf
no_exported Environ doesn't correspond to exported (-x) vars

env_obj Init ENV Obj at startup; use it when starting
child processes
for_loop_frames YSH can create closures from loop vars

<h3 id="ysh:all">ysh:all</h3>

Expand All @@ -232,7 +233,7 @@ Details on options that are not in `ysh:upgrade` and `strict:all`:
... source unset printf [un]alias
... getopts
X old_syntax (-u) ( ) ${x%prefix} ${a[@]} $$
env_obj Populate the ENV object
no_exported Environ doesn't correspond to exported (-x) vars
no_init_globals At startup, don't set vars like PWD, SHELLOPTS
simple_echo echo doesn't accept flags -e -n
simple_eval_builtin eval takes exactly 1 argument
Expand Down
3 changes: 3 additions & 0 deletions frontend/option_def.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ def DoneWithImplementedOptions(self):

# create ENV at startup; read from it when starting processes
('env_obj', False),

# Can create closures from loop variables, like JS / C# / Go
('for_loop_frames', False),
]

# TODO: Add strict_arg_parse? For example, 'trap 1 2 3' shouldn't be
Expand Down
3 changes: 2 additions & 1 deletion osh/cmd_eval.py
Original file line number Diff line number Diff line change
Expand Up @@ -1304,7 +1304,8 @@ def _DoForEach(self, node):
status = 0 # in case we loop zero times
with ctx_LoopLevel(self):
while True:
with state.ctx_LoopFrame(self.mem, name1.name):
with state.ctx_LoopFrame(self.mem,
self.exec_opts.for_loop_frames()):
first = it2.FirstValue()
#log('first %s', first)
if first is None: # for StdinIterator
Expand Down
7 changes: 6 additions & 1 deletion spec/loop.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,23 @@ fun() {
for i; do
echo $i
done
echo "finished=$i"
}
fun 1 2 3
## STDOUT:
1
2
3
finished=3
## END

#### empty for loop (has "in")
set -- 1 2 3
for i in ; do
echo $i
done
## stdout-json: ""
## STDOUT:
## END

#### for loop with invalid identifier
# should be compile time error, but runtime error is OK too
Expand All @@ -37,10 +40,12 @@ done
for in in a b c; do
echo $in
done
echo finished=$in
## STDOUT:
a
b
c
finished=c
## END

#### Tilde expansion within for loop
Expand Down
5 changes: 3 additions & 2 deletions spec/ysh-bugs.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,11 @@ shvar FOO=bar {
setvar Q = Q=>replace("hello","world")
}
}
echo $Q
#echo $Q
echo hi

## STDOUT:
world
hi
## END


Expand Down
9 changes: 5 additions & 4 deletions spec/ysh-closures.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ p
## END

#### Expr Closures in a Loop !

# see demo/survey-closure.sh

shopt --set ysh:upgrade

proc task (; tasks, expr) {
Expand All @@ -50,8 +53,7 @@ proc task (; tasks, expr) {
func makeTasks() {
var tasks = []
var x = 'x'
for __hack__ in (0 ..< 3) {
var i = __hack__
for i in (0 ..< 3) {
var j = i + 2
task (tasks, ^"$x: i = $i, j = $j")
}
Expand Down Expand Up @@ -82,8 +84,7 @@ proc task (; tasks; ; b) {
func makeTasks() {
var tasks = []
var x = 'x'
for __hack__ in (0 ..< 3) {
var i = __hack__
for i in (0 ..< 3) {
var j = i + 2
task (tasks) { echo "$x: i = $i, j = $j" }
}
Expand Down
1 change: 1 addition & 0 deletions spec/ysh-options.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ shopt -s command_sub_errexit
shopt -u dashglob
shopt -s env_obj
shopt -s errexit
shopt -s for_loop_frames
shopt -s inherit_errexit
shopt -s nounset
shopt -s nullglob
Expand Down
4 changes: 3 additions & 1 deletion spec/ysh-proc.test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,12 @@ G

shopt --set ysh:upgrade

var p = null
for x in 1 2 {
proc p {
proc inner {
echo 'loop'
}
setvar p = inner
}
p

Expand Down
Loading