mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 08:52:44 +00:00 
			
		
		
		
	JSSpecCompiler: Add if branch merging pass
It merges standalone IfBranch/ElseIfBranch nodes into IfElseIfChain nodes. This will ease CFG generation later.
This commit is contained in:
		
							parent
							
								
									092ed1cc8a
								
							
						
					
					
						commit
						4c4e1e1aed
					
				
					 8 changed files with 184 additions and 0 deletions
				
			
		|  | @ -71,6 +71,18 @@ Vector<NodeSubtreePointer> ElseIfBranch::subtrees() | |||
|     return { { &m_branch } }; | ||||
| } | ||||
| 
 | ||||
| Vector<NodeSubtreePointer> IfElseIfChain::subtrees() | ||||
| { | ||||
|     Vector<NodeSubtreePointer> result; | ||||
|     for (size_t i = 0; i < branches_count(); ++i) { | ||||
|         result.append({ &m_conditions[i] }); | ||||
|         result.append({ &m_branches[i] }); | ||||
|     } | ||||
|     if (m_else_branch) | ||||
|         result.append({ &m_else_branch }); | ||||
|     return result; | ||||
| } | ||||
| 
 | ||||
| Vector<NodeSubtreePointer> TreeList::subtrees() | ||||
| { | ||||
|     Vector<NodeSubtreePointer> result; | ||||
|  |  | |||
|  | @ -277,6 +277,29 @@ protected: | |||
|     void dump_tree(StringBuilder& builder) override; | ||||
| }; | ||||
| 
 | ||||
| class IfElseIfChain : public Node { | ||||
| public: | ||||
|     IfElseIfChain(Vector<Tree>&& conditions, Vector<Tree>&& branches, NullableTree else_branch) | ||||
|         : m_conditions(move(conditions)) | ||||
|         , m_branches(move(branches)) | ||||
|         , m_else_branch(else_branch) | ||||
|     { | ||||
|         VERIFY(m_branches.size() == m_conditions.size()); | ||||
|     } | ||||
| 
 | ||||
|     Vector<NodeSubtreePointer> subtrees() override; | ||||
| 
 | ||||
|     // Excluding else branch, if one is present
 | ||||
|     size_t branches_count() { return m_branches.size(); } | ||||
| 
 | ||||
|     Vector<Tree> m_conditions; | ||||
|     Vector<Tree> m_branches; | ||||
|     NullableTree m_else_branch; | ||||
| 
 | ||||
| protected: | ||||
|     void dump_tree(StringBuilder& builder) override; | ||||
| }; | ||||
| 
 | ||||
| class TreeList : public Node { | ||||
| public: | ||||
|     TreeList(Vector<Tree>&& expressions_) | ||||
|  |  | |||
|  | @ -97,6 +97,18 @@ void ElseIfBranch::dump_tree(StringBuilder& builder) | |||
|     m_branch->format_tree(builder); | ||||
| } | ||||
| 
 | ||||
| void IfElseIfChain::dump_tree(StringBuilder& builder) | ||||
| { | ||||
|     dump_node(builder, "IfElseIfChain"); | ||||
| 
 | ||||
|     for (size_t i = 0; i < branches_count(); ++i) { | ||||
|         m_conditions[i]->format_tree(builder); | ||||
|         m_branches[i]->format_tree(builder); | ||||
|     } | ||||
|     if (m_else_branch) | ||||
|         m_else_branch->format_tree(builder); | ||||
| } | ||||
| 
 | ||||
