| 1 | # Copyright 2016 Andy Chu. All rights reserved.
 | 
| 2 | # Licensed under the Apache License, Version 2.0 (the "License");
 | 
| 3 | # you may not use this file except in compliance with the License.
 | 
| 4 | # You may obtain a copy of the License at
 | 
| 5 | #
 | 
| 6 | #   http://www.apache.org/licenses/LICENSE-2.0
 | 
| 7 | """
 | 
| 8 | ui.py - User interface constructs.
 | 
| 9 | """
 | 
| 10 | from __future__ import print_function
 | 
| 11 | 
 | 
| 12 | from _devbuild.gen.id_kind_asdl import Id, Id_t, Id_str
 | 
| 13 | from _devbuild.gen.syntax_asdl import (
 | 
| 14 |     Token,
 | 
| 15 |     SourceLine,
 | 
| 16 |     loc,
 | 
| 17 |     loc_e,
 | 
| 18 |     loc_t,
 | 
| 19 |     command_t,
 | 
| 20 |     command_str,
 | 
| 21 |     source,
 | 
| 22 |     source_e,
 | 
| 23 | )
 | 
| 24 | from _devbuild.gen.value_asdl import (value_t, value_str)
 | 
| 25 | from asdl import format as fmt
 | 
| 26 | from data_lang import pretty
 | 
| 27 | from frontend import lexer
 | 
| 28 | from frontend import location
 | 
| 29 | from mycpp import mylib
 | 
| 30 | from mycpp.mylib import print_stderr, tagswitch, log
 | 
| 31 | from data_lang import j8_lite
 | 
| 32 | import libc
 | 
| 33 | 
 | 
| 34 | from typing import List, Optional, Any, cast, TYPE_CHECKING
 | 
| 35 | if TYPE_CHECKING:
 | 
| 36 |     from _devbuild.gen import arg_types
 | 
| 37 |     from core import error
 | 
| 38 |     from core.error import _ErrorWithLocation
 | 
| 39 | 
 | 
| 40 | _ = log
 | 
| 41 | 
 | 
| 42 | 
 | 
| 43 | def ValType(val):
 | 
| 44 |     # type: (value_t) -> str
 | 
| 45 |     """For displaying type errors in the UI."""
 | 
| 46 | 
 | 
| 47 |     return value_str(val.tag(), dot=False)
 | 
| 48 | 
 | 
| 49 | 
 | 
| 50 | def CommandType(cmd):
 | 
| 51 |     # type: (command_t) -> str
 | 
| 52 |     """For displaying commands in the UI."""
 | 
| 53 | 
 | 
| 54 |     # Displays 'command.Simple' for now, maybe change it.
 | 
| 55 |     return command_str(cmd.tag())
 | 
| 56 | 
 | 
| 57 | 
 | 
| 58 | def PrettyId(id_):
 | 
| 59 |     # type: (Id_t) -> str
 | 
| 60 |     """For displaying type errors in the UI."""
 | 
| 61 | 
 | 
| 62 |     # Displays 'Id.BoolUnary_v' for now
 | 
| 63 |     return Id_str(id_)
 | 
| 64 | 
 | 
| 65 | 
 | 
| 66 | def PrettyToken(tok):
 | 
| 67 |     # type: (Token) -> str
 | 
| 68 |     """Returns a readable token value for the user.
 | 
| 69 | 
 | 
| 70 |     For syntax errors.
 | 
| 71 |     """
 | 
| 72 |     if tok.id == Id.Eof_Real:
 | 
| 73 |         return 'EOF'
 | 
| 74 | 
 | 
| 75 |     val = tok.line.content[tok.col:tok.col + tok.length]
 | 
| 76 |     # TODO: Print length 0 as 'EOF'?
 | 
| 77 |     return repr(val)
 | 
| 78 | 
 | 
| 79 | 
 | 
| 80 | def PrettyDir(dir_name, home_dir):
 | 
| 81 |     # type: (str, Optional[str]) -> str
 | 
| 82 |     """Maybe replace the home dir with ~.
 | 
| 83 | 
 | 
| 84 |     Used by the 'dirs' builtin and the prompt evaluator.
 | 
| 85 |     """
 | 
| 86 |     if home_dir is not None:
 | 
