summary refs log tree commit diff
path: root/src/parser.rs
diff options
context:
space:
mode:
authorSophie Forrest <git@sophieforrest.com>2024-12-04 17:53:46 +1300
committerSophie Forrest <git@sophieforrest.com>2024-12-04 17:53:46 +1300
commit3725fe07e58f459bb7ab9fcbc10775cf4b138ec8 (patch)
treee3c07e8bf7ba53a164538973787deb3e6693ea3e /src/parser.rs
parentf20503aa26ec2e91fb585defa338993985dac2e5 (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.rs102
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)
+		);
+	}
 }