| 1 | from __future__ import print_function
|
| 2 |
|
| 3 | from _devbuild.gen import arg_types
|
| 4 | from _devbuild.gen.runtime_asdl import cmd_value
|
| 5 | from _devbuild.gen.syntax_asdl import loc, loc_t
|
| 6 | from _devbuild.gen.value_asdl import value, LeftName
|
| 7 | from builtin import read_osh
|
| 8 | from core import error
|
| 9 | from core.error import e_usage
|
| 10 | from core import pyos
|
| 11 | from core import state
|
| 12 | from core import vm
|
| 13 | from data_lang import j8
|
| 14 | from frontend import flag_util
|
| 15 | from frontend import args
|
| 16 | from frontend import typed_args
|
| 17 | from mycpp import mops
|
| 18 | from mycpp import mylib
|
| 19 | from mycpp.mylib import log
|
| 20 |
|
| 21 | import posix_ as posix
|
| 22 |
|
| 23 | from typing import TYPE_CHECKING
|
| 24 | if TYPE_CHECKING:
|
| 25 | from core.ui import ErrorFormatter
|
| 26 |
|
| 27 | _ = log
|
| 28 |
|
| 29 | _JSON_ACTION_ERROR = "builtin expects 'read' or 'write'"
|
| 30 |
|
| 31 |
|
| 32 | class Json(vm._Builtin):
|
| 33 | """JSON read and write.
|
| 34 |
|
| 35 | --pretty=0 writes it on a single line
|
| 36 | --indent=2 controls multiline indentation
|
| 37 | """
|
| 38 |
|
| 39 | def __init__(self, mem, errfmt, is_j8):
|
| 40 | # type: (state.Mem, ErrorFormatter, bool) -> None
|
| 41 | self.mem = mem
|
| 42 | self.errfmt = errfmt
|
| 43 |
|
| 44 | self.is_j8 = is_j8
|
| 45 | self.name = 'j8' if is_j8 else 'json' # for error messages
|
| 46 |
|
| 47 | self.stdout_ = mylib.Stdout()
|
| 48 |
|
| 49 | def Run(self, cmd_val):
|
| 50 | # type: (cmd_value.Argv) -> int
|
| 51 | arg_r = args.Reader(cmd_val.argv, locs=cmd_val.arg_locs)
|
| 52 | arg_r.Next() # skip 'json'
|
| 53 |
|
| 54 | action, action_loc = arg_r.Peek2()
|
| 55 | if action is None:
|
| 56 | raise error.Usage(_JSON_ACTION_ERROR, loc.Missing)
|
| 57 | arg_r.Next()
|
| 58 |
|
| 59 | if action == 'write':
|
| 60 | # NOTE slightly different flags
|
| 61 | # json write --surrogate-ok $'\udc00'
|
| 62 | # not valid for j8 write
|
| 63 | attrs = flag_util.Parse('json_write', arg_r)
|
| 64 |
|
| 65 | arg_jw = arg_types.json_write(attrs.attrs)
|
| 66 |
|
| 67 | if not arg_r.AtEnd():
|
| 68 | e_usage('write got too many args', arg_r.Location())
|
| 69 |
|
| 70 | rd = typed_args.ReaderForProc(cmd_val)
|
| 71 | val = rd.PosValue()
|
| 72 | rd.Done()
|
| 73 |
|
| 74 | if arg_jw.pretty: # C++ BUG Here!
|
| 75 | indent = mops.BigTruncate(arg_jw.indent)
|
| 76 | else:
|
| 77 | # How yajl works: if indent is -1, then everything is on one line.
|
| 78 | indent = -1
|
| 79 |
|
| 80 | #log('json write indent %d', indent)
|
| 81 |
|
| 82 | buf = mylib.BufWriter()
|
| 83 |
|
| 84 | try:
|
| 85 | if self.is_j8:
|
| 86 | j8.PrintMessage(val, buf, indent)
|
| 87 | else:
|
| 88 | j8.PrintJsonMessage(val, buf, indent)
|
| 89 | except error.Encode as e:
|
| 90 | self.errfmt.PrintMessage(
|
| 91 | '%s write: %s' % (self.name, e.Message()), action_loc)
|
| 92 | return 1
|
| 93 |
|
| 94 | self.stdout_.write(buf.getvalue())
|
| 95 | self.stdout_.write('\n')
|
| 96 |
|
| 97 | elif action == 'read':
|
| 98 | attrs = flag_util.Parse('json_read', arg_r)
|
| 99 | arg_jr = arg_types.json_read(attrs.attrs)
|
| 100 | # TODO:
|
| 101 | # Respect -validate=F
|
| 102 |
|
| 103 | if cmd_val.typed_args: # json read (&x)
|
| 104 | rd = typed_args.ReaderForProc(cmd_val)
|
| 105 | place = rd.PosPlace()
|
| 106 | rd.Done()
|
| 107 |
|
| 108 | blame_loc = cmd_val.typed_args.left # type: loc_t
|
| 109 |
|
| 110 | else: # json read
|
| 111 | var_name = '_reply'
|
| 112 |
|
| 113 | #log('VAR %s', var_name)
|
| 114 | blame_loc = cmd_val.arg_locs[0]
|
| 115 | place = value.Place(LeftName(var_name, blame_loc),
|
| 116 | self.mem.TopNamespace())
|
| 117 |
|
| 118 | if not arg_r.AtEnd():
|
| 119 | e_usage('read got too many args', arg_r.Location())
|
| 120 |
|
| 121 | try:
|
| 122 | contents = read_osh.ReadAll()
|
| 123 | except pyos.ReadError as e: # different paths for read -d, etc.
|
| 124 | # don't quote code since YSH errexit will likely quote
|
| 125 | self.errfmt.PrintMessage("read error: %s" %
|
| 126 | posix.strerror(e.err_num))
|
| 127 | return 1
|
| 128 |
|
| 129 | p = j8.Parser(contents, self.is_j8)
|
| 130 | try:
|
| 131 | val = p.ParseValue()
|
| 132 | except error.Decode as err:
|
| 133 | # TODO: Need to show position info
|
| 134 | self.errfmt.Print_('%s read: %s' % (self.name, err.Message()),
|
| 135 | blame_loc=action_loc)
|
| 136 | return 1
|
| 137 |
|
| 138 | self.mem.SetPlace(place, val, blame_loc)
|
| 139 |
|
| 140 | else:
|
| 141 | raise error.Usage(_JSON_ACTION_ERROR, action_loc)
|
| 142 |
|
| 143 | return 0
|