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
|