impl Parseable trait

This commit is contained in:
John Turner
2025-10-28 09:44:32 +00:00
parent 92a8e46082
commit b54616a6dd
6 changed files with 367 additions and 272 deletions

View File

@@ -334,14 +334,14 @@ impl fmt::Display for Atom {
mod test { mod test {
use mon::{Parser, input::InputIter}; use mon::{Parser, input::InputIter};
use crate::atom::parsers; use super::*;
use crate::Parseable;
#[test] #[test]
fn test_version_display() { fn test_version_display() {
let s = "1.0.0_alpha1_beta1-r1"; let s = "1.0.0_alpha1_beta1-r1";
let version = parsers::version() let version = Version::parser().parse_finished(InputIter::new(s)).unwrap();
.parse_finished(InputIter::new(s))
.unwrap();
assert_eq!(version.to_string().as_str(), s); assert_eq!(version.to_string().as_str(), s);
} }
@@ -349,7 +349,7 @@ mod test {
#[test] #[test]
fn test_display_atom() { fn test_display_atom() {
let s = "!!>=foo/bar-1.0.0v_alpha1_beta1-r1:slot/sub=[a,b,c]"; let s = "!!>=foo/bar-1.0.0v_alpha1_beta1-r1:slot/sub=[a,b,c]";
let atom = parsers::atom().parse_finished(InputIter::new(s)).unwrap(); let atom = Atom::parser().parse_finished(InputIter::new(s)).unwrap();
assert_eq!(atom.to_string().as_str(), s); assert_eq!(atom.to_string().as_str(), s);
} }

View File

@@ -3,21 +3,29 @@ use core::option::Option::None;
use mon::{Parser, r#if, numeric1, one_of, tag}; use mon::{Parser, r#if, numeric1, one_of, tag};
use crate::{ use crate::{
Parseable,
atom::{ atom::{
Atom, Blocker, Category, Name, Slot, SlotName, SlotOperator, UseDep, UseDepCondition, Atom, Blocker, Category, Name, Slot, SlotName, SlotOperator, UseDep, UseDepCondition,
UseDepNegate, UseDepSign, Version, VersionNumber, VersionOperator, VersionSuffix, UseDepNegate, UseDepSign, Version, VersionNumber, VersionOperator, VersionSuffix,
VersionSuffixKind, VersionSuffixKind,
}, },
useflag::parsers::useflag, useflag::UseFlag,
}; };
pub fn blocker<'a>() -> impl Parser<&'a str, Output = Blocker> { impl<'a> Parseable<'a, &'a str> for Blocker {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
tag("!!") tag("!!")
.map(|_| Blocker::Strong) .map(|_| Blocker::Strong)
.or(tag("!").map(|_| Blocker::Weak)) .or(tag("!").map(|_| Blocker::Weak))
}
} }
pub fn version_operator<'a>() -> impl Parser<&'a str, Output = VersionOperator> { impl<'a> Parseable<'a, &'a str> for VersionOperator {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
tag("<=") tag("<=")
.map(|_| VersionOperator::LtEq) .map(|_| VersionOperator::LtEq)
.or(tag(">=").map(|_| VersionOperator::GtEq)) .or(tag(">=").map(|_| VersionOperator::GtEq))
@@ -25,34 +33,50 @@ pub fn version_operator<'a>() -> impl Parser<&'a str, Output = VersionOperator>
.or(tag(">").map(|_| VersionOperator::Gt)) .or(tag(">").map(|_| VersionOperator::Gt))
.or(tag("=").map(|_| VersionOperator::Eq)) .or(tag("=").map(|_| VersionOperator::Eq))
.or(tag("~").map(|_| VersionOperator::Roughly)) .or(tag("~").map(|_| VersionOperator::Roughly))
}
} }
pub fn version_number<'a>() -> impl Parser<&'a str, Output = VersionNumber> { impl<'a> Parseable<'a, &'a str> for VersionNumber {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
numeric1() numeric1()
.followed_by(tag("*").opt()) .followed_by(tag("*").opt())
.recognize() .recognize()
.map(|output: &str| VersionNumber(output.to_string())) .map(|output: &str| VersionNumber(output.to_string()))
}
} }
pub fn version_suffix_kind<'a>() -> impl Parser<&'a str, Output = VersionSuffixKind> { impl<'a> Parseable<'a, &'a str> for VersionSuffixKind {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
tag("alpha") tag("alpha")
.map(|_| VersionSuffixKind::Alpha) .map(|_| VersionSuffixKind::Alpha)
.or(tag("beta").map(|_| VersionSuffixKind::Beta)) .or(tag("beta").map(|_| VersionSuffixKind::Beta))
.or(tag("pre").map(|_| VersionSuffixKind::Pre)) .or(tag("pre").map(|_| VersionSuffixKind::Pre))
.or(tag("rc").map(|_| VersionSuffixKind::Rc)) .or(tag("rc").map(|_| VersionSuffixKind::Rc))
.or(tag("p").map(|_| VersionSuffixKind::P)) .or(tag("p").map(|_| VersionSuffixKind::P))
}
} }
pub fn version_suffix<'a>() -> impl Parser<&'a str, Output = VersionSuffix> { impl<'a> Parseable<'a, &'a str> for VersionSuffix {
version_suffix_kind() type Parser = impl Parser<&'a str, Output = Self>;
.and(version_number().opt())
fn parser() -> Self::Parser {
VersionSuffixKind::parser()
.and(VersionNumber::parser().opt())
.map(|(kind, number)| VersionSuffix { kind, number }) .map(|(kind, number)| VersionSuffix { kind, number })
}
} }
pub fn version<'a>() -> impl Parser<&'a str, Output = Version> { impl<'a> Parseable<'a, &'a str> for Version {
let numbers = version_number().separated_list(tag("."), 1..); type Parser = impl Parser<&'a str, Output = Self>;
let suffixes = version_suffix().separated_list(tag("_"), 0..);
let rev = version_number().preceded_by(tag("-r")); fn parser() -> Self::Parser {
let numbers = VersionNumber::parser().separated_list(tag("."), 1..);
let suffixes = VersionSuffix::parser().separated_list(tag("_"), 0..);
let rev = VersionNumber::parser().preceded_by(tag("-r"));
numbers numbers
.and(r#if(|c: &char| c.is_ascii_alphabetic() && c.is_ascii_lowercase()).opt()) .and(r#if(|c: &char| c.is_ascii_alphabetic() && c.is_ascii_lowercase()).opt())
@@ -64,9 +88,13 @@ pub fn version<'a>() -> impl Parser<&'a str, Output = Version> {
suffixes: suffixes.unwrap_or(Vec::new()), suffixes: suffixes.unwrap_or(Vec::new()),
rev, rev,
}) })
}
} }
pub fn category<'a>() -> impl Parser<&'a str, Output = Category> { impl<'a> Parseable<'a, &'a str> for Category {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_'); let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_.-".contains(*c)).list(0..); let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_.-".contains(*c)).list(0..);
@@ -74,31 +102,43 @@ pub fn category<'a>() -> impl Parser<&'a str, Output = Category> {
.and(rest) .and(rest)
.recognize() .recognize()
.map(|output: &str| Category(output.to_string())) .map(|output: &str| Category(output.to_string()))
}
} }
pub fn name<'a>() -> impl Parser<&'a str, Output = Name> { impl<'a> Parseable<'a, &'a str> for Name {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_'); let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "_+".contains(*c)) let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "_+".contains(*c))
.or(one_of("-".chars()).and_not( .or(
version().preceded_by(tag("-")).followed_by( one_of("-".chars()).and_not(Version::parser().preceded_by(tag("-")).followed_by(
r#if(|c: &char| c.is_ascii_alphanumeric() || "_+-".contains(*c)).not(), r#if(|c: &char| c.is_ascii_alphanumeric() || "_+-".contains(*c)).not(),
), )),
)) )
.list(0..); .list(0..);
start start
.and(rest) .and(rest)
.recognize() .recognize()
.map(|output: &str| Name(output.to_string())) .map(|output: &str| Name(output.to_string()))
}
} }
pub fn slot_operator<'a>() -> impl Parser<&'a str, Output = SlotOperator> { impl<'a> Parseable<'a, &'a str> for SlotOperator {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
tag("=") tag("=")
.map(|_| SlotOperator::Eq) .map(|_| SlotOperator::Eq)
.or(tag("*").map(|_| SlotOperator::Star)) .or(tag("*").map(|_| SlotOperator::Star))
}
} }
pub fn slotname<'a>() -> impl Parser<&'a str, Output = SlotName> { impl<'a> Parseable<'a, &'a str> for SlotName {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_'); let start = r#if(|c: &char| c.is_ascii_alphanumeric() || *c == '_');
let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_.-".contains(*c)).list(0..); let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_.-".contains(*c)).list(0..);
@@ -106,29 +146,41 @@ pub fn slotname<'a>() -> impl Parser<&'a str, Output = SlotName> {
.and(rest) .and(rest)
.recognize() .recognize()
.map(|output: &str| SlotName(output.to_string())) .map(|output: &str| SlotName(output.to_string()))
}
} }
pub fn slot<'a>() -> impl Parser<&'a str, Output = Slot> { impl<'a> Parseable<'a, &'a str> for Slot {
slotname() type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
SlotName::parser()
.opt() .opt()
.and(slotname().preceded_by(tag("/")).opt()) .and(SlotName::parser().preceded_by(tag("/")).opt())
.and(slot_operator().opt()) .and(SlotOperator::parser().opt())
.map(|((slot, sub), operator)| Slot { .map(|((slot, sub), operator)| Slot {
slot, slot,
sub, sub,
operator, operator,
}) })
}
} }
pub fn usedep_sign<'a>() -> impl Parser<&'a str, Output = UseDepSign> { impl<'a> Parseable<'a, &'a str> for UseDepSign {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
tag("(-)") tag("(-)")
.map(|_| UseDepSign::Disabled) .map(|_| UseDepSign::Disabled)
.or(tag("(+)").map(|_| UseDepSign::Enabled)) .or(tag("(+)").map(|_| UseDepSign::Enabled))
}
} }
pub fn usedep<'a>() -> impl Parser<&'a str, Output = UseDep> { impl<'a> Parseable<'a, &'a str> for UseDep {
let a = useflag() type Parser = impl Parser<&'a str, Output = Self>;
.and(usedep_sign().opt())
fn parser() -> Self::Parser {
let a = UseFlag::parser()
.and(UseDepSign::parser().opt())
.preceded_by(tag("-")) .preceded_by(tag("-"))
.map(|(flag, sign)| UseDep { .map(|(flag, sign)| UseDep {
negate: Some(UseDepNegate::Minus), negate: Some(UseDepNegate::Minus),
@@ -137,8 +189,8 @@ pub fn usedep<'a>() -> impl Parser<&'a str, Output = UseDep> {
condition: None, condition: None,
}); });
let b = useflag() let b = UseFlag::parser()
.and(usedep_sign().opt()) .and(UseDepSign::parser().opt())
.preceded_by(tag("!")) .preceded_by(tag("!"))
.followed_by(tag("?")) .followed_by(tag("?"))
.map(|(flag, sign)| UseDep { .map(|(flag, sign)| UseDep {
@@ -148,8 +200,8 @@ pub fn usedep<'a>() -> impl Parser<&'a str, Output = UseDep> {
condition: Some(UseDepCondition::Question), condition: Some(UseDepCondition::Question),
}); });
let c = useflag() let c = UseFlag::parser()
.and(usedep_sign().opt()) .and(UseDepSign::parser().opt())
.followed_by(tag("?")) .followed_by(tag("?"))
.map(|(flag, sign)| UseDep { .map(|(flag, sign)| UseDep {
negate: None, negate: None,
@@ -158,8 +210,8 @@ pub fn usedep<'a>() -> impl Parser<&'a str, Output = UseDep> {
condition: Some(UseDepCondition::Question), condition: Some(UseDepCondition::Question),
}); });
let d = useflag() let d = UseFlag::parser()
.and(usedep_sign().opt()) .and(UseDepSign::parser().opt())
.preceded_by(tag("!")) .preceded_by(tag("!"))
.followed_by(tag("=")) .followed_by(tag("="))
.map(|(flag, sign)| UseDep { .map(|(flag, sign)| UseDep {
@@ -169,8 +221,8 @@ pub fn usedep<'a>() -> impl Parser<&'a str, Output = UseDep> {
condition: Some(UseDepCondition::Eq), condition: Some(UseDepCondition::Eq),
}); });
let e = useflag() let e = UseFlag::parser()
.and(usedep_sign().opt()) .and(UseDepSign::parser().opt())
.followed_by(tag("=")) .followed_by(tag("="))
.map(|(flag, sign)| UseDep { .map(|(flag, sign)| UseDep {
negate: None, negate: None,
@@ -179,8 +231,8 @@ pub fn usedep<'a>() -> impl Parser<&'a str, Output = UseDep> {
condition: Some(UseDepCondition::Eq), condition: Some(UseDepCondition::Eq),
}); });
let f = useflag() let f = UseFlag::parser()
.and(usedep_sign().opt()) .and(UseDepSign::parser().opt())
.map(|(flag, sign)| UseDep { .map(|(flag, sign)| UseDep {
negate: None, negate: None,
flag, flag,
@@ -189,24 +241,29 @@ pub fn usedep<'a>() -> impl Parser<&'a str, Output = UseDep> {
}); });
a.or(b).or(c).or(d).or(e).or(f) a.or(b).or(c).or(d).or(e).or(f)
}
} }
pub fn atom<'a>() -> impl Parser<&'a str, Output = Atom> { impl<'a> Parseable<'a, &'a str> for Atom {
blocker() type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
Blocker::parser()
.opt() .opt()
.and(version_operator().opt()) .and(VersionOperator::parser().opt())
.and(category()) .and(Category::parser())
.and(name().preceded_by(tag("/"))) .and(Name::parser().preceded_by(tag("/")))
.and(version().preceded_by(tag("-")).opt()) .and(Version::parser().preceded_by(tag("-")).opt())
.and(slot().preceded_by(tag(":")).opt()) .and(Slot::parser().preceded_by(tag(":")).opt())
.and( .and(
usedep() UseDep::parser()
.separated_list(tag(","), 0..) .separated_list(tag(","), 0..)
.delimited_by(tag("["), tag("]")) .delimited_by(tag("["), tag("]"))
.opt(), .opt(),
) )
.map( .map(
|((((((blocker, version_operator), category), name), version), slot), usedeps)| Atom { |((((((blocker, version_operator), category), name), version), slot), usedeps)| {
Atom {
blocker, blocker,
version_operator, version_operator,
category, category,
@@ -214,6 +271,7 @@ pub fn atom<'a>() -> impl Parser<&'a str, Output = Atom> {
version, version,
slot, slot,
usedeps: usedeps.unwrap_or(Vec::new()), usedeps: usedeps.unwrap_or(Vec::new()),
}
}, },
) )
.verify_output(|atom| match (&atom.version_operator, &atom.version) { .verify_output(|atom| match (&atom.version_operator, &atom.version) {
@@ -229,6 +287,7 @@ pub fn atom<'a>() -> impl Parser<&'a str, Output = Atom> {
(None, None) => true, (None, None) => true,
_ => false, _ => false,
}) })
}
} }
#[cfg(test)] #[cfg(test)]
@@ -242,14 +301,14 @@ mod test {
fn test_version() { fn test_version() {
let it = InputIter::new("1.0.0v_alpha1_beta1-r1"); let it = InputIter::new("1.0.0v_alpha1_beta1-r1");
version().check_finished(it).unwrap(); Version::parser().check_finished(it).unwrap();
} }
#[test] #[test]
fn test_name() { fn test_name() {
let it = InputIter::new("foo-1-bar-1.0.0"); let it = InputIter::new("foo-1-bar-1.0.0");
match name().parse(it) { match Name::parser().parse(it) {
Ok((_, output)) => { Ok((_, output)) => {
assert_eq!(output.0.as_str(), "foo-1-bar"); assert_eq!(output.0.as_str(), "foo-1-bar");
} }
@@ -263,7 +322,7 @@ mod test {
"!!>=cat/pkg-1-foo-1.0.0v_alpha1_p20250326-r1:primary/sub=[use,use=,!use=,use?,!use?,-use,use(+),use(-)]", "!!>=cat/pkg-1-foo-1.0.0v_alpha1_p20250326-r1:primary/sub=[use,use=,!use=,use?,!use?,-use,use(+),use(-)]",
); );
atom().check_finished(it).unwrap(); Atom::parser().check_finished(it).unwrap();
} }
#[test] #[test]
@@ -272,83 +331,83 @@ mod test {
"!!>=_.+-0-/_-test-T-123_beta1_-4a-6+-_p--1.00.02b_alpha3_pre_p4-r5:slot/_-+6-9=[test(+),test(-)]", "!!>=_.+-0-/_-test-T-123_beta1_-4a-6+-_p--1.00.02b_alpha3_pre_p4-r5:slot/_-+6-9=[test(+),test(-)]",
); );
atom().check_finished(it).unwrap(); Atom::parser().check_finished(it).unwrap();
} }
#[test] #[test]
fn test_atom_with_star_in_non_empty_slot() { fn test_atom_with_star_in_non_empty_slot() {
let it = InputIter::new("foo/bar:*/subslot"); let it = InputIter::new("foo/bar:*/subslot");
assert!(atom().check_finished(it).is_err()); assert!(Atom::parser().check_finished(it).is_err());
} }
#[test] #[test]
fn test_invalid_usedep() { fn test_invalid_usedep() {
let it = InputIter::new("foo-bar:slot/sub=[!use]"); let it = InputIter::new("foo-bar:slot/sub=[!use]");
assert!(atom().check_finished(it).is_err()) assert!(Atom::parser().check_finished(it).is_err())
} }
#[test] #[test]
fn test_empty_slot() { fn test_empty_slot() {
let it = InputIter::new("foo/bar:="); let it = InputIter::new("foo/bar:=");
atom().check_finished(it).unwrap(); Atom::parser().check_finished(it).unwrap();
} }
#[test] #[test]
fn test_usedep_with_underscore() { fn test_usedep_with_underscore() {
let it = InputIter::new("foo/bar[use_dep]"); let it = InputIter::new("foo/bar[use_dep]");
atom().check_finished(it).unwrap(); Atom::parser().check_finished(it).unwrap();
} }
#[test] #[test]
fn test_version_with_uppercase_letter() { fn test_version_with_uppercase_letter() {
let it = InputIter::new("=foo/bar-1.0.0V"); let it = InputIter::new("=foo/bar-1.0.0V");
assert!(atom().check_finished(it).is_err()); assert!(Atom::parser().check_finished(it).is_err());
} }
#[test] #[test]
fn test_version_with_version_operator_without_version() { fn test_version_with_version_operator_without_version() {
let it = InputIter::new("=foo/bar"); let it = InputIter::new("=foo/bar");
assert!(atom().check_finished(it).is_err()); assert!(Atom::parser().check_finished(it).is_err());
} }
#[test] #[test]
fn test_version_with_version_without_version_operator() { fn test_version_with_version_without_version_operator() {
let it = InputIter::new("foo/bar-1.0.0"); let it = InputIter::new("foo/bar-1.0.0");
assert!(atom().check_finished(it).is_err()); assert!(Atom::parser().check_finished(it).is_err());
} }
#[test] #[test]
fn test_atom_with_eq_version_operator() { fn test_atom_with_eq_version_operator() {
let it = InputIter::new("=foo/bar-1.0.0"); let it = InputIter::new("=foo/bar-1.0.0");
atom().check_finished(it).unwrap(); Atom::parser().check_finished(it).unwrap();
} }
#[test] #[test]
fn test_atom_with_star_in_version() { fn test_atom_with_star_in_version() {
let it = InputIter::new("=foo/bar-1.2*"); let it = InputIter::new("=foo/bar-1.2*");
atom().check_finished(it).unwrap(); Atom::parser().check_finished(it).unwrap();
} }
#[test] #[test]
fn test_atom_with_star_in_version_without_eq_version_operator() { fn test_atom_with_star_in_version_without_eq_version_operator() {
let it = InputIter::new(">=foo/bar-1.2*"); let it = InputIter::new(">=foo/bar-1.2*");
assert!(atom().check_finished(it).is_err()); assert!(Atom::parser().check_finished(it).is_err());
} }
#[test] #[test]
fn test_atom_with_trailing_dash_and_letter() { fn test_atom_with_trailing_dash_and_letter() {
let it = InputIter::new("dev-db/mysql-connector-c"); let it = InputIter::new("dev-db/mysql-connector-c");
atom().check_finished(it).unwrap(); Atom::parser().check_finished(it).unwrap();
} }
} }

View File

@@ -1,61 +1,73 @@
use mon::{Parser, ParserResult, input::InputIter, tag, whitespace1}; use mon::{Parser, tag, whitespace1};
use crate::{ use crate::{
atom, Parseable,
atom::Atom,
depend::{Conditional, Expr}, depend::{Conditional, Expr},
useflag, useflag::UseFlag,
}; };
fn expr(it: InputIter<&str>) -> ParserResult<&str, Expr> { impl<'a> Parseable<'a, &'a str> for Expr {
let all_of = expr type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
|it| {
let all_of = Expr::parser()
.separated_list(whitespace1(), 1..) .separated_list(whitespace1(), 1..)
.delimited_by(tag("(").followed_by(whitespace1()), tag(")")) .delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
.map(|exprs| Expr::AllOf(exprs)); .map(|exprs| Expr::AllOf(exprs));
let any_of = expr let any_of = Expr::parser()
.separated_list(whitespace1(), 1..) .separated_list(whitespace1(), 1..)
.delimited_by(tag("(").followed_by(whitespace1()), tag(")")) .delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
.preceded_by(tag("||").followed_by(whitespace1())) .preceded_by(tag("||").followed_by(whitespace1()))
.map(|exprs| Expr::AnyOf(exprs)); .map(|exprs| Expr::AnyOf(exprs));
let one_of = expr let one_of = Expr::parser()
.separated_list(whitespace1(), 1..) .separated_list(whitespace1(), 1..)
.delimited_by(tag("(").followed_by(whitespace1()), tag(")")) .delimited_by(tag("(").followed_by(whitespace1()), tag(")"))
.preceded_by(tag("^^").followed_by(whitespace1())) .preceded_by(tag("^^").followed_by(whitespace1()))
.map(|exprs| Expr::OneOf(exprs)); .map(|exprs| Expr::OneOf(exprs));
atom::parsers::atom() Atom::parser()
.map(|atom| Expr::Atom(atom)) .map(|atom| Expr::Atom(atom))
.or(conditional().map(|conditional| Expr::Conditional(conditional))) .or(Conditional::parser().map(|conditional| Expr::Conditional(conditional)))
.or(any_of) .or(any_of)
.or(all_of) .or(all_of)
.or(one_of) .or(one_of)
.parse(it) .parse(it)
}
}
} }
fn conditional<'a>() -> impl Parser<&'a str, Output = Conditional> { impl<'a> Parseable<'a, &'a str> for Conditional {
useflag::parsers::useflag() type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
UseFlag::parser()
.preceded_by(tag("!")) .preceded_by(tag("!"))
.followed_by(tag("?")) .followed_by(tag("?"))
.map(|flag| Conditional::Negative(flag)) .map(|flag| Conditional::Negative(flag))
.or(useflag::parsers::useflag() .or(UseFlag::parser()
.followed_by(tag("?")) .followed_by(tag("?"))
.map(|flag| Conditional::Positive(flag))) .map(|flag| Conditional::Positive(flag)))
} }
pub fn exprs<'a>() -> impl Parser<&'a str, Output = Vec<Expr>> {
expr.separated_list(whitespace1(), 0..)
} }
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use mon::input::InputIter;
use super::*; use super::*;
#[test] #[test]
fn test_expr() { fn test_expr() {
let it = InputIter::new("flag? ( || ( foo/bar foo/bar ) )"); let it = InputIter::new("flag? ( || ( foo/bar foo/bar ) )");
exprs().check_finished(it).unwrap(); Expr::parser()
.separated_list(whitespace1(), 0..)
.check_finished(it)
.unwrap();
} }
} }

