OILS / mycpp / util.py View on Github | oilshell.org

99 lines, 44 significant
1"""
2util.py
3"""
4from __future__ import print_function
5
6import sys
7from mypy.nodes import CallExpr, IfStmt, Block, Expression, MypyFile
8
9from typing import Any, Sequence, Optional
10
11# Used by cppgen_pass and const_pass
12
13# mycpp/examples/small_str.py sorta works with this!
14#SMALL_STR = True
15
16SMALL_STR = False
17
18SymbolPath = Sequence[str]
19
20
21def log(msg: str, *args: Any) -> None:
22 if args:
23 msg = msg % args
24 print(msg, file=sys.stderr)
25
26def join_name(parts: SymbolPath, strip_package: bool = False, delim: str = '::') -> str:
27 """
28 Join the given name path into a string with the given delimiter.
29 Use strip_package to remove the top-level directory (e.g. `core`, `ysh`)
30 when dealing with C++ namespaces.
31 """
32 if not strip_package:
33 return delim.join(parts)
34
35 if len(parts) > 1:
36 return delim.join(('',) + parts[1:])
37
38 return parts[0]
39
40def split_py_name(name: str) -> SymbolPath:
41 ret = tuple(name.split('.'))
42 if len(ret) and ret[0] == 'mycpp':
43 # Drop the prefix 'mycpp.' if present. This makes names compatible with
44 # the examples that use testpkg.
45 return ret[1:]
46
47 return ret
48
49
50def _collect_cases(module_path: str, if_node: IfStmt, out: list[tuple[Expression, Block]], errors = None) -> Optional[Block] | bool:
51 """
52 The MyPy AST has a recursive structure for if-elif-elif rather than a
53 flat one. It's a bit confusing.
54
55 Appends (expr, block) cases to out param, and returns the default
56 block, which has no expression.
57
58 default block may be None.
59
60 Returns False if there is no default block.
61 """
62 assert isinstance(if_node, IfStmt), if_node
63 assert len(if_node.expr) == 1, if_node.expr
64 assert len(if_node.body) == 1, if_node.body
65
66 expr = if_node.expr[0]
67 body = if_node.body[0]
68
69 if not isinstance(expr, CallExpr):
70 if errors is not None:
71 errors.append((module_path, expr.line,
72 'Expected call like case(x), got %s' % expr))
73 return
74
75 out.append((expr, body))
76
77 if if_node.else_body:
78 first_of_block = if_node.else_body.body[0]
79 # BUG: this is meant for 'elif' only. But it also triggers for
80 #
81 # else:
82 # if 0:
83
84 if isinstance(first_of_block, IfStmt):
85 return _collect_cases(module_path, first_of_block, out, errors)
86 else:
87 # default case - no expression
88 return if_node.else_body
89
90 return False # NO DEFAULT BLOCK - Different than None
91
92
93def ShouldSkipPyFile(node: MypyFile) -> bool:
94 # Skip some stdlib stuff. A lot of it is brought in by 'import
95 # typing'. These module are special; their contents are currently all
96 # built-in primitives.
97 return node.fullname in ('__future__', 'sys', 'types', 'typing', 'abc',
98 '_ast', 'ast', '_weakrefset', 'collections',
99 'cStringIO', 're', 'builtins')