/
compiler.cpp
344 lines (301 loc) · 11.4 KB
/
compiler.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
#include "compiler.h"
#include <cstdarg>
#include <fstream>
#include <iostream>
#include <set>
#include <sys/stat.h>
#include <vector>
#include "ast.h"
#include "disk.h"
#include "import_rules.h"
#include "lexer.h"
#include "link_ins.h"
#include "parse_state.h"
#include "parser.h"
#include "prefix.h"
#include "utils.h"
#include "zion.h"
namespace zion {
using namespace ast;
std::string strip_zion_extension(std::string module_name) {
if (ends_with(module_name, ".zion")) {
/* as a courtesy, strip the extension from the filename here */
return module_name.substr(0, module_name.size() - strlen(".zion"));
} else {
return module_name;
}
}
const std::vector<std::string> &get_zion_paths() {
static bool checked = false;
static std::vector<std::string> zion_paths;
if (!checked) {
checked = true;
if (getenv("ZION_PATH") != nullptr) {
for (auto &path : split(getenv("ZION_PATH"), ":")) {
if (path != "") {
/* just be careful that user didn't put in an empty ZION_PATH */
zion_paths.push_back(path);
}
}
} else {
log(log_error,
"ZION_PATH is not set. It should be set to the dirname of std.zion. "
"That is typically /usr/local/share/zion/lib.");
exit(1);
}
zion_paths.insert(zion_paths.begin(), ".");
for (auto &zion_path : zion_paths) {
/* fix any paths to be absolute */
real_path(zion_path, zion_path);
}
}
return zion_paths;
}
namespace compiler {
std::string resolve_module_filename(Location location,
std::string name,
std::string extension) {
std::string filename_test_resolution;
if (real_path(name, filename_test_resolution)) {
if (name == filename_test_resolution) {
if (!ends_with(filename_test_resolution, extension)) {
filename_test_resolution = filename_test_resolution + extension;
}
if (file_exists(filename_test_resolution)) {
/* short circuit if we're given a real path */
return filename_test_resolution;
} else {
panic(string_format("filename %s does not exist", name.c_str()));
}
} else if (file_exists(filename_test_resolution) &&
(extension == "" ||
ends_with(filename_test_resolution, extension))) {
return filename_test_resolution;
}
}
std::string leaf_name;
if (ends_with(leaf_name, extension)) {
leaf_name = name;
} else {
leaf_name = name + extension;
}
std::string working_resolution;
for (auto zion_path : get_zion_paths()) {
auto test_path = zion_path + "/" + leaf_name;
if (file_exists(test_path)) {
std::string test_resolution;
if (real_path(test_path, test_resolution)) {
if (working_resolution.size() &&
working_resolution != test_resolution) {
throw user_error(location,
"multiple " C_FILENAME "%s" C_RESET
" modules found with the same name in source "
"path [%s, %s]",
name.c_str(), working_resolution.c_str(),
test_resolution.c_str());
} else {
working_resolution = test_resolution;
debug_above(11, log(log_info, "searching for file %s, found it at %s",
name.c_str(), working_resolution.c_str()));
}
} else {
/* if the file exists, it should have a real_path */
panic(string_format(
"searching for file %s, unable to resolve its real path (%s)",
name.c_str(), test_path.c_str()));
}
} else {
debug_above(11,
log(log_info, "searching for file %s, did not find it at %s",
name.c_str(), test_path.c_str()));
}
}
if (working_resolution.size() != 0) {
/* cool, we found one and only one module with the requested name in
* the source paths */
return working_resolution;
} else {
throw user_error(
location,
"module not found: " c_error("`%s`") ". Looked in ZION_PATH=[%s]",
name.c_str(), join(get_zion_paths(), ":").c_str());
return "";
}
}
struct GlobalParserState {
GlobalParserState(const std::map<std::string, int> &builtin_arities)
: builtin_arities(builtin_arities) {
}
std::vector<const Module *> modules;
std::map<std::string, const Module *> modules_map_by_filename;
std::map<std::string, const Module *> modules_map_by_name;
parser::SymbolExports symbol_exports;
parser::SymbolImports symbol_imports;
std::vector<Token> comments;
std::set<LinkIn> link_ins;
const std::map<std::string, int> &builtin_arities;
const Module *parse_module_statefully(Identifier module_id) {
if (auto module = get(modules_map_by_name, module_id.name,
static_cast<const Module *>(nullptr))) {
return module;
}
std::string module_filename = compiler::resolve_module_filename(
module_id.location, module_id.name, ".zion");
if (auto module = get(modules_map_by_filename, module_filename,
static_cast<const Module *>(nullptr))) {
return module;
}
/* we found an unparsed file */
std::ifstream ifs;
ifs.open(module_filename.c_str());
if (ifs.good()) {
debug_above(11, log(log_info, "parsing module " c_id("%s"),
module_filename.c_str()));
Lexer lexer({module_filename}, ifs);
parser::ParseState ps(module_filename, "", lexer, comments, link_ins,
symbol_exports, symbol_imports, builtin_arities);
std::set<Identifier> dependencies;
const Module *module = parse_module(ps, {modules_map_by_name["std"]},
dependencies);
modules.push_back(module);
/* break any circular dependencies. inject this module into the graph */
modules_map_by_name[ps.module_name] = module;
modules_map_by_filename[ps.filename] = module;
debug_above(8, log("while parsing %s got dependencies {%s}",
ps.module_name.c_str(),
join(dependencies, ", ").c_str()));
for (auto dependency : dependencies) {
parse_module_statefully(dependency);
}
return module;
} else {
auto error = user_error(
module_id.location,
"could not open \"%s\" when trying to link module",
module_filename.c_str());
error.add_info(module_id.location, "imported here");
throw error;
}
}
};
std::set<std::string> get_top_level_decls(
const std::vector<const Decl *> &decls,
const std::vector<const TypeDecl *> &type_decls,
const std::vector<const TypeClass *> &type_classes,
const std::vector<const Identifier> &imports) {
std::map<std::string, Location> module_decls;
for (const Decl *decl : decls) {
if (module_decls.find(decl->id.name) != module_decls.end()) {
auto error = user_error(decl->id.location, "duplicate symbol");
error.add_info(module_decls[decl->id.name], "see prior definition here");
throw error;
}
module_decls[decl->id.name] = decl->id.location;
}
std::set<std::string> top_level_decls;
for (auto pair : module_decls) {
top_level_decls.insert(pair.first);
}
for (auto type_decl : type_decls) {
top_level_decls.insert(type_decl->id.name);
}
for (auto type_class : type_classes) {
top_level_decls.insert(type_class->id.name);
for (auto overload_pair : type_class->overloads) {
top_level_decls.insert(overload_pair.first);
}
}
for (auto &import : imports) {
top_level_decls.insert(import.name);
}
debug_above(8, log("tlds are %s", ::join(top_level_decls, ", ").c_str()));
return top_level_decls;
}
std::shared_ptr<Compilation> merge_compilation(
std::string program_filename,
std::string program_name,
std::vector<const Module *> modules,
const std::vector<Token> &comments,
const std::set<LinkIn> &link_ins) {
std::vector<const Decl *> program_decls;
std::vector<const TypeClass *> program_type_classes;
std::vector<const Instance *> program_instances;
ParsedCtorIdMap ctor_id_map;
ParsedDataCtorsMap data_ctors_map;
types::TypeEnv type_env;
/* next, merge the entire set of modules into one program */
for (const Module *module : modules) {
/* get a list of all top-level decls */
std::set<std::string> bindings = get_top_level_decls(
module->decls, module->type_decls, module->type_classes,
module->imports);
const Module *module_rebound = prefix(bindings, module);
/* now all locally referring vars are fully qualified */
for (const Decl *decl : module_rebound->decls) {
program_decls.push_back(decl);
}
for (const TypeClass *type_class : module_rebound->type_classes) {
program_type_classes.push_back(type_class);
}
for (const Instance *instance : module_rebound->instances) {
program_instances.push_back(instance);
}
for (auto pair : module_rebound->ctor_id_map) {
assert(!in(pair.first, ctor_id_map));
ctor_id_map[pair.first] = pair.second;
}
for (auto pair : module_rebound->data_ctors_map) {
assert(!in(pair.first, data_ctors_map));
data_ctors_map[pair.first] = pair.second;
}
for (auto pair : module_rebound->type_env) {
assert(!in(pair.first, type_env));
type_env[pair.first] = pair.second;
}
}
return std::make_shared<Compilation>(
program_filename, program_name,
new Program(program_decls, program_type_classes, program_instances,
new Application(new Var(make_iid("main")),
{unit_expr(INTERNAL_LOC())})),
comments, link_ins, DataCtorsMap{data_ctors_map, ctor_id_map}, type_env);
}
Compilation::ref parse_program(
std::string user_program_name,
const std::map<std::string, int> &builtin_arities) {
std::string program_name = strip_zion_extension(
leaf_from_file_path(user_program_name));
try {
/* first just parse all the modules that are reachable from the initial
* module and bring them into our whole ast */
auto module_name = program_name;
GlobalParserState gps(builtin_arities);
/* include the builtins library */
if (getenv("NO_PRELUDE") == nullptr || atoi(getenv("NO_PRELUDE")) == 0) {
gps.parse_module_statefully({"std" /* lib/std */, Location{"std", 0, 0}});
} else {
/* in the case that we are omitting the prelude, still include the GC */
gps.link_ins.insert(LinkIn{
lit_pkgconfig, Token{INTERNAL_LOC(), tk_string, "\"bdw-gc\""}});
}
/* now parse the main program module */
gps.parse_module_statefully(
{user_program_name, Location{"command line build parameters", 0, 0}});
debug_above(11, log(log_info, "parse_module of %s succeeded",
module_name.c_str(), false /*global*/));
/* find the import rewriting rules */
RewriteImportRules rewriting_imports_rules = solve_rewriting_imports(
gps.symbol_imports, gps.symbol_exports);
std::string program_filename = compiler::resolve_module_filename(
INTERNAL_LOC(), user_program_name, ".zion");
return merge_compilation(
program_filename, program_name,
rewrite_modules(rewriting_imports_rules, gps.modules), gps.comments,
gps.link_ins);
} catch (user_error &e) {
print_exception(e);
return nullptr;
}
}
} // namespace compiler
} // namespace zion