| 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 display import ui
|
| 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, ui.ErrorFormatter, bool) -> None
|
| 41 | self.mem = mem
|
| 42 | self.errfmt = errfmt
|
| 43 |
|
| 44 | self.is_j8 = is_j8
|
| 45 | self.name = 'json8' 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 | attrs = flag_util.Parse('json_write', arg_r)
|
| 63 |
|
| 64 | arg_jw = arg_types.json_write(attrs.attrs)
|
| 65 |
|
| 66 | if not arg_r.AtEnd():
|
| 67 | e_usage('write got too many args', arg_r.Location())
|
| 68 |
|
| 69 | rd = typed_args.ReaderForProc(cmd_val)
|
| 70 | val = rd.PosValue()
|
| 71 | # default is 2, rather than 0 for toJson()
|
| 72 | space = mops.BigTruncate(rd.NamedInt('space', 2))
|
| 73 | type_errors = rd.NamedBool('type_errors', True)
|
| 74 | rd.Done()
|
| 75 |
|
| 76 | # Convert from external JS-like API to internal API.
|
| 77 | if space <= 0:
|
| 78 | indent = -1
|
| 79 | else:
|
| 80 | indent = space
|
| 81 |
|
| 82 | buf = mylib.BufWriter()
|
| 83 | try:
|
| 84 | if self.is_j8:
|
| 85 | j8.PrintMessage(val, buf, indent, type_errors)
|
| 86 | else:
|
| 87 | j8.PrintJsonMessage(val, buf, indent, type_errors)
|
| 88 | except error.Encode as e:
|
| 89 | self.errfmt.PrintMessage(
|
| 90 | '%s write: %s' % (self.name, e.Message()), action_loc)
|
| 91 | return 1
|
| 92 |
|
| 93 | self.stdout_.write(buf.getvalue())
|
| 94 | self.stdout_.write('\n')
|
| 95 |
|
| 96 | elif action == 'read':
|
| 97 | attrs = flag_util.Parse('json_read', arg_r)
|
| 98 | #arg_jr = arg_types.json_read(attrs.attrs)
|
| 99 |
|
| 100 | if cmd_val.proc_args: # json read (&x)
|
| 101 | rd = typed_args.ReaderForProc(cmd_val)
|
| 102 | place = rd.PosPlace()
|
| 103 | rd.Done()
|
| 104 |
|
| 105 | blame_loc = cmd_val.proc_args.typed_args.left # type: loc_t
|
| 106 |
|
| 107 | else: # json read
|
| 108 | var_name = '_reply'
|
| 109 |
|
| 110 | #log('VAR %s', var_name)
|
| 111 | blame_loc = cmd_val.arg_locs[0]
|
| 112 | place = value.Place(LeftName(var_name, blame_loc),
|
| 113 | self.mem.CurrentFrame())
|
| 114 |
|
| 115 | if not arg_r.AtEnd():
|
| 116 | e_usage('read got too many args', arg_r.Location())
|
| 117 |
|
| 118 | try:
|
| 119 | contents = read_osh.ReadAll()
|
| 120 | except pyos.ReadError as e: # different paths for read -d, etc.
|
| 121 | # don't quote code since YSH errexit will likely quote
|
| 122 | self.errfmt.PrintMessage("read error: %s" %
|
| 123 | posix.strerror(e.err_num))
|
| 124 | return 1
|
| 125 |
|
| 126 | p = j8.Parser(contents, self.is_j8)
|
| 127 | try:
|
| 128 | val = p.ParseValue()
|
| 129 | except error.Decode as err:
|
| 130 | # TODO: Need to show position info
|
| 131 | self.errfmt.Print_('%s read: %s' % (self.name, err.Message()),
|
| 132 | blame_loc=action_loc)
|
| 133 | return 1
|
| 134 |
|
| 135 | self.mem.SetPlace(place, val, blame_loc)
|
| 136 |
|
| 137 | else:
|
| 138 | raise error.Usage(_JSON_ACTION_ERROR, action_loc)
|
| 139 |
|
| 140 | return 0
|