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 = '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.typed_args: # json read (&x)
|
100 | rd = typed_args.ReaderForProc(cmd_val)
|
101 | place = rd.PosPlace()
|
102 | rd.Done()
|
103 |
|
104 | blame_loc = cmd_val.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
|