| 87 |         if dir_name == home_dir or dir_name.startswith(home_dir + '/'):
 | 
| 88 |             return '~' + dir_name[len(home_dir):]
 | 
| 89 | 
 | 
| 90 |     return dir_name
 | 
| 91 | 
 | 
| 92 | 
 | 
| 93 | def _PrintCodeExcerpt(line, col, length, f):
 | 
| 94 |     # type: (str, int, int, mylib.Writer) -> None
 | 
| 95 | 
 | 
| 96 |     buf = mylib.BufWriter()
 | 
| 97 | 
 | 
| 98 |     buf.write('  ')
 | 
| 99 | 
 | 
| 100 |     # TODO: Be smart about horizontal space when printing code snippet
 | 
| 101 |     # - Accept max_width param, which is terminal width or perhaps 100
 | 
| 102 |     #   when there's no terminal
 | 
| 103 |     # - If 'length' of token is greater than max_width, then perhaps print 10
 | 
| 104 |     #   chars on each side
 | 
| 105 |     # - If len(line) is less than max_width, then print everything normally
 | 
| 106 |     # - If len(line) is greater than max_width, then print up to max_width
 | 
| 107 |     #   but make sure to include the entire token, with some context
 | 
| 108 |     #   Print > < or ... to show truncation
 | 
| 109 |     #
 | 
| 110 |     #   ^col 80  ^~~~~ error
 | 
| 111 | 
 | 
| 112 |     buf.write(line.rstrip())
 | 
| 113 | 
 | 
| 114 |     buf.write('\n  ')
 | 
| 115 |     # preserve tabs
 | 
| 116 |     for c in line[:col]:
 | 
| 117 |         buf.write('\t' if c == '\t' else ' ')
 | 
| 118 |     buf.write('^')
 | 
| 119 |     buf.write('~' * (length - 1))
 | 
| 120 |     buf.write('\n')
 | 
| 121 | 
 | 
| 122 |     # Do this all in a single write() call so it's less likely to be
 | 
| 123 |     # interleaved.  See test/runtime-errors.sh errexit_multiple_processes
 | 
| 124 |     f.write(buf.getvalue())
 | 
| 125 | 
 | 
| 126 | 
 | 
| 127 | def _PrintTokenTooLong(loc_tok, f):
 | 
| 128 |     # type: (loc.TokenTooLong, mylib.Writer) -> None
 | 
| 129 |     line = loc_tok.line
 | 
| 130 |     col = loc_tok.col
 | 
| 131 | 
 | 
| 132 |     buf = mylib.BufWriter()
 | 
| 133 | 
 | 
| 134 |     buf.write('  ')
 | 
| 135 |     # Only print 10 characters, since it's probably very long
 | 
| 136 |     buf.write(line.content[:col + 10].rstrip())
 | 
| 137 |     buf.write('\n  ')
 | 
| 138 | 
 | 
| 139 |     # preserve tabs, like _PrintCodeExcerpt
 | 
| 140 |     for c in line.content[:col]:
 | 
| 141 |         buf.write('\t' if c == '\t' else ' ')
 | 
| 142 | 
 | 
| 143 |     buf.write('^\n')
 | 
| 144 | 
 | 
| 145 |     source_str = GetLineSourceString(loc_tok.line, quote_filename=True)
 | 
| 146 |     buf.write(
 | 
| 147 |         '%s:%d: Token starting at column %d is too long: %d bytes (%s)\n' %
 | 
| 148 |         (source_str, line.line_num, loc_tok.col, loc_tok.length,
 | 
| 149 |          Id_str(loc_tok.id)))
 | 
| 150 | 
 | 
| 151 |     # single write() call
 | 
| 152 |     f.write(buf.getvalue())
 | 
| 153 | 
 | 
| 154 | 
 | 
| 155 | def GetLineSourceString(line, quote_filename=False):
 | 
| 156 |     # type: (SourceLine, bool) -> str
 | 
| 157 |     """Returns a human-readable string for dev tools.
 | 
| 158 | 
 | 
| 159 |     This function is RECURSIVE because there may be dynamic parsing.
 | 
| 160 |     """
 | 
| 161 |     src = line.src
 | 
| 162 |     UP_src = src
 | 
| 163 | 
 | 
| 164 |     with tagswitch(src) as case:
 | 
