2023-08-12 13:27:24 +01:00
/*
* Copyright ( c ) 2022 - 2023 , Sam Atkins < atkinssj @ serenityos . org >
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include "GeneratorUtil.h"
# include <AK/SourceGenerator.h>
# include <LibCore/ArgsParser.h>
# include <LibMain/Main.h>
ErrorOr < void > generate_header_file ( JsonObject & pseudo_classes_data , Core : : File & file ) ;
ErrorOr < void > generate_implementation_file ( JsonObject & pseudo_classes_data , Core : : File & file ) ;
2025-07-08 12:14:08 +02:00
ErrorOr < int > ladybird_main ( Main : : Arguments arguments )
2023-08-12 13:27:24 +01:00
{
StringView generated_header_path ;
StringView generated_implementation_path ;
2024-08-14 14:06:03 +01:00
StringView json_path ;
2023-08-12 13:27:24 +01:00
Core : : ArgsParser args_parser ;
args_parser . add_option ( generated_header_path , " Path to the PseudoClasses header file to generate " , " generated-header-path " , ' h ' , " generated-header-path " ) ;
args_parser . add_option ( generated_implementation_path , " Path to the PseudoClasses implementation file to generate " , " generated-implementation-path " , ' c ' , " generated-implementation-path " ) ;
2024-08-14 14:06:03 +01:00
args_parser . add_option ( json_path , " Path to the JSON file to read from " , " json-path " , ' j ' , " json-path " ) ;
2023-08-12 13:27:24 +01:00
args_parser . parse ( arguments ) ;
2024-08-14 14:06:03 +01:00
auto json = TRY ( read_entire_file_as_json ( json_path ) ) ;
2023-08-12 13:27:24 +01:00
VERIFY ( json . is_object ( ) ) ;
auto data = json . as_object ( ) ;
auto generated_header_file = TRY ( Core : : File : : open ( generated_header_path , Core : : File : : OpenMode : : Write ) ) ;
auto generated_implementation_file = TRY ( Core : : File : : open ( generated_implementation_path , Core : : File : : OpenMode : : Write ) ) ;
TRY ( generate_header_file ( data , * generated_header_file ) ) ;
TRY ( generate_implementation_file ( data , * generated_implementation_file ) ) ;
return 0 ;
}
ErrorOr < void > generate_header_file ( JsonObject & pseudo_classes_data , Core : : File & file )
{
StringBuilder builder ;
SourceGenerator generator { builder } ;
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-08-12 13:27:24 +01:00
# pragma once
# include <AK/Optional.h>
# include <AK/StringView.h>
namespace Web : : CSS {
enum class PseudoClass {
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-12 13:27:24 +01:00
2023-08-22 09:09:07 +02:00
pseudo_classes_data . for_each_member ( [ & ] ( auto & name , auto & ) {
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-12 13:27:24 +01:00
2023-08-21 16:06:29 +02:00
member_generator . appendln ( " @name:titlecase@, " ) ;
2023-08-22 09:09:07 +02:00
} ) ;
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2025-04-17 13:39:30 +02:00
__Count ,
2023-08-12 13:27:24 +01:00
} ;
Optional < PseudoClass > pseudo_class_from_string ( StringView ) ;
StringView pseudo_class_name ( PseudoClass ) ;
struct PseudoClassMetadata {
enum class ParameterType {
None ,
ANPlusB ,
2025-08-12 11:55:35 +01:00
ANPlusBList ,
2023-08-12 13:27:24 +01:00
ANPlusBOf ,
2023-08-12 13:47:45 +01:00
CompoundSelector ,
2023-08-12 13:27:24 +01:00
ForgivingSelectorList ,
2024-07-13 09:19:30 -07:00
ForgivingRelativeSelectorList ,
2023-08-12 18:11:50 +01:00
Ident ,
2023-08-12 13:27:24 +01:00
LanguageRanges ,
2024-11-14 11:22:46 +00:00
RelativeSelectorList ,
2023-08-12 13:27:24 +01:00
SelectorList ,
} parameter_type ;
bool is_valid_as_function ;
bool is_valid_as_identifier ;
} ;
PseudoClassMetadata pseudo_class_metadata ( PseudoClass ) ;
}
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-12 13:27:24 +01:00
TRY ( file . write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
return { } ;
}
ErrorOr < void > generate_implementation_file ( JsonObject & pseudo_classes_data , Core : : File & file )
{
StringBuilder builder ;
SourceGenerator generator { builder } ;
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-08-12 13:27:24 +01:00
# include <LibWeb/CSS/PseudoClass.h>
namespace Web : : CSS {
Optional < PseudoClass > pseudo_class_from_string ( StringView string )
{
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-12 13:27:24 +01:00
2023-08-22 09:09:07 +02:00
pseudo_classes_data . for_each_member ( [ & ] ( auto & name , auto & ) {
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-22 09:09:07 +02:00
member_generator . set ( " name " , name ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-12 13:27:24 +01:00
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2023-08-12 13:27:24 +01:00
if ( string . equals_ignoring_ascii_case ( " @name@ " sv ) )
return PseudoClass : : @ name : titlecase @ ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-22 09:09:07 +02:00
} ) ;
2023-08-12 13:27:24 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-08-12 13:27:24 +01:00
return { } ;
}
StringView pseudo_class_name ( PseudoClass pseudo_class )
{
switch ( pseudo_class ) {
2025-04-17 13:39:30 +02:00
case PseudoClass : : __Count :
VERIFY_NOT_REACHED ( ) ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-12 13:27:24 +01:00
2023-08-22 09:09:07 +02:00
pseudo_classes_data . for_each_member ( [ & ] ( auto & name , auto & ) {
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-22 09:09:07 +02:00
member_generator . set ( " name " , name ) ;
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-12 13:27:24 +01:00
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2023-08-12 13:27:24 +01:00
case PseudoClass : : @ name : titlecase @ :
return " @name@ " sv ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-22 09:09:07 +02:00
} ) ;
2023-08-12 13:27:24 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-08-12 13:27:24 +01:00
}
VERIFY_NOT_REACHED ( ) ;
}
PseudoClassMetadata pseudo_class_metadata ( PseudoClass pseudo_class )
{
switch ( pseudo_class ) {
2025-04-17 13:39:30 +02:00
case PseudoClass : : __Count :
VERIFY_NOT_REACHED ( ) ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-12 13:27:24 +01:00
2023-08-22 09:09:07 +02:00
pseudo_classes_data . for_each_member ( [ & ] ( auto & name , JsonValue const & value ) {
2023-08-21 16:42:48 +02:00
auto member_generator = generator . fork ( ) ;
2023-08-12 13:27:24 +01:00
auto & pseudo_class = value . as_object ( ) ;
2025-02-17 15:04:56 -05:00
auto argument_string = pseudo_class . get_string ( " argument " sv ) . value ( ) ;
2023-08-12 13:27:24 +01:00
bool is_valid_as_identifier = argument_string . is_empty ( ) ;
bool is_valid_as_function = ! argument_string . is_empty ( ) ;
if ( argument_string . ends_with ( ' ? ' ) ) {
is_valid_as_identifier = true ;
2025-02-17 15:04:56 -05:00
argument_string = MUST ( argument_string . substring_from_byte_offset ( 0 , argument_string . byte_count ( ) - 1 ) ) ;
2023-08-12 13:27:24 +01:00
}
String parameter_type = " None " _string ;
if ( is_valid_as_function ) {
if ( argument_string = = " <an+b> " sv ) {
parameter_type = " ANPlusB " _string ;
2025-08-12 11:55:35 +01:00
} else if ( argument_string = = " <an+b># " sv ) {
parameter_type = " ANPlusBList " _string ;
2023-08-12 13:27:24 +01:00
} else if ( argument_string = = " <an+b-of> " sv ) {
parameter_type = " ANPlusBOf " _string ;
2023-08-12 13:47:45 +01:00
} else if ( argument_string = = " <compound-selector> " sv ) {
parameter_type = " CompoundSelector " _string ;
2023-08-12 13:27:24 +01:00
} else if ( argument_string = = " <forgiving-selector-list> " sv ) {
parameter_type = " ForgivingSelectorList " _string ;
2024-07-13 09:19:30 -07:00
} else if ( argument_string = = " <forgiving-relative-selector-list> " sv ) {
parameter_type = " ForgivingRelativeSelectorList " _string ;
2023-08-12 18:11:50 +01:00
} else if ( argument_string = = " <ident> " sv ) {
parameter_type = " Ident " _string ;
2023-08-12 13:27:24 +01:00
} else if ( argument_string = = " <language-ranges> " sv ) {
parameter_type = " LanguageRanges " _string ;
2024-11-14 11:22:46 +00:00
} else if ( argument_string = = " <relative-selector-list> " sv ) {
parameter_type = " RelativeSelectorList " _string ;
2023-08-12 13:27:24 +01:00
} else if ( argument_string = = " <selector-list> " sv ) {
parameter_type = " SelectorList " _string ;
} else {
warnln ( " Unrecognized pseudo-class argument type: `{}` " , argument_string ) ;
VERIFY_NOT_REACHED ( ) ;
}
}
2023-08-21 16:59:41 +02:00
member_generator . set ( " name:titlecase " , title_casify ( name ) ) ;
2023-08-21 14:38:55 +02:00
member_generator . set ( " parameter_type " , parameter_type ) ;
member_generator . set ( " is_valid_as_function " , is_valid_as_function ? " true " _string : " false " _string ) ;
member_generator . set ( " is_valid_as_identifier " , is_valid_as_identifier ? " true " _string : " false " _string ) ;
2023-08-12 13:27:24 +01:00
2023-08-21 16:39:43 +02:00
member_generator . append ( R " ~~~(
2023-08-12 13:27:24 +01:00
case PseudoClass : : @ name : titlecase @ :
return {
. parameter_type = PseudoClassMetadata : : ParameterType : : @ parameter_type @ ,
. is_valid_as_function = @ is_valid_as_function @ ,
. is_valid_as_identifier = @ is_valid_as_identifier @ ,
} ;
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-22 09:09:07 +02:00
} ) ;
2023-08-12 13:27:24 +01:00
2023-08-21 16:39:43 +02:00
generator . append ( R " ~~~(
2023-08-12 13:27:24 +01:00
}
VERIFY_NOT_REACHED ( ) ;
}
}
2023-08-21 16:39:43 +02:00
) ~ ~ ~ " );
2023-08-12 13:27:24 +01:00
TRY ( file . write_until_depleted ( generator . as_string_view ( ) . bytes ( ) ) ) ;
return { } ;
}