Skip to content

Commit

Permalink
[doc] Document (( vs ( ( parsing issue
Browse files Browse the repository at this point in the history
And clarify $(( vs $( ( as well
  • Loading branch information
Andy C committed Oct 16, 2024
1 parent cd41783 commit ed2e2ac
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 10 deletions.
44 changes: 40 additions & 4 deletions doc/known-differences.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,12 +88,14 @@ Undecidable](https://www.oilshell.org/blog/2016/10/20.html) (2016).
- [OILS-ERR-101](error-catalog.html#oils-err-101) explains more ways to fix
this.

### Subshell in command sub
### Subshell in command sub - `$((` versus `$( (`

You can have a subshell in a command sub, but it usually doesn't make sense.
You can have a subshell `(` in a command sub `$(`, but it usually doesn't make
sense.

In OSH you need a space after `$(`. The characters `$((` always start an
arith sub.
In OSH you need a space after `$(`, so it would be `$( (`.

characters `$((` always start an arith sub.

No:

Expand All @@ -105,6 +107,40 @@ Yes:
$({ cd / && ls; }) # Use {} for grouping, not (). Note trailing ;
$(cd / && ls) # Even better

### Nested Subshells - `((` versus `( (`

You should never need nested subshells with `((` in Bourne shell or Oils.

If you do, you should add a space with `( (` instead of `((`, similar to the
issue above.

In OSH, `((` always starts bash-style arithmetic.

---

The only place I see `((` arise is when shell users try to use `( )` to mean
**grouping**, because they are used to C or Python.

But it means **subshell**, not grouping. In shell, `{ }` is the way to group
commands.

No:

if ((test -f a || test -f b) && grep foo c); then
echo ok
fi

Allowed, but not what you want:

if ( (test -f a || test -f b) && grep foo c); then
echo ok
fi

Yes:

if { test -f a || test -f b; } && grep foo c; then
echo ok
fi

### Extended glob vs. Negation of boolean expression

Expand Down
69 changes: 63 additions & 6 deletions stdlib/ysh/stream.ysh
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,30 @@ proc test-slurp-by {
seq 8 | slurp-by (3)
}

# Note:
# - these are all the same algorithm
# - also word, block, etc. are all optional
### Awk

# Naming
#
# TEXT INPUT
# each-word # this doesn't go by lines, it does a global regex split or something?
#
# LINE INPUT
# each-line --j8 { echo "-- $_line" } # similar to @()
# each-line --j8 (^"-- $_line") # is this superfluous?
#
# each-split name1 name2
# (delim=' ')
# (ifs=' ')
# (pat=/d+/)
# # also assign names for each part?
#
# each-match # regex match
# must-match # assert that every line matches
#
# TABLE INPUT
# each-row # TSV and TSV8 input?
#
# They all take templates or blocks?

proc each-line (...words; template=null; ; block=null) {
# TODO:
Expand Down Expand Up @@ -112,7 +133,9 @@ proc must-split-by (; ; ifs=null; block) {
echo TODO
}

proc if-match (; pattern; ; block) {
# Naming: each-match, each-split?

proc if-match (; pattern, template=null; ; block=null) {
### like 'grep' but with submatches

for line in (io.stdin) {
Expand All @@ -122,7 +145,14 @@ proc if-match (; pattern; ; block) {
#var groups = m.groups()

# Should we also pass _line?
call io->eval(block, dollar0=m.group(0))

if (block) {
call io->eval(block, dollar0=m.group(0))
} elif (template) {
echo TEMPLATE
} else {
echo TSV
}
}
}

Expand All @@ -144,6 +174,8 @@ proc line-data {
/// 42 bar'''
}

const pat = /<capture d+> s+ <capture w+>/

proc test-if-match {
var z = 'z' # test out scoping
var count = 0 # test out mutation
Expand All @@ -153,7 +185,6 @@ proc test-if-match {
# sed: print a new line based on submatches
# awk: re-arrange the cols, and also accumulate counters

var pat = /<capture d+> s+ <capture w+>/
line-data | if-match (pat) {
echo "$z $0 $z"
# TODO: need pos_args
Expand All @@ -165,6 +196,32 @@ proc test-if-match {
echo "count = $count"
}

proc test-if-match-2 {
# If there's no block or template, it should print out a TSV with:
#
# $0 ...
# $1 $2
# $_line maybe?

#line-data | if-match (pat)

var z = 'z' # scoping
line-data | if-match (pat, ^"$z $0 $z")
line-data | if-match (pat, ^"-- $0 --")
}

# might be a nice way to write it, not sure if byo.sh can discover it
if false {
tests 'if-match' {
proc case-block {
echo TODO
}
proc case-template {
echo TODO
}
}
}

# Protocol:
#
# - The file lists its tests the "actions"
Expand Down

0 comments on commit ed2e2ac

Please sign in to comment.