| 165 |         if case(source_e.Interactive):
 | 
| 166 |             s = '[ interactive ]'  # This might need some changes
 | 
| 167 |         elif case(source_e.Headless):
 | 
| 168 |             s = '[ headless ]'
 | 
| 169 |         elif case(source_e.CFlag):
 | 
| 170 |             s = '[ -c flag ]'
 | 
| 171 |         elif case(source_e.Stdin):
 | 
| 172 |             src = cast(source.Stdin, UP_src)
 | 
| 173 |             s = '[ stdin%s ]' % src.comment
 | 
| 174 | 
 | 
| 175 |         elif case(source_e.MainFile):
 | 
| 176 |             src = cast(source.MainFile, UP_src)
 | 
| 177 |             # This will quote a file called '[ -c flag ]' to disambiguate it!
 | 
| 178 |             # also handles characters that are unprintable in a terminal.
 | 
| 179 |             s = src.path
 | 
| 180 |             if quote_filename:
 | 
| 181 |                 s = j8_lite.EncodeString(s, unquoted_ok=True)
 | 
| 182 |         elif case(source_e.SourcedFile):
 | 
| 183 |             src = cast(source.SourcedFile, UP_src)
 | 
| 184 |             # ditto
 | 
| 185 |             s = src.path
 | 
| 186 |             if quote_filename:
 | 
| 187 |                 s = j8_lite.EncodeString(s, unquoted_ok=True)
 | 
| 188 | 
 | 
| 189 |         elif case(source_e.ArgvWord):
 | 
| 190 |             src = cast(source.ArgvWord, UP_src)
 | 
| 191 | 
 | 
| 192 |             # Note: _PrintWithLocation() uses this more specifically
 | 
| 193 | 
 | 
| 194 |             # TODO: check loc.Missing; otherwise get Token from loc_t, then line
 | 
| 195 |             blame_tok = location.TokenFor(src.location)
 | 
| 196 |             if blame_tok is None:
 | 
| 197 |                 s = '[ %s word at ? ]' % src.what
 | 
| 198 |             else:
 | 
| 199 |                 line = blame_tok.line
 | 
| 200 |                 line_num = line.line_num
 | 
| 201 |                 outer_source = GetLineSourceString(
 | 
| 202 |                     line, quote_filename=quote_filename)
 | 
| 203 |                 s = '[ %s word at line %d of %s ]' % (src.what, line_num,
 | 
| 204 |                                                       outer_source)
 | 
| 205 | 
 | 
| 206 |         elif case(source_e.Variable):
 | 
| 207 |             src = cast(source.Variable, UP_src)
 | 
| 208 | 
 | 
| 209 |             if src.var_name is None:
 | 
| 210 |                 var_name = '?'
 | 
| 211 |             else:
 | 
| 212 |                 var_name = repr(src.var_name)
 | 
| 213 | 
 | 
| 214 |             if src.location.tag() == loc_e.Missing:
 | 
| 215 |                 where = '?'
 | 
| 216 |             else:
 | 
| 217 |                 blame_tok = location.TokenFor(src.location)
 | 
| 218 |                 assert blame_tok is not None
 | 
| 219 |                 line_num = blame_tok.line.line_num
 | 
| 220 |                 outer_source = GetLineSourceString(
 | 
| 221 |                     blame_tok.line, quote_filename=quote_filename)
 | 
| 222 |                 where = 'line %d of %s' % (line_num, outer_source)
 | 
| 223 | 
 | 
| 224 |             s = '[ var %s at %s ]' % (var_name, where)
 | 
| 225 | 
 | 
| 226 |         elif case(source_e.VarRef):
 | 
| 227 |             src = cast(source.VarRef, UP_src)
 | 
| 228 | 
 | 
| 229 |             orig_tok = src.orig_tok
 | 
| 230 |             line_num = orig_tok.line.line_num
 | 
| 231 |             outer_source = GetLineSourceString(orig_tok.line,
 | 
| 232 |                                                quote_filename=quote_filename)
 | 
| 233 |             where = 'line %d of %s' % (line_num, outer_source)
 | 
| 234 | 
 | 
| 235 |             var_name = lexer.TokenVal(orig_tok)
 | 
| 236 |             s = '[ contents of var %r at %s ]' % (var_name, where)
 | 
