mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 07:52:45 +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 } }; |     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> TreeList::subtrees() | ||||||
| { | { | ||||||
|     Vector<NodeSubtreePointer> result; |     Vector<NodeSubtreePointer> result; | ||||||
|  |  | ||||||
|  | @ -277,6 +277,29 @@ protected: | ||||||
|     void dump_tree(StringBuilder& builder) override; |     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 { | class TreeList : public Node { | ||||||
| public: | public: | ||||||
|     TreeList(Vector<Tree>&& expressions_) |     TreeList(Vector<Tree>&& expressions_) | ||||||
|  |  | ||||||
|  | @ -97,6 +97,18 @@ void ElseIfBranch::dump_tree(StringBuilder& builder) | ||||||
|     m_branch->format_tree(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) | void TreeList::dump_tree(StringBuilder& builder) | ||||||
| { | { | ||||||
|     dump_node(builder, "TreeList"); |     dump_node(builder, "TreeList"); | ||||||
|  |  | ||||||
|  | @ -3,6 +3,7 @@ set(SOURCES | ||||||
|     AST/ASTPrinting.cpp |     AST/ASTPrinting.cpp | ||||||
|     Compiler/FunctionCallCanonicalizationPass.cpp |     Compiler/FunctionCallCanonicalizationPass.cpp | ||||||
|     Compiler/GenericASTPass.cpp |     Compiler/GenericASTPass.cpp | ||||||
|  |     Compiler/IfBranchMergingPass.cpp | ||||||
|     Parser/Lexer.cpp |     Parser/Lexer.cpp | ||||||
|     Parser/ParseError.cpp |     Parser/ParseError.cpp | ||||||
|     Parser/SpecParser.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 AssertExpression; | ||||||
| class IfBranch; | class IfBranch; | ||||||
| class ElseIfBranch; | class ElseIfBranch; | ||||||
|  | class IfElseIfChain; | ||||||
| class TreeList; | class TreeList; | ||||||
| class RecordDirectListInitialization; | class RecordDirectListInitialization; | ||||||
| class FunctionCall; | class FunctionCall; | ||||||
|  |  | ||||||
|  | @ -10,6 +10,7 @@ | ||||||
| #include <LibXML/Parser/Parser.h> | #include <LibXML/Parser/Parser.h> | ||||||
| 
 | 
 | ||||||
| #include "Compiler/FunctionCallCanonicalizationPass.h" | #include "Compiler/FunctionCallCanonicalizationPass.h" | ||||||
|  | #include "Compiler/IfBranchMergingPass.h" | ||||||
| #include "Function.h" | #include "Function.h" | ||||||
| #include "Parser/SpecParser.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); |     auto function = make_ref_counted<JSSpecCompiler::Function>(&context, spec_function.m_name, spec_function.m_algorithm.m_tree); | ||||||
| 
 | 
 | ||||||
|     FunctionCallCanonicalizationPass(function).run(); |     FunctionCallCanonicalizationPass(function).run(); | ||||||
|  |     IfBranchMergingPass(function).run(); | ||||||
| 
 | 
 | ||||||
|     out("{}", function->m_ast); |     out("{}", function->m_ast); | ||||||
|     return 0; |     return 0; | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Dan Klishch
						Dan Klishch