//===--- swift-syntax-parser-test.cpp - Test util for C parser library ----===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2019 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // Testing utility for the C API of the parser library. // //===----------------------------------------------------------------------===// #include "swift-c/SyntaxParser/SwiftSyntaxParser.h" #include "swift/Basic/LLVM.h" #include "swift/Basic/LLVMInitialize.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/Timer.h" using namespace swift; using namespace llvm; enum class ActionType { DumpTree, Time, }; namespace options { static cl::opt Action(cl::desc("Action (required):"), cl::init(ActionType::DumpTree), cl::values( clEnumValN(ActionType::DumpTree, "dump-tree", "Parse the source file and dump syntax tree"), clEnumValN(ActionType::Time, "time", "Time parsing, use '-n' to specify number of invocations"))); static cl::list Filename(cl::Positional, cl::desc("source file"), cl::Required); static cl::opt NumParses("n", cl::desc("number of invocations"), cl::init(1)); } namespace { struct SPNode { swiftparse_syntax_kind_t kind; StringRef nodeText; Optional tokKind; StringRef leadingTriviaText; StringRef tokenText; StringRef trailingTriviaText; std::vector> members; LLVM_DUMP_METHOD void dump() { dump(errs()); } void dump(raw_ostream &OS) { // FIXME: Return the syntax/token kinds directly from the C API, instead // of the serialization number, and print the kind identifier for each, // instead of the number. if (tokKind.hasValue()) { OS << "'; OS << leadingTriviaText << '|' << tokenText << '|' << trailingTriviaText; OS << "'; } else { OS << "'; for (const auto &mn : members) { if (mn) { mn->dump(OS); } else { OS << ""; } } OS << "'; } } }; } static std::unique_ptr convertClientNode(swiftparse_client_node_t client_node) { return std::unique_ptr((SPNode*)client_node); } static size_t trivialLen(ArrayRef trivia) { size_t len = 0; for (const auto &piece : trivia) { len += piece.length; } return len; } static swiftparse_client_node_t makeNode(const swiftparse_syntax_node_t *raw_node, StringRef source) { SPNode *node = new SPNode(); node->kind = raw_node->kind; auto range = raw_node->range; node->nodeText = source.substr(range.offset, range.length); if (raw_node->kind == 0) { node->tokKind = raw_node->token_data.kind; size_t leadingTriviaLen = trivialLen(makeArrayRef(raw_node->token_data.leading_trivia, raw_node->token_data.leading_trivia_count)); size_t trailingTriviaLen = trivialLen(makeArrayRef(raw_node->token_data.trailing_trivia, raw_node->token_data.trailing_trivia_count)); node->leadingTriviaText = node->nodeText.take_front(leadingTriviaLen); node->tokenText = node->nodeText.substr(leadingTriviaLen, range.length-leadingTriviaLen-trailingTriviaLen); node->trailingTriviaText = node->nodeText.take_back(trailingTriviaLen); } else { for (unsigned i = 0, e = raw_node->layout_data.nodes_count; i != e; ++i) { auto subnode = convertClientNode(raw_node->layout_data.nodes[i]); node->members.push_back(std::move(subnode)); } } return node; } static swiftparse_client_node_t parse(const char *source, swiftparse_node_handler_t node_handler) { swiftparse_parser_t parser = swiftparse_parser_create(); swiftparse_parser_set_node_handler(parser, node_handler); swiftparse_client_node_t top = swiftparse_parse_string(parser, source); swiftparse_parser_dispose(parser); return top; } static int dumpTree(const char *source) { swiftparse_node_handler_t nodeHandler = ^swiftparse_client_node_t(const swiftparse_syntax_node_t *raw_node) { return makeNode(raw_node, source); }; std::unique_ptr top = convertClientNode(parse(source, nodeHandler)); top->dump(outs()); return 0; } static void printTimeRecord(unsigned numInvoks, const TimeRecord &total, raw_ostream &OS) { if (total.getUserTime()) OS << " ---User Time---"; if (total.getSystemTime()) OS << " --System Time--"; if (total.getProcessTime()) OS << " --User+System--"; OS << " ---Wall Time---"; if (total.getMemUsed()) OS << " ---Mem---"; OS << '\n'; auto printVal = [&](double Val) { OS << format(" %8.5f ", Val); }; printVal(total.getUserTime()/numInvoks); printVal(total.getSystemTime()/numInvoks); printVal(total.getProcessTime()/numInvoks); printVal(total.getWallTime()/numInvoks); OS << '\n'; } static int timeParsing(const char *source, unsigned numInvoks) { swiftparse_node_handler_t nodeHandler = ^swiftparse_client_node_t(const swiftparse_syntax_node_t *raw_node) { return nullptr; }; Timer timer; timer.startTimer(); for (unsigned i = 0; i != numInvoks; ++i) { parse(source, nodeHandler); } timer.stopTimer(); printTimeRecord(numInvoks, timer.getTotalTime(), outs()); return 0; } int main(int argc, char *argv[]) { PROGRAM_START(argc, argv); cl::ParseCommandLineOptions(argc, argv, "Swift Syntax Parser Test\n"); StringRef fname = options::Filename[0]; auto fileBufOrErr = MemoryBuffer::getFile(fname); if (!fileBufOrErr) { errs() << "error opening file '" << fname << "': " << fileBufOrErr.getError().message(); return 1; } StringRef source = fileBufOrErr.get()->getBuffer(); switch (options::Action) { case ActionType::DumpTree: return dumpTree(source.data()); case ActionType::Time: return timeParsing(source.data(), options::NumParses); } }