| 237 | 
 | 
| 238 |         elif case(source_e.Alias):
 | 
| 239 |             src = cast(source.Alias, UP_src)
 | 
| 240 |             s = '[ expansion of alias %r ]' % src.argv0
 | 
| 241 | 
 | 
| 242 |         elif case(source_e.Reparsed):
 | 
| 243 |             src = cast(source.Reparsed, UP_src)
 | 
| 244 |             span2 = src.left_token
 | 
| 245 |             outer_source = GetLineSourceString(span2.line,
 | 
| 246 |                                                quote_filename=quote_filename)
 | 
| 247 |             s = '[ %s in %s ]' % (src.what, outer_source)
 | 
| 248 | 
 | 
| 249 |         elif case(source_e.Synthetic):
 | 
| 250 |             src = cast(source.Synthetic, UP_src)
 | 
| 251 |             s = '-- %s' % src.s  # use -- to say it came from a flag
 | 
| 252 | 
 | 
| 253 |         else:
 | 
| 254 |             raise AssertionError(src)
 | 
| 255 | 
 | 
| 256 |     return s
 | 
| 257 | 
 | 
| 258 | 
 | 
| 259 | def _PrintWithLocation(prefix, msg, blame_loc, show_code):
 | 
| 260 |     # type: (str, str, loc_t, bool) -> None
 | 
| 261 |     """Should we have multiple error formats:
 | 
| 262 | 
 | 
| 263 |     - single line and verbose?
 | 
| 264 |     - and turn on "stack" tracing?  For 'source' and more?
 | 
| 265 |     """
 | 
| 266 |     f = mylib.Stderr()
 | 
| 267 |     if blame_loc.tag() == loc_e.TokenTooLong:
 | 
| 268 |         _PrintTokenTooLong(cast(loc.TokenTooLong, blame_loc), f)
 | 
| 269 |         return
 | 
| 270 | 
 | 
| 271 |     blame_tok = location.TokenFor(blame_loc)
 | 
| 272 |     if blame_tok is None:  # When does this happen?
 | 
| 273 |         f.write('[??? no location ???] %s%s\n' % (prefix, msg))
 | 
| 274 |         return
 | 
| 275 | 
 | 
| 276 |     orig_col = blame_tok.col
 | 
| 277 |     src = blame_tok.line.src
 | 
| 278 |     line = blame_tok.line.content
 | 
| 279 |     line_num = blame_tok.line.line_num  # overwritten by source__LValue case
 | 
| 280 | 
 | 
| 281 |     if show_code:
 | 
| 282 |         UP_src = src
 | 
| 283 | 
 | 
| 284 |         with tagswitch(src) as case:
 | 
| 285 |             if case(source_e.Reparsed):
 | 
| 286 |                 # Special case for LValue/backticks
 | 
| 287 | 
 | 
| 288 |                 # We want the excerpt to look like this:
 | 
| 289 |                 #   a[x+]=1
 | 
| 290 |                 #       ^
 | 
| 291 |                 # Rather than quoting the internal buffer:
 | 
| 292 |                 #   x+
 | 
| 293 |                 #     ^
 | 
| 294 | 
 | 
| 295 |                 # Show errors:
 | 
| 296 |                 #   test/parse-errors.sh text-arith-context
 | 
| 297 | 
 | 
| 298 |                 src = cast(source.Reparsed, UP_src)
 | 
| 299 |                 tok2 = src.left_token
 | 
| 300 |                 line_num = tok2.line.line_num
 | 
| 301 | 
 | 
| 302 |                 line2 = tok2.line.content
 | 
| 303 |                 lbracket_col = tok2.col + tok2.length
 | 
| 304 |                 # NOTE: The inner line number is always 1 because of reparsing.
 | 
| 305 |                 # We overwrite it with the original token.
 | 
| 306 |                 _PrintCodeExcerpt(line2, orig_col + lbracket_col, 1, f)
 | 
| 307 | 
 | 
| 308 |             elif case(source_e.ArgvWord):
 | 
| 309 |                 src = cast(source.ArgvWord, UP_src)
 | 
| 310 |                 # Special case for eval, unset, printf -v, etc.
 | 
| 311 | 
 | 
| 312 |                 # Show errors:
 | 
