Skip to content

Commit

Permalink
[ysh/builtins] Implement write --json -- $s
Browse files Browse the repository at this point in the history
This is a JSON *string* encoding, which is lossy.

In contrast, write --j8 is not lossy.

TODO: we haven't done the "maybe" part.  This is "J8 Lines".

doc and flag_def: specify that read also has --json and --j8.  Not
implemented yet.
  • Loading branch information
Andy C committed Jan 28, 2024
1 parent 1535631 commit 67c116e
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 35 deletions.
18 changes: 11 additions & 7 deletions builtin/io_ysh.py
Expand Up @@ -170,12 +170,12 @@ def Run(self, cmd_val):

class Write(_Builtin):
"""
write -- @strs
write --sep ' ' --end '' -- @strs
write -n -- @
write --qsn -- @strs # argv serialization
write --qsn --sep $'\t' -- @strs # this is like QTSV
"""
write -- @strs
write --sep ' ' --end '' -- @strs
write -n -- @
write --j8 -- @strs # argv serialization
write --j8 --sep $'\t' -- @strs # this is like TSV8
"""

def __init__(self, mem, errfmt):
# type: (state.Mem, ErrorFormatter) -> None
Expand Down Expand Up @@ -204,9 +204,13 @@ def Run(self, cmd_val):
self.stdout_.write(arg.sep)
s = arg_r.Peek()

if arg.j8:
if arg.json:
s = self.j8print.MaybeEncodeJsonString(s)

elif arg.j8:
s = self.j8print.MaybeEncodeString(s)

# TODO: remove this
elif arg.qsn:
s = qsn.maybe_encode(s, bit8_display)

Expand Down
20 changes: 11 additions & 9 deletions data_lang/j8.py
Expand Up @@ -135,20 +135,22 @@ def PrintLine(self, val, f):

def MaybeEncodeString(self, s):
# type: (str) -> str
""" For write --j8 $s and compexport
Do we also have write --json or --json-string? That requires handling
error.Encode()
Do we also want write (x) to use J8 notation? It's the default
serialization. But j8 write (x) is simple enough.
"""
# There should be an option to not quote "plain words" like
""" For write --j8 $s and compexport """
# TODO: There should be an option to not quote "plain words" like
# /usr/local/foo-bar/x.y/a_b
buf = mylib.BufWriter()
self._Print(value.Str(s), buf, -1)
return buf.getvalue()

def MaybeEncodeJsonString(self, s):
# type: (str) -> str
""" For write --json """
# TODO: There should be an option to not quote "plain words" like
# /usr/local/foo-bar/x.y/a_b
buf = mylib.BufWriter()
self._Print(value.Str(s), buf, -1, options=LOSSY_JSON)
return buf.getvalue()


class InstancePrinter(object):
"""Print a value tree as J8/JSON."""
Expand Down
20 changes: 13 additions & 7 deletions doc/ref/chap-builtin-cmd.md
Expand Up @@ -265,16 +265,19 @@ YSH adds buffered, line-oriented I/O to shell's `read`.
read --line (&x) # fills $x (&x is a place)

read --line --with-eol # keep the \n
read --line --qsn # decode QSN too
read --line --json # decode JSON string
read --line --j8 # decode J8 string

read --all # whole file including newline, in $_reply
read --all (&x) # fills $x

read -0 # read until NUL, synonym for read -r -d ''

When --qsn is passed, the line is check for an opening single quote. If so,
it's decoded as QSN. The line must have a closing single quote, and there
can't be any non-whitespace characters after it.
When --json is passed, the line is checked for an opening `"`. If present,
it's decoded as a JSON string.

When --j8 is passed, the line is checked for an opening `"` or `'` or `u'` or
`b'`. If present, it's decoded as a J8 string.

<!--
TODO: read --netstr
Expand All @@ -289,9 +292,12 @@ newline.

Examples:

write -- ale bean # write two lines
write --qsn -- ale bean # QSN encode, guarantees two lines
write -n -- ale bean # synonym for --end '', like echo -n
write -- ale bean # write two lines

write --json -- ale bean # JSON encode, guarantees two lines
write --j8 -- ale bean # J8 encode, guarantees two lines

write -n -- ale bean # synonym for --end '', like echo -n
write --sep '' --end '' -- a b # write 2 bytes
write --sep $'\t' --end $'\n' -- a b # TSV line

Expand Down
4 changes: 2 additions & 2 deletions doc/ref/toc-ysh.md
Expand Up @@ -125,9 +125,9 @@ Siblings: [OSH Topics](toc-osh.html), [Data Topics](toc-data.html)
module guard against duplicate 'source'
is-main false when sourcing a file
use change first word lookup
[I/O] ysh-read Buffered I/O with --line, --all, --qsn
[I/O] ysh-read Buffered I/O with --line, --all, --j8
ysh-echo no -e -n with simple_echo
write Like echo, with --, --sep, --end, ()
write Like echo, with --, --sep, --end
fork forkwait Replace & and (), and takes a block
fopen Open multiple streams, takes a block
X dbg Only thing that can be used in funcs
Expand Down
16 changes: 15 additions & 1 deletion frontend/flag_def.py
Expand Up @@ -118,6 +118,16 @@
READ_SPEC.LongFlag('--line')
# don't strip the trailing newline
READ_SPEC.LongFlag('--with-eol')
READ_SPEC.LongFlag('--json',
args.Bool,
default=False,
help='Read elements as JSON strings')
READ_SPEC.LongFlag('--j8',
args.Bool,
default=False,
help='Read elements as J8 strings')

# TODO: remove
# Decode QSN after reading a line. Note: A QSN string can't have literal
# newlines or tabs; they must be escaped.
READ_SPEC.ShortFlag('-q', long_name='--qsn')
Expand Down Expand Up @@ -445,10 +455,14 @@ def _DefineCompletionActions(spec):
WRITE_SPEC.ShortFlag('-n',
args.Bool,
help="Omit newline (synonym for -end '')")
WRITE_SPEC.LongFlag('--json',
args.Bool,
default=False,
help='Write elements as JSON strings(lossy)')
WRITE_SPEC.LongFlag('--j8',
args.Bool,
default=False,
help='Write elements with J8 notation, one per line')
help='Write elements as J8 strings')
# TODO: --jlines for conditional j"" prefix? Like maybe_shell_encode()

# TODO: remove this
Expand Down
29 changes: 20 additions & 9 deletions spec/ysh-builtins.test.sh
@@ -1,5 +1,4 @@

## oils_failures_allowed: 6
## oils_failures_allowed: 5

#### append onto BashArray a=(1 2)
shopt -s parse_at
Expand Down Expand Up @@ -99,12 +98,26 @@ __
'one\ttwo\n'
## END

#### write --json
shopt --set ysh:upgrade

write --json u'\u{3bc}'
write --json b'\yfe\yff'

## STDOUT:
"μ"
"��"
## END

#### write --j8
shopt --set ysh:upgrade

write --j8 j"\u{3bc}"
write --j8 u'\u{3bc}'
write --j8 b'\yfe\yff'

## STDOUT:
'μ'
"μ"
b'\yfe\yff'
## END

#### write --j8 --unicode
Expand Down Expand Up @@ -232,13 +245,11 @@ write --qsn -- "$_reply"
'$'
## END

#### read --line --qsn accepts optional $''
#### read --line --j8

# PROBLEM: is it limited to $' ? What about $3.99 ?
# I think you just check for those 2 chars

echo $'$\'foo\'' | read --line --qsn
echo $'u\'foo\'' | read --line --j8
write -- "$_reply"

## STDOUT:
foo
## END
Expand Down

0 comments on commit 67c116e

Please sign in to comment.