mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 02:57:36 +00:00
Meta: Split and refactor the WrapperGenerator a bit
The single 4000-line WrapperGenerator.cpp file was proving to be a pain to hack, and was filled with spaghetti, split it into a bunch of files to lessen the impact of the spaghetti. Also refactor the whole parser to use a class instead of a giant function with a million lambdas.
This commit is contained in:
parent
7685d53654
commit
e9c76d339b
7 changed files with 1431 additions and 1296 deletions
|
@ -4,5 +4,5 @@ lagom_tool(Generate_CSS_PropertyID_h SOURCES Generate_CSS_PropertyID_h.cpp)
|
|||
lagom_tool(Generate_CSS_PropertyID_cpp SOURCES Generate_CSS_PropertyID_cpp.cpp)
|
||||
lagom_tool(Generate_CSS_ValueID_h SOURCES Generate_CSS_ValueID_h.cpp)
|
||||
lagom_tool(Generate_CSS_ValueID_cpp SOURCES Generate_CSS_ValueID_cpp.cpp)
|
||||
lagom_tool(WrapperGenerator SOURCES WrapperGenerator.cpp)
|
||||
target_compile_options(WrapperGenerator PUBLIC -g)
|
||||
|
||||
add_subdirectory(WrapperGenerator)
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
set(SOURCES "")
|
||||
|
||||
lagom_tool(WrapperGenerator SOURCES
|
||||
IDLGenerators.cpp
|
||||
IDLParser.cpp
|
||||
main.cpp)
|
||||
|
||||
target_compile_options(WrapperGenerator PUBLIC -g)
|
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,757 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "IDLParser.h"
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <LibCore/File.h>
|
||||
|
||||
[[noreturn]] static void report_parsing_error(StringView message, StringView filename, StringView input, size_t offset)
|
||||
{
|
||||
// FIXME: Spaghetti code ahead.
|
||||
|
||||
size_t lineno = 1;
|
||||
size_t colno = 1;
|
||||
size_t start_line = 0;
|
||||
size_t line_length = 0;
|
||||
for (size_t index = 0; index < input.length(); ++index) {
|
||||
if (offset == index)
|
||||
colno = index - start_line + 1;
|
||||
|
||||
if (input[index] == '\n') {
|
||||
if (index >= offset)
|
||||
break;
|
||||
|
||||
start_line = index + 1;
|
||||
line_length = 0;
|
||||
++lineno;
|
||||
} else {
|
||||
++line_length;
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder error_message;
|
||||
error_message.appendff("{}\n", input.substring_view(start_line, line_length));
|
||||
for (size_t i = 0; i < colno - 1; ++i)
|
||||
error_message.append(' ');
|
||||
error_message.append("\033[1;31m^\n");
|
||||
error_message.appendff("{}:{}: error: {}\033[0m\n", filename, lineno, message);
|
||||
|
||||
warnln("{}", error_message.string_view());
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static String convert_enumeration_value_to_cpp_enum_member(String const& value, HashTable<String>& names_already_seen)
|
||||
{
|
||||
StringBuilder builder;
|
||||
GenericLexer lexer { value };
|
||||
|
||||
while (!lexer.is_eof()) {
|
||||
lexer.ignore_while([](auto c) { return is_ascii_space(c) || c == '-' || c == '_'; });
|
||||
auto word = lexer.consume_while([](auto c) { return is_ascii_alphanumeric(c); });
|
||||
if (!word.is_empty()) {
|
||||
builder.append(word.to_titlecase_string());
|
||||
} else {
|
||||
auto non_alnum_string = lexer.consume_while([](auto c) { return !is_ascii_alphanumeric(c); });
|
||||
if (!non_alnum_string.is_empty())
|
||||
builder.append("_");
|
||||
}
|
||||
}
|
||||
|
||||
if (builder.is_empty())
|
||||
builder.append("Empty");
|
||||
|
||||
while (names_already_seen.contains(builder.string_view()))
|
||||
builder.append('_');
|
||||
|
||||
names_already_seen.set(builder.string_view());
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
namespace IDL {
|
||||
|
||||
HashTable<String> Parser::s_all_imported_paths {};
|
||||
|
||||
void Parser::assert_specific(char ch)
|
||||
{
|
||||
if (!lexer.consume_specific(ch))
|
||||
report_parsing_error(String::formatted("expected '{}'", ch), filename, input, lexer.tell());
|
||||
}
|
||||
|
||||
void Parser::consume_whitespace()
|
||||
{
|
||||
bool consumed = true;
|
||||
while (consumed) {
|
||||
consumed = lexer.consume_while(is_ascii_space).length() > 0;
|
||||
|
||||
if (lexer.consume_specific("//")) {
|
||||
lexer.consume_until('\n');
|
||||
lexer.ignore();
|
||||
consumed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::assert_string(StringView expected)
|
||||
{
|
||||
if (!lexer.consume_specific(expected))
|
||||
report_parsing_error(String::formatted("expected '{}'", expected), filename, input, lexer.tell());
|
||||
}
|
||||
|
||||
HashMap<String, String> Parser::parse_extended_attributes()
|
||||
{
|
||||
HashMap<String, String> extended_attributes;
|
||||
for (;;) {
|
||||
consume_whitespace();
|
||||
if (lexer.consume_specific(']'))
|
||||
break;
|
||||
auto name = lexer.consume_until([](auto ch) { return ch == ']' || ch == '=' || ch == ','; });
|
||||
if (lexer.consume_specific('=')) {
|
||||
auto value = lexer.consume_until([](auto ch) { return ch == ']' || ch == ','; });
|
||||
extended_attributes.set(name, value);
|
||||
} else {
|
||||
extended_attributes.set(name, {});
|
||||
}
|
||||
lexer.consume_specific(',');
|
||||
}
|
||||
consume_whitespace();
|
||||
return extended_attributes;
|
||||
}
|
||||
|
||||
Optional<NonnullOwnPtr<Interface>> Parser::resolve_import(auto path)
|
||||
{
|
||||
auto include_path = LexicalPath::join(import_base_path, path).string();
|
||||
if (!Core::File::exists(include_path))
|
||||
report_parsing_error(String::formatted("{}: No such file or directory", include_path), filename, input, lexer.tell());
|
||||
|
||||
auto real_path = Core::File::real_path_for(include_path);
|
||||
if (s_all_imported_paths.contains(real_path))
|
||||
return {};
|
||||
|
||||
s_all_imported_paths.set(real_path);
|
||||
|
||||
auto file_or_error = Core::File::open(real_path, Core::OpenMode::ReadOnly);
|
||||
if (file_or_error.is_error())
|
||||
report_parsing_error(String::formatted("Failed to open {}: {}", real_path, file_or_error.error()), filename, input, lexer.tell());
|
||||
|
||||
auto data = file_or_error.value()->read_all();
|
||||
return Parser(real_path, data, import_base_path).parse();
|
||||
}
|
||||
|
||||
NonnullRefPtr<Type> Parser::parse_type()
|
||||
{
|
||||
if (lexer.consume_specific('(')) {
|
||||
NonnullRefPtrVector<Type> union_member_types;
|
||||
union_member_types.append(parse_type());
|
||||
consume_whitespace();
|
||||
assert_string("or");
|
||||
consume_whitespace();
|
||||
union_member_types.append(parse_type());
|
||||
consume_whitespace();
|
||||
|
||||
while (lexer.consume_specific("or")) {
|
||||
consume_whitespace();
|
||||
union_member_types.append(parse_type());
|
||||
consume_whitespace();
|
||||
}
|
||||
|
||||
assert_specific(')');
|
||||
|
||||
bool nullable = lexer.consume_specific('?');
|
||||
|
||||
return adopt_ref(*new UnionType("", nullable, move(union_member_types)));
|
||||
}
|
||||
|
||||
bool unsigned_ = lexer.consume_specific("unsigned");
|
||||
if (unsigned_)
|
||||
consume_whitespace();
|
||||
|
||||
auto name = lexer.consume_until([](auto ch) { return !is_ascii_alphanumeric(ch) && ch != '_'; });
|
||||
NonnullRefPtrVector<Type> parameters;
|
||||
bool is_parameterized_type = false;
|
||||
if (lexer.consume_specific('<')) {
|
||||
is_parameterized_type = true;
|
||||
parameters.append(parse_type());
|
||||
while (lexer.consume_specific(',')) {
|
||||
consume_whitespace();
|
||||
parameters.append(parse_type());
|
||||
}
|
||||
lexer.consume_specific('>');
|
||||
}
|
||||
auto nullable = lexer.consume_specific('?');
|
||||
StringBuilder builder;
|
||||
if (unsigned_)
|
||||
builder.append("unsigned ");
|
||||
builder.append(name);
|
||||
|
||||
if (is_parameterized_type)
|
||||
return adopt_ref(*new ParameterizedType(builder.to_string(), nullable, move(parameters)));
|
||||
|
||||
return adopt_ref(*new Type(builder.to_string(), nullable));
|
||||
}
|
||||
|
||||
void Parser::parse_attribute(HashMap<String, String>& extended_attributes, Interface& interface)
|
||||
{
|
||||
bool readonly = lexer.consume_specific("readonly");
|
||||
if (readonly)
|
||||
consume_whitespace();
|
||||
|
||||
if (lexer.consume_specific("attribute"))
|
||||
consume_whitespace();
|
||||
|
||||
auto type = parse_type();
|
||||
consume_whitespace();
|
||||
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch) || ch == ';'; });
|
||||
consume_whitespace();
|
||||
|
||||
assert_specific(';');
|
||||
|
||||
auto name_as_string = name.to_string();
|
||||
auto getter_callback_name = String::formatted("{}_getter", name_as_string.to_snakecase());
|
||||
auto setter_callback_name = String::formatted("{}_setter", name_as_string.to_snakecase());
|
||||
|
||||
Attribute attribute {
|
||||
readonly,
|
||||
move(type),
|
||||
move(name_as_string),
|
||||
move(extended_attributes),
|
||||
move(getter_callback_name),
|
||||
move(setter_callback_name),
|
||||
};
|
||||
interface.attributes.append(move(attribute));
|
||||
}
|
||||
|
||||
void Parser::parse_constant(Interface& interface)
|
||||
{
|
||||
lexer.consume_specific("const");
|
||||
consume_whitespace();
|
||||
|
||||
auto type = parse_type();
|
||||
consume_whitespace();
|
||||
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch) || ch == '='; });
|
||||
consume_whitespace();
|
||||
lexer.consume_specific('=');
|
||||
consume_whitespace();
|
||||
auto value = lexer.consume_while([](auto ch) { return !is_ascii_space(ch) && ch != ';'; });
|
||||
consume_whitespace();
|
||||
assert_specific(';');
|
||||
|
||||
Constant constant {
|
||||
move(type),
|
||||
move(name),
|
||||
move(value),
|
||||
};
|
||||
interface.constants.append(move(constant));
|
||||
}
|
||||
|
||||
Vector<Parameter> Parser::parse_parameters()
|
||||
{
|
||||
consume_whitespace();
|
||||
Vector<Parameter> parameters;
|
||||
for (;;) {
|
||||
if (lexer.next_is(')'))
|
||||
break;
|
||||
HashMap<String, String> extended_attributes;
|
||||
if (lexer.consume_specific('['))
|
||||
extended_attributes = parse_extended_attributes();
|
||||
bool optional = lexer.consume_specific("optional");
|
||||
if (optional)
|
||||
consume_whitespace();
|
||||
auto type = parse_type();
|
||||
bool variadic = lexer.consume_specific("..."sv);
|
||||
consume_whitespace();
|
||||
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch) || ch == ',' || ch == ')' || ch == '='; });
|
||||
Parameter parameter = { move(type), move(name), optional, {}, extended_attributes, variadic };
|
||||
consume_whitespace();
|
||||
if (variadic) {
|
||||
// Variadic parameters must be last and do not have default values.
|
||||
parameters.append(move(parameter));
|
||||
break;
|
||||
}
|
||||
if (lexer.next_is(')')) {
|
||||
parameters.append(move(parameter));
|
||||
break;
|
||||
}
|
||||
if (lexer.next_is('=') && optional) {
|
||||
assert_specific('=');
|
||||
consume_whitespace();
|
||||
auto default_value = lexer.consume_until([](auto ch) { return is_ascii_space(ch) || ch == ',' || ch == ')'; });
|
||||
parameter.optional_default_value = default_value;
|
||||
}
|
||||
parameters.append(move(parameter));
|
||||
if (lexer.next_is(')'))
|
||||
break;
|
||||
assert_specific(',');
|
||||
consume_whitespace();
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
Function Parser::parse_function(HashMap<String, String>& extended_attributes, Interface& interface, IsSpecialOperation is_special_operation)
|
||||
{
|
||||
bool static_ = false;
|
||||
if (lexer.consume_specific("static")) {
|
||||
static_ = true;
|
||||
consume_whitespace();
|
||||
}
|
||||
|
||||
auto return_type = parse_type();
|
||||
consume_whitespace();
|
||||
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch) || ch == '('; });
|
||||
consume_whitespace();
|
||||
assert_specific('(');
|
||||
auto parameters = parse_parameters();
|
||||
assert_specific(')');
|
||||
consume_whitespace();
|
||||
assert_specific(';');
|
||||
|
||||
Function function { move(return_type), name, move(parameters), move(extended_attributes) };
|
||||
|
||||
// "Defining a special operation with an identifier is equivalent to separating the special operation out into its own declaration without an identifier."
|
||||
if (is_special_operation == IsSpecialOperation::No || (is_special_operation == IsSpecialOperation::Yes && !name.is_empty())) {
|
||||
if (!static_)
|
||||
interface.functions.append(function);
|
||||
else
|
||||
interface.static_functions.append(function);
|
||||
}
|
||||
|
||||
return function;
|
||||
}
|
||||
|
||||
void Parser::parse_constructor(Interface& interface)
|
||||
{
|
||||
assert_string("constructor");
|
||||
consume_whitespace();
|
||||
assert_specific('(');
|
||||
auto parameters = parse_parameters();
|
||||
assert_specific(')');
|
||||
consume_whitespace();
|
||||
assert_specific(';');
|
||||
|
||||
interface.constructors.append(Constructor { interface.name, move(parameters) });
|
||||
}
|
||||
|
||||
void Parser::parse_stringifier(HashMap<String, String>& extended_attributes, Interface& interface)
|
||||
{
|
||||
assert_string("stringifier");
|
||||
consume_whitespace();
|
||||
interface.has_stringifier = true;
|
||||
if (lexer.next_is("readonly") || lexer.next_is("attribute")) {
|
||||
parse_attribute(extended_attributes, interface);
|
||||
interface.stringifier_attribute = interface.attributes.last().name;
|
||||
} else {
|
||||
assert_specific(';');
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::parse_iterable(Interface& interface)
|
||||
{
|
||||
assert_string("iterable");
|
||||
assert_specific('<');
|
||||
auto first_type = parse_type();
|
||||
if (lexer.next_is(',')) {
|
||||
if (interface.supports_indexed_properties())
|
||||
report_parsing_error("Interfaces with a pair iterator must not supported indexed properties.", filename, input, lexer.tell());
|
||||
|
||||
assert_specific(',');
|
||||
consume_whitespace();
|
||||
auto second_type = parse_type();
|
||||
interface.pair_iterator_types = Tuple { move(first_type), move(second_type) };
|
||||
} else {
|
||||
if (!interface.supports_indexed_properties())
|
||||
report_parsing_error("Interfaces with a value iterator must supported indexed properties.", filename, input, lexer.tell());
|
||||
|
||||
interface.value_iterator_type = move(first_type);
|
||||
}
|
||||
assert_specific('>');
|
||||
assert_specific(';');
|
||||
}
|
||||
|
||||
void Parser::parse_getter(HashMap<String, String>& extended_attributes, Interface& interface)
|
||||
{
|
||||
assert_string("getter");
|
||||
consume_whitespace();
|
||||
auto function = parse_function(extended_attributes, interface, IsSpecialOperation::Yes);
|
||||
|
||||
if (function.parameters.size() != 1)
|
||||
report_parsing_error(String::formatted("Named/indexed property getters must have only 1 parameter, got {} parameters.", function.parameters.size()), filename, input, lexer.tell());
|
||||
|
||||
auto& identifier = function.parameters.first();
|
||||
|
||||
if (identifier.type->nullable)
|
||||
report_parsing_error("identifier's type must not be nullable.", filename, input, lexer.tell());
|
||||
|
||||
if (identifier.optional)
|
||||
report_parsing_error("identifier must not be optional.", filename, input, lexer.tell());
|
||||
|
||||
// FIXME: Disallow variadic functions once they're supported.
|
||||
|
||||
if (identifier.type->name == "DOMString") {
|
||||
if (interface.named_property_getter.has_value())
|
||||
report_parsing_error("An interface can only have one named property getter.", filename, input, lexer.tell());
|
||||
|
||||
interface.named_property_getter = move(function);
|
||||
} else if (identifier.type->name == "unsigned long") {
|
||||
if (interface.indexed_property_getter.has_value())
|
||||
report_parsing_error("An interface can only have one indexed property getter.", filename, input, lexer.tell());
|
||||
|
||||
interface.indexed_property_getter = move(function);
|
||||
} else {
|
||||
report_parsing_error(String::formatted("Named/indexed property getter's identifier's type must be either 'DOMString' or 'unsigned long', got '{}'.", identifier.type->name), filename, input, lexer.tell());
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::parse_setter(HashMap<String, String>& extended_attributes, Interface& interface)
|
||||
{
|
||||
assert_string("setter");
|
||||
consume_whitespace();
|
||||
auto function = parse_function(extended_attributes, interface, IsSpecialOperation::Yes);
|
||||
|
||||
if (function.parameters.size() != 2)
|
||||
report_parsing_error(String::formatted("Named/indexed property setters must have only 2 parameters, got {} parameter(s).", function.parameters.size()), filename, input, lexer.tell());
|
||||
|
||||
auto& identifier = function.parameters.first();
|
||||
|
||||
if (identifier.type->nullable)
|
||||
report_parsing_error("identifier's type must not be nullable.", filename, input, lexer.tell());
|
||||
|
||||
if (identifier.optional)
|
||||
report_parsing_error("identifier must not be optional.", filename, input, lexer.tell());
|
||||
|
||||
// FIXME: Disallow variadic functions once they're supported.
|
||||
|
||||
if (identifier.type->name == "DOMString") {
|
||||
if (interface.named_property_setter.has_value())
|
||||
report_parsing_error("An interface can only have one named property setter.", filename, input, lexer.tell());
|
||||
|
||||
if (!interface.named_property_getter.has_value())
|
||||
report_parsing_error("A named property setter must be accompanied by a named property getter.", filename, input, lexer.tell());
|
||||
|
||||
interface.named_property_setter = move(function);
|
||||
} else if (identifier.type->name == "unsigned long") {
|
||||
if (interface.indexed_property_setter.has_value())
|
||||
report_parsing_error("An interface can only have one indexed property setter.", filename, input, lexer.tell());
|
||||
|
||||
if (!interface.indexed_property_getter.has_value())
|
||||
report_parsing_error("An indexed property setter must be accompanied by an indexed property getter.", filename, input, lexer.tell());
|
||||
|
||||
interface.indexed_property_setter = move(function);
|
||||
} else {
|
||||
report_parsing_error(String::formatted("Named/indexed property setter's identifier's type must be either 'DOMString' or 'unsigned long', got '{}'.", identifier.type->name), filename, input, lexer.tell());
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::parse_deleter(HashMap<String, String>& extended_attributes, Interface& interface)
|
||||
{
|
||||
assert_string("deleter");
|
||||
consume_whitespace();
|
||||
auto function = parse_function(extended_attributes, interface, IsSpecialOperation::Yes);
|
||||
|
||||
if (function.parameters.size() != 1)
|
||||
report_parsing_error(String::formatted("Named property deleter must have only 1 parameter, got {} parameters.", function.parameters.size()), filename, input, lexer.tell());
|
||||
|
||||
auto& identifier = function.parameters.first();
|
||||
|
||||
if (identifier.type->nullable)
|
||||
report_parsing_error("identifier's type must not be nullable.", filename, input, lexer.tell());
|
||||
|
||||
if (identifier.optional)
|
||||
report_parsing_error("identifier must not be optional.", filename, input, lexer.tell());
|
||||
|
||||
// FIXME: Disallow variadic functions once they're supported.
|
||||
|
||||
if (identifier.type->name == "DOMString") {
|
||||
if (interface.named_property_deleter.has_value())
|
||||
report_parsing_error("An interface can only have one named property deleter.", filename, input, lexer.tell());
|
||||
|
||||
if (!interface.named_property_getter.has_value())
|
||||
report_parsing_error("A named property deleter must be accompanied by a named property getter.", filename, input, lexer.tell());
|
||||
|
||||
interface.named_property_deleter = move(function);
|
||||
} else {
|
||||
report_parsing_error(String::formatted("Named property deleter's identifier's type must be 'DOMString', got '{}'.", identifier.type->name), filename, input, lexer.tell());
|
||||
}
|
||||
}
|
||||
|
||||
void Parser::parse_interface(Interface& interface)
|
||||
{
|
||||
consume_whitespace();
|
||||
interface.name = lexer.consume_until([](auto ch) { return is_ascii_space(ch); });
|
||||
consume_whitespace();
|
||||
if (lexer.consume_specific(':')) {
|
||||
consume_whitespace();
|
||||
interface.parent_name = lexer.consume_until([](auto ch) { return is_ascii_space(ch); });
|
||||
consume_whitespace();
|
||||
}
|
||||
assert_specific('{');
|
||||
|
||||
for (;;) {
|
||||
HashMap<String, String> extended_attributes;
|
||||
|
||||
consume_whitespace();
|
||||
|
||||
if (lexer.consume_specific('}')) {
|
||||
consume_whitespace();
|
||||
assert_specific(';');
|
||||
break;
|
||||
}
|
||||
|
||||
if (lexer.consume_specific('[')) {
|
||||
extended_attributes = parse_extended_attributes();
|
||||
if (!interface.has_unscopable_member && extended_attributes.contains("Unscopable"))
|
||||
interface.has_unscopable_member = true;
|
||||
}
|
||||
|
||||
if (lexer.next_is("constructor")) {
|
||||
parse_constructor(interface);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lexer.next_is("const")) {
|
||||
parse_constant(interface);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lexer.next_is("stringifier")) {
|
||||
parse_stringifier(extended_attributes, interface);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lexer.next_is("iterable")) {
|
||||
parse_iterable(interface);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lexer.next_is("readonly") || lexer.next_is("attribute")) {
|
||||
parse_attribute(extended_attributes, interface);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lexer.next_is("getter")) {
|
||||
parse_getter(extended_attributes, interface);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lexer.next_is("setter")) {
|
||||
parse_setter(extended_attributes, interface);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (lexer.next_is("deleter")) {
|
||||
parse_deleter(extended_attributes, interface);
|
||||
continue;
|
||||
}
|
||||
|
||||
parse_function(extended_attributes, interface);
|
||||
}
|
||||
|
||||
interface.wrapper_class = String::formatted("{}Wrapper", interface.name);
|
||||
interface.wrapper_base_class = String::formatted("{}Wrapper", interface.parent_name.is_empty() ? String::empty() : interface.parent_name);
|
||||
interface.constructor_class = String::formatted("{}Constructor", interface.name);
|
||||
interface.prototype_class = String::formatted("{}Prototype", interface.name);
|
||||
interface.prototype_base_class = String::formatted("{}Prototype", interface.parent_name.is_empty() ? "Object" : interface.parent_name);
|
||||
consume_whitespace();
|
||||
}
|
||||
|
||||
void Parser::parse_enumeration(Interface& interface)
|
||||
{
|
||||
assert_string("enum");
|
||||
consume_whitespace();
|
||||
|
||||
Enumeration enumeration {};
|
||||
|
||||
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch); });
|
||||
consume_whitespace();
|
||||
|
||||
assert_specific('{');
|
||||
|
||||
bool first = true;
|
||||
for (; !lexer.is_eof();) {
|
||||
consume_whitespace();
|
||||
if (lexer.next_is('}'))
|
||||
break;
|
||||
if (!first) {
|
||||
assert_specific(',');
|
||||
consume_whitespace();
|
||||
}
|
||||
|
||||
assert_specific('"');
|
||||
auto string = lexer.consume_until('"');
|
||||
assert_specific('"');
|
||||
consume_whitespace();
|
||||
|
||||
if (enumeration.values.contains(string))
|
||||
report_parsing_error(String::formatted("Enumeration {} contains duplicate member '{}'", name, string), filename, input, lexer.tell());
|
||||
else
|
||||
enumeration.values.set(string);
|
||||
|
||||
if (first)
|
||||
enumeration.first_member = move(string);
|
||||
|
||||
first = false;
|
||||
}
|
||||
|
||||
consume_whitespace();
|
||||
assert_specific('}');
|
||||
assert_specific(';');
|
||||
|
||||
HashTable<String> names_already_seen;
|
||||
for (auto& entry : enumeration.values)
|
||||
enumeration.translated_cpp_names.set(entry, convert_enumeration_value_to_cpp_enum_member(entry, names_already_seen));
|
||||
|
||||
interface.enumerations.set(name, move(enumeration));
|
||||
consume_whitespace();
|
||||
}
|
||||
|
||||
void Parser::parse_dictionary(Interface& interface)
|
||||
{
|
||||
assert_string("dictionary");
|
||||
consume_whitespace();
|
||||
|
||||
Dictionary dictionary {};
|
||||
|
||||
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch); });
|
||||
consume_whitespace();
|
||||
|
||||
if (lexer.consume_specific(':')) {
|
||||
consume_whitespace();
|
||||
dictionary.parent_name = lexer.consume_until([](auto ch) { return is_ascii_space(ch); });
|
||||
consume_whitespace();
|
||||
}
|
||||
assert_specific('{');
|
||||
|
||||
for (;;) {
|
||||
consume_whitespace();
|
||||
|
||||
if (lexer.consume_specific('}')) {
|
||||
consume_whitespace();
|
||||
assert_specific(';');
|
||||
break;
|
||||
}
|
||||
|
||||
bool required = false;
|
||||
HashMap<String, String> extended_attributes;
|
||||
|
||||
if (lexer.consume_specific("required")) {
|
||||
required = true;
|
||||
consume_whitespace();
|
||||
if (lexer.consume_specific('['))
|
||||
extended_attributes = parse_extended_attributes();
|
||||
}
|
||||
|
||||
auto type = parse_type();
|
||||
consume_whitespace();
|
||||
|
||||
auto name = lexer.consume_until([](auto ch) { return is_ascii_space(ch) || ch == ';'; });
|
||||
consume_whitespace();
|
||||
|
||||
Optional<StringView> default_value;
|
||||
|
||||
if (lexer.consume_specific('=')) {
|
||||
VERIFY(!required);
|
||||
consume_whitespace();
|
||||
default_value = lexer.consume_until([](auto ch) { return is_ascii_space(ch) || ch == ';'; });
|
||||
consume_whitespace();
|
||||
}
|
||||
|
||||
assert_specific(';');
|
||||
|
||||
DictionaryMember member {
|
||||
required,
|
||||
move(type),
|
||||
name,
|
||||
move(extended_attributes),
|
||||
Optional<String>(move(default_value)),
|
||||
};
|
||||
dictionary.members.append(move(member));
|
||||
}
|
||||
|
||||
// dictionary members need to be evaluated in lexicographical order
|
||||
quick_sort(dictionary.members, [&](auto& one, auto& two) {
|
||||
return one.name < two.name;
|
||||
});
|
||||
|
||||
interface.dictionaries.set(name, move(dictionary));
|
||||
consume_whitespace();
|
||||
}
|
||||
|
||||
void Parser::parse_non_interface_entities(bool allow_interface, Interface& interface)
|
||||
{
|
||||
while (!lexer.is_eof()) {
|
||||
if (lexer.next_is("dictionary")) {
|
||||
parse_dictionary(interface);
|
||||
} else if (lexer.next_is("enum")) {
|
||||
parse_enumeration(interface);
|
||||
} else if ((allow_interface && !lexer.next_is("interface")) || !allow_interface) {
|
||||
report_parsing_error("expected 'enum' or 'dictionary'", filename, input, lexer.tell());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NonnullOwnPtr<Interface> Parser::parse()
|
||||
{
|
||||
auto this_module = Core::File::real_path_for(filename);
|
||||
s_all_imported_paths.set(this_module);
|
||||
|
||||
auto interface = make<Interface>();
|
||||
interface->module_own_path = this_module;
|
||||
|
||||
NonnullOwnPtrVector<Interface> imports;
|
||||
while (lexer.consume_specific("#import")) {
|
||||
consume_whitespace();
|
||||
assert_specific('<');
|
||||
auto path = lexer.consume_until('>');
|
||||
lexer.ignore();
|
||||
auto maybe_interface = resolve_import(path);
|
||||
if (maybe_interface.has_value()) {
|
||||
for (auto& entry : maybe_interface.value()->imported_paths)
|
||||
s_all_imported_paths.set(entry);
|
||||
imports.append(maybe_interface.release_value());
|
||||
}
|
||||
consume_whitespace();
|
||||
}
|
||||
interface->imported_paths = s_all_imported_paths;
|
||||
|
||||
if (lexer.consume_specific('['))
|
||||
interface->extended_attributes = parse_extended_attributes();
|
||||
|
||||
parse_non_interface_entities(true, *interface);
|
||||
|
||||
if (lexer.consume_specific("interface"))
|
||||
parse_interface(*interface);
|
||||
|
||||
parse_non_interface_entities(false, *interface);
|
||||
|
||||
for (auto& import : imports) {
|
||||
// FIXME: Instead of copying every imported entity into the current interface, query imports directly
|
||||
for (auto& dictionary : import.dictionaries)
|
||||
interface->dictionaries.set(dictionary.key, dictionary.value);
|
||||
|
||||
for (auto& enumeration : import.enumerations) {
|
||||
auto enumeration_copy = enumeration.value;
|
||||
enumeration_copy.is_original_definition = false;
|
||||
interface->enumerations.set(enumeration.key, move(enumeration_copy));
|
||||
}
|
||||
}
|
||||
interface->imported_modules = move(imports);
|
||||
|
||||
return interface;
|
||||
}
|
||||
|
||||
Parser::Parser(String filename, StringView contents, String import_base_path)
|
||||
: import_base_path(move(import_base_path))
|
||||
, filename(move(filename))
|
||||
, input(contents)
|
||||
, lexer(input)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "IDLTypes.h"
|
||||
#include <AK/CharacterTypes.h>
|
||||
#include <AK/GenericLexer.h>
|
||||
|
||||
namespace IDL {
|
||||
|
||||
class Parser {
|
||||
public:
|
||||
Parser(String filename, StringView contents, String import_base_path);
|
||||
NonnullOwnPtr<Interface> parse();
|
||||
|
||||
private:
|
||||
// https://webidl.spec.whatwg.org/#dfn-special-operation
|
||||
// A special operation is a getter, setter or deleter.
|
||||
enum class IsSpecialOperation {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
|
||||
void assert_specific(char ch);
|
||||
void assert_string(StringView expected);
|
||||
void consume_whitespace();
|
||||
Optional<NonnullOwnPtr<Interface>> resolve_import(auto path);
|
||||
|
||||
HashMap<String, String> parse_extended_attributes();
|
||||
void parse_attribute(HashMap<String, String>& extended_attributes, Interface&);
|
||||
void parse_interface(Interface&);
|
||||
void parse_non_interface_entities(bool allow_interface, Interface&);
|
||||
void parse_enumeration(Interface&);
|
||||
void parse_dictionary(Interface&);
|
||||
void parse_constructor(Interface&);
|
||||
void parse_getter(HashMap<String, String>& extended_attributes, Interface&);
|
||||
void parse_setter(HashMap<String, String>& extended_attributes, Interface&);
|
||||
void parse_deleter(HashMap<String, String>& extended_attributes, Interface&);
|
||||
void parse_stringifier(HashMap<String, String>& extended_attributes, Interface&);
|
||||
void parse_iterable(Interface&);
|
||||
Function parse_function(HashMap<String, String>& extended_attributes, Interface&, IsSpecialOperation is_special_operation = IsSpecialOperation::No);
|
||||
Vector<Parameter> parse_parameters();
|
||||
NonnullRefPtr<Type> parse_type();
|
||||
void parse_constant(Interface&);
|
||||
|
||||
static HashTable<String> s_all_imported_paths;
|
||||
String import_base_path;
|
||||
String filename;
|
||||
StringView input;
|
||||
GenericLexer lexer;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/HashMap.h>
|
||||
#include <AK/NonnullOwnPtrVector.h>
|
||||
#include <AK/NonnullRefPtrVector.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <AK/SourceGenerator.h>
|
||||
#include <AK/String.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <AK/Tuple.h>
|
||||
#include <AK/TypeCasts.h>
|
||||
|
||||
namespace IDL {
|
||||
|
||||
template<typename FunctionType>
|
||||
static size_t get_function_length(FunctionType& function)
|
||||
{
|
||||
size_t length = 0;
|
||||
for (auto& parameter : function.parameters) {
|
||||
if (!parameter.optional && !parameter.variadic)
|
||||
length++;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
enum class SequenceStorageType {
|
||||
Vector, // Used to safely store non-JS values
|
||||
MarkedVector, // Used to safely store JS::Value and anything that inherits JS::Cell, e.g. JS::Object
|
||||
};
|
||||
|
||||
struct CppType {
|
||||
String name;
|
||||
SequenceStorageType sequence_storage_type;
|
||||
};
|
||||
|
||||
struct Type : public RefCounted<Type> {
|
||||
Type() = default;
|
||||
|
||||
Type(String name, bool nullable)
|
||||
: name(move(name))
|
||||
, nullable(nullable)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~Type() = default;
|
||||
|
||||
String name;
|
||||
bool nullable { false };
|
||||
bool is_string() const { return name.is_one_of("ByteString", "CSSOMString", "DOMString", "USVString"); }
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-integer-type
|
||||
bool is_integer() const { return name.is_one_of("byte", "octet", "short", "unsigned short", "long", "unsigned long", "long long", "unsigned long long"); }
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-numeric-type
|
||||
bool is_numeric() const { return is_integer() || name.is_one_of("float", "unrestricted float", "double", "unrestricted double"); }
|
||||
};
|
||||
|
||||
CppType idl_type_name_to_cpp_type(Type const& type);
|
||||
|
||||
struct UnionType : public Type {
|
||||
UnionType() = default;
|
||||
|
||||
UnionType(String name, bool nullable, NonnullRefPtrVector<Type> member_types)
|
||||
: Type(move(name), nullable)
|
||||
, member_types(move(member_types))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~UnionType() override = default;
|
||||
|
||||
NonnullRefPtrVector<Type> member_types;
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-flattened-union-member-types
|
||||
NonnullRefPtrVector<Type> flattened_member_types() const
|
||||
{
|
||||
// 1. Let T be the union type.
|
||||
|
||||
// 2. Initialize S to ∅.
|
||||
NonnullRefPtrVector<Type> types;
|
||||
|
||||
// 3. For each member type U of T:
|
||||
for (auto& type : member_types) {
|
||||
// FIXME: 1. If U is an annotated type, then set U to be the inner type of U.
|
||||
|
||||
// 2. If U is a nullable type, then set U to be the inner type of U. (NOTE: Not necessary as nullable is stored with Type and not as a separate struct)
|
||||
|
||||
// 3. If U is a union type, then add to S the flattened member types of U.
|
||||
if (is<UnionType>(type)) {
|
||||
auto& union_member_type = verify_cast<UnionType>(type);
|
||||
types.extend(union_member_type.flattened_member_types());
|
||||
} else {
|
||||
// 4. Otherwise, U is not a union type. Add U to S.
|
||||
types.append(type);
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Return S.
|
||||
return types;
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-number-of-nullable-member-types
|
||||
size_t number_of_nullable_member_types() const
|
||||
{
|
||||
// 1. Let T be the union type.
|
||||
|
||||
// 2. Initialize n to 0.
|
||||
size_t num_nullable_member_types = 0;
|
||||
|
||||
// 3. For each member type U of T:
|
||||
for (auto& type : member_types) {
|
||||
// 1. If U is a nullable type, then:
|
||||
if (type.nullable) {
|
||||
// 1. Set n to n + 1.
|
||||
++num_nullable_member_types;
|
||||
|
||||
// 2. Set U to be the inner type of U. (NOTE: Not necessary as nullable is stored with Type and not as a separate struct)
|
||||
}
|
||||
|
||||
// 2. If U is a union type, then:
|
||||
if (is<UnionType>(type)) {
|
||||
auto& union_member_type = verify_cast<UnionType>(type);
|
||||
|
||||
// 1. Let m be the number of nullable member types of U.
|
||||
// 2. Set n to n + m.
|
||||
num_nullable_member_types += union_member_type.number_of_nullable_member_types();
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Return n.
|
||||
return num_nullable_member_types;
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-includes-a-nullable-type
|
||||
bool includes_nullable_type() const
|
||||
{
|
||||
// -> the type is a union type and its number of nullable member types is 1.
|
||||
return number_of_nullable_member_types() == 1;
|
||||
}
|
||||
|
||||
// -> https://webidl.spec.whatwg.org/#dfn-includes-undefined
|
||||
bool includes_undefined() const
|
||||
{
|
||||
// -> the type is a union type and one of its member types includes undefined.
|
||||
for (auto& type : member_types) {
|
||||
if (is<UnionType>(type)) {
|
||||
auto& union_type = verify_cast<UnionType>(type);
|
||||
if (union_type.includes_undefined())
|
||||
return true;
|
||||
}
|
||||
|
||||
if (type.name == "undefined"sv)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String to_variant() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
builder.append("Variant<");
|
||||
|
||||
auto flattened_types = flattened_member_types();
|
||||
for (size_t type_index = 0; type_index < flattened_types.size(); ++type_index) {
|
||||
auto& type = flattened_types.at(type_index);
|
||||
|
||||
if (type_index > 0)
|
||||
builder.append(", ");
|
||||
|
||||
auto cpp_type = idl_type_name_to_cpp_type(type);
|
||||
builder.append(cpp_type.name);
|
||||
}
|
||||
|
||||
if (includes_undefined())
|
||||
builder.append(", Empty");
|
||||
|
||||
builder.append('>');
|
||||
return builder.to_string();
|
||||
}
|
||||
};
|
||||
|
||||
struct Parameter {
|
||||
NonnullRefPtr<Type> type;
|
||||
String name;
|
||||
bool optional { false };
|
||||
Optional<String> optional_default_value;
|
||||
HashMap<String, String> extended_attributes;
|
||||
bool variadic { false };
|
||||
};
|
||||
|
||||
struct Function {
|
||||
NonnullRefPtr<Type> return_type;
|
||||
String name;
|
||||
Vector<Parameter> parameters;
|
||||
HashMap<String, String> extended_attributes;
|
||||
|
||||
size_t length() const { return get_function_length(*this); }
|
||||
};
|
||||
|
||||
struct Constructor {
|
||||
String name;
|
||||
Vector<Parameter> parameters;
|
||||
|
||||
size_t length() const { return get_function_length(*this); }
|
||||
};
|
||||
|
||||
struct Constant {
|
||||
NonnullRefPtr<Type> type;
|
||||
String name;
|
||||
String value;
|
||||
};
|
||||
|
||||
struct Attribute {
|
||||
bool readonly { false };
|
||||
NonnullRefPtr<Type> type;
|
||||
String name;
|
||||
HashMap<String, String> extended_attributes;
|
||||
|
||||
// Added for convenience after parsing
|
||||
String getter_callback_name;
|
||||
String setter_callback_name;
|
||||
};
|
||||
|
||||
struct DictionaryMember {
|
||||
bool required { false };
|
||||
NonnullRefPtr<Type> type;
|
||||
String name;
|
||||
HashMap<String, String> extended_attributes;
|
||||
Optional<String> default_value;
|
||||
};
|
||||
|
||||
struct Dictionary {
|
||||
String parent_name;
|
||||
Vector<DictionaryMember> members;
|
||||
};
|
||||
|
||||
struct Enumeration {
|
||||
HashTable<String> values;
|
||||
HashMap<String, String> translated_cpp_names;
|
||||
String first_member;
|
||||
bool is_original_definition { true };
|
||||
};
|
||||
|
||||
struct Interface;
|
||||
|
||||
struct ParameterizedType : public Type {
|
||||
ParameterizedType() = default;
|
||||
|
||||
ParameterizedType(String name, bool nullable, NonnullRefPtrVector<Type> parameters)
|
||||
: Type(move(name), nullable)
|
||||
, parameters(move(parameters))
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~ParameterizedType() override = default;
|
||||
|
||||
NonnullRefPtrVector<Type> parameters;
|
||||
|
||||
void generate_sequence_from_iterable(SourceGenerator& generator, String const& cpp_name, String const& iterable_cpp_name, String const& iterator_method_cpp_name, IDL::Interface const&, size_t recursion_depth) const;
|
||||
};
|
||||
|
||||
struct Interface {
|
||||
String name;
|
||||
String parent_name;
|
||||
|
||||
HashMap<String, String> extended_attributes;
|
||||
|
||||
Vector<Attribute> attributes;
|
||||
Vector<Constant> constants;
|
||||
Vector<Constructor> constructors;
|
||||
Vector<Function> functions;
|
||||
Vector<Function> static_functions;
|
||||
bool has_stringifier { false };
|
||||
Optional<String> stringifier_attribute;
|
||||
bool has_unscopable_member { false };
|
||||
|
||||
Optional<NonnullRefPtr<Type>> value_iterator_type;
|
||||
Optional<Tuple<NonnullRefPtr<Type>, NonnullRefPtr<Type>>> pair_iterator_types;
|
||||
|
||||
Optional<Function> named_property_getter;
|
||||
Optional<Function> named_property_setter;
|
||||
|
||||
Optional<Function> indexed_property_getter;
|
||||
Optional<Function> indexed_property_setter;
|
||||
|
||||
Optional<Function> named_property_deleter;
|
||||
|
||||
HashMap<String, Dictionary> dictionaries;
|
||||
HashMap<String, Enumeration> enumerations;
|
||||
|
||||
// Added for convenience after parsing
|
||||
String wrapper_class;
|
||||
String wrapper_base_class;
|
||||
String fully_qualified_name;
|
||||
String constructor_class;
|
||||
String prototype_class;
|
||||
String prototype_base_class;
|
||||
|
||||
String module_own_path;
|
||||
HashTable<String> imported_paths;
|
||||
NonnullOwnPtrVector<Interface> imported_modules;
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-support-indexed-properties
|
||||
bool supports_indexed_properties() const { return indexed_property_getter.has_value(); }
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-support-named-properties
|
||||
bool supports_named_properties() const { return named_property_getter.has_value(); }
|
||||
|
||||
// https://webidl.spec.whatwg.org/#dfn-legacy-platform-object
|
||||
bool is_legacy_platform_object() const { return !extended_attributes.contains("Global") && (supports_indexed_properties() || supports_named_properties()); }
|
||||
};
|
||||
|
||||
}
|
168
Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/main.cpp
Normal file
168
Meta/Lagom/Tools/CodeGenerators/LibWeb/WrapperGenerator/main.cpp
Normal file
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2021, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2021, Luke Wilde <lukew@serenityos.org>
|
||||
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include "IDLParser.h"
|
||||
#include "IDLTypes.h"
|
||||
#include <AK/Debug.h>
|
||||
#include <AK/LexicalPath.h>
|
||||
#include <LibCore/ArgsParser.h>
|
||||
#include <LibCore/File.h>
|
||||
|
||||
extern Vector<StringView> s_header_search_paths;
|
||||
|
||||
namespace IDL {
|
||||
void generate_constructor_header(IDL::Interface const&);
|
||||
void generate_constructor_implementation(IDL::Interface const&);
|
||||
void generate_prototype_header(IDL::Interface const&);
|
||||
void generate_prototype_implementation(IDL::Interface const&);
|
||||
void generate_header(IDL::Interface const&);
|
||||
void generate_implementation(IDL::Interface const&);
|
||||
void generate_iterator_prototype_header(IDL::Interface const&);
|
||||
void generate_iterator_prototype_implementation(IDL::Interface const&);
|
||||
void generate_iterator_header(IDL::Interface const&);
|
||||
void generate_iterator_implementation(IDL::Interface const&);
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
Core::ArgsParser args_parser;
|
||||
StringView path = nullptr;
|
||||
StringView import_base_path = nullptr;
|
||||
bool header_mode = false;
|
||||
bool implementation_mode = false;
|
||||
bool constructor_header_mode = false;
|
||||
bool constructor_implementation_mode = false;
|
||||
bool prototype_header_mode = false;
|
||||
bool prototype_implementation_mode = false;
|
||||
bool iterator_header_mode = false;
|
||||
bool iterator_implementation_mode = false;
|
||||
bool iterator_prototype_header_mode = false;
|
||||
bool iterator_prototype_implementation_mode = false;
|
||||
args_parser.add_option(header_mode, "Generate the wrapper .h file", "header", 'H');
|
||||
args_parser.add_option(implementation_mode, "Generate the wrapper .cpp file", "implementation", 'I');
|
||||
args_parser.add_option(constructor_header_mode, "Generate the constructor .h file", "constructor-header", 'C');
|
||||
args_parser.add_option(constructor_implementation_mode, "Generate the constructor .cpp file", "constructor-implementation", 'O');
|
||||
args_parser.add_option(prototype_header_mode, "Generate the prototype .h file", "prototype-header", 'P');
|
||||
args_parser.add_option(prototype_implementation_mode, "Generate the prototype .cpp file", "prototype-implementation", 'R');
|
||||
args_parser.add_option(iterator_header_mode, "Generate the iterator wrapper .h file", "iterator-header", 0);
|
||||
args_parser.add_option(iterator_implementation_mode, "Generate the iterator wrapper .cpp file", "iterator-implementation", 0);
|
||||
args_parser.add_option(iterator_prototype_header_mode, "Generate the iterator prototype .h file", "iterator-prototype-header", 0);
|
||||
args_parser.add_option(iterator_prototype_implementation_mode, "Generate the iterator prototype .cpp file", "iterator-prototype-implementation", 0);
|
||||
args_parser.add_option(Core::ArgsParser::Option {
|
||||
.requires_argument = true,
|
||||
.help_string = "Add a header search path passed to the compiler",
|
||||
.long_name = "header-include-path",
|
||||
.short_name = 'i',
|
||||
.value_name = "path",
|
||||
.accept_value = [&](char const* s) {
|
||||
s_header_search_paths.append(s);
|
||||
return true;
|
||||
},
|
||||
});
|
||||
args_parser.add_positional_argument(path, "IDL file", "idl-file");
|
||||
args_parser.add_positional_argument(import_base_path, "Import base path", "import-base-path", Core::ArgsParser::Required::No);
|
||||
args_parser.parse(argc, argv);
|
||||
|
||||
auto file_or_error = Core::File::open(path, Core::OpenMode::ReadOnly);
|
||||
if (file_or_error.is_error()) {
|
||||
warnln("Failed to open {}: {}", path, file_or_error.error());
|
||||
return 1;
|
||||
}
|
||||
|
||||
LexicalPath lexical_path(path);
|
||||
auto& namespace_ = lexical_path.parts_view().at(lexical_path.parts_view().size() - 2);
|
||||
|
||||
auto data = file_or_error.value()->read_all();
|
||||
|
||||
if (import_base_path.is_null())
|
||||
import_base_path = lexical_path.dirname();
|
||||
|
||||
auto interface = IDL::Parser(path, data, import_base_path).parse();
|
||||
|
||||
if (namespace_.is_one_of("Crypto", "CSS", "DOM", "Encoding", "HTML", "UIEvents", "Geometry", "HighResolutionTime", "IntersectionObserver", "NavigationTiming", "RequestIdleCallback", "ResizeObserver", "SVG", "Selection", "XHR", "URL")) {
|
||||
StringBuilder builder;
|
||||
builder.append(namespace_);
|
||||
builder.append("::");
|
||||
builder.append(interface->name);
|
||||
interface->fully_qualified_name = builder.to_string();
|
||||
} else {
|
||||
interface->fully_qualified_name = interface->name;
|
||||
}
|
||||
|
||||
if constexpr (WRAPPER_GENERATOR_DEBUG) {
|
||||
dbgln("Attributes:");
|
||||
for (auto& attribute : interface->attributes) {
|
||||
dbgln(" {}{}{} {}",
|
||||
attribute.readonly ? "readonly " : "",
|
||||
attribute.type->name,
|
||||
attribute.type->nullable ? "?" : "",
|
||||
attribute.name);
|
||||
}
|
||||
|
||||
dbgln("Functions:");
|
||||
for (auto& function : interface->functions) {
|
||||
dbgln(" {}{} {}",
|
||||
function.return_type->name,
|
||||
function.return_type->nullable ? "?" : "",
|
||||
function.name);
|
||||
for (auto& parameter : function.parameters) {
|
||||
dbgln(" {}{} {}",
|
||||
parameter.type->name,
|
||||
parameter.type->nullable ? "?" : "",
|
||||
parameter.name);
|
||||
}
|
||||
}
|
||||
|
||||
dbgln("Static Functions:");
|
||||
for (auto& function : interface->static_functions) {
|
||||
dbgln(" static {}{} {}",
|
||||
function.return_type->name,
|
||||
function.return_type->nullable ? "?" : "",
|
||||
function.name);
|
||||
for (auto& parameter : function.parameters) {
|
||||
dbgln(" {}{} {}",
|
||||
parameter.type->name,
|
||||
parameter.type->nullable ? "?" : "",
|
||||
parameter.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (header_mode)
|
||||
IDL::generate_header(*interface);
|
||||
|
||||
if (implementation_mode)
|
||||
IDL::generate_implementation(*interface);
|
||||
|
||||
if (constructor_header_mode)
|
||||
IDL::generate_constructor_header(*interface);
|
||||
|
||||
if (constructor_implementation_mode)
|
||||
IDL::generate_constructor_implementation(*interface);
|
||||
|
||||
if (prototype_header_mode)
|
||||
IDL::generate_prototype_header(*interface);
|
||||
|
||||
if (prototype_implementation_mode)
|
||||
IDL::generate_prototype_implementation(*interface);
|
||||
|
||||
if (iterator_header_mode)
|
||||
IDL::generate_iterator_header(*interface);
|
||||
|
||||
if (iterator_implementation_mode)
|
||||
IDL::generate_iterator_implementation(*interface);
|
||||
|
||||
if (iterator_prototype_header_mode)
|
||||
IDL::generate_iterator_prototype_header(*interface);
|
||||
|
||||
if (iterator_prototype_implementation_mode)
|
||||
IDL::generate_iterator_prototype_implementation(*interface);
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue