/* * Copyright (c) 2023, Dan Klishch * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include "Function.h" #include "Parser/Lexer.h" #include "Parser/SpecParser.h" #include "Parser/TextParser.h" #include "Parser/XMLUtils.h" namespace JSSpecCompiler { ParseErrorOr AlgorithmStep::create(XML::Node const* node) { VERIFY(node->as_element().name == tag_li); auto [tokens, substeps] = TRY(tokenize_tree(node, true)); AlgorithmStep result { .m_tokens = move(tokens), .m_node = node }; if (substeps) result.m_substeps = TRY(AlgorithmStepList::create(substeps->as_element())).m_expression; result.m_expression = TRY(result.parse()); return result; } ParseErrorOr AlgorithmStep::parse() { TextParser parser(m_tokens, m_node); if (m_substeps) return parser.parse_step_with_substeps(RefPtr(m_substeps).release_nonnull()); else return parser.parse_step_without_substeps(); } ParseErrorOr AlgorithmStepList::create(XML::Node::Element const& element) { VERIFY(element.name == tag_ol); AlgorithmStepList result; auto& steps = result.m_steps; Vector step_expressions; for (auto const& child : element.children) { TRY(child->content.visit( [&](XML::Node::Element const& element) -> ParseErrorOr { if (element.name != tag_li) return ParseError::create("
    > :not(
  1. ) should not match any elements"sv, child); steps.append(TRY(AlgorithmStep::create(child))); step_expressions.append(steps.last().m_expression); return {}; }, [&](XML::Node::Text const&) -> ParseErrorOr { if (!contains_empty_text(child)) return ParseError::create("
      should not have non-empty child text nodes"sv, child); return {}; }, move(ignore_comments))); } result.m_expression = make_ref_counted(move(step_expressions)); return result; } ParseErrorOr Algorithm::create(XML::Node const* node) { VERIFY(node->as_element().name == tag_emu_alg); XML::Node::Element const* steps_list = nullptr; for (auto const& child : node->as_element().children) { TRY(child->content.visit( [&](XML::Node::Element const& element) -> ParseErrorOr { if (element.name == tag_ol) { if (steps_list != nullptr) return ParseError::create(" should have exactly one
        child"sv, child); steps_list = &element; return {}; } else { return ParseError::create(" should not have children other than
          "sv, child); } }, [&](XML::Node::Text const&) -> ParseErrorOr { if (!contains_empty_text(child)) return ParseError::create(" should not have non-empty child text nodes"sv, child); return {}; }, move(ignore_comments))); } if (steps_list == nullptr) return ParseError::create(" should have exactly one
            child"sv, node); Algorithm algorithm; algorithm.m_steps = TRY(AlgorithmStepList::create(*steps_list)); algorithm.m_tree = algorithm.m_steps.m_expression; return algorithm; } ParseErrorOr SpecFunction::create(XML::Node const* element) { VERIFY(element->as_element().name == tag_emu_clause); SpecFunction result; result.m_id = TRY(get_attribute_by_name(element, attribute_id)); result.m_name = TRY(get_attribute_by_name(element, attribute_aoid)); u32 children_count = 0; bool has_definition = false; XML::Node const* algorithm_node = nullptr; XML::Node const* prose_node = nullptr; for (auto const& child : element->as_element().children) { TRY(child->content.visit( [&](XML::Node::Element const& element) -> ParseErrorOr { ++children_count; if (element.name == tag_h1) { if (children_count != 1) return ParseError::create("

            should be the first child of a "sv, child); TRY(result.parse_definition(child)); has_definition = true; } else if (element.name == tag_p) { if (prose_node == nullptr) prose_node = child; } else if (element.name == tag_emu_alg) { algorithm_node = child; } else { return ParseError::create("Unknown child of "sv, child); } return {}; }, [&](XML::Node::Text const&) -> ParseErrorOr { if (!contains_empty_text(child)) { return ParseError::create(" should not have non-empty child text nodes"sv, child); } return {}; }, move(ignore_comments))); } if (algorithm_node == nullptr) return ParseError::create("No "sv, element); if (prose_node == nullptr) return ParseError::create("No prose element"sv, element); if (!has_definition) return ParseError::create("Definition was not found"sv, element); result.m_algorithm = TRY(Algorithm::create(algorithm_node)); return result; } ParseErrorOr SpecFunction::parse_definition(XML::Node const* element) { auto tokens = TRY(tokenize_tree(element)); TextParser parser(tokens.tokens, element); auto [section_number, function_name, arguments] = TRY(parser.parse_definition()); if (function_name != m_name) return ParseError::create("Function name in definition differs from [aoid]"sv, element); m_section_number = section_number; for (auto const& argument : arguments) m_arguments.append({ argument }); return {}; } SpecParsingStep::SpecParsingStep() : CompilationStep("parser"sv) { } SpecParsingStep::~SpecParsingStep() = default; void SpecParsingStep::run(TranslationUnitRef translation_unit) { auto filename = translation_unit->filename; auto file = Core::File::open_file_or_standard_stream(filename, Core::File::OpenMode::Read).release_value_but_fixme_should_propagate_errors(); m_input = file->read_until_eof().release_value_but_fixme_should_propagate_errors(); XML::Parser parser { m_input }; auto document = parser.parse().release_value_but_fixme_should_propagate_errors(); m_document = AK::adopt_own_if_nonnull(new XML::Document(move(document))); auto spec_function = SpecFunction::create(&m_document->root()).release_value_but_fixme_should_propagate_errors(); Vector argument_names; for (auto const& argument : spec_function.m_arguments) argument_names.append(argument.name); translation_unit->adopt_function( make_ref_counted(spec_function.m_name, spec_function.m_algorithm.m_tree, move(argument_names))); } }