diff --git a/Dockerfile b/Dockerfile index cb01866..6b9c665 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,6 @@ # syntax=docker/dockerfile:1.4 ARG BASE_IMAGE=ubuntu:noble FROM ${BASE_IMAGE} -WORKDIR /autograder WORKDIR /opt/SYsU-lang COPY </autograder/results/results.json @@ -41,6 +39,7 @@ apt-get install -y --no-install-recommends \ apt-get autoremove -y apt-get clean -y rm -rf /var/lib/apt/lists/* +mkdir -p /autograder/results mv /opt/SYsU-lang/run.sh /autograder/run chmod +x /autograder/run chmod +x /opt/SYsU-lang/build_install.sh diff --git a/grammar/README.md b/grammar/README.md index 8c75bcf..d251d56 100644 --- a/grammar/README.md +++ b/grammar/README.md @@ -274,6 +274,12 @@ flowchart TD; 2. 输出到 json,因为 json 格式非常容易理解,不需要像 [LLVM 官方教程](https://releases.llvm.org/17.0.0/docs/tutorial/MyFirstLanguageFrontend/LangImpl02.html) 一样定义很多节点。 3. 输出到 `llvm::json`,可以让同学们提前上手 LLVM 库的使用,平滑下一个实验的难度。 +### Q & A:该选择 Visitor 还是 Listener? + +TL;DR: Vistor 更符合任务需求,但是 Listener 仍然更加趁手。 + +Visitor 有更强的可定制性,但是需要自行实现每个节点到子节点的访问规则;Listener 则适合更轻量的任务,例如只提取某些语法规则的上下文。在本实验中,按道理每个节点都需要被访问,因此 Vistor 更符合任务需求,Listener 的优势没有那么大。然而,根据助教的使用经验,Listener 仍然是一个更常用且好用的选择。 + ## 评分规则 本实验的评分分为两部分:基础部分和挑战部分。 diff --git a/grammar/main.cc b/grammar/main.cc index 31dfeb9..c9b59b1 100644 --- a/grammar/main.cc +++ b/grammar/main.cc @@ -6,6 +6,7 @@ #include "CVisitor.h" #include #include +#include #include #include #include @@ -48,27 +49,28 @@ int main(int argc, char **argv) { return 0; } sysu_grammar::CParser parser(&tokens); -#ifndef SYSU_GRAMMAR_USE_LISTENER // 任 君 选 择 - struct Visitor : sysu_grammar::CBaseVisitor { - antlrcpp::Any visitCompilationUnit( - sysu_grammar::CParser::CompilationUnitContext *ctx) override { - return visit(ctx->translationUnit()); - } - antlrcpp::Any visitTranslationUnit( +#ifndef SYSU_GRAMMAR_NO_USE_LISTENER // 任 君 选 择 + struct Listener : sysu_grammar::CBaseListener { + llvm::json::Array stak; + void exitTranslationUnit( sysu_grammar::CParser::TranslationUnitContext *ctx) override { llvm::json::Value ret = llvm::json::Object{ {"kind", "TranslationUnitDecl"}, {"inner", llvm::json::Array{}}}; - for (auto child : ctx->externalDeclaration()) { - ret.getAsObject()->get("inner")->getAsArray()->push_back( - visit(child).as()); + ret.getAsObject()->get("inner")->getAsArray()->reserve( + ctx->externalDeclaration().size()); + for (const auto &child : ctx->externalDeclaration()) { + ret.getAsObject()->get("inner")->getAsArray()->push_back(stak.back()); + stak.pop_back(); } - return ret; + std::reverse(ret.getAsObject()->get("inner")->getAsArray()->begin(), + ret.getAsObject()->get("inner")->getAsArray()->end()); + stak.push_back(ret); } - // 请续写 visitExternalDeclaration 及之后的遍历逻辑,将 ParseTree 转换为 + // 请续写 exitExternalDeclaration 及之后的遍历逻辑,将 ParseTree 转换为 // json 格式的 AbstractSyntaxTree,简而言之就是把树拍扁 - antlrcpp::Any visitExternalDeclaration( + void exitExternalDeclaration( sysu_grammar::CParser::ExternalDeclarationContext *ctx) override { - return llvm::json::Value(llvm::json::Object{ + stak.push_back(llvm::json::Object{ {"kind", "FunctionDecl"}, {"name", "main"}, {"inner", @@ -82,32 +84,31 @@ int main(int argc, char **argv) { }}}, }}}}); } - }; - llvm::outs() - << Visitor().visit(parser.compilationUnit()).as() - << "\n"; + } listener; + antlr4::tree::ParseTreeWalker::DEFAULT.walk(&listener, + parser.compilationUnit()); + llvm::outs() << listener.stak.back() << "\n"; #else - struct Listener : sysu_grammar::CBaseListener { - llvm::json::Array stak; - void exitTranslationUnit( + struct Visitor : sysu_grammar::CBaseVisitor { + antlrcpp::Any visitCompilationUnit( + sysu_grammar::CParser::CompilationUnitContext *ctx) override { + return visit(ctx->translationUnit()); + } + antlrcpp::Any visitTranslationUnit( sysu_grammar::CParser::TranslationUnitContext *ctx) override { llvm::json::Value ret = llvm::json::Object{ {"kind", "TranslationUnitDecl"}, {"inner", llvm::json::Array{}}}; - ret.getAsObject()->get("inner")->getAsArray()->reserve( - ctx->externalDeclaration().size()); - for (const auto &child : ctx->externalDeclaration()) { - ret.getAsObject()->get("inner")->getAsArray()->push_back(stak.back()); - stak.pop_back(); + for (auto child : ctx->externalDeclaration()) { + ret.getAsObject()->get("inner")->getAsArray()->push_back( + std::any_cast(visit(child))); } - std::reverse(ret.getAsObject()->get("inner")->getAsArray()->begin(), - ret.getAsObject()->get("inner")->getAsArray()->end()); - stak.push_back(ret); + return ret; } - // 请续写 exitExternalDeclaration 及之后的遍历逻辑,将 ParseTree 转换为 + // 请续写 visitExternalDeclaration 及之后的遍历逻辑,将 ParseTree 转换为 // json 格式的 AbstractSyntaxTree,简而言之就是把树拍扁 - void exitExternalDeclaration( + antlrcpp::Any visitExternalDeclaration( sysu_grammar::CParser::ExternalDeclarationContext *ctx) override { - stak.push_back(llvm::json::Object{ + return llvm::json::Value(llvm::json::Object{ {"kind", "FunctionDecl"}, {"name", "main"}, {"inner", @@ -121,9 +122,9 @@ int main(int argc, char **argv) { }}}, }}}}); } - } listener; - antlr4::tree::ParseTreeWalker::DEFAULT.walk(&listener, - parser.compilationUnit()); - llvm::outs() << listener.stak.back() << "\n"; + }; + llvm::outs() << std::any_cast( + Visitor().visit(parser.compilationUnit())) + << "\n"; #endif } \ No newline at end of file