View File

@@ -1,5 +1,14 @@
#![deny(clippy::pedantic)] #![deny(clippy::pedantic)]
#![allow(dead_code, unstable_name_collisions)] #![allow(dead_code, unstable_name_collisions)]
#![feature(impl_trait_in_assoc_type)]
use mon::{Parser, input::Input};
pub trait Parseable<'a, I: Input + 'a> {
type Parser: Parser<I, Output = Self>;
fn parser() -> Self::Parser;
}
pub mod atom; pub mod atom;
pub mod depend; pub mod depend;

View File

@@ -1,8 +1,14 @@
use mon::{Parser, r#if, tag}; use mon::{Parser, r#if, tag};
use crate::useflag::{IUseFlag, UseFlag}; use crate::{
Parseable,
useflag::{IUseFlag, UseFlag},
};
pub fn useflag<'a>() -> impl Parser<&'a str, Output = UseFlag> { impl<'a> Parseable<'a, &'a str> for UseFlag {
type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
let start = r#if(|c: &char| c.is_ascii_alphanumeric()); let start = r#if(|c: &char| c.is_ascii_alphanumeric());
let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_@-".contains(*c)).list(0..); let rest = r#if(|c: &char| c.is_ascii_alphanumeric() || "+_@-".contains(*c)).list(0..);
@@ -10,17 +16,22 @@ pub fn useflag<'a>() -> impl Parser<&'a str, Output = UseFlag> {
.and(rest) .and(rest)
.recognize() .recognize()
.map(|output: &str| UseFlag(output.to_string())) .map(|output: &str| UseFlag(output.to_string()))
}
} }
pub fn iuseflag<'a>() -> impl Parser<&'a str, Output = IUseFlag> { impl<'a> Parseable<'a, &'a str> for IUseFlag {
useflag() type Parser = impl Parser<&'a str, Output = Self>;
fn parser() -> Self::Parser {
UseFlag::parser()
.preceded_by(tag("+")) .preceded_by(tag("+"))
.map(|flag| IUseFlag { .map(|flag| IUseFlag {
default: true, default: true,
flag, flag,
}) })
.or(useflag().map(|flag| IUseFlag { .or(UseFlag::parser().map(|flag| IUseFlag {
default: false, default: false,
flag, flag,
})) }))
}
} }

View File

@@ -1,5 +1,8 @@
use gentoo_utils::depend; use gentoo_utils::{
use mon::{Parser, eof, input::InputIter, tag}; Parseable,
depend::{self, Expr},
};
use mon::{Parser, eof, input::InputIter, tag, whitespace1};
use std::fs; use std::fs;
#[test] #[test]
@@ -14,7 +17,8 @@ fn parse_md5_cache() {
if line.starts_with("DEPEND=") { if line.starts_with("DEPEND=") {
eprintln!("{line}"); eprintln!("{line}");
eprintln!(); eprintln!();
depend::parsers::exprs() Expr::parser()
.separated_list(whitespace1(), 0..)
.ignore() .ignore()
.or(eof()) .or(eof())
.preceded_by(tag("DEPEND=")) .preceded_by(tag("DEPEND="))