| void TreeList::dump_tree(StringBuilder& builder) | ||||
| { | ||||
|     dump_node(builder, "TreeList"); | ||||
|  |  | |||
|  | @ -3,6 +3,7 @@ set(SOURCES | |||
|     AST/ASTPrinting.cpp | ||||
|     Compiler/FunctionCallCanonicalizationPass.cpp | ||||
|     Compiler/GenericASTPass.cpp | ||||
|     Compiler/IfBranchMergingPass.cpp | ||||
|     Parser/Lexer.cpp | ||||
|     Parser/ParseError.cpp | ||||
|     Parser/SpecParser.cpp | ||||
|  |  | |||
|  | @ -0,0 +1,97 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #include <AK/TypeCasts.h> | ||||
| 
 | ||||
| #include "AST/AST.h" | ||||
| #include "Compiler/IfBranchMergingPass.h" | ||||
| 
 | ||||
| namespace JSSpecCompiler { | ||||
| 
 | ||||
| RecursionDecision IfBranchMergingPass::on_entry(Tree tree) | ||||
| { | ||||
|     if (auto list = as<TreeList>(tree); list) { | ||||
|         Vector<Tree> result; | ||||
|         Vector<Tree> unmerged_branches; | ||||
| 
 | ||||
|         auto merge_if_needed = [&] { | ||||
|             if (!unmerged_branches.is_empty()) { | ||||
|                 result.append(merge_branches(unmerged_branches)); | ||||
|                 unmerged_branches.clear(); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         for (auto const& node : list->m_expressions) { | ||||
|             if (is<IfBranch>(node.ptr())) { | ||||
|                 merge_if_needed(); | ||||
|                 unmerged_branches.append(node); | ||||
|             } else if (is<ElseIfBranch>(node.ptr())) { | ||||
|                 unmerged_branches.append(node); | ||||
|             } else { | ||||
|                 merge_if_needed(); | ||||
|                 result.append(node); | ||||
|             } | ||||
|         } | ||||
|         merge_if_needed(); | ||||
| 
 | ||||
|         list->m_expressions = move(result); | ||||
|     } | ||||
|     return RecursionDecision::Recurse; | ||||
| } | ||||
| 
 | ||||
| Tree IfBranchMergingPass::merge_branches(Vector<Tree> const& unmerged_branches) | ||||
| { | ||||
|     static const Tree error = make_ref_counted<ErrorNode>("Cannot make sense of if-elseif-else chain"sv); | ||||
| 
 | ||||
|     VERIFY(unmerged_branches.size() >= 1); | ||||
| 
 | ||||
|     Vector<Tree> conditions; | ||||
|     Vector<Tree> branches; | ||||
|     NullableTree else_branch; | ||||
| 
 | ||||
|     if (auto if_branch = as<IfBranch>(unmerged_branches[0]); if_branch) { | ||||
|         conditions.append(if_branch->m_condition); | ||||
|         branches.append(if_branch->m_branch); | ||||
|     } else { | ||||
|         return error; | ||||
|     } | ||||
| 
 | ||||
|     for (size_t i = 1; i < unmerged_branches.size(); ++i) { | ||||
|         auto branch = as<ElseIfBranch>(unmerged_branches[i]); | ||||
| 
 | ||||
|         if (!branch) | ||||
|             return error; | ||||
| 
 | ||||
|         if (!branch->m_condition) { | ||||
|             // There might be situation like:
 | ||||
|             //   1. If <condition>, then
 | ||||
|             //      ...
 | ||||
|             //   2. Else,
 | ||||
|             //      a. If <condition>, then
 | ||||
|             //         ...
 | ||||
|             //   3. Else,
 | ||||
|             //      ...
 | ||||
|             auto substep_list = as<TreeList>(branch->m_branch); | ||||
|             if (substep_list && substep_list->m_expressions.size() == 1) { | ||||
|                 if (auto nested_if = as<IfBranch>(substep_list->m_expressions[0]); nested_if) | ||||
|                     branch = make_ref_counted<ElseIfBranch>(nested_if->m_condition, nested_if->m_branch); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (branch->m_condition) { | ||||
|             conditions.append(branch->m_condition.release_nonnull()); | ||||
|             branches.append(branch->m_branch); | ||||
|         } else { | ||||
|             if (i + 1 != unmerged_branches.size()) | ||||
|                 return error; | ||||
|             else_branch = branch->m_branch; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return make_ref_counted<IfElseIfChain>(move(conditions), move(branches), else_branch); | ||||
| } | ||||
| 
 | ||||
| } | ||||
|  | @ -0,0 +1,36 @@ | |||
| /*
 | ||||
|  * Copyright (c) 2023, Dan Klishch <danilklishch@gmail.com> | ||||
|  * | ||||
|  * SPDX-License-Identifier: BSD-2-Clause | ||||
|  */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "Compiler/GenericASTPass.h" | ||||
| 
 | ||||
| namespace JSSpecCompiler { | ||||
| 
 | ||||
| // IfBranchMergingPass, unsurprisingly, merges if-elseif-else chains, represented as a separate
 | ||||
| // nodes after parsing, into one IfElseIfChain node. It also deals with the following nonsense from
 | ||||
| // the spec:
 | ||||
| // ```
 | ||||
| //   1. If <condition>, then
 | ||||
| //      ...
 | ||||
| //   2. Else,
 | ||||
| //      a. If <condition>, then
 | ||||
| //         ...
 | ||||
| //   3. Else,
 | ||||
| //      ...
 | ||||
| // ```
 | ||||
| class IfBranchMergingPass : public GenericASTPass { | ||||
| public: | ||||
|     using GenericASTPass::GenericASTPass; | ||||
| 
 | ||||
| protected: | ||||
|     RecursionDecision on_entry(Tree tree) override; | ||||
| 
 | ||||
| private: | ||||
|     static Tree merge_branches(Vector<Tree> const& unmerged_branches); | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | @ -29,6 +29,7 @@ class ReturnExpression; | |||
| class AssertExpression; | ||||
| class IfBranch; | ||||
| class ElseIfBranch; | ||||
| class IfElseIfChain; | ||||
| class TreeList; | ||||
| class RecordDirectListInitialization; | ||||
| class FunctionCall; | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #include <LibXML/Parser/Parser.h> | ||||
| 
 | ||||
| #include "Compiler/FunctionCallCanonicalizationPass.h" | ||||
| #include "Compiler/IfBranchMergingPass.h" | ||||
| #include "Function.h" | ||||
| #include "Parser/SpecParser.h" | ||||
| 
 | ||||
|  | @ -39,6 +40,7 @@ ErrorOr<int> serenity_main(Main::Arguments) | |||
|     auto function = make_ref_counted<JSSpecCompiler::Function>(&context, spec_function.m_name, spec_function.m_algorithm.m_tree); | ||||
| 
 | ||||
|     FunctionCallCanonicalizationPass(function).run(); | ||||
|     IfBranchMergingPass(function).run(); | ||||
| 
 | ||||
|     out("{}", function->m_ast); | ||||
|     return 0; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Klishch
						Dan Klishch