| 313 |                 #   test/runtime-errors.sh test-assoc-array
 | 
| 314 | 
 | 
| 315 |                 #print('OUTER blame_loc', blame_loc)
 | 
| 316 |                 #print('OUTER tok', blame_tok)
 | 
| 317 |                 #print('INNER src.location', src.location)
 | 
| 318 | 
 | 
| 319 |                 # Print code and location for MOST SPECIFIC location
 | 
| 320 |                 _PrintCodeExcerpt(line, blame_tok.col, blame_tok.length, f)
 | 
| 321 |                 source_str = GetLineSourceString(blame_tok.line,
 | 
| 322 |                                                  quote_filename=True)
 | 
| 323 |                 f.write('%s:%d\n' % (source_str, line_num))
 | 
| 324 |                 f.write('\n')
 | 
| 325 | 
 | 
| 326 |                 # Now print OUTER location, with error message
 | 
| 327 |                 _PrintWithLocation(prefix, msg, src.location, show_code)
 | 
| 328 |                 return
 | 
| 329 | 
 | 
| 330 |             else:
 | 
| 331 |                 _PrintCodeExcerpt(line, blame_tok.col, blame_tok.length, f)
 | 
| 332 | 
 | 
| 333 |     source_str = GetLineSourceString(blame_tok.line, quote_filename=True)
 | 
| 334 | 
 | 
| 335 |     # TODO: If the line is blank, it would be nice to print the last non-blank
 | 
| 336 |     # line too?
 | 
| 337 |     f.write('%s:%d: %s%s\n' % (source_str, line_num, prefix, msg))
 | 
| 338 | 
 | 
| 339 | 
 | 
| 340 | class ctx_Location(object):
 | 
| 341 | 
 | 
| 342 |     def __init__(self, errfmt, location):
 | 
| 343 |         # type: (ErrorFormatter, loc_t) -> None
 | 
| 344 |         errfmt.loc_stack.append(location)
 | 
| 345 |         self.errfmt = errfmt
 | 
| 346 | 
 | 
| 347 |     def __enter__(self):
 | 
| 348 |         # type: () -> None
 | 
| 349 |         pass
 | 
| 350 | 
 | 
| 351 |     def __exit__(self, type, value, traceback):
 | 
| 352 |         # type: (Any, Any, Any) -> None
 | 
| 353 |         self.errfmt.loc_stack.pop()
 | 
| 354 | 
 | 
| 355 | 
 | 
| 356 | # TODO:
 | 
| 357 | # - ColorErrorFormatter
 | 
| 358 | # - BareErrorFormatter?  Could just display the foo.sh:37:8: and not quotation.
 | 
| 359 | #
 | 
| 360 | # Are these controlled by a flag?  It's sort of like --comp-ui.  Maybe
 | 
| 361 | # --error-ui.
 | 
| 362 | 
 | 
| 363 | 
 | 
| 364 | class ErrorFormatter(object):
 | 
| 365 |     """Print errors with code excerpts.
 | 
| 366 | 
 | 
| 367 |     Philosophy:
 | 
| 368 |     - There should be zero or one code quotation when a shell exits non-zero.
 | 
| 369 |       Showing the same line twice is noisy.
 | 
| 370 |     - When running parallel processes, avoid interleaving multi-line code
 | 
| 371 |       quotations.  (TODO: turn off in child processes?)
 | 
| 372 |     """
 | 
| 373 | 
 | 
| 374 |     def __init__(self):
 | 
| 375 |         # type: () -> None
 | 
| 376 |         self.loc_stack = []  # type: List[loc_t]
 | 
| 377 |         self.one_line_errexit = False  # root process
 | 
| 378 | 
 | 
| 379 |     def OneLineErrExit(self):
 | 
| 380 |         # type: () -> None
 | 
| 381 |         """Unused now.
 | 
| 382 | 
 | 
| 383 |         For SubprogramThunk.
 | 
| 384 |         """
 | 
| 385 |         self.one_line_errexit = True
 | 
| 386 | 
 | 
| 387 |     # A stack used for the current builtin.  A fallback for UsageError.
 | 
| 388 |     # TODO: Should we have PushBuiltinName?  Then we can have a consistent style
 | 
| 389 |     # like foo.sh:1: (compopt) Not currently executing.
 | 
