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

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