Skip to content

Commit

Permalink
Merge pull request #1105 from antmicro/mglb/ExtractTokenPartitionTree…
Browse files Browse the repository at this point in the history
…-LayoutFunctionConversionCode

Refactoring: Extract `TokenPartitionTree` → `LayoutFunction` conversion code
  • Loading branch information
tgorochowik committed Nov 26, 2021
2 parents 00df698 + db9fbf9 commit 036cd0d
Show file tree
Hide file tree
Showing 3 changed files with 457 additions and 227 deletions.
176 changes: 96 additions & 80 deletions common/formatting/layout_optimizer.cc
Expand Up @@ -37,90 +37,14 @@ void OptimizeTokenPartitionTree(const BasicFormatStyle& style,
TokenPartitionTree* node,
std::vector<PreFormatToken>* ftokens) {
CHECK_NOTNULL(node);
CHECK_NOTNULL(ftokens);

VLOG(4) << __FUNCTION__ << ", before:\n" << *node;
const auto indentation = node->Value().IndentationSpaces();

LayoutFunctionFactory factory(style);

const std::function<LayoutFunction(const TokenPartitionTree&)> TraverseTree =
[&TraverseTree, &style, &factory](const TokenPartitionTree& subnode) {
const auto policy = subnode.Value().PartitionPolicy();

if (subnode.is_leaf()) {
return factory.Line(subnode.Value());
}

switch (policy) {
case PartitionPolicyEnum::kOptimalFunctionCallLayout: {
// Support only function/macro/system calls for now
CHECK_EQ(subnode.Children().size(), 2);

const auto& function_header = subnode.Children()[0];
const auto& function_args = subnode.Children()[1];

auto header = TraverseTree(function_header);
auto args = TraverseTree(function_args);

auto stack_layout = factory.Stack({
header,
factory.Indent(args, style.wrap_spaces),
});
if (args.MustWrap()) {
return stack_layout;
}
auto juxtaposed_layout = factory.Juxtaposition({
header,
args,
});
return factory.Choice({
std::move(juxtaposed_layout),
std::move(stack_layout),
});
}

case PartitionPolicyEnum::kAppendFittingSubPartitions:
case PartitionPolicyEnum::kFitOnLineElseExpand: {
absl::FixedArray<LayoutFunction> layouts(subnode.Children().size());
std::transform(subnode.Children().begin(), subnode.Children().end(),
layouts.begin(), TraverseTree);
return factory.Wrap(layouts.begin(), layouts.end());
}

case PartitionPolicyEnum::kAlwaysExpand:
case PartitionPolicyEnum::kTabularAlignment: {
absl::FixedArray<LayoutFunction> layouts(subnode.Children().size());
std::transform(subnode.Children().begin(), subnode.Children().end(),
layouts.begin(), TraverseTree);
return factory.Stack(layouts.begin(), layouts.end());
}

// TODO(mglb): Think about introducing PartitionPolicies that
// correspond directly to combinators in LayoutFunctionFactory.
// kOptimalFunctionCallLayout strategy could then be implemented
// directly in TreeUnwrapper. It would also allow for proper
// handling of other policies (e.g. kTabularAlignment) in subtrees.

default: {
LOG(FATAL) << "Unsupported policy: " << policy << "\n"
<< "Node:\n"
<< subnode;
return LayoutFunction();
}
}
};

const LayoutFunction layout_function = TraverseTree(*node);
CHECK(!layout_function.empty());
VLOG(4) << __FUNCTION__ << ", layout function:\n" << layout_function;

auto iter = layout_function.AtOrToTheLeftOf(indentation);
CHECK(iter != layout_function.end());
VLOG(4) << __FUNCTION__ << ", layout:\n" << iter->layout;
const auto optimizer = TokenPartitionsLayoutOptimizer(style);
const auto indentation = node->Value().IndentationSpaces();
optimizer.Optimize(indentation, node, ftokens);

TreeReconstructor tree_reconstructor(indentation, style);
tree_reconstructor.TraverseTree(iter->layout);
tree_reconstructor.ReplaceTokenPartitionTreeNode(node, ftokens);
VLOG(4) << __FUNCTION__ << ", after:\n" << *node;
}

Expand Down Expand Up @@ -499,6 +423,98 @@ LayoutFunction LayoutFunctionFactory::Choice(
return result;
}

