mirror of
https://github.com/RGBCube/serenity
synced 2025-05-18 08:15:06 +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