MimIR
MimIR is my Intermediate Representation
Loading...
Searching...
No Matches
ast.cpp
Go to the documentation of this file.
1#include "mim/ast/ast.h"
2
3#include <set>
4
5#include "mim/ast/parser.h"
6
7using namespace std::literals;
8
9namespace mim::ast {
10
11// Node map: Parser::import holds the slot across the nested parses that may insert further entries.
12struct AST::Files : std::unordered_map<const fe::Src*, Ptr<File>> {};
13
15 : world_(&world)
16 , files_(std::make_unique<Files>()) {}
17
18AST::AST(AST&& other)
19 : AST(other.world()) {
20 swap(*this, other);
21}
22
23AST::~AST() = default;
24
25std::pair<Ptr<File>&, bool> AST::file(const fe::Src* src) {
26 auto [i, fresh] = files_->try_emplace(src);
27 return {i->second, fresh};
28}
29
30namespace {
31
32constexpr auto Cpp_Keywords = std::to_array<std::string_view>({
33 "alignas", "alignof", "and", "and_eq", "asm", "auto", "bitand",
34 "bitor", "bool", "break", "case", "catch", "char", "char8_t",
35 "char16_t", "char32_t", "class", "compl", "concept", "const", "const_cast",
36 "consteval", "constexpr", "constinit", "continue", "co_await", "co_return", "co_yield",
37 "decltype", "default", "delete", "do", "double", "dynamic_cast", "else",
38 "enum", "explicit", "export", "extern", "false", "float", "for",
39 "friend", "goto", "if", "inline", "int", "long", "mutable",
40 "namespace", "new", "noexcept", "not", "not_eq", "nullptr", "operator",
41 "or", "or_eq", "private", "protected", "public", "register", "reinterpret_cast",
42 "requires", "return", "short", "signed", "sizeof", "static", "static_assert",
43 "static_cast", "struct", "switch", "template", "this", "thread_local", "throw",
44 "true", "try", "typedef", "typeid", "typename", "union", "unsigned",
45 "using", "virtual", "void", "volatile", "wchar_t", "while", "xor",
46 "xor_eq",
47});
48
49constexpr auto Py_Keywords = std::to_array<std::string_view>({
50 "False", "None", "True", "and", "as", "assert", "async", "await", "break",
51 "class", "continue", "def", "del", "elif", "else", "except", "finally", "for",
52 "from", "global", "if", "import", "in", "is", "lambda", "nonlocal", "not",
53 "or", "pass", "raise", "return", "try", "while", "with", "yield",
54});
55
56/// @p sym as an identifier of the target language: a keyword gets a trailing `_`.
57std::string escape(Sym sym, std::span<const std::string_view> keywords) {
58 auto id = std::string(sym.view());
59 return std::ranges::contains(keywords, sym.view()) ? id + "_" : id;
60}
61
62std::string_view kind(const AnnexInfo& annex) { return annex.fields ? "field" : "ctor"; }
63
64} // namespace
65
66void AST::bootstrap(Sym plugin, std::ostream& h) {
67 auto tab = fe::Tab::spaces();
68 std::println(h, "{}#pragma once\n", tab);
69 std::println(h, "{}#include <mim/axm.h>", tab);
70 std::println(h, "{}#include <mim/plugin.h>\n", tab);
71
72 const auto& foreign = plugin2foreign_aliases(plugin);
73 auto targets = std::set<std::string>();
74 for (const auto& alias : foreign)
75 targets.emplace(alias.annex->sym.plugin.str());
76 for (const auto& target : targets)
77 std::println(h, "{}#include <mim/plug/{}/autogen.h>", tab, target);
78 if (!targets.empty()) std::println(h, "");
79
80 std::println(h, "{}/// @namespace mim::plug::{} @ref {}", tab, plugin, plugin);
81 std::println(h, "{}namespace mim {{", tab);
82 std::println(h, "{}namespace plug::{} {{\n", tab, plugin);
83
84 plugin_t plugin_id = *Annex::mangle(plugin);
85 std::vector<std::ostringstream> normalizers, outer_namespace;
86
87 std::println(h, "{}static constexpr plugin_t Plugin_Id = 0x{:x};\n", tab, plugin_id);
88
89 for (const auto& [alias, annex, sub] : foreign) {
90 auto id = escape(alias, Cpp_Keywords);
91 auto tag = escape(annex->sym.tag, Cpp_Keywords);
92 if (annex->subs.empty())
93 std::println(h, "{}using {} = plug::{}::{};", tab, id, annex->sym.plugin, tag);
94 else
95 std::println(h, "{}static constexpr auto {} = plug::{}::{}::{};", tab, id, annex->sym.plugin, tag,
96 escape(annex->subs[sub].front(), Cpp_Keywords));
97 }
98 if (!foreign.empty()) std::println(h, "");
99
100 const auto& unordered = plugin2annexes(plugin);
101 std::deque<std::pair<Sym, AnnexInfo>> infos(unordered.begin(), unordered.end());
102 std::ranges::sort(infos, [&](const auto& p1, const auto& p2) { return p1.second.id.tag < p2.second.id.tag; });
103
104 // clang-format off
105 for (const auto& [key, annex] : infos) {
106 const auto& sym = annex.sym;
107 if (sym.plugin != plugin) continue; // this is from an import
108 auto tag = escape(sym.tag, Cpp_Keywords);
109
110 std::println(h, "{}/// @name `{}.{}`\n///@{{", tab, plugin, sym.tag);
111 std::println(h, "{}enum class {} : flags_t {{", tab, tag);
112 ++tab;
113 flags_t ax_id = annex.base();
114
115 auto& os = outer_namespace.emplace_back();
116 std::print(os, "template<> constexpr flags_t Annex::Base<plug::{}::{}> = 0x{:x};\n", plugin, tag, ax_id);
117
118 if (auto& subs = annex.subs; !subs.empty()) {
119 for (const auto& aliases : subs) {
120 auto id = ax_id++;
121 for (const auto alias : aliases)
122 std::println(h, "{}{} = 0x{:x},", tab, escape(alias, Cpp_Keywords), id);
123
124 if (auto norm = annex.normalizer) {
125 auto sub = escape(aliases.front(), Cpp_Keywords);
126 auto& os = normalizers.emplace_back();
127 std::print(os, "normalizers[flags_t({}::{})] = &{}<{}::{}>;", tag, sub, norm, tag, sub);
128 }
129 }
130 } else {
131 if (auto norm = annex.normalizer)
132 std::print(normalizers.emplace_back(), "normalizers[flags_t(Annex::Base<{}>)] = &{};", tag, norm);
133 }
134 --tab;
135 std::println(h, "{}}};\n", tab);
136
137 std::println(outer_namespace.emplace_back(), "template<> constexpr size_t Annex::Num<plug::{}::{}> = {};", plugin, tag, annex.subs.size());
138
139 if (auto norm = annex.normalizer) {
140 if (auto& subs = annex.subs; !subs.empty()) {
141 std::println(h, "{}template<{}>\nconst Def* {}(const Def*, const Def*, const Def*);\n", tab, tag,
142 norm);
143 } else {
144 std::println(h, "{}const Def* {}(const Def*, const Def*, const Def*);", tab, norm);
145 }
146 }
147
148 if (!annex.names.empty()) {
149 std::println(h, "{}/// The {}s of `{}.{}`, by index.", tab, kind(annex), plugin, sym.tag);
150 std::print(h, "{}struct {}_{} {{ enum : nat_t {{", tab, sym.tag, kind(annex));
151 for (size_t i = 0, e = annex.names.size(); i != e; ++i)
152 if (auto name = annex.names[i]) std::print(h, " {} = {},", escape(name, Cpp_Keywords), i);
153 std::println(h, " }}; }};\n");
154 }
155 std::println(h, "{}///@}}\n", tab);
156 }
157 // clang-format on
158
159 if (!normalizers.empty()) {
160 std::println(h, "{}void register_normalizers(Normalizers& normalizers);\n", tab);
161 std::println(h, "{}#define MIM_{}_NORMALIZER_IMPL \\", tab, plugin);
162 ++tab;
163 std::println(h, "{}void register_normalizers(Normalizers& normalizers) {{\\", tab);
164 ++tab;
165 for (const auto& normalizer : normalizers)
166 std::println(h, "{}{} \\", tab, normalizer.str());
167 --tab;
168 std::println(h, "{}}}", tab);
169 --tab;
170 }
171
172 std::println(h, "{}}} // namespace plug::{}\n", tab, plugin);
173
174 std::println(h, "{}#ifndef DOXYGEN // don't include in Doxygen documentation\n", tab);
175 for (const auto& line : outer_namespace)
176 std::print(h, "{}{}", tab, line.str());
177 std::println(h, "{}", tab);
178
179 // emit helpers for non-function axm
180 for (const auto& [tag, ax] : infos) {
181 auto sym = ax.sym;
182 if ((ax.pi && *ax.pi) || sym.plugin != plugin) continue; // from function or other plugin?
183 std::println(h, "{}template<> struct Axm::IsANode<plug::{}::{}> {{ using type = Axm; }};", tab, sym.plugin,
184 escape(sym.tag, Cpp_Keywords));
185 }
186
187 std::println(h, "{}\n#endif", tab);
188 std::println(h, "{}}} // namespace mim\n", tab);
189
190 std::println(h, "{}#ifndef DOXYGEN // don't include in Doxygen documentation\n", tab);
191 for (const auto& [key, annex] : infos) {
192 if (!annex.subs.empty()) {
193 auto sym = annex.sym;
194 std::println(h, "{}template<> struct fe::is_bit_enum<mim::plug::{}::{}> : std::true_type {{}};", tab,
195 sym.plugin, escape(sym.tag, Cpp_Keywords));
196 }
197 }
198
199 std::println(h, "{}\n#endif", tab);
200}
201
202void AST::bootstrap_py(Sym plugin, std::ostream& h) {
203 fe::Tab tab;
204 plugin_t plugin_id = *Annex::mangle(plugin);
205
206 std::print(h, "from enum import IntEnum\n\n");
207 std::println(h, "class {}(IntEnum):", plugin);
208 ++tab;
209 std::println(h, "{}ID = 0x{:x}", tab, plugin_id);
210 std::vector<const AnnexInfo*> annexes_with_subs;
211
212 const auto& unordered = plugin2annexes(plugin);
213 std::deque<std::pair<Sym, AnnexInfo>> infos(unordered.begin(), unordered.end());
214 std::ranges::sort(infos, [&](const auto& p1, const auto& p2) { return p1.second.id.tag < p2.second.id.tag; });
215 for (const auto& [key, annex] : infos) {
216 const auto& sym = annex.sym;
217 if (sym.plugin != plugin) continue;
218
219 flags_t ax_id = annex.base();
220
221 if (auto& subs = annex.subs; subs.empty())
222 std::println(h, "{}{} = 0x{:x}", tab, escape(sym.tag, Py_Keywords), ax_id);
223 else
224 annexes_with_subs.emplace_back(&annex);
225 }
226 for (const auto& [alias, annex, sub] : plugin2foreign_aliases(plugin))
227 std::println(h, "{}{} = 0x{:x}", tab, escape(alias, Py_Keywords), annex->base() + sub);
228 std::print(h, "\n");
229 --tab;
230
231 for (auto annex : annexes_with_subs) {
232 flags_t ax_id = annex->base();
233 auto tag = escape(annex->sym.tag, Py_Keywords);
234 std::println(h, "class _{}_{}(IntEnum):", plugin, tag);
235 ++tab;
236
237 for (const auto& aliases : annex->subs) {
238 auto id = ax_id++;
239 for (const auto alias : aliases)
240 std::println(h, "{}{} = 0x{:x}", tab, escape(alias, Py_Keywords), id);
241 }
242
243 --tab;
244 std::println(h, "\n{}.{} = _{}_{}\n", plugin, tag, plugin, tag);
245 }
246
247 for (const auto& [key, annex] : infos) {
248 if (annex.sym.plugin != plugin || annex.names.empty()) continue;
249 auto type = std::format("{}_{}", annex.sym.tag, kind(annex));
250 std::println(h, "class _{}_{}(IntEnum):", plugin, type);
251 ++tab;
252 for (size_t i = 0, e = annex.names.size(); i != e; ++i)
253 if (auto name = annex.names[i]) std::println(h, "{}{} = {}", tab, escape(name, Py_Keywords), i);
254 --tab;
255 std::println(h, "\n{}.{} = _{}_{}\n", plugin, type, plugin, type);
256 }
257}
258
259/*
260 * Other
261 */
262
263Sym TuplePtrn::field(size_t i) const {
264 auto p = ptrn(i);
265 return (p->isa<IdPtrn>() || p->isa<GrpPtrn>()) && !p->dbg().is_anon() ? p->dbg().sym() : Sym();
266}
267
269 : Expr(lam->loc())
270 , lam_(lam) {}
271
272/*
273 * Ptrn::to_expr
274 */
275
277 if (auto idp = ptrn->isa<IdPtrn>(); idp && !idp->dbg() && idp->type()) {
278 if (auto pe = idp->type()->isa<PathExpr>())
279 return ast.ptr<PathExpr>(ast.ptr<Path>(pe->path()->loc(), pe->path()->dbgs()));
280 } else if (auto tuple = ptrn->isa<TuplePtrn>(); tuple && tuple->is_brckt()) {
281 return ast.ptr<SigmaExpr>(Ptr<TuplePtrn>(tuple));
282 }
283 return {};
284}
285
286void File::compile(AST& ast) const {
287 bind(ast);
288 ast.error().ack();
289 emit(ast);
290 ast.error().report();
291}
292
293AST load_plugins(World& world, fe::View<std::string> plugins) {
294 auto tag = world.driver().flags().bootstrap ? Tok::Tag::K_import : Tok::Tag::K_plugin;
295 auto ast = AST(world);
296 auto parser = Parser(ast);
297
298 if (!plugins.empty()) {
299 auto imports = parser.import_plugins(plugins, tag);
300 auto decls = Ptrs<ValDecl>();
301 for (auto import : imports)
302 decls.emplace_back(import);
303
304 // No Loc: this File spans no source, and hulling the imports would mix Loc%s of different files.
305 auto file = ast.ptr<File>(Loc(), ast.scope(), ast.copy(decls));
306 file->compile(ast);
307 }
308
309 return ast;
310}
311
312} // namespace mim::ast
Flags & flags()
Definition driver.h:77
The World represents the whole program and manages creation of MimIR nodes (Defs).
Definition world.h:43
Driver & driver()
Definition world.h:107
Owns the arena all AST nodes live in as well as the AnnexInfos of all plugins.
Definition ast.h:107
friend void swap(AST &a1, AST &a2) noexcept
Definition ast.h:179
void bootstrap_py(Sym plugin, std::ostream &h)
Definition ast.cpp:202
World & world() const
Definition ast.h:116
const auto & plugin2annexes(Sym plugin)
Definition ast.h:161
void bootstrap(Sym plugin, std::ostream &h)
Definition ast.cpp:66
const auto & plugin2foreign_aliases(Sym plugin)
Definition ast.h:170
Sym sym(const char *s)
Definition ast.h:123
AST(const AST &)=delete
std::pair< Ptr< File > &, bool > file(const fe::Src *src)
Definition ast.cpp:25
Expr(Loc loc)
Definition ast.h:229
The AST of one source file: an anonymous ModDecl that a UseDecl binds under a name of its own.
Definition ast.h:1355
void compile(AST &) const
Definition ast.cpp:286
void bind(AST &) const
Definition bind.cpp:151
void emit(AST &) const
Definition emit.cpp:188
dbg_i of a group dbg_0 ... dbg_n-1: type for i > 0; the leading IdPtrn::id owns type.
Definition ast.h:381
dbg: type
Definition ast.h:348
Dbg dbg() const override
The name this Decl introduces; anonymous if it has none.
Definition ast.h:355
const LamDecl * lam() const
Definition ast.h:842
LamExpr(Ptr< LamDecl > lam)
Definition ast.cpp:268
Loc loc() const
Definition ast.h:217
Parser(AST &ast)
Definition parser.h:34
dbg_0.....dbg_n-1.
Definition ast.h:484
static Ptr< Expr > to_expr(AST &, Ptr< Ptrn >)
Definition ast.cpp:276
Wraps a TuplePtrn as Expr.
Definition ast.h:905
(ptrn_0, ..., ptrn_n-1), [ptrn_0, ..., ptrn_n-1], or {ptrn_0, ..., ptrn_n-1}
Definition ast.h:424
const Ptrn * ptrn(size_t i) const
Definition ast.h:438
Sym field(size_t i) const
The field name of ptrn(i), or the empty Sym if it binds none.
Definition ast.cpp:263
bool is_brckt() const
Definition ast.h:434
Definition ast.h:16
AST load_plugins(World &, fe::View< std::string >)
Definition ast.cpp:293
fe::Arena::Ref< const T > Ptr
Nodes live in the AST's Arena and are never destroyed, so this merely points at one.
Definition ast.h:27
fe::Vector< Ptr< T > > Ptrs
Definition ast.h:34
u64 flags_t
Definition types.h:39
u64 plugin_t
Definition types.h:40
static std::optional< plugin_t > mangle(std::string_view plugin)
Mangles s into a dense 48-bit representation.
Definition plugin.cpp:9
bool bootstrap
Definition flags.h:26
Bookkeeping of an annex introduced by an AxmDecl.
Definition ast.h:72