Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 102 additions & 29 deletions cfgrammar/src/lib/header.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Spanned for HeaderError<Span> {

// This is essentially a tuple that needs a newtype so we can implement `From` for it.
// Thus we aren't worried about it being `pub`.
#[derive(Debug, PartialEq)]
#[derive(Debug, PartialEq, Clone)]
#[doc(hidden)]
pub struct HeaderValue<T>(pub T, pub Value<T>);

Expand All @@ -71,6 +71,15 @@ pub enum HeaderErrorKind {
ConversionError(&'static str, &'static str),
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum GrmtoolsSectionValue {
String(String, Span),
Num(u64, Span),
Bool(bool, Span),
Array(Vec<GrmtoolsSectionValue>, Span),
RustLike(String, Span),
}

impl fmt::Display for HeaderErrorKind {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self {
Expand Down Expand Up @@ -116,14 +125,14 @@ impl<T> HeaderError<T> {
/// member: ("Bar", ...)
/// }
/// ```
#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq, Clone)]
#[doc(hidden)]
pub struct Namespaced<T> {
pub namespace: Option<(String, T)>,
pub member: (String, T),
}

#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq, Clone)]
#[doc(hidden)]
pub enum Setting<T> {
/// A value like `YaccKind::Grmtools`
Expand Down Expand Up @@ -152,7 +161,7 @@ pub struct GrmtoolsSectionParser<'input> {
///
/// To be useful across diverse crates this types fields are limited to types derived from `core::` types.
/// like booleans, numeric types, and string values.
#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq, Clone)]
#[doc(hidden)]
pub enum Value<T> {
Flag(bool, T),
Expand Down Expand Up @@ -236,6 +245,73 @@ impl<T> Namespaced<T> {
}
}

impl From<Value<Span>> for GrmtoolsSectionValue {
fn from(value: Value<Span>) -> GrmtoolsSectionValue {
match value {
Value::Flag(value, value_span) => GrmtoolsSectionValue::Bool(value, value_span),
Value::Setting(setting) => match setting {
Setting::Array(v, start_span, end_span) => {
let array_span = Span::new(start_span.start(), end_span.end());
let mut out = Vec::with_capacity(v.capacity());
for setting in v {
// To call this function recursively we need to convert the `Setting<Span>` to a `Value<Span>`
out.push(GrmtoolsSectionValue::from(Value::Setting(setting)));
}
GrmtoolsSectionValue::Array(out, array_span)
}
Setting::String(s, val_span) => GrmtoolsSectionValue::String(s.clone(), val_span),
Setting::Num(n, val_span) => GrmtoolsSectionValue::Num(n, val_span),
Setting::Unitary(Namespaced {
namespace,
member: (member, member_span),
}) => {
let mut s = String::new();
let mut start_pos = member_span.start();
if let Some((ns, ns_span)) = namespace {
s.push_str(&ns);
s.push_str("::");
start_pos = ns_span.start();
}
s.push_str(&member);
GrmtoolsSectionValue::RustLike(s, Span::new(start_pos, member_span.end()))
}
Setting::Constructor {
ctor:
Namespaced {
namespace: ctor_namespace,
member: (ctor_member, ctor_member_span),
},
arg:
Namespaced {
namespace: arg_namespace,
member: (arg_member, arg_member_span),
},
} => {
let mut s = String::new();
let mut ctor_start_pos = ctor_member_span.start();
if let Some((ns, namespace_span)) = ctor_namespace {
ctor_start_pos = namespace_span.start();
s.push_str(&ns);
s.push_str("::");
}
s.push_str(&ctor_member);
s.push('(');
if let Some((arg_ns, _)) = arg_namespace {
s.push_str(&arg_ns);
s.push_str("::");

@ratmice ratmice Aug 27, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot to mention in the commit message this round of testing found two bugs in the RustLike values.

}
s.push_str(&arg_member);
s.push(')');
GrmtoolsSectionValue::RustLike(
s,
Span::new(ctor_start_pos, arg_member_span.end() + ")".len()),
)
}
},
}
}
}

static RE_LEADING_WS: LazyLock<Regex> =
LazyLock::new(|| Regex::new(r"^[\p{Pattern_White_Space}]*").unwrap());
static RE_NAME: LazyLock<Regex> = LazyLock::new(|| {
Expand Down Expand Up @@ -552,10 +628,7 @@ impl<'input> GrmtoolsSectionParser<'input> {
match RE_NAME.find(&self.src[i..]) {
Some(m) => {
assert_eq!(m.start(), 0);
Ok((
self.src[i..i + m.end()].to_string().to_lowercase(),
i + m.end(),
))
Ok((self.src[i..i + m.end()].to_string(), i + m.end()))
}
None => {
if self.src[i..].starts_with("*") {
Expand Down Expand Up @@ -602,30 +675,30 @@ impl TryFrom<YaccKind> for Value<Location> {
let from_loc = Location::Other("From<YaccKind>".to_string());
Ok(match kind {
YaccKind::Grmtools => Value::Setting(Setting::Unitary(Namespaced {
namespace: Some(("yacckind".to_string(), from_loc.clone())),
member: ("grmtools".to_string(), from_loc),
namespace: Some(("YaccKind".to_string(), from_loc.clone())),
member: ("Grmtools".to_string(), from_loc),
})),
YaccKind::Eco => Value::Setting(Setting::Unitary(Namespaced {
namespace: Some(("yacckind".to_string(), from_loc.clone())),
member: ("eco".to_string(), from_loc),
namespace: Some(("YaccKind".to_string(), from_loc.clone())),
member: ("Eco".to_string(), from_loc),
})),
YaccKind::Original(action_kind) => Value::Setting(Setting::Constructor {
ctor: Namespaced {
namespace: Some(("yacckind".to_string(), from_loc.clone())),
member: ("original".to_string(), from_loc.clone()),
namespace: Some(("YaccKind".to_string(), from_loc.clone())),
member: ("Original".to_string(), from_loc.clone()),
},
arg: match action_kind {
YaccOriginalActionKind::NoAction => Namespaced {
namespace: Some(("yaccoriginalactionkind".to_string(), from_loc.clone())),
member: ("noaction".to_string(), from_loc),
namespace: Some(("YaccOriginalActionKind".to_string(), from_loc.clone())),
member: ("NoAction".to_string(), from_loc),
},
YaccOriginalActionKind::UserAction => Namespaced {
namespace: Some(("yaccoriginalactionkind".to_string(), from_loc.clone())),
member: ("useraction".to_string(), from_loc),
namespace: Some(("YaccOriginalActionKind".to_string(), from_loc.clone())),
member: ("UserAction".to_string(), from_loc),
},
YaccOriginalActionKind::GenericParseTree => Namespaced {
namespace: Some(("yaccoriginalactionkind".to_string(), from_loc.clone())),
member: ("genericparsetree".to_string(), from_loc),
namespace: Some(("YaccOriginalActionKind".to_string(), from_loc.clone())),
member: ("GenericParseTree".to_string(), from_loc),
},
},
}),
Expand All @@ -643,13 +716,13 @@ impl<T: Clone> TryFrom<&Value<T>> for YaccKind {
member: (yk_value, yk_value_loc),
})) => {
if let Some((ns, ns_loc)) = namespace
&& ns != "yacckind"
&& ns != "YaccKind"
{
err_locs.push(ns_loc.clone());
}
let yacckinds = [
("grmtools".to_string(), YaccKind::Grmtools),
("eco".to_string(), YaccKind::Eco),
("Grmtools".to_string(), YaccKind::Grmtools),
("Eco".to_string(), YaccKind::Eco),
];
let yk_found = yacckinds
.iter()
Expand Down Expand Up @@ -684,24 +757,24 @@ impl<T: Clone> TryFrom<&Value<T>> for YaccKind {
},
}) => {
if let Some((yk_ns, yk_ns_loc)) = yk_namespace
&& yk_ns != "yacckind"
&& yk_ns != "YaccKind"
{
err_locs.push(yk_ns_loc.clone());
}

if yk_str != "original" {
if yk_str != "Original" {
err_locs.push(yk_loc.clone());
}

if let Some((ak_ns, ak_ns_loc)) = ak_namespace
&& ak_ns != "yaccoriginalactionkind"
&& ak_ns != "YaccOriginalActionKind"
{
err_locs.push(ak_ns_loc.clone());
}
let actionkinds = [
("noaction", YaccOriginalActionKind::NoAction),
("useraction", YaccOriginalActionKind::UserAction),
("genericparsetree", YaccOriginalActionKind::GenericParseTree),
("NoAction", YaccOriginalActionKind::NoAction),
("UserAction", YaccOriginalActionKind::UserAction),
("GenericParseTree", YaccOriginalActionKind::GenericParseTree),
];
let yk_found = actionkinds.iter().find_map(|(actionkind_str, actionkind)| {
(ak_str == actionkind_str).then_some(YaccKind::Original(*actionkind))
Expand Down
Loading