6 #include "ast/DeclContext.h"
7 #include "diagnostics/Diagnostics.h"
8 #include "diagnostics/Location.h"
9 #include "diagnostics/SourceManager.h"
10 #include "grammar/Joos1WGrammar.h"
11 #include "parsetree/ParseTreeVisitor.h"
12 #include "semantic/HierarchyChecker.h"
13 #include "semantic/NameResolver.h"
14 #include "semantic/Semantic.h"
15 #include "third-party/CLI11.h"
16 #include "utils/BumpAllocator.h"
17 #include "utils/PassManager.h"
18 #include "utils/Utils.h"
20 using std::string_view;
22 using utils::PassManager;
30 void Joos1WParserPass::
Run() {
32 if(
PM().Diag().Verbose()) {
33 auto os =
PM().Diag().ReportDebug();
34 os <<
"Parsing file ";
35 SourceManager::print(os.get(), file_);
38 checkNonAscii(SourceManager::getBuffer(file_));
40 BumpAllocator alloc{NewHeap()};
41 Joos1WParser parser{file_, alloc, &
PM().Diag()};
42 int result = parser.parse(tree_);
44 if((result != 0 || !tree_) && !
PM().Diag().hasErrors())
46 if(result != 0 || !tree_)
return;
49 PM().Diag().ReportError(
SourceRange{file_}) <<
"parse tree is poisoned";
53 if(!isLiteralTypeValid(tree_)) {
55 <<
"invalid literal types in parse tree";
60 bool Joos1WParserPass::isLiteralTypeValid(parsetree::Node* node) {
61 if(node ==
nullptr)
return true;
62 if(node->get_node_type() == parsetree::Node::Type::Literal)
63 return static_cast<parsetree::Literal*>(node)->isValid();
64 for(size_t i = 0; i < node->num_children(); i++) {
65 if(!isLiteralTypeValid(node->child(i)))
return false;
70 void Joos1WParserPass::checkNonAscii(std::string_view str) {
71 for(
unsigned i = 0; i < str.length(); i++) {
72 if(
static_cast<
unsigned char>(str[i]) > 127) {
73 PM().Diag().ReportError(SourceRange{file_})
74 <<
"non-ASCII character in file";
84 void AstContextPass::
Init() {
86 alloc = std::make_unique<BumpAllocator>(NewHeap());
89 void AstContextPass::
Run() {
90 sema = std::make_unique<ast::Semantic>(*alloc, PM().Diag());
93 AstContextPass::~AstContextPass() {
105 static inline void trace_node(parsetree::Node
const* node, std::ostream& os) {
106 if(node->parent() !=
nullptr) {
107 trace_node(node->parent(), os);
110 os << node->type_string() << std::endl;
115 static inline void mark_node(parsetree::Node* node) {
117 mark_node(node->parent());
121 AstBuilderPass::AstBuilderPass(PassManager& PM, Joos1WParserPass& dep)
noexcept
125 optCheckName =
PM().PO().GetExistingOption(
"--enable-filename-check");
128 void AstBuilderPass::
Run() {
130 auto& sema = GetPass<AstContextPass>().Sema();
131 auto* PT = dep.Tree();
133 BumpAllocator alloc{NewHeap()};
134 parsetree::ParseTreeVisitor visitor{sema, alloc};
137 cu_ = visitor.visitCompilationUnit(PT);
138 }
catch(
const parsetree::ParseTreeException& e) {
140 std::cerr <<
"ParseTreeException: " << e.what() <<
" in file ";
141 SourceManager::print(std::cerr, dep.File());
142 std::cerr << std::endl;
143 std::cerr <<
"Parse tree trace:" << std::endl;
144 trace_node(e.get_where(), std::cerr);
147 if(cu_ ==
nullptr && !
PM().Diag().hasErrors()) {
152 bool shouldCheck = optCheckName && optCheckName->as<
bool>();
153 std::pmr::string fileName{SourceManager::getFileName(dep.File()), alloc};
154 if(!fileName.empty() && shouldCheck) {
155 auto cuBody = cu_->bodyAsDecl();
157 fileName = fileName.substr(0, fileName.find_last_of(
'.'));
158 fileName = fileName.substr(fileName.find_last_of(
'/') + 1);
159 if(cuBody->name() != fileName) {
160 PM().Diag().ReportError(cuBody->location())
161 <<
"class/interface name does not match file name: "
162 << cuBody->name() <<
" != " << fileName;
173 std::pmr::vector<ast::CompilationUnit*> cus{};
175 auto& sema = GetPass<AstContextPass>().Sema();
177 for(
auto* pass : GetPasses<AstBuilderPass>())
178 cus.push_back(pass->CompilationUnit());
179 lu_ = sema.BuildLinkingUnit(cus);
186 void HierarchyCheckerPass::
Run() {
187 auto lu = GetPass<LinkerPass>().LinkingUnit();
189 for(
auto* cu : lu->compliationUnits()) {
190 auto* classDecl = dyn_cast_or_null<ast::ClassDecl>(cu->body());
191 if(!classDecl)
continue;
193 for(
auto* super : classDecl->superClasses()) {
195 if(!dyn_cast_or_null<ast::ClassDecl>(super->decl()))
continue;
196 if(cast<ast::ClassDecl>(super->decl())->hasDefaultCtor())
continue;
197 PM().Diag().ReportError(super->location())
199 << (super->decl()->hasCanonicalName()
200 ? super->decl()->getCanonicalName()
201 : super->decl()->name())
203 << (classDecl->hasCanonicalName() ? classDecl->getCanonicalName()
205 <<
" does not have a default constructor";
215 void NameResolverPass::
Init() {
216 alloc = std::make_unique<BumpAllocator>(NewHeap());
219 void NameResolverPass::
Run() {
220 auto lu = GetPass<LinkerPass>().LinkingUnit();
221 auto sema = &GetPass<AstContextPass>().Sema();
222 NR = std::make_unique<semantic::NameResolver>(*alloc, PM().Diag());
224 if(
PM().Diag().hasErrors())
return;
225 resolveRecursive(lu);
228 void NameResolverPass::replaceObjectClass(ast::
AstNode* node) {
229 auto decl = dyn_cast_or_null<ast::ClassDecl*>(node);
232 if(decl != NR->GetJavaLang().Object)
return;
234 for(
int i = 0; i < 2; i++) {
235 auto super = decl->superClasses()[i];
238 if(!
PM().Diag().hasErrors())
239 assert(super->isResolved() &&
"Superclass should be resolved");
243 if(super->decl() == NR->GetJavaLang().Object)
244 decl->mut_superClasses()[i] =
nullptr;
248 void NameResolverPass::resolveExpr(ast::
Expr* expr) {
250 for(
auto node : expr->mut_nodes()) {
251 auto tyNode = dyn_cast<ast::exprnode::TypeNode>(node);
252 if(!tyNode)
continue;
253 if(tyNode->isTypeResolved())
continue;
254 tyNode->resolveUnderlyingType(*NR);
258 void NameResolverPass::resolveRecursive(ast::
AstNode* node) {
259 assert(node &&
"Node must not be null here!");
260 for(
auto child : node->mut_children()) {
262 if(
auto cu = dyn_cast<ast::CompilationUnit*>(child)) {
264 if(!cu->body())
return;
266 NR->BeginContext(cu);
267 resolveRecursive(cu->mut_body());
268 replaceObjectClass(cu->mut_body());
270 }
else if(
auto ty = dyn_cast<ast::Type*>(child)) {
271 if(ty->isInvalid())
continue;
273 if(!ty->isResolved()) ty->resolve(*NR);
276 if(
auto decl = dyn_cast<ast::TypedDecl*>(child)) {
277 resolveExpr(decl->mut_init());
278 }
else if(
auto stmt = dyn_cast<ast::Stmt*>(child)) {
279 for(
auto expr : stmt->mut_exprs()) resolveExpr(expr);
282 resolveRecursive(child);
287 NameResolverPass::~NameResolverPass() {
297 class PrintASTPass
final :
public Pass {
299 PrintASTPass(PassManager& PM)
noexcept :
Pass(PM
) {}
304 optDot =
PM().PO().GetExistingOption(
"--print-dot");
305 optOutput =
PM().PO().GetExistingOption(
"--print-output");
306 optSplit =
PM().PO().GetExistingOption(
"--print-split");
307 optIgnoreStd =
PM().PO().GetExistingOption(
"--print-ignore-std");
312 auto& pass = GetPass<LinkerPass>();
313 auto* node = pass.LinkingUnit();
316 BumpAllocator alloc{NewHeap()};
318 if(optIgnoreStd && optIgnoreStd->count()) {
319 std::pmr::vector<ast::CompilationUnit*> cus{alloc};
320 for(
auto* cu : node->compliationUnits())
321 if(!cu->isStdLib()) cus.push_back(cu);
322 node = newSema.BuildLinkingUnit(cus);
325 bool printDot = optDot && optDot->count();
326 bool printSplit = optSplit && optSplit->count();
327 std::string outputPath = optOutput ? optOutput->as<std::string>() :
"";
331 std::ofstream file{};
332 if(!outputPath.empty()) {
333 file = std::ofstream{outputPath};
334 if(!file.is_open()) {
335 PM().Diag().ReportError(SourceRange{})
336 <<
"failed to open output file " << outputPath;
341 std::ostream& os = file.is_open() ? file : std::cout;
348 namespace fs = std::filesystem;
350 fs::create_directories(outputPath);
352 for(
auto const& cu : node->compliationUnits()) {
353 if(!cu->body() || !cu->bodyAsDecl()->hasCanonicalName())
continue;
354 std::string canonName{cu->bodyAsDecl()->getCanonicalName()};
355 auto filepath = fs::path{outputPath} / fs::path{canonName +
".dot"};
356 std::ofstream file{filepath};
357 if(!file.is_open()) {
358 PM().Diag().ReportError(SourceRange{})
359 <<
"failed to open output file " << filepath.string();
363 std::ostream& os = file;
371 void computeDependencies()
override {
375 CLI::Option *optDot, *optOutput, *optSplit, *optIgnoreStd;
390 Pass& NewJoos1WParserPass(PassManager& PM,
SourceFile file, Pass* prev) {
391 using namespace joos1;
392 return PM.AddPass<Joos1WParserPass>(file, prev);
395 Pass& NewAstBuilderPass(PassManager& PM, Pass* depends) {
396 using namespace joos1;
398 auto* p = cast<Joos1WParserPass*>(depends);
399 return PM.AddPass<AstBuilderPass>(*p);