| 390 |     def _FallbackLocation(self, blame_loc):
 | 
| 391 |         # type: (Optional[loc_t]) -> loc_t
 | 
| 392 |         if blame_loc is None or blame_loc.tag() == loc_e.Missing:
 | 
| 393 |             if len(self.loc_stack):
 | 
| 394 |                 return self.loc_stack[-1]
 | 
| 395 |             return loc.Missing
 | 
| 396 | 
 | 
| 397 |         return blame_loc
 | 
| 398 | 
 | 
| 399 |     def PrefixPrint(self, msg, prefix, blame_loc):
 | 
| 400 |         # type: (str, str, loc_t) -> None
 | 
| 401 |         """Print a hard-coded message with a prefix, and quote code."""
 | 
| 402 |         _PrintWithLocation(prefix,
 | 
| 403 |                            msg,
 | 
| 404 |                            self._FallbackLocation(blame_loc),
 | 
| 405 |                            show_code=True)
 | 
| 406 | 
 | 
| 407 |     def Print_(self, msg, blame_loc=None):
 | 
| 408 |         # type: (str, loc_t) -> None
 | 
| 409 |         """Print message and quote code."""
 | 
| 410 |         _PrintWithLocation('',
 | 
| 411 |                            msg,
 | 
| 412 |                            self._FallbackLocation(blame_loc),
 | 
| 413 |                            show_code=True)
 | 
| 414 | 
 | 
| 415 |     def PrintMessage(self, msg, blame_loc=None):
 | 
| 416 |         # type: (str, loc_t) -> None
 | 
| 417 |         """Print a message WITHOUT quoting code."""
 | 
| 418 |         _PrintWithLocation('',
 | 
| 419 |                            msg,
 | 
| 420 |                            self._FallbackLocation(blame_loc),
 | 
| 421 |                            show_code=False)
 | 
| 422 | 
 | 
| 423 |     def StderrLine(self, msg):
 | 
| 424 |         # type: (str) -> None
 | 
| 425 |         """Just print to stderr."""
 | 
| 426 |         print_stderr(msg)
 | 
| 427 | 
 | 
| 428 |     def PrettyPrintError(self, err, prefix=''):
 | 
| 429 |         # type: (_ErrorWithLocation, str) -> None
 | 
| 430 |         """Print an exception that was caught, with a code quotation.
 | 
| 431 | 
 | 
| 432 |         Unlike other methods, this doesn't use the GetLocationForLine()
 | 
| 433 |         fallback. That only applies to builtins; instead we check
 | 
| 434 |         e.HasLocation() at a higher level, in CommandEvaluator.
 | 
| 435 |         """
 | 
| 436 |         # TODO: Should there be a special span_id of 0 for EOF?  runtime.NO_SPID
 | 
| 437 |         # means there is no location info, but 0 could mean that the location is EOF.
 | 
| 438 |         # So then you query the arena for the last line in that case?
 | 
| 439 |         # Eof_Real is the ONLY token with 0 span, because it's invisible!
 | 
| 440 |         # Well Eol_Tok is a sentinel with span_id == runtime.NO_SPID.  I think that
 | 
| 441 |         # is OK.
 | 
| 442 |         # Problem: the column for Eof could be useful.
 | 
| 443 | 
 | 
| 444 |         _PrintWithLocation(prefix, err.UserErrorString(), err.location, True)
 | 
| 445 | 
 | 
| 446 |     def PrintErrExit(self, err, pid):
 | 
| 447 |         # type: (error.ErrExit, int) -> None
 | 
| 448 | 
 | 
| 449 |         # TODO:
 | 
| 450 |         # - Don't quote code if you already quoted something on the same line?
 | 
| 451 |         #   - _PrintWithLocation calculates the line_id.  So you need to remember that?
 | 
| 452 |         #   - return it here?
 | 
| 453 |         prefix = 'errexit PID %d: ' % pid
 | 
| 454 |         _PrintWithLocation(prefix, err.UserErrorString(), err.location,
 | 
| 455 |                            err.show_code)
 | 
| 456 | 
 | 
| 457 | 
 | 
| 458 | def PrintAst(node, flag):
 | 
| 459 |     # type: (command_t, arg_types.main) -> None
 | 
| 460 | 
 | 
| 461 |     if flag.ast_format == 'none':
 | 
