OILS / pea / pea_main.py View on Github | oils.pub

195 lines, 113 significant
1#!/usr/bin/env python3
2"""
3pea_main.py
4
5A potential rewrite of mycpp.
6"""
7import io
8import optparse
9import pickle
10import sys
11import time
12
13START_TIME = time.time()
14
15if 0:
16 for p in sys.path:
17 print('*** syspath: %s' % p)
18
19from typing import Any, Dict, List, Tuple
20
21from mycpp import translate
22
23from pea import gen_cpp
24from pea import mypy_shim
25from pea import parse
26from pea.header import (TypeSyntaxError, Program, log)
27
28
29def Options() -> optparse.OptionParser:
30 """Returns an option parser instance."""
31
32 p = optparse.OptionParser()
33 p.add_option('-v',
34 '--verbose',
35 dest='verbose',
36 action='store_true',
37 default=False,
38 help='Show details about translation')
39
40 # Control which modules are exported to the header. Used by
41 # build/translate.sh.
42 p.add_option('--to-header',
43 dest='to_header',
44 action='append',
45 default=[],
46 help='Export this module to a header, e.g. frontend.args')
47
48 p.add_option('--header-out',
49 dest='header_out',
50 default=None,
51 help='Write this header')
52
53 return p
54
55
56def main(argv: list[str]) -> int:
57
58 o = Options()
59 opts, argv = o.parse_args(argv)
60
61 action = argv[1]
62
63 # TODO: get rid of 'parse'
64 if action in ('parse', 'cpp'):
65 files = argv[2:]
66
67 # TODO:
68 # pass_state.Virtual
69 # this loops over functions and methods. But it has to be done BEFORE
70 # the PrototypesPass, or we need two passes. Gah!
71 # Could it be done in ConstVisitor? ConstVirtualVisitor?
72
73 # local_vars
74
75 prog = Program()
76 log('Pea begin')
77
78 if not parse.ParseFiles(files, prog):
79 return 1
80 log('Parsed %d files and their type comments', len(files))
81 prog.PrintStats()
82
83 # This is the first pass
84
85 const_lookup: dict[str, int] = {}
86
87 v = gen_cpp.ConstVisitor(const_lookup)
88 for py_file in prog.py_files:
89 v.visit(py_file.module)
90
91 log('Collected %d constants', len(const_lookup))
92
93 # TODO: respect header_out for these two passes
94 #out_f = sys.stdout
95 out_f = io.StringIO()
96
97 # ForwardDeclPass: module -> class
98 # TODO: Move trivial ForwardDeclPass into ParsePass, BEFORE constants,
99 # after comparing output with mycpp.
100 pass2 = gen_cpp.ForwardDeclPass(out_f)
101 for py_file in prog.py_files:
102 namespace = py_file.namespace
103 pass2.DoPyFile(py_file)
104
105 log('Wrote forward declarations')
106 prog.PrintStats()
107
108 try:
109 # PrototypesPass: module -> class/method, func
110
111 pass3 = gen_cpp.PrototypesPass(opts, prog, out_f)
112 for py_file in prog.py_files:
113 pass3.DoPyFile(py_file) # parses type comments in signatures
114
115 log('Wrote prototypes')
116 prog.PrintStats()
117
118 # ImplPass: module -> class/method, func; then probably a fully recursive thing
119
120 pass4 = gen_cpp.ImplPass(prog, out_f)
121 for py_file in prog.py_files:
122 pass4.DoPyFile(py_file) # parses type comments in assignments
123
124 log('Wrote implementation')
125 prog.PrintStats()
126
127 except TypeSyntaxError as e:
128 log('Type comment syntax error on line %d of %s: %r', e.lineno,
129 py_file.filename, e.code_str)
130 return 1
131
132 log('Done')
133
134 elif action == 'mycpp':
135 paths = argv[2:]
136 _ = paths
137
138 #log('pea mycpp %s', sys.argv)
139
140 timer = translate.Timer(START_TIME)
141 timer.Section('PEA loading %s', ' '.join(paths))
142
143 f = sys.stdout
144 header_f = sys.stdout
145
146 # TODO: Dict[Expression, Type]
147 types: Dict[Any, Any] = {}
148
149 to_header: List[str] = []
150 to_compile: List[Tuple[str, Any]] = []
151
152 for path in paths:
153 # defs, imports
154 # Ah this is an empty file!
155 m = mypy_shim.CreateMyPyFile(path)
156
157 to_compile.append((path, m))
158
159 return translate.Run(timer, f, header_f, types, to_header, to_compile)
160
161 elif action == 'dump-pickles':
162 files = argv[2:]
163
164 prog = Program()
165 log('Pea begin')
166
167 if not parse.ParseFiles(files, prog):
168 return 1
169 log('Parsed %d files and their type comments', len(files))
170 prog.PrintStats()
171
172 # Note: can't use marshal here, because it only accepts simple types
173 pickle.dump(prog.py_files, sys.stdout.buffer)
174 log('Dumped pickle')
175
176 elif action == 'load-pickles':
177 while True:
178 try:
179 py_files = pickle.load(sys.stdin.buffer)
180 except EOFError:
181 break
182 log('Loaded pickle with %d files', len(py_files))
183
184 else:
185 raise RuntimeError('Invalid action %r' % action)
186
187 return 0
188
189
190if __name__ == '__main__':
191 try:
192 sys.exit(main(sys.argv))
193 except RuntimeError as e:
194 print('FATAL: %s' % e, file=sys.stderr)
195 sys.exit(1)