Support cloning of nodes in the AST. This will be useful for sharding targets. Change-Id: Ibc4f4f1cf84bf91a2cde8ead520eb4c76a6a6964 Reviewed-on: https://gn-review.googlesource.com/c/gn/+/25600 Reviewed-by: Takuto Ikuta <tikuta@google.com> Commit-Queue: Matt Stark <msta@google.com>
diff --git a/src/gn/operators_unittest.cc b/src/gn/operators_unittest.cc index b812aa9..cdbbb7c 100644 --- a/src/gn/operators_unittest.cc +++ b/src/gn/operators_unittest.cc
@@ -42,6 +42,11 @@ } base::Value GetJSONNode() const override { return base::Value(); } + protected: + std::unique_ptr<ParseNode> CloneImpl() const override { + return std::make_unique<TestParseNode>(value_); + } + private: Value value_; };
diff --git a/src/gn/parse_tree.cc b/src/gn/parse_tree.cc index 03fec79..7328b21 100644 --- a/src/gn/parse_tree.cc +++ b/src/gn/parse_tree.cc
@@ -184,6 +184,10 @@ Comments::~Comments() = default; +std::unique_ptr<Comments> Comments::Clone() const { + return std::unique_ptr<Comments>(new Comments(*this)); +} + void Comments::ReverseSuffix() { for (int i = 0, j = static_cast<int>(suffix_.size() - 1); i < j; ++i, --j) std::swap(suffix_[i], suffix_[j]); @@ -278,6 +282,14 @@ } } +std::unique_ptr<ParseNode> ParseNode::Clone() const { + std::unique_ptr<ParseNode> clone = CloneImpl(); + if (comments_) { + clone->comments_ = comments_->Clone(); + } + return clone; +} + // static std::unique_ptr<ParseNode> ParseNode::BuildFromJSON(const base::Value& value) { const std::string& str_type = value.FindKey(kJsonNodeType)->GetString(); @@ -354,6 +366,16 @@ return dict; } +std::unique_ptr<ParseNode> AccessorNode::CloneImpl() const { + auto node = std::make_unique<AccessorNode>(); + node->set_base(base_); + if (subscript_) + node->set_subscript(subscript_->Clone()); + if (member_) + node->set_member(member_->Clone()); + return node; +} + #define DECLARE_CHILD_AS_LIST_OR_FAIL() \ const base::Value* child = value.FindKey(kJsonNodeChild); \ if (!child || !child->is_list()) { \ @@ -553,6 +575,16 @@ return dict; } +std::unique_ptr<ParseNode> BinaryOpNode::CloneImpl() const { + auto node = std::make_unique<BinaryOpNode>(); + node->set_op(op_); + if (left_) + node->set_left(left_->Clone()); + if (right_) + node->set_right(right_->Clone()); + return node; +} + // static std::unique_ptr<BinaryOpNode> BinaryOpNode::NewFromJSON( const base::Value& value) { @@ -660,6 +692,18 @@ return dict; } +std::unique_ptr<ParseNode> BlockNode::CloneImpl() const { + auto node = std::make_unique<BlockNode>(result_mode_); + node->set_begin_token(begin_token_); + if (end_) + node->set_end(end_->Clone()); + for (const auto& statement : statements_) { + if (statement) + node->append_statement(statement->Clone()); + } + return node; +} + // static std::unique_ptr<BlockNode> BlockNode::NewFromJSON(const base::Value& value) { const std::string& result_mode = value.FindKey(kDumpResultMode)->GetString(); @@ -748,6 +792,18 @@ return dict; } +std::unique_ptr<ParseNode> ConditionNode::CloneImpl() const { + auto node = std::make_unique<ConditionNode>(); + node->set_if_token(if_token_); + if (condition_) + node->set_condition(condition_->Clone()); + if (if_true_) + node->set_if_true(if_true_->Clone()); + if (if_false_) + node->set_if_false(if_false_->Clone()); + return node; +} + // static std::unique_ptr<ConditionNode> ConditionNode::NewFromJSON( const base::Value& value) { @@ -805,6 +861,16 @@ return dict; } +std::unique_ptr<ParseNode> FunctionCallNode::CloneImpl() const { + auto node = std::make_unique<FunctionCallNode>(); + node->set_function(function_); + if (args_) + node->set_args(args_->Clone()); + if (block_) + node->set_block(block_->Clone()); + return node; +} + // static std::unique_ptr<FunctionCallNode> FunctionCallNode::NewFromJSON( const base::Value& value) { @@ -881,6 +947,10 @@ return CreateJSONNode(kDumpNodeName, value_.value(), GetRange()); } +std::unique_ptr<ParseNode> IdentifierNode::CloneImpl() const { + return std::make_unique<IdentifierNode>(value_); +} + // static std::unique_ptr<IdentifierNode> IdentifierNode::NewFromJSON( const base::Value& value) { @@ -947,6 +1017,18 @@ return dict; } +std::unique_ptr<ParseNode> ListNode::CloneImpl() const { + auto node = std::make_unique<ListNode>(); + node->set_begin_token(begin_token_); + if (end_) + node->set_end(end_->Clone()); + for (const auto& item : contents_) { + if (item) + node->append_item(item->Clone()); + } + return node; +} + // static std::unique_ptr<ListNode> ListNode::NewFromJSON(const base::Value& value) { auto ret = std::make_unique<ListNode>(); @@ -1212,6 +1294,16 @@ return CreateJSONNode(kDumpNodeName, value_.value(), GetRange()); } +std::unique_ptr<ParseNode> LiteralNode::CloneImpl() const { + auto node = std::make_unique<LiteralNode>(value_); + if (!shortened_value_.empty()) { + node->shortened_value_ = shortened_value_; + node->value_ = + Token(value_.location(), value_.type(), node->shortened_value_); + } + return node; +} + // static std::unique_ptr<LiteralNode> LiteralNode::NewFromJSON( const base::Value& value) { @@ -1302,6 +1394,14 @@ return dict; } +std::unique_ptr<ParseNode> UnaryOpNode::CloneImpl() const { + auto node = std::make_unique<UnaryOpNode>(); + node->set_op(op_); + if (operand_) + node->set_operand(operand_->Clone()); + return node; +} + // static std::unique_ptr<UnaryOpNode> UnaryOpNode::NewFromJSON( const base::Value& value) { @@ -1341,6 +1441,12 @@ return CreateJSONNode(kDumpNodeName, comment_.value(), GetRange()); } +std::unique_ptr<ParseNode> BlockCommentNode::CloneImpl() const { + auto node = std::make_unique<BlockCommentNode>(); + node->set_comment(comment_); + return node; +} + // static std::unique_ptr<BlockCommentNode> BlockCommentNode::NewFromJSON( const base::Value& value) { @@ -1378,6 +1484,10 @@ return CreateJSONNode(kDumpNodeName, value_.value(), GetRange()); } +std::unique_ptr<ParseNode> EndNode::CloneImpl() const { + return std::make_unique<EndNode>(value_); +} + // static std::unique_ptr<EndNode> EndNode::NewFromJSON(const base::Value& value) { auto ret = std::make_unique<EndNode>(TokenFromValue(value));
diff --git a/src/gn/parse_tree.h b/src/gn/parse_tree.h index 47a7b43..4d23ebb 100644 --- a/src/gn/parse_tree.h +++ b/src/gn/parse_tree.h
@@ -44,6 +44,8 @@ Comments(); virtual ~Comments(); + std::unique_ptr<Comments> Clone() const; + const std::vector<Token>& before() const { return before_; } void append_before(Token c) { before_.push_back(c); } void clear_before() { before_.clear(); } @@ -59,6 +61,9 @@ void append_after(Token c) { after_.push_back(c); } private: + Comments(const Comments&) = default; + Comments& operator=(const Comments&) = delete; + // Whole line comments before the expression. std::vector<Token> before_; @@ -68,9 +73,6 @@ // For top-level expressions only, after_ lists whole-line comments // following the expression. std::vector<Token> after_; - - Comments(const Comments&) = delete; - Comments& operator=(const Comments&) = delete; }; // ParseNode ------------------------------------------------------------------- @@ -133,12 +135,16 @@ // exporting the tree as a JSON or formatted text with indents. virtual base::Value GetJSONNode() const = 0; + // Clones the node. Useful for AST manipulation. + std::unique_ptr<ParseNode> Clone() const; + const Comments* comments() const { return comments_.get(); } Comments* comments_mutable(); static std::unique_ptr<ParseNode> BuildFromJSON(const base::Value& value); protected: + virtual std::unique_ptr<ParseNode> CloneImpl() const = 0; // Helper functions for GetJSONNode. Creates and fills a Value object with // given type (and value). base::Value CreateJSONNode(const char* type, LocationRange location) const; @@ -224,6 +230,9 @@ static constexpr const char* kDumpNodeName = "ACCESSOR"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: Value ExecuteSubscriptAccess(Scope* scope, Err* err) const; Value ExecuteArrayAccess(Scope* scope, @@ -285,6 +294,9 @@ static constexpr const char* kDumpNodeName = "BINARY"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: std::unique_ptr<ParseNode> left_; Token op_; @@ -319,6 +331,10 @@ const std::string& msg, const std::string& help = std::string()) const override; base::Value GetJSONNode() const override; + std::unique_ptr<BlockNode> Clone() const { + return std::unique_ptr<BlockNode>( + static_cast<BlockNode*>(ParseNode::Clone().release())); + } static std::unique_ptr<BlockNode> NewFromJSON(const base::Value& value); void set_begin_token(const Token& t) { begin_token_ = t; } @@ -337,6 +353,9 @@ static constexpr const char* kDumpNodeName = "BLOCK"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: static constexpr const char* kDumpResultMode = "result_mode"; static constexpr const char* kDumpResultModeReturnsScope = "returns_scope"; @@ -392,6 +411,9 @@ static constexpr const char* kDumpNodeName = "CONDITION"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: // Token corresponding to the "if" string. Token if_token_; @@ -436,6 +458,9 @@ static constexpr const char* kDumpNodeName = "FUNCTION"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: Token function_; std::unique_ptr<ListNode> args_; @@ -460,6 +485,10 @@ const std::string& msg, const std::string& help = std::string()) const override; base::Value GetJSONNode() const override; + std::unique_ptr<IdentifierNode> Clone() const { + return std::unique_ptr<IdentifierNode>( + static_cast<IdentifierNode*>(ParseNode::Clone().release())); + } static std::unique_ptr<IdentifierNode> NewFromJSON(const base::Value& value); const Token& value() const { return value_; } @@ -469,6 +498,9 @@ static constexpr const char* kDumpNodeName = "IDENTIFIER"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: Token value_; @@ -490,6 +522,10 @@ const std::string& msg, const std::string& help = std::string()) const override; base::Value GetJSONNode() const override; + std::unique_ptr<ListNode> Clone() const { + return std::unique_ptr<ListNode>( + static_cast<ListNode*>(ParseNode::Clone().release())); + } static std::unique_ptr<ListNode> NewFromJSON(const base::Value& value); void set_begin_token(const Token& t) { begin_token_ = t; } @@ -520,6 +556,9 @@ static constexpr const char* kDumpNodeName = "LIST"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: template <typename Comparator> void SortList(Comparator comparator); @@ -560,6 +599,9 @@ static constexpr const char* kDumpNodeName = "LITERAL"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: Token value_; std::string shortened_value_; @@ -594,6 +636,9 @@ static constexpr const char* kDumpNodeName = "UNARY"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: Token op_; std::unique_ptr<ParseNode> operand_; @@ -629,6 +674,9 @@ static constexpr const char* kDumpNodeName = "BLOCK_COMMENT"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: Token comment_; @@ -654,6 +702,10 @@ const std::string& msg, const std::string& help = std::string()) const override; base::Value GetJSONNode() const override; + std::unique_ptr<EndNode> Clone() const { + return std::unique_ptr<EndNode>( + static_cast<EndNode*>(ParseNode::Clone().release())); + } static std::unique_ptr<EndNode> NewFromJSON(const base::Value& value); const Token& value() const { return value_; } @@ -661,6 +713,9 @@ static constexpr const char* kDumpNodeName = "END"; + protected: + std::unique_ptr<ParseNode> CloneImpl() const override; + private: Token value_;
diff --git a/src/gn/parse_tree_unittest.cc b/src/gn/parse_tree_unittest.cc index e5f41ff..7621bc5 100644 --- a/src/gn/parse_tree_unittest.cc +++ b/src/gn/parse_tree_unittest.cc
@@ -366,3 +366,30 @@ EXPECT_TRUE(err.has_error()); } } + +TEST(ParseTree, Clone) { + TestParseInput input( + "# Top comment\n" + "a = [ \"foo\", \"bar\" ]\n" + "if (a != []) {\n" + " b = a[0]\n" + " c = !false\n" + "}\n" + "print(a)\n"); + EXPECT_SUCCESS(input); + + const ParseNode* original = input.parsed(); + std::unique_ptr<ParseNode> cloned = original->Clone(); + ASSERT_NE(nullptr, cloned); + EXPECT_EQ(original->GetJSONNode(), cloned->GetJSONNode()); + + TestWithScope setup_orig; + Err err_orig; + original->Execute(setup_orig.scope(), &err_orig); + EXPECT_SUCCESS(err_orig); + + TestWithScope setup_clone; + Err err_clone; + cloned->Execute(setup_clone.scope(), &err_clone); + EXPECT_SUCCESS(err_clone); +}