| 462 |         print_stderr('AST not printed.')
 | 
| 463 |         if 0:
 | 
| 464 |             from _devbuild.gen.id_kind_asdl import Id_str
 | 
| 465 |             from frontend.lexer import ID_HIST, LAZY_ID_HIST
 | 
| 466 | 
 | 
| 467 |             print(LAZY_ID_HIST)
 | 
| 468 |             print(len(LAZY_ID_HIST))
 | 
| 469 | 
 | 
| 470 |             for id_, count in ID_HIST.most_common(10):
 | 
| 471 |                 print('%8d %s' % (count, Id_str(id_)))
 | 
| 472 |             print()
 | 
| 473 |             total = sum(ID_HIST.values())
 | 
| 474 |             uniq = len(ID_HIST)
 | 
| 475 |             print('%8d total tokens' % total)
 | 
| 476 |             print('%8d unique tokens IDs' % uniq)
 | 
| 477 |             print()
 | 
| 478 | 
 | 
| 479 |             for id_, count in LAZY_ID_HIST.most_common(10):
 | 
| 480 |                 print('%8d %s' % (count, Id_str(id_)))
 | 
| 481 |             print()
 | 
| 482 |             total = sum(LAZY_ID_HIST.values())
 | 
| 483 |             uniq = len(LAZY_ID_HIST)
 | 
| 484 |             print('%8d total tokens' % total)
 | 
| 485 |             print('%8d tokens with LazyVal()' % total)
 | 
| 486 |             print('%8d unique tokens IDs' % uniq)
 | 
| 487 |             print()
 | 
| 488 | 
 | 
| 489 |         if 0:
 | 
| 490 |             from osh.word_parse import WORD_HIST
 | 
| 491 |             #print(WORD_HIST)
 | 
| 492 |             for desc, count in WORD_HIST.most_common(20):
 | 
| 493 |                 print('%8d %s' % (count, desc))
 | 
| 494 | 
 | 
| 495 |     else:  # text output
 | 
| 496 |         f = mylib.Stdout()
 | 
| 497 | 
 | 
| 498 |         afmt = flag.ast_format  # note: mycpp rewrite to avoid 'in'
 | 
| 499 |         if afmt in ('text', 'abbrev-text'):
 | 
| 500 |             ast_f = fmt.DetectConsoleOutput(f)
 | 
| 501 |         elif afmt in ('html', 'abbrev-html'):
 | 
| 502 |             ast_f = fmt.HtmlOutput(f)
 | 
| 503 |         else:
 | 
| 504 |             raise AssertionError()
 | 
| 505 | 
 | 
| 506 |         if 'abbrev-' in afmt:
 | 
| 507 |             # ASDL "abbreviations" are only supported by asdl/gen_python.py
 | 
| 508 |             if mylib.PYTHON:
 | 
| 509 |                 tree = node.AbbreviatedTree()
 | 
| 510 |             else:
 | 
| 511 |                 tree = node.PrettyTree()
 | 
| 512 |         else:
 | 
| 513 |             tree = node.PrettyTree()
 | 
| 514 | 
 | 
| 515 |         ast_f.FileHeader()
 | 
| 516 |         fmt.PrintTree(tree, ast_f)
 | 
| 517 |         ast_f.FileFooter()
 | 
| 518 |         ast_f.write('\n')
 | 
| 519 | 
 | 
| 520 | 
 | 
| 521 | def PrettyPrintValue(val, f):
 | 
| 522 |     # type: (value_t, mylib.Writer) -> None
 | 
| 523 |     """For the = keyword"""
 | 
| 524 | 
 | 
| 525 |     printer = pretty.PrettyPrinter()
 | 
| 526 |     printer.SetUseStyles(f.isatty())
 | 
| 527 |     try:
 | 
| 528 |         width = libc.get_terminal_width()
 | 
| 529 |         if width > 0:
 | 
| 530 |             printer.SetMaxWidth(width)
 | 
| 531 |     except (IOError, OSError):
 | 
| 532 |         pass
 | 
| 533 | 
 | 
| 534 |     buf = mylib.BufWriter()
 | 
| 535 |     printer.PrintValue(val, buf)
 | 
| 536 |     f.write(buf.getvalue())
 | 
| 537 |     f.write('\n')
 |