Bug 1553482 - Exposing some of the grammar as macros;r=arai

To extract Huffman tables (see bug 1552435), we need the ability to walk through the grammar.
This patch starts implementing grammar walking, as macros - at this stage, sufficiently to walk
through interfaces and start dealing with their fields.

Depends on D32291

Differential Revision: https://phabricator.services.mozilla.com/D32295

--HG--
extra : moz-landing-system : lando
This commit is contained in:
David Teller 2019-05-24 12:10:29 +00:00
Родитель a0811ecf81
Коммит b39f990648
4 изменённых файлов: 2351 добавлений и 407 удалений

Просмотреть файл

@ -19,7 +19,7 @@ namespace js {
namespace frontend {
const BinaryASTSupport::CharSlice BINASTKIND_DESCRIPTIONS[] = {
#define WITH_VARIANT(_, SPEC_NAME) \
#define WITH_VARIANT(_, SPEC_NAME, _2) \
BinaryASTSupport::CharSlice(SPEC_NAME, sizeof(SPEC_NAME) - 1),
FOR_EACH_BIN_KIND(WITH_VARIANT)
#undef WITH_VARIANT

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -8,6 +8,7 @@ autobins = false
binjs_meta = "^0.4.3"
clap = "^2"
env_logger = "^0.5.6"
Inflector = "^0.11"
itertools = "^0.7.6"
log = "0.4"
yaml-rust = "^0.4.2"

Просмотреть файл

@ -1,6 +1,7 @@
extern crate binjs_meta;
extern crate clap;
extern crate env_logger;
extern crate inflector;
extern crate itertools;
#[macro_use] extern crate log;
extern crate webidl;
@ -24,6 +25,16 @@ use clap::{ App, Arg };
use itertools::Itertools;
/// An extension of `ToCases` to produce macro-style names, e.g. `FOO_BAR`
/// from `FooBar` or `foo_bar`.
trait ToCases2: ToCases {
fn to_cpp_macro_case(&self) -> String {
use inflector::cases::screamingsnakecase::to_screaming_snake_case;
to_screaming_snake_case(&self.to_cpp_enum_case())
}
}
impl<T: ToCases> ToCases2 for T {}
/// Rules for generating the code for parsing a single field
/// of a node.
///
@ -959,13 +970,14 @@ impl CPPExporter {
let kind_limit = node_names.len();
buffer.push_str(&format!("\n#define FOR_EACH_BIN_KIND(F) \\\n{nodes}\n",
nodes = node_names.iter()
.map(|name| format!(" F({enum_name}, \"{spec_name}\")",
.map(|name| format!(" F({enum_name}, \"{spec_name}\", {macro_name})",
enum_name = name.to_cpp_enum_case(),
spec_name = name))
spec_name = name,
macro_name = name.to_cpp_macro_case()))
.format(" \\\n")));
buffer.push_str("
enum class BinASTKind: uint16_t {
#define EMIT_ENUM(name, _) name,
#define EMIT_ENUM(name, _1, _2) name,
FOR_EACH_BIN_KIND(EMIT_ENUM)
#undef EMIT_ENUM
};
@ -1023,6 +1035,135 @@ enum class BinASTInterfaceAndField: uint16_t {
};
");
buffer.push_str("
// Strongly typed iterations through the fields of interfaces.
//
// Each of these macros accepts the following arguments:
// - F: callback
// - PRIMITIVE: wrapper for primitive type names - called as `PRIMITIVE(typename)`
// - INTERFACE: wrapper for non-optional interface type names - called as `INTERFACE(typename)`
// - OPTIONAL_INTERFACE: wrapper for optional interface type names - called as `OPTIONAL_INTERFACE(typename)` where
// `typename` is the name of the interface (e.g. no `Maybe` prefix)
// - LIST: wrapper for list types - called as `LIST(list_typename, element_typename)`
// - SUM: wrapper for non-optional type names - called as `SUM(typename)`
// - OPTIONAL_SUM: wrapper for optional sum type names - called as `OPTIONAL_SUM(typename)` where
// `typename` is the name of the sum (e.g. no `Maybe` prefix)
// - STRING_ENUM: wrapper for non-optional string enum types - called as `STRING_ENUNM(typename)`
// - OPTIONAL_STRING_ENUM: wrapper for optional string enum type names - called as `OPTIONAL_STRING_ENUM(typename)` where
// `typename` is the name of the string enum (e.g. no `Maybe` prefix)
");
for (interface_name, interface) in self.syntax.interfaces_by_name().iter().sorted_by_key(|a| a.0) {
use std::borrow::Cow;
let interface_enum_name = interface_name.to_cpp_enum_case();
let interface_spec_name = interface_name.clone();
let interface_macro_name = interface.name().to_cpp_macro_case();
buffer.push_str(&format!("\n\n
// Strongly typed iteration through the fields of interface {interface_enum_name}.
#define FOR_EACH_BIN_FIELD_IN_INTERFACE_{interface_macro_name}(F, PRIMITIVE, INTERFACE, OPTIONAL_INTERFACE, LIST, SUM, OPTIONAL_SUM, STRING_ENUM, OPTIONAL_STRING_ENUM) \\\n{nodes}\n",
interface_macro_name = interface_macro_name,
interface_enum_name = interface_enum_name.clone(),
nodes = interface.contents().fields()
.iter()
.enumerate()
.map(|(i, field)| {
fn get_field_type_name(spec: &Spec, type_: &Type, make_optional: bool) -> Cow<'static, str> {
let optional = make_optional || type_.is_optional();
match *type_.spec() {
TypeSpec::Boolean if optional => Cow::from("PRIMITIVE(MaybeBoolean)"),
TypeSpec::Boolean => Cow::from("PRIMITIVE(Boolean)"),
TypeSpec::String if optional => Cow::from("PRIMITIVE(MaybeString)"),
TypeSpec::String => Cow::from("PRIMITIVE(String)"),
TypeSpec::Number if optional => Cow::from("PRIMITIVE(MaybeNumber)"),
TypeSpec::Number => Cow::from("PRIMITIVE(Number)"),
TypeSpec::UnsignedLong if optional => Cow::from("PRIMITIVE(MaybeUnsignedLong)"),
TypeSpec::UnsignedLong => Cow::from("PRIMITIVE(UnsignedLong)"),
TypeSpec::Offset if optional => Cow::from("PRIMITIVE(MaybeLazy)"),
TypeSpec::Offset => Cow::from("PRIMITIVE(Lazy)"),
TypeSpec::Void if optional => Cow::from("PRIMITIVE(MaybeVoid)"),
TypeSpec::Void => Cow::from("PRIMITIVE(Void)"),
TypeSpec::IdentifierName if optional => Cow::from("PRIMITIVE(MaybeIdentifierName)"),
TypeSpec::IdentifierName => Cow::from("PRIMITIVE(IdentifierName)"),
TypeSpec::PropertyKey if optional => Cow::from("PRIMITIVE(MaybePropertyKey)"),
TypeSpec::PropertyKey => Cow::from("PRIMITIVE(PropertyKey)"),
TypeSpec::Array { ref contents, .. } => Cow::from(
format!("LIST({name}, {contents})",
name = TypeName::type_(type_),
contents = TypeName::type_(contents),
)),
TypeSpec::NamedType(ref name) => match spec.get_type_by_name(name).expect("By now, all types MUST exist") {
NamedType::Typedef(alias_type) => {
if alias_type.is_optional() {
return get_field_type_name(spec, alias_type.as_ref(), true)
}
// Keep the simple name of sums and lists if there is one.
match *alias_type.spec() {
TypeSpec::TypeSum(ref contents ) => {
if optional {
Cow::from(format!("OPTIONAL_SUM({name})", name = name.to_cpp_enum_case()))
} else {
Cow::from(format!("SUM({name})", name = name.to_cpp_enum_case()))
}
}
TypeSpec::Array { ref contents, .. } => {
let contents = TypeName::type_(contents);
if optional {
Cow::from(format!("OPTIONAL_LIST({name}, {contents})",
name = name.to_cpp_enum_case(),
contents = contents))
} else {
Cow::from(format!("LIST({name}, {contents})",
name = name.to_cpp_enum_case(),
contents = contents))
}
}
_ => {
get_field_type_name(spec, alias_type.as_ref(), optional)
}
}
}
NamedType::StringEnum(_) if type_.is_optional() => Cow::from(
format!("OPTIONAL_STRING_ENUM({name})",
name = TypeName::type_(type_))),
NamedType::StringEnum(_) => Cow::from(
format!("STRING_ENUM({name})",
name = TypeName::type_(type_))),
NamedType::Interface(ref interface) if type_.is_optional() => Cow::from(
format!("OPTIONAL_INTERFACE({name})",
name = interface.name().to_class_cases())),
NamedType::Interface(ref interface) => Cow::from(
format!("INTERFACE({name})",
name = interface.name().to_class_cases())),
}
TypeSpec::TypeSum(ref contents) if type_.is_optional() => {
// We need to make sure that we don't count the `optional` part twice.
// FIXME: The problem seems to only show up in this branch, but it looks like
// it might (should?) appear in other branches, too.
let non_optional_type = Type::sum(contents.types()).required();
let name = TypeName::type_(&non_optional_type);
Cow::from(format!("OPTIONAL_SUM({name})", name = name))
}
TypeSpec::TypeSum(_) => Cow::from(format!("SUM({name})", name = TypeName::type_(type_))),
}
}
let field_type_name = get_field_type_name(&self.syntax, field.type_(), false);
format!(" F({interface_enum_name}, {field_enum_name}, {field_index}, {field_type}, \"{interface_spec_name}::{field_spec_name}\")",
interface_enum_name = interface_enum_name,
field_enum_name = field.name().to_cpp_enum_case(),
field_index = i,
interface_spec_name = interface_spec_name,
field_spec_name = field.name().to_str(),
field_type = field_type_name
)
})
.format(" \\\n")));
buffer.push_str(&format!("
// The number of fields of interface {interface_spec_name}.
const size_t BINAST_NUMBER_OF_FIELDS_IN_INTERFACE_{interface_macro_name} = {len};",
interface_spec_name = interface_spec_name,
interface_macro_name = interface_macro_name,
len = interface.contents().fields().len()));
}
let total_number_of_fields: usize = self.syntax.interfaces_by_name()
.values()
.map(|interface| interface.contents().fields().len())
@ -1057,8 +1198,61 @@ enum class BinASTVariant: uint16_t {
buffer.push_str(&format!("\n// The number of distinct values of BinASTVariant.\nconst size_t BINASTVARIANT_LIMIT = {};\n\n\n",
variants_limit));
buffer.push_str(&format!("\n#define FOR_EACH_BIN_STRING_ENUM(F) \\\n{nodes}\n",
nodes = self.syntax.string_enums_by_name()
.keys()
.sorted()
.into_iter()
.map(|name| format!(" F({enum_name}, \"{spec_name}\")",
enum_name = name.to_cpp_enum_case(),
spec_name = name.to_str()))
.format(" \\\n")));
buffer.push_str("
enum class BinASTStringEnum: uint16_t {
#define EMIT_ENUM(name, _) name,
FOR_EACH_BIN_STRING_ENUM(EMIT_ENUM)
#undef EMIT_ENUM
};
");
buffer.push_str(&format!("\n// The number of distinct values of BinASTStringEnum.\nconst size_t BINASTSTRINGENUM_LIMIT = {};\n\n\n",
self.syntax.string_enums_by_name().len()));
buffer.push_str(&format!("\n#define FOR_EACH_BIN_LIST(F) \\\n{nodes}\n",
nodes = self.list_parsers_to_generate.iter()
.sorted_by_key(|data| &data.name)
.into_iter()
.map(|data| format!(" F({list_name}, {content_name}, \"{spec_name}\")",
list_name = data.name.to_cpp_enum_case(),
content_name = data.elements.to_cpp_enum_case(),
spec_name = data.name.to_str()))
.format(" \\\n")));
buffer.push_str("
enum class BinASTList: uint16_t {
#define EMIT_ENUM(name, _content, _user) name,
FOR_EACH_BIN_LIST(EMIT_ENUM)
#undef EMIT_ENUM
};
");
buffer.push_str(&format!("\n// The number of distinct list types in the grammar. Used typically to maintain a probability table per list type.\nconst size_t BINAST_NUMBER_OF_LIST_TYPES = {};\n\n\n", self.list_parsers_to_generate.len()));
buffer.push_str(&format!("\n#define FOR_EACH_BIN_SUM(F) \\\n{nodes}\n",
nodes = self.syntax.resolved_sums_of_interfaces_by_name()
.iter()
.sorted_by(|a, b| a.0.cmp(&b.0))
.into_iter()
.map(|(name, _)| format!(" F({name}, \"{spec_name}\")",
name = name.to_cpp_enum_case(),
spec_name = name.to_str()))
.format(" \\\n")));
buffer.push_str("
enum class BinASTSum: uint16_t {
#define EMIT_ENUM(name, _user) name,
FOR_EACH_BIN_SUM(EMIT_ENUM)
#undef EMIT_ENUM
};
");
buffer.push_str(&self.rules.hpp_tokens_footer.reindent(""));
buffer.push_str("\n");
}