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