void TokenPartitionsLayoutOptimizer::Optimize(
int indentation, TokenPartitionTree* node,
std::vector<PreFormatToken>* ftokens) const {
CHECK_NOTNULL(node);
CHECK_NOTNULL(ftokens);
CHECK_GE(indentation, 0);

const LayoutFunction layout_function = CalculateOptimalLayout(*node);

CHECK(!layout_function.empty());
VLOG(4) << __FUNCTION__ << ", layout function:\n" << layout_function;

auto iter = layout_function.AtOrToTheLeftOf(indentation);
CHECK(iter != layout_function.end());
VLOG(4) << __FUNCTION__ << ", layout:\n" << iter->layout;

TreeReconstructor tree_reconstructor(indentation, style_);
tree_reconstructor.TraverseTree(iter->layout);
tree_reconstructor.ReplaceTokenPartitionTreeNode(node, ftokens);
}

LayoutFunction TokenPartitionsLayoutOptimizer::CalculateOptimalLayout(
const TokenPartitionTree& node) const {
const auto policy = node.Value().PartitionPolicy();

if (node.is_leaf()) {
return factory_.Line(node.Value());
}

const auto calculate_optimal_layout_func =
std::bind(&TokenPartitionsLayoutOptimizer::CalculateOptimalLayout, this,
std::placeholders::_1);

switch (policy) {
case PartitionPolicyEnum::kOptimalFunctionCallLayout: {
// Support only function/macro/system calls for now
CHECK_EQ(node.Children().size(), 2);

const auto& function_header = node.Children()[0];
const auto& function_args = node.Children()[1];

auto header = CalculateOptimalLayout(function_header);
auto args = CalculateOptimalLayout(function_args);

auto stack_layout = factory_.Stack({
header,
factory_.Indent(args, style_.wrap_spaces),
});
if (args.MustWrap()) {
return stack_layout;
}
auto juxtaposed_layout = factory_.Juxtaposition({
header,
args,
});
return factory_.Choice({
std::move(juxtaposed_layout),
std::move(stack_layout),
});
}

case PartitionPolicyEnum::kAppendFittingSubPartitions:
case PartitionPolicyEnum::kFitOnLineElseExpand: {
absl::FixedArray<LayoutFunction> layouts(node.Children().size());
std::transform(node.Children().begin(), node.Children().end(),
layouts.begin(), calculate_optimal_layout_func);
return factory_.Wrap(layouts.begin(), layouts.end());
}

case PartitionPolicyEnum::kAlwaysExpand:
case PartitionPolicyEnum::kTabularAlignment: {
absl::FixedArray<LayoutFunction> layouts(node.Children().size());
std::transform(node.Children().begin(), node.Children().end(),
layouts.begin(), calculate_optimal_layout_func);
return factory_.Stack(layouts.begin(), layouts.end());
}

// TODO(mglb): Think about introducing PartitionPolicies that
// correspond directly to combinators in LayoutFunctionFactory.
// kOptimalFunctionCallLayout strategy could then be implemented
// directly in TreeUnwrapper. It would also allow for proper
// handling of other policies (e.g. kTabularAlignment) in subtrees.

default: {
LOG(FATAL) << "Unsupported policy: " << policy << "\n"
<< "Node:\n"
<< node;
return LayoutFunction();
}
}
}

void TreeReconstructor::TraverseTree(const LayoutTree& layout_tree) {
const auto relative_indentation = layout_tree.Value().IndentationSpaces();
const ValueSaver<int> indent_saver(
Expand Down
23 changes: 23 additions & 0 deletions common/formatting/layout_optimizer_internal.h
Expand Up @@ -621,6 +621,29 @@ class LayoutFunctionFactory {
const BasicFormatStyle& style_;
};

class TokenPartitionsLayoutOptimizer {
public:
explicit TokenPartitionsLayoutOptimizer(const BasicFormatStyle& style)
: style_(style), factory_(style) {}

TokenPartitionsLayoutOptimizer(const TokenPartitionsLayoutOptimizer&) =
delete;
TokenPartitionsLayoutOptimizer(TokenPartitionsLayoutOptimizer&&) = delete;
TokenPartitionsLayoutOptimizer& operator=(
const TokenPartitionsLayoutOptimizer&) = delete;
TokenPartitionsLayoutOptimizer& operator=(TokenPartitionsLayoutOptimizer&&) =
delete;

void Optimize(int indentation, TokenPartitionTree* node,
std::vector<PreFormatToken>* ftokens) const;

LayoutFunction CalculateOptimalLayout(const TokenPartitionTree& node) const;

private:
const BasicFormatStyle& style_;
const LayoutFunctionFactory factory_;
};

class TreeReconstructor {
public:
TreeReconstructor(int indentation_spaces, const BasicFormatStyle& style)
Expand Down

0 comments on commit 036cd0d

Please sign in to comment.