diff options
| author | Sophie Forrest <git@sophieforrest.com> | 2024-12-04 17:53:46 +1300 |
|---|---|---|
| committer | Sophie Forrest <git@sophieforrest.com> | 2024-12-04 17:53:46 +1300 |
| commit | 3725fe07e58f459bb7ab9fcbc10775cf4b138ec8 (patch) | |
| tree | e3c07e8bf7ba53a164538973787deb3e6693ea3e /src/parser.rs | |
| parent | f20503aa26ec2e91fb585defa338993985dac2e5 (diff) | |
feat(parser): finish nom rewrite with coursepoints parser
This parser can correctly parse course prerequisites, corequisites, and restrictions, which the previous parser could not do. These cannot be split into a truly computer readable format yet, and I believe this would be out of scope for this project.
Diffstat (limited to '')
| -rw-r--r-- | src/parser.rs | 102 |
1 files changed, 99 insertions, 3 deletions
diff --git a/src/parser.rs b/src/parser.rs index 87da6be..0f3ef7b 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -4,10 +4,11 @@ use nom::{ branch::alt, - bytes::complete::{tag, take, take_till, take_while}, + bytes::complete::{tag, take, take_till, take_until, take_while}, character::complete::{char, multispace0}, - combinator::{map_res, rest}, - sequence::{delimited, pair, preceded, separated_pair}, + combinator::{map, map_res, opt, rest}, + number::complete::float, + sequence::{delimited, pair, preceded, separated_pair, tuple}, IResult, }; @@ -101,6 +102,84 @@ pub fn subtitle(input: &str) -> IResult<&str, &str> { preceded(tag("\u{2013} "), rest)(input) } +/// Parses course prerequisites from an input. +/// +/// # Errors +/// +/// This function will return an error if the input is not preceded by (P). +pub fn prerequisites(input: &str) -> IResult<&str, &str> { + map( + preceded( + tag("(P)"), + alt((take_until("(C)"), take_until("(X)"), rest)), + ), + // The data will often end up with leading and trailing spaces. Trimming is the easiest + // way to get rid of these. + str::trim, + )(input) +} + +/// Parses course corequisites from an input. +/// +/// # Errors +/// +/// This function will return an error if the input is not preceded by (C). +pub fn corequisites(input: &str) -> IResult<&str, &str> { + map( + preceded(tag("(C)"), alt((take_until("(X)"), rest))), + // The data will often end up with leading and trailing spaces. Trimming is the easiest + // way to get rid of these. + str::trim, + )(input) +} + +/// Parses course restrictions from an input. +/// +/// # Errors +/// +/// This function will return an error if the input is not preceded by (X). +pub fn restrictions(input: &str) -> IResult<&str, &str> { + map( + preceded(tag("(X)"), rest), + // The data will often end up with leading and trailing spaces. Trimming is the easiest + // way to get rid of these. + str::trim, + )(input) +} + +/// Alias for the return type of the requirements parser. +type RequirementsReturn<'a> = (Option<&'a str>, Option<&'a str>, Option<&'a str>); + +/// Parses course requirements from an input. +/// +/// # Errors +/// +/// This function should not return an error, and errors are to be considered unreachable. +pub fn requirements(input: &str) -> IResult<&str, RequirementsReturn> { + tuple((opt(prerequisites), opt(corequisites), opt(restrictions)))(input) +} + +/// Parses the course points from an input. +/// +/// # Errors +/// +/// This function will return an error if the input provided does not contain a float. +pub fn course_points(input: &str) -> IResult<&str, f32> { + float(input) +} + +/// Parses the entire "coursepoints" section of the course offering. +/// +/// # Errors +/// +/// This function will return an error if the course points cannot be parsed. +pub fn course_offering(input: &str) -> IResult<&str, (f32, Option<RequirementsReturn>)> { + tuple(( + course_points, + opt(preceded(tag(" pts \u{2022} "), requirements)), + ))(input) +} + #[cfg(test)] #[allow(clippy::unwrap_used)] mod tests { @@ -168,4 +247,21 @@ mod tests { "Identification, Assessment and Control of Hazards and Risks" ); } + + #[test] + fn prereq_parser() { + let parsed_prereq = prerequisites("(P) LING 123 (C) SOPH 184 (X) SOPH 185").unwrap(); + + assert_eq!(parsed_prereq.1, "LING 123"); + } + + #[test] + fn req_parser() { + let parsed_prereq = requirements("(P) LING 229, LING 228; (C) MATH 883").unwrap(); + + assert_eq!( + parsed_prereq.1, + (Some("LING 229, LING 228;"), Some("MATH 883"), None) + ); + } } |