Skip to content

Commit b0bbc9e

Browse files
committed
Add 'Header', TryFrom<YaccKind> for Value and it's inverse.
1 parent 55d249f commit b0bbc9e

2 files changed

Lines changed: 170 additions & 2 deletions

File tree

cfgrammar/src/lib/header.rs

Lines changed: 170 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
1-
use crate::{yacc::ParserError, Location, Span};
1+
use crate::{
2+
markmap::MarkMap,
3+
yacc::{ParserError, YaccKind, YaccOriginalActionKind},
4+
Location, Span,
5+
};
26
use lazy_static::lazy_static;
37
use regex::{Regex, RegexBuilder};
48
use std::collections::{hash_map::Entry, HashMap};
59
use std::{error::Error, fmt};
610

11+
/// An error regarding the `%grmtools` header section.
12+
///
13+
/// It could be any of:
14+
///
15+
/// * An error during parsing the section.
16+
/// * An error resulting from a value in the section having an invalid value.
717
#[derive(Debug, Clone)]
818
pub struct HeaderError {
919
pub kind: HeaderErrorKind,
@@ -32,6 +42,7 @@ pub enum HeaderErrorKind {
3242
ExpectedToken(char),
3343
DuplicateEntry,
3444
InvalidEntry(&'static str),
45+
ConversionError(&'static str, &'static str),
3546
}
3647

3748
impl fmt::Display for HeaderErrorKind {
@@ -42,6 +53,9 @@ impl fmt::Display for HeaderErrorKind {
4253
HeaderErrorKind::ExpectedToken(c) => &format!("Expected token: '{}", c),
4354
HeaderErrorKind::InvalidEntry(s) => &format!("Invalid entry: '{}'", s),
4455
HeaderErrorKind::DuplicateEntry => "Duplicate Entry",
56+
HeaderErrorKind::ConversionError(t, err_str) => {
57+
&format!("Converting header value to type '{}': {}", t, err_str)
58+
}
4559
};
4660
write!(f, "{}", s)
4761
}
@@ -64,6 +78,7 @@ impl fmt::Display for HeaderErrorKind {
6478
/// }
6579
/// ```
6680
#[derive(Debug, Eq, PartialEq)]
81+
#[doc(hidden)]
6782
pub struct Namespaced {
6883
pub namespace: Option<(String, Location)>,
6984
pub member: (String, Location),
@@ -91,7 +106,12 @@ pub struct GrmtoolsSectionParser<'input> {
91106
required: bool,
92107
}
93108

109+
/// The value contained within a `Header`
110+
///
111+
/// To be useful across diverse crates this types fields are limited to types derived from `core::` types.
112+
/// like booleans, numeric types, and string values.
94113
#[derive(Debug, Eq, PartialEq)]
114+
#[doc(hidden)]
95115
pub enum Value {
96116
Flag(bool, Location),
97117
Setting(Setting),
@@ -345,6 +365,155 @@ impl<'input> GrmtoolsSectionParser<'input> {
345365
}
346366
}
347367

368+
/// A data structure representation of the %grmtools section.
369+
pub type Header = MarkMap<String, (Location, Value)>;
370+
371+
impl TryFrom<YaccKind> for Value {
372+
type Error = HeaderError;
373+
fn try_from(kind: YaccKind) -> Result<Value, HeaderError> {
374+
let from_loc = Location::Other("From<YaccKind>".to_string());
375+
Ok(match kind {
376+
YaccKind::Grmtools => Value::Setting(Setting::Unitary(Namespaced {
377+
namespace: Some(("yacckind".to_string(), from_loc.clone())),
378+
member: ("grmtools".to_string(), from_loc),
379+
})),
380+
YaccKind::Eco => Value::Setting(Setting::Unitary(Namespaced {
381+
namespace: Some(("yacckind".to_string(), from_loc.clone())),
382+
member: ("eco".to_string(), from_loc),
383+
})),
384+
YaccKind::Original(action_kind) => Value::Setting(Setting::Constructor {
385+
ctor: Namespaced {
386+
namespace: Some(("yacckind".to_string(), from_loc.clone())),
387+
member: ("original".to_string(), from_loc.clone()),
388+
},
389+
arg: match action_kind {
390+
YaccOriginalActionKind::NoAction => Namespaced {
391+
namespace: Some(("yaccoriginalactionkind".to_string(), from_loc.clone())),
392+
member: ("noaction".to_string(), from_loc),
393+
},
394+
YaccOriginalActionKind::UserAction => Namespaced {
395+
namespace: Some(("yaccoriginalactionkind".to_string(), from_loc.clone())),
396+
member: ("useraction".to_string(), from_loc),
397+
},
398+
YaccOriginalActionKind::GenericParseTree => Namespaced {
399+
namespace: Some(("yaccoriginalactionkind".to_string(), from_loc.clone())),
400+
member: ("genericparsetree".to_string(), from_loc),
401+
},
402+
},
403+
}),
404+
})
405+
}
406+
}
407+
408+
impl TryFrom<&Value> for YaccKind {
409+
type Error = HeaderError;
410+
fn try_from(value: &Value) -> Result<YaccKind, HeaderError> {
411+
let mut err_locs = Vec::new();
412+
match value {
413+
Value::Flag(_, loc) => Err(HeaderError {
414+
kind: HeaderErrorKind::ConversionError(
415+
"From<YaccKind>",
416+
"Cannot convert boolean to YaccKind",
417+
),
418+
locations: vec![loc.clone()],
419+
}),
420+
Value::Setting(Setting::Num(_, loc)) => Err(HeaderError {
421+
kind: HeaderErrorKind::ConversionError(
422+
"From<YaccKind>",
423+
"Cannot convert number to YaccKind",
424+
),
425+
locations: vec![loc.clone()],
426+
}),
427+
Value::Setting(Setting::Unitary(Namespaced {
428+
namespace,
429+
member: (yk_value, yk_value_loc),
430+
})) => {
431+
if let Some((ns, ns_loc)) = namespace {
432+
if ns != "yacckind" {
433+
err_locs.push(ns_loc.clone());
434+
}
435+
}
436+
let yacckinds = [
437+
("grmtools".to_string(), YaccKind::Grmtools),
438+
("eco".to_string(), YaccKind::Eco),
439+
];
440+
let yk_found = yacckinds
441+
.iter()
442+
.find_map(|(yk_str, yk)| (yk_str == yk_value).then_some(yk));
443+
if let Some(yk) = yk_found {
444+
if err_locs.is_empty() {
445+
Ok(*yk)
446+
} else {
447+
Err(HeaderError {
448+
kind: HeaderErrorKind::InvalidEntry("yacckind"),
449+
locations: err_locs,
450+
})
451+
}
452+
} else {
453+
err_locs.push(yk_value_loc.clone());
454+
Err(HeaderError {
455+
kind: HeaderErrorKind::InvalidEntry("yacckind"),
456+
locations: err_locs,
457+
})
458+
}
459+
}
460+
Value::Setting(Setting::Constructor {
461+
ctor:
462+
Namespaced {
463+
namespace: yk_namespace,
464+
member: (yk_str, yk_loc),
465+
},
466+
arg:
467+
Namespaced {
468+
namespace: ak_namespace,
469+
member: (ak_str, ak_loc),
470+
},
471+
}) => {
472+
if let Some((yk_ns, yk_ns_loc)) = yk_namespace {
473+
if yk_ns != "yacckind" {
474+
err_locs.push(yk_ns_loc.clone());
475+
}
476+
}
477+
478+
if yk_str != "original" {
479+
err_locs.push(yk_loc.clone());
480+
}
481+
482+
if let Some((ak_ns, ak_ns_loc)) = ak_namespace {
483+
if ak_ns != "yaccoriginalactionkind" {
484+
err_locs.push(ak_ns_loc.clone());
485+
}
486+
}
487+
let actionkinds = [
488+
("noaction", YaccOriginalActionKind::NoAction),
489+
("useraction", YaccOriginalActionKind::UserAction),
490+
("genericparsetree", YaccOriginalActionKind::GenericParseTree),
491+
];
492+
let yk_found = actionkinds.iter().find_map(|(actionkind_str, actionkind)| {
493+
(ak_str == actionkind_str).then_some(YaccKind::Original(*actionkind))
494+
});
495+
496+
if let Some(yk) = yk_found {
497+
if err_locs.is_empty() {
498+
Ok(yk)
499+
} else {
500+
Err(HeaderError {
501+
kind: HeaderErrorKind::InvalidEntry("yacckind"),
502+
locations: err_locs,
503+
})
504+
}
505+
} else {
506+
err_locs.push(ak_loc.clone());
507+
Err(HeaderError {
508+
kind: HeaderErrorKind::InvalidEntry("yacckind"),
509+
locations: err_locs,
510+
})
511+
}
512+
}
513+
}
514+
}
515+
}
516+
348517
#[cfg(test)]
349518
mod test {
350519
use super::*;

cfgrammar/src/lib/mod.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ use bincode::{Decode, Encode};
5656
#[cfg(feature = "serde")]
5757
use serde::{Deserialize, Serialize};
5858

59-
#[doc(hidden)]
6059
pub mod header;
6160
mod idxnewtype;
6261
pub mod markmap;

0 commit comments

Comments
 (0)