unsupported/tools/rustgen/rustgen.cpp (455 lines of code) (raw):

/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */ #include "llvh/ADT/StringSwitch.h" #include "llvh/Support/raw_ostream.h" #include <map> #include <vector> enum class FieldType { Error, NodeString, NodeLabel, Boolean, Number, NodePtr, NodeList }; static const llvh::StringLiteral typeName_[][2] = { {"**ERROR**", "**ERROR**"}, {"NodeString", "NodeStringOpt"}, {"NodeLabel", "NodeLabelOpt"}, {"bool", "bool"}, {"f64", "f64"}, {"NodePtr", "NodePtrOpt"}, {"NodeListRef", "NodeListOptRef"}, }; struct Field { FieldType type; std::string name; bool optional; Field(FieldType type, std::string const &name, bool optional) : type(type), name(name), optional(optional) {} llvh::StringRef typeName() const { return typeName_[(int)type][optional]; } std::string rustName() const { if (name == "async" || name == "await" || name == "static") return "is_" + name; bool fix = false; for (char c : name) { if (isupper(c)) { fix = true; break; } } if (!fix) return name; std::string res; for (char c : name) { if (isupper(c)) { res.push_back('_'); res.push_back(tolower(c)); } else { res.push_back(c); } } return res; } }; enum class SentinelType { None, First, Last, }; struct TreeClass { std::string name; std::string base; SentinelType sentinel; std::vector<Field> fields; TreeClass( std::string const &name, std::string const &base, SentinelType sentinel = SentinelType::None) : name(name), base(base), sentinel(sentinel) {} std::string enumName() const { if (sentinel == SentinelType::First) return "_" + name + "First"; else if (sentinel == SentinelType::Last) return "_" + name + "Last"; else return name; } std::string className() const { return name + "Node"; } llvh::Optional<std::string> getDecoration() const { // FIXME: obviously this is just an ugly hardcoded hack. bool haveDecoration = false; if (sentinel == SentinelType::First) { haveDecoration = llvh::StringSwitch<bool>(name) .Case("Statement", false) .Case("CallExpressionLike", false) .Case("MemberExpressionLike", false) .Case("Pattern", false) .Case("Cover", false) .Default(true); } else if (sentinel == SentinelType::None) { haveDecoration = llvh::StringSwitch<bool>(name) .Case("BlockStatement", true) .Case("BreakStatement", true) .Case("ContinueStatement", true) .Case("SwitchStatement", true) .Case("LabeledStatement", true) .Case("Program", true) .Default(false); } if (!haveDecoration) return llvh::None; return name + "Decoration"; } }; static std::vector<TreeClass> treeClasses_{}; static std::map<std::string, size_t> treeNames_{}; static void initClasses() { auto baseName = [](llvh::StringRef n) { return n == "Base" ? "" : n; }; auto type = [](llvh::StringRef n) { return llvh::StringSwitch<FieldType>(n) .Case("NodeString", FieldType::NodeString) .Case("NodeLabel", FieldType::NodeLabel) .Case("NodeBoolean", FieldType::Boolean) .Case("NodeNumber", FieldType::Number) .Case("NodePtr", FieldType::NodePtr) .Case("NodeList", FieldType::NodeList) .Default(FieldType::Error); }; #define STRUCT(NAME, BASE) \ treeClasses_.emplace_back(#NAME, baseName(#BASE)); \ treeNames_.emplace(#NAME, treeClasses_.size() - 1) #define ARG(TY, NM, OPT) \ treeClasses_.back().fields.emplace_back(type(#TY), #NM, OPT) #define ESTREE_FIRST(NAME, BASE) \ treeClasses_.emplace_back(#NAME, baseName(#BASE), SentinelType::First); \ treeNames_.emplace(#NAME, treeClasses_.size() - 1); #define ESTREE_LAST(NAME) \ treeClasses_.emplace_back(#NAME, "", SentinelType::Last); #define ESTREE_NODE_0_ARGS(NAME, BASE) STRUCT(NAME, BASE); #define ESTREE_NODE_1_ARGS(NAME, BASE, ARG0TY, ARG0NM, ARG0OPT) \ STRUCT(NAME, BASE); \ ARG(ARG0TY, ARG0NM, ARG0OPT); #define ESTREE_NODE_2_ARGS( \ NAME, BASE, ARG0TY, ARG0NM, ARG0OPT, ARG1TY, ARG1NM, ARG1OPT) \ STRUCT(NAME, BASE); \ ARG(ARG0TY, ARG0NM, ARG0OPT); \ ARG(ARG1TY, ARG1NM, ARG1OPT); #define ESTREE_NODE_3_ARGS( \ NAME, \ BASE, \ ARG0TY, \ ARG0NM, \ ARG0OPT, \ ARG1TY, \ ARG1NM, \ ARG1OPT, \ ARG2TY, \ ARG2NM, \ ARG2OPT) \ STRUCT(NAME, BASE); \ ARG(ARG0TY, ARG0NM, ARG0OPT); \ ARG(ARG1TY, ARG1NM, ARG1OPT); \ ARG(ARG2TY, ARG2NM, ARG2OPT); #define ESTREE_NODE_4_ARGS( \ NAME, \ BASE, \ ARG0TY, \ ARG0NM, \ ARG0OPT, \ ARG1TY, \ ARG1NM, \ ARG1OPT, \ ARG2TY, \ ARG2NM, \ ARG2OPT, \ ARG3TY, \ ARG3NM, \ ARG3OPT) \ STRUCT(NAME, BASE); \ ARG(ARG0TY, ARG0NM, ARG0OPT); \ ARG(ARG1TY, ARG1NM, ARG1OPT); \ ARG(ARG2TY, ARG2NM, ARG2OPT); \ ARG(ARG3TY, ARG3NM, ARG3OPT); #define ESTREE_NODE_5_ARGS( \ NAME, \ BASE, \ ARG0TY, \ ARG0NM, \ ARG0OPT, \ ARG1TY, \ ARG1NM, \ ARG1OPT, \ ARG2TY, \ ARG2NM, \ ARG2OPT, \ ARG3TY, \ ARG3NM, \ ARG3OPT, \ ARG4TY, \ ARG4NM, \ ARG4OPT) \ STRUCT(NAME, BASE); \ ARG(ARG0TY, ARG0NM, ARG0OPT); \ ARG(ARG1TY, ARG1NM, ARG1OPT); \ ARG(ARG2TY, ARG2NM, ARG2OPT); \ ARG(ARG3TY, ARG3NM, ARG3OPT); \ ARG(ARG4TY, ARG4NM, ARG4OPT); #define ESTREE_NODE_6_ARGS( \ NAME, \ BASE, \ ARG0TY, \ ARG0NM, \ ARG0OPT, \ ARG1TY, \ ARG1NM, \ ARG1OPT, \ ARG2TY, \ ARG2NM, \ ARG2OPT, \ ARG3TY, \ ARG3NM, \ ARG3OPT, \ ARG4TY, \ ARG4NM, \ ARG4OPT, \ ARG5TY, \ ARG5NM, \ ARG5OPT) \ STRUCT(NAME, BASE); \ ARG(ARG0TY, ARG0NM, ARG0OPT); \ ARG(ARG1TY, ARG1NM, ARG1OPT); \ ARG(ARG2TY, ARG2NM, ARG2OPT); \ ARG(ARG3TY, ARG3NM, ARG3OPT); \ ARG(ARG4TY, ARG4NM, ARG4OPT); \ ARG(ARG5TY, ARG5NM, ARG5OPT); #define ESTREE_NODE_7_ARGS( \ NAME, \ BASE, \ ARG0TY, \ ARG0NM, \ ARG0OPT, \ ARG1TY, \ ARG1NM, \ ARG1OPT, \ ARG2TY, \ ARG2NM, \ ARG2OPT, \ ARG3TY, \ ARG3NM, \ ARG3OPT, \ ARG4TY, \ ARG4NM, \ ARG4OPT, \ ARG5TY, \ ARG5NM, \ ARG5OPT, \ ARG6TY, \ ARG6NM, \ ARG6OPT) \ STRUCT(NAME, BASE); \ ARG(ARG0TY, ARG0NM, ARG0OPT); \ ARG(ARG1TY, ARG1NM, ARG1OPT); \ ARG(ARG2TY, ARG2NM, ARG2OPT); \ ARG(ARG3TY, ARG3NM, ARG3OPT); \ ARG(ARG4TY, ARG4NM, ARG4OPT); \ ARG(ARG5TY, ARG5NM, ARG5OPT); \ ARG(ARG6TY, ARG6NM, ARG6OPT); #define ESTREE_NODE_8_ARGS( \ NAME, \ BASE, \ ARG0TY, \ ARG0NM, \ ARG0OPT, \ ARG1TY, \ ARG1NM, \ ARG1OPT, \ ARG2TY, \ ARG2NM, \ ARG2OPT, \ ARG3TY, \ ARG3NM, \ ARG3OPT, \ ARG4TY, \ ARG4NM, \ ARG4OPT, \ ARG5TY, \ ARG5NM, \ ARG5OPT, \ ARG6TY, \ ARG6NM, \ ARG6OPT, \ ARG7TY, \ ARG7NM, \ ARG7OPT) \ STRUCT(NAME, BASE); \ ARG(ARG0TY, ARG0NM, ARG0OPT); \ ARG(ARG1TY, ARG1NM, ARG1OPT); \ ARG(ARG2TY, ARG2NM, ARG2OPT); \ ARG(ARG3TY, ARG3NM, ARG3OPT); \ ARG(ARG4TY, ARG4NM, ARG4OPT); \ ARG(ARG5TY, ARG5NM, ARG5OPT); \ ARG(ARG6TY, ARG6NM, ARG6OPT); \ ARG(ARG7TY, ARG7NM, ARG7OPT); #include "hermes/AST/ESTree.def" } /* /// Get all decorations for a class, recursively. void getAllDecorations( const TreeClass &cls, std::vector<std::string> &decs) { if (!cls.base.empty()) getAllDecorations(treeClasses_[treeNames_.find(cls.base)->second], decs); auto decoration = cls.getDecoration(); if (decoration) decs.push_back(std::move(*decoration)); } */ static void genGetters() { auto genStruct = [](const TreeClass &cls) { if (cls.sentinel != SentinelType::None) return; if (cls.fields.empty()) return; llvh::outs() << " // " << cls.name << "\n"; for (const auto &fld : cls.fields) { llvh::outs() << " pub fn hermes_get_" << cls.name << "_" << fld.name << "(node: NodePtr) -> "; llvh::outs() << fld.typeName() << ";\n"; } }; llvh::outs() << "extern \"C\" {\n"; for (const auto &cls : treeClasses_) { if (cls.sentinel != SentinelType::None) continue; genStruct(cls); } llvh::outs() << "}\n"; } static void genConvert() { llvh::outs() << "pub unsafe fn cvt_node_ptr<'parser, 'gc>(\n" " cvt: &mut Converter<'parser>, \n" " gc: &'gc ast::GCLock, \n" " n: NodePtr) -> &'gc ast::Node<'gc> {\n"; llvh::outs() << " let nr = n.as_ref();\n" " let range = ast::SourceRange {\n" " file: cvt.file_id,\n" " start: cvt.cvt_smloc(nr.source_range.start),\n" " end: ast::SourceLoc::invalid(),\n" " };\n" "\n"; llvh::outs() << " let res = match nr.kind {\n"; auto genStruct = [](const TreeClass &cls) { if (cls.sentinel != SentinelType::None) return; if (strncmp(cls.name.c_str(), "Cover", 5) == 0) return; llvh::outs() << " NodeKind::" << cls.name << " => {\n"; // Declare all the fields as local vars to avoid multiple borrows // of the context. for (const auto &fld : cls.fields) { llvh::outs() << " let " << fld.rustName() << " = "; bool close = true; switch (fld.type) { case FieldType::NodeString: llvh::outs() << "cvt_string" << (fld.optional ? "_opt" : "") << "("; break; case FieldType::NodeLabel: if ((cls.name == "UnaryExpression" && fld.name == "operator") || (cls.name == "BinaryExpression" && fld.name == "operator") || (cls.name == "LogicalExpression" && fld.name == "operator") || (cls.name == "UpdateExpression" && fld.name == "operator") || (cls.name == "VariableDeclaration" && fld.name == "kind") || (cls.name == "Property" && fld.name == "kind") || (cls.name == "MethodDefinition" && fld.name == "kind") || (cls.name == "ImportDeclaration" && fld.name == "importKind") || (cls.name == "ImportSpecifier" && fld.name == "importKind") || (cls.name == "ExportNamedDeclaration" && fld.name == "exportKind") || (cls.name == "ExportAllDeclaration" && fld.name == "exportKind") || (cls.name == "AssignmentExpression" && fld.name == "operator")) llvh::outs() << "cvt_enum("; else llvh::outs() << "cvt.cvt_label" << (fld.optional ? "_opt" : "") << "(gc, "; break; case FieldType::Boolean: case FieldType::Number: default: close = false; break; case FieldType::NodePtr: llvh::outs() << "cvt_node_ptr" << (fld.optional ? "_opt" : "") << "(cvt, gc, "; break; case FieldType::NodeList: llvh::outs() << "cvt_node_list" << (fld.optional ? "_opt" : "") << "(cvt, gc, "; break; } llvh::outs() << "hermes_get_" << cls.name << "_" << fld.name << "(n)"; if (close) llvh::outs() << ")"; llvh::outs() << ";\n"; } llvh::outs() << " let mut template = ast::template::" << cls.name << " {\n" << " metadata: ast::TemplateMetadata {range, ..Default::default()},\n"; for (const auto &fld : cls.fields) { // Shorthand initialization of each field. llvh::outs() << " " << fld.rustName() << ",\n"; } llvh::outs() << " };\n" // kind " template.metadata.range.end = cvt.cvt_smloc(nr.source_range.end.pred());\n" << " ast::builder::" << cls.name << "::build_template(gc, template)\n" << " }\n"; // match block }; for (const auto &cls : treeClasses_) { if (cls.sentinel != SentinelType::None) continue; genStruct(cls); } llvh::outs() << " _ => {\n" " cvt.report_invalid_node(gc, n, range);\n" " let template = ast::template::Empty {\n" " metadata: ast::TemplateMetadata {range, ..Default::default()}\n" " };\n" " ast::builder::Empty::build_template(gc, template)\n" " }\n" " };\n\n"; llvh::outs() << " res\n"; llvh::outs() << "}\n"; } static void genEnum() { llvh::outs() << "#[repr(u32)]\n"; llvh::outs() << "#[derive(Debug, PartialEq)]\n"; llvh::outs() << "pub enum NodeKind {\n"; for (const auto &cls : treeClasses_) { llvh::outs() << " " << cls.enumName(); llvh::outs() << ",\n"; } llvh::outs() << "}\n"; } int main(int argc, char **argv) { if (argc != 2) { llvh::errs() << "syntax: " << argv[0] << " ffi|cvt\n"; return 1; } initClasses(); llvh::outs() << "/*\n" " * Copyright (c) Meta Platforms, Inc. and affiliates.\n" " *\n" " * This source code is licensed under the MIT license found in the\n" " * LICENSE file in the root directory of this source tree.\n" " */\n\n"; llvh::outs() << "// @" "generated by Hermes rustgen\n"; llvh::outs() << "// DO NOT EDIT\n\n"; if (llvh::StringRef(argv[1]) == "ffi") { llvh::outs() << "use super::node::*;\n\n"; genEnum(); llvh::outs() << "\n"; genGetters(); } else if (llvh::StringRef(argv[1]) == "cvt") { llvh::outs() << "use hermes::parser::*;\n" "use super::convert::*;\n" "use crate::ast;\n\n"; genConvert(); } else { llvh::errs() << "Invalid command\n"; return 1; } return 0; }