Skip to content

Commit 7ea2138

Browse files
DanShadersADKaster
authored andcommitted
JSSpecCompiler: Split Parser/SpecParser.cpp into 8 files
This SpecParser.cpp had an ever increasing number of lines and contained an implementation of 8 different classes. So I figured out it's about the time to split it. No behavior change.
1 parent b9cfb50 commit 7ea2138

File tree

11 files changed

+606
-513
lines changed

11 files changed

+606
-513
lines changed

Meta/Lagom/Tools/CodeGenerators/JSSpecCompiler/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,16 @@ set(SOURCES
1010
Compiler/Passes/IfBranchMergingPass.cpp
1111
Compiler/Passes/ReferenceResolvingPass.cpp
1212
Compiler/Passes/SSABuildingPass.cpp
13+
Parser/Algorithm.cpp
14+
Parser/AlgorithmStep.cpp
15+
Parser/AlgorithmStepList.cpp
1316
Parser/CppASTConverter.cpp
1417
Parser/Lexer.cpp
15-
Parser/SpecParser.cpp
18+
Parser/SpecFunction.cpp
19+
Parser/Specification.cpp
20+
Parser/SpecificationClause.cpp
21+
Parser/SpecificationParsingContext.cpp
22+
Parser/SpecParsingStep.cpp
1623
Parser/TextParser.cpp
1724
Parser/XMLUtils.cpp
1825
DiagnosticEngine.cpp
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright (c) 2023-2024, Dan Klishch <danilklishch@gmail.com>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include "Parser/Lexer.h"
8+
#include "Parser/SpecParser.h"
9+
#include "Parser/XMLUtils.h"
10+
11+
namespace JSSpecCompiler {
12+
13+
Optional<Algorithm> Algorithm::create(SpecificationParsingContext& ctx, XML::Node const* element)
14+
{
15+
VERIFY(element->as_element().name == tag_emu_alg);
16+
17+
Vector<XML::Node const*> steps_list;
18+
for (auto const& child : element->as_element().children) {
19+
child->content.visit(
20+
[&](XML::Node::Element const& element) {
21+
if (element.name == tag_ol) {
22+
steps_list.append(child);
23+
return;
24+
}
25+
26+
ctx.diag().error(ctx.location_from_xml_offset(child->offset),
27+
"<{}> should not be a child of <emu-alg>"sv, element.name);
28+
},
29+
[&](XML::Node::Text const&) {
30+
if (!contains_empty_text(child)) {
31+
ctx.diag().error(ctx.location_from_xml_offset(child->offset),
32+
"non-empty text node should not be a child of <emu-alg>");
33+
}
34+
},
35+
[&](auto const&) {});
36+
}
37+
38+
if (steps_list.size() != 1) {
39+
ctx.diag().error(ctx.location_from_xml_offset(element->offset),
40+
"<emu-alg> should have exactly one <ol> child"sv);
41+
return {};
42+
}
43+
44+
auto steps_creation_result = AlgorithmStepList::create(ctx, steps_list[0]);
45+
if (steps_creation_result.has_value()) {
46+
Algorithm algorithm;
47+
algorithm.m_tree = steps_creation_result.release_value().tree();
48+
return algorithm;
49+
}
50+
return {};
51+
}
52+
53+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* Copyright (c) 2023-2024, Dan Klishch <danilklishch@gmail.com>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include "Parser/Lexer.h"
8+
#include "Parser/SpecParser.h"
9+
10+
namespace JSSpecCompiler {
11+
12+
Optional<AlgorithmStep> AlgorithmStep::create(SpecificationParsingContext& ctx, XML::Node const* element)
13+
{
14+
VERIFY(element->as_element().name == tag_li);
15+
16+
auto [maybe_tokens, substeps] = tokenize_step(ctx, element);
17+
18+
AlgorithmStep result(ctx);
19+
result.m_node = element;
20+
21+
if (substeps) {
22+
// FIXME: Remove this once macOS Lagom CI updates to Clang >= 16.
23+
auto substeps_copy = substeps;
24+
25+
auto step_list = ctx.with_new_step_list_nesting_level([&] {
26+
return AlgorithmStepList::create(ctx, substeps_copy);
27+
});
28+
result.m_substeps = step_list.has_value() ? step_list->tree() : error_tree;
29+
}
30+
31+
if (!maybe_tokens.has_value())
32+
return {};
33+
result.m_tokens = maybe_tokens.release_value();
34+
35+
if (!result.parse())
36+
return {};
37+
return result;
38+
}
39+
40+
bool AlgorithmStep::parse()
41+
{
42+
TextParser parser(m_ctx, m_tokens, m_node);
43+
44+
TextParseErrorOr<NullableTree> parse_result = TextParseError {};
45+
if (m_substeps)
46+
parse_result = parser.parse_step_with_substeps(RefPtr(m_substeps).release_nonnull());
47+
else
48+
parse_result = parser.parse_step_without_substeps();
49+
50+
if (parse_result.is_error()) {
51+
auto [location, message] = parser.get_diagnostic();
52+
m_ctx.diag().error(location, "{}", message);
53+
return false;
54+
} else {
55+
m_expression = parse_result.release_value();
56+
return true;
57+
}
58+
}
59+
60+
}
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/*
2+
* Copyright (c) 2023-2024, Dan Klishch <danilklishch@gmail.com>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include "Parser/Lexer.h"
8+
#include "Parser/SpecParser.h"
9+
#include "Parser/XMLUtils.h"
10+
11+
namespace JSSpecCompiler {
12+
13+
Optional<AlgorithmStepList> AlgorithmStepList::create(SpecificationParsingContext& ctx, XML::Node const* element)
14+
{
15+
VERIFY(element->as_element().name == tag_ol);
16+
17+
AlgorithmStepList result;
18+
19+
Vector<Tree> step_expressions;
20+
bool all_steps_parsed = true;
21+
int step_number = 0;
22+
23+
auto const& parent_scope = ctx.current_logical_scope();
24+
25+
for (auto const& child : element->as_element().children) {
26+
child->content.visit(
27+
[&](XML::Node::Element const& element) {
28+
if (element.name == tag_li) {
29+
auto step_creation_result = ctx.with_new_logical_scope([&] {
30+
update_logical_scope_for_step(ctx, parent_scope, step_number);
31+
return AlgorithmStep::create(ctx, child);
32+
});
33+
if (!step_creation_result.has_value()) {
34+
all_steps_parsed = false;
35+
} else {
36+
if (auto expression = step_creation_result.release_value().tree())
37+
step_expressions.append(expression.release_nonnull());
38+
}
39+
++step_number;
40+
return;
41+
}
42+
43+
ctx.diag().error(ctx.location_from_xml_offset(child->offset),
44+
"<{}> should not be a child of algorithm step list"sv, element.name);
45+
},
46+
[&](XML::Node::Text const&) {
47+
if (!contains_empty_text(child)) {
48+
ctx.diag().error(ctx.location_from_xml_offset(child->offset),
49+
"non-empty text node should not be a child of algorithm step list");
50+
}
51+
},
52+
[&](auto const&) {});
53+
}
54+
55+
if (!all_steps_parsed)
56+
return {};
57+
58+
result.m_expression = make_ref_counted<TreeList>(move(step_expressions));
59+
return result;
60+
}
61+
62+
void AlgorithmStepList::update_logical_scope_for_step(SpecificationParsingContext& ctx, LogicalLocation const& parent_scope, int step_number)
63+
{
64+
int nesting_level = ctx.step_list_nesting_level();
65+
String list_step_number;
66+
67+
if (nesting_level == 0 || nesting_level == 3) {
68+
list_step_number = MUST(String::formatted("{}", step_number + 1));
69+
} else if (nesting_level == 1 || nesting_level == 4) {
70+
if (step_number < 26)
71+
list_step_number = String::from_code_point('a' + step_number);
72+
else
73+
list_step_number = MUST(String::formatted("{}", step_number + 1));
74+
} else {
75+
list_step_number = MUST(String::from_byte_string(ByteString::roman_number_from(step_number + 1).to_lowercase()));
76+
}
77+
78+
auto& scope = ctx.current_logical_scope();
79+
scope.section = parent_scope.section;
80+
81+
if (parent_scope.step.is_empty())
82+
scope.step = list_step_number;
83+
else
84+
scope.step = MUST(String::formatted("{}.{}", parent_scope.step, list_step_number));
85+
}
86+
87+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright (c) 2023-2024, Dan Klishch <danilklishch@gmail.com>
3+
*
4+
* SPDX-License-Identifier: BSD-2-Clause
5+
*/
6+
7+
#include "Parser/Lexer.h"
8+
#include "Parser/SpecParser.h"
9+
#include "Parser/XMLUtils.h"
10+
11+
namespace JSSpecCompiler {
12+
13+
bool SpecFunction::post_initialize(XML::Node const* element)
14+
{
15+
VERIFY(element->as_element().name == tag_emu_clause);
16+
17+
auto& ctx = context();
18+
19+
auto maybe_id = get_attribute_by_name(element, attribute_id);
20+
if (!maybe_id.has_value()) {
21+
ctx.diag().error(ctx.location_from_xml_offset(element->offset),
22+
"no id attribute");
23+
} else {
24+
m_id = maybe_id.value();
25+
}
26+
27+
m_header.header.visit(
28+
[&](ClauseHeader::AbstractOperation const& abstract_operation) {
29+
auto maybe_abstract_operation_id = get_attribute_by_name(element, attribute_aoid);
30+
if (maybe_abstract_operation_id.has_value())
31+
m_name = MUST(String::from_utf8(maybe_abstract_operation_id.value()));
32+
33+
auto const& [function_name, arguments] = abstract_operation;
34+
m_arguments = arguments;
35+
36+
if (m_name != function_name) {
37+
ctx.diag().warn(ctx.location_from_xml_offset(element->offset),
38+
"function name in header and <emu-clause>[aoid] do not match");
39+
}
40+
},
41+
[&](ClauseHeader::Accessor const& accessor) {
42+
m_name = MUST(String::formatted("%get {}%", MUST(String::join("."sv, accessor.qualified_name))));
43+
},
44+
[&](ClauseHeader::Method const& method) {
45+
m_name = MUST(String::formatted("%{}%", MUST(String::join("."sv, method.qualified_name))));
46+
m_arguments = method.arguments;
47+
},
48+
[&](auto const&) {
49+
VERIFY_NOT_REACHED();
50+
});
51+
52+
Vector<XML::Node const*> algorithm_nodes;
53+
54+
for (auto const& child : element->as_element().children) {
55+
child->content.visit(
56+
[&](XML::Node::Element const& element) {
57+
if (element.name == tag_h1) {
58+
// Processed in SpecificationClause
59+
} else if (element.name == tag_p) {
60+
ctx.diag().warn(ctx.location_from_xml_offset(child->offset),
61+
"prose is ignored");
62+
} else if (element.name == tag_emu_alg) {
63+
algorithm_nodes.append(child);
64+
} else {
65+
ctx.diag().error(ctx.location_from_xml_offset(child->offset),
66+
"<{}> should not be a child of <emu-clause> specifing function"sv, element.name);
67+
}
68+
},
69+
[&](auto const&) {});
70+
}
71+
72+
if (algorithm_nodes.size() != 1) {
73+
ctx.diag().error(ctx.location_from_xml_offset(element->offset),
74+
"<emu-clause> specifing function should have exactly one <emu-alg> child"sv);
75+
return false;
76+
}
77+
78+
auto maybe_algorithm = Algorithm::create(ctx, algorithm_nodes[0]);
79+
if (maybe_algorithm.has_value()) {
80+
m_algorithm = maybe_algorithm.release_value();
81+
return true;
82+
} else {
83+
return false;
84+
}
85+
}
86+
87+
void SpecFunction::do_collect(TranslationUnitRef translation_unit)
88+
{
89+
translation_unit->adopt_function(make_ref_counted<FunctionDefinition>(m_name, m_algorithm.tree(), move(m_arguments)));
90+
}
91+
92+
}

0 commit comments

Comments
 (0)