From 4e2dc0a0728b280d2ff01da452eea319b660187c Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 22 Oct 2017 12:45:50 +0200 Subject: Initial import for parser --- Cargo.toml | 5 +++++ build.rs | 6 ++++++ src/grammar.rustpeg | 2 ++ src/lib.rs | 4 ++++ src/parser.rs | 20 ++++++++++++++++++++ 5 files changed, 37 insertions(+) create mode 100644 build.rs create mode 100644 src/grammar.rustpeg create mode 100644 src/parser.rs diff --git a/Cargo.toml b/Cargo.toml index 5752988..bc8f016 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,12 +12,17 @@ license = "MPL-2.0" repository = "https://github.com/matthiasbeyer/kairos" +build = "build.rs" + [dependencies] chrono = "0.4" error-chain = "0.10" filters = { version = "0.1.1", optional = true } +[build-dependencies] +peg = { version = "0.5" } + [dev-dependencies] env_logger = "0.4" log = "0.3" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..af09e85 --- /dev/null +++ b/build.rs @@ -0,0 +1,6 @@ +extern crate peg; + +fn main() { + peg::cargo_build("src/grammar.rustpeg"); +} + diff --git a/src/grammar.rustpeg b/src/grammar.rustpeg new file mode 100644 index 0000000..36a3fa0 --- /dev/null +++ b/src/grammar.rustpeg @@ -0,0 +1,2 @@ +use timetype::TimeType; + diff --git a/src/lib.rs b/src/lib.rs index 7aeab51..8f7fed9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,9 @@ extern crate error_chain; extern crate chrono; +#[macro_use] +extern crate peg; + #[cfg(feature = "with-filters")] extern crate filters; @@ -17,5 +20,6 @@ pub mod iter; pub mod timetype; pub mod indicator; pub mod matcher; +pub mod parser; mod util; diff --git a/src/parser.rs b/src/parser.rs new file mode 100644 index 0000000..83c05a1 --- /dev/null +++ b/src/parser.rs @@ -0,0 +1,20 @@ +//! The definition of the "kairos" syntax, for parsing user input into TimeType objects +//! +//! The syntax itself is described in the grammar.rustpeg file. +//! Here goes a documentation on the syntax +//! +//! # Syntax +//! +//! +//! +//! +//! +//! +//! +//! +//! + +mod grammar { + include!(concat!(env!("OUT_DIR"), "/grammar.rs")); +} + -- cgit v1.2.3 From 1ac1641dc5a0e54e0362081c2113427f2e35a8cc Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 22 Oct 2017 21:47:14 +0200 Subject: Design syntax --- src/parser.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index 83c05a1..4820668 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -5,13 +5,34 @@ //! //! # Syntax //! +//! ## Units //! +//! UnitSec = "second" | "seconds" | "sec" | "secs" | "s" +//! UnitMin = "minute" | "minutes" | "min" | "mins" +//! UnitHr = "hour" | "hours" | "hr" | "hrs" +//! UnitDay = "day" | "days" | "d" +//! UnitWeek = "week" | "weeks" | "w" +//! UnitMonth = "month" | "months" | +//! UnitYear = "year" | "years" | "yrs" +//! Unit = UnitSec | UnitMin | UnitHr | UnitDay | UnitWeek | UnitMonth | UnitYear //! +//! ## Operators //! +//! Operator = "+" | "-" //! +//! ## Intermediate syntax nodes //! +//! Amount = "" //! +//! TextIterSpec = "secondly" | "minutely" | "hourly" | "daily" | "weekly" | "monthly" | "yearly" +//! Iterspec = TextIterSpec | "every" //! +//! ## User-facing syntax nodes +//! +//! AmountExp = ( )? +//! ExactDate = "today" | "yesterday" | "tomorrow" | +//! Date = ( )? +//! Iterator = ("until" | "times")? //! mod grammar { -- cgit v1.2.3 From ac01c4a224f9b2db08248c45170bea202e08ada9 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 27 Oct 2017 20:37:43 +0200 Subject: Initial bootstrapping of nom-based parser --- Cargo.toml | 7 +-- build.rs | 6 -- src/grammar.rustpeg | 2 - src/lib.rs | 3 +- src/parser.rs | 177 ++++++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 175 insertions(+), 20 deletions(-) delete mode 100644 build.rs delete mode 100644 src/grammar.rustpeg diff --git a/Cargo.toml b/Cargo.toml index bc8f016..c0a4c1a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,17 +12,14 @@ license = "MPL-2.0" repository = "https://github.com/matthiasbeyer/kairos" -build = "build.rs" - [dependencies] chrono = "0.4" error-chain = "0.10" +nom = "3.2" +iso8601 = { git = "https://github.com/matthiasbeyer/iso8601", branch = "update-nom" } filters = { version = "0.1.1", optional = true } -[build-dependencies] -peg = { version = "0.5" } - [dev-dependencies] env_logger = "0.4" log = "0.3" diff --git a/build.rs b/build.rs deleted file mode 100644 index af09e85..0000000 --- a/build.rs +++ /dev/null @@ -1,6 +0,0 @@ -extern crate peg; - -fn main() { - peg::cargo_build("src/grammar.rustpeg"); -} - diff --git a/src/grammar.rustpeg b/src/grammar.rustpeg deleted file mode 100644 index 36a3fa0..0000000 --- a/src/grammar.rustpeg +++ /dev/null @@ -1,2 +0,0 @@ -use timetype::TimeType; - diff --git a/src/lib.rs b/src/lib.rs index 8f7fed9..d39493a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,8 @@ extern crate error_chain; extern crate chrono; #[macro_use] -extern crate peg; +extern crate nom; +extern crate iso8601; #[cfg(feature = "with-filters")] extern crate filters; diff --git a/src/parser.rs b/src/parser.rs index 4820668..d50cbec 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -29,13 +29,178 @@ //! //! ## User-facing syntax nodes //! -//! AmountExp = ( )? -//! ExactDate = "today" | "yesterday" | "tomorrow" | -//! Date = ( )? -//! Iterator = ("until" | "times")? +//! AmountExpr = ( )? +//! ExactDate = "today" | "yesterday" | "tomorrow" | +//! Date = ( )? +//! Iterator = ("until" | "times")? //! -mod grammar { - include!(concat!(env!("OUT_DIR"), "/grammar.rs")); +use nom::{IResult, space, alpha, alphanumeric, digit}; +use std::str; +use std::str::FromStr; + +named!(integer, alt!( + map_res!( + map_res!( + ws!(digit), + str::from_utf8 + ), + FromStr::from_str + ) +)); + +named!(unit_parser, alt!( + tag!("second") => { |_| Unit::Second } | + tag!("seconds") => { |_| Unit::Second } | + tag!("sec") => { |_| Unit::Second } | + tag!("secs") => { |_| Unit::Second } | + tag!("s") => { |_| Unit::Second } | + tag!("minute") => { |_| Unit::Minute } | + tag!("minutes") => { |_| Unit::Minute } | + tag!("min") => { |_| Unit::Minute } | + tag!("mins") => { |_| Unit::Minute } | + tag!("hour") => { |_| Unit::Hour } | + tag!("hours") => { |_| Unit::Hour } | + tag!("hr") => { |_| Unit::Hour } | + tag!("hrs") => { |_| Unit::Hour } | + tag!("day") => { |_| Unit::Day } | + tag!("days") => { |_| Unit::Day } | + tag!("d") => { |_| Unit::Day } | + tag!("week") => { |_| Unit::Week } | + tag!("weeks") => { |_| Unit::Week } | + tag!("w") => { |_| Unit::Week } | + tag!("month") => { |_| Unit::Month } | + tag!("months") => { |_| Unit::Month } | + tag!("year") => { |_| Unit::Year } | + tag!("years") => { |_| Unit::Year } | + tag!("yrs") => { |_| Unit::Year } +)); + +pub enum Unit { + Second, + Minute, + Hour, + Day, + Week, + Month, + Year, +} + +named!(operator_parser, alt!( + tag!("+") => { |_| Operator::Plus } | + tag!("-") => { |_| Operator::Minus } +)); + +pub enum Operator { + Plus, + Minus, +} + +named!(amount_parser, do_parse!( + number: integer >> + unit : unit_parser >> + (Amount(number, unit)) +)); + +pub struct Amount(i64, Unit); + +named!(iter_spec, alt!( + tag!("secondly") => { |_| Iterspec::Secondly } | + tag!("minutely") => { |_| Iterspec::Minutely } | + tag!("hourly") => { |_| Iterspec::Hourly } | + tag!("daily") => { |_| Iterspec::Daily } | + tag!("weekly") => { |_| Iterspec::Weekly } | + tag!("monthly") => { |_| Iterspec::Monthly } | + tag!("yearly") => { |_| Iterspec::Yearly } | + do_parse!( + tag!("every") >> + number:integer >> + unit:unit_parser >> + (Iterspec::Every(number, unit)) + ) +)); + +pub enum Iterspec { + Secondly, + Minutely, + Hourly, + Daily, + Weekly, + Monthly, + Yearly, + Every(i64, Unit), +} + +named!(amount_expr, do_parse!( + amount:amount_parser >> + o: opt!(do_parse!(op:operator_parser >> amexp:amount_expr >> ((op, Box::new(amexp))))) >> + (AmountExpr { amount: amount, next: o, }) +)); + +pub struct AmountExpr { + amount: Amount, + next: Option<(Operator, Box)>, +} + +impl AmountExpr { + fn new(amount: Amount, next: Option<(Operator, Box)>) -> AmountExpr { + AmountExpr { + amount: amount, + next: next + } + } } +use iso8601::parsers::parse_date; +use iso8601::parsers::parse_datetime; +named!(exact_date_parser, alt!( + tag!("today") => { |_| ExactDate::Today } | + tag!("yesterday") => { |_| ExactDate::Yesterday } | + tag!("tomorrow") => { |_| ExactDate::Tomorrow } | + do_parse!(d: parse_date >> (ExactDate::Iso8601Date(d))) | + do_parse!(d: parse_datetime >> (ExactDate::Iso8601DateTime(d))) +)); + +pub enum ExactDate { + Today, + Yesterday, + Tomorrow, + Iso8601Date(::iso8601::Date), + Iso8601DateTime(::iso8601::DateTime) +} + +named!(date, do_parse!( + exact:exact_date_parser >> + o: opt!(do_parse!(op:operator_parser >> a:amount_expr >> (op, a))) >> + (Date(exact, o)) +)); + +pub struct Date(ExactDate, Option<(Operator, AmountExpr)>); + +named!(until_spec, alt!( + do_parse!( + tag!("until") >> + exact: exact_date_parser >> + (UntilSpec::Exact(exact)) + ) | + do_parse!( + num: integer >> + tag!("times") >> + (UntilSpec::Times(num)) + ) +)); + +pub enum UntilSpec { + Exact(ExactDate), + Times(i64) +} + +named!(iterator, do_parse!( + d: date >> + spec: iter_spec >> + until: opt!(until_spec) >> + (Iterator(d, spec, until)) +)); + +pub struct Iterator(Date, Iterspec, Option); + -- cgit v1.2.3 From 17fab62f7af0eb8aa67480111a95c3f042331e66 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 28 Oct 2017 21:34:42 +0200 Subject: Sort tags --- src/parser.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index d50cbec..b1d8955 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -49,30 +49,31 @@ named!(integer, alt!( ) )); -named!(unit_parser, alt!( - tag!("second") => { |_| Unit::Second } | +// WARNING: Order is important here. Long tags first, shorter tags later +named!(unit_parser, alt_complete!( tag!("seconds") => { |_| Unit::Second } | - tag!("sec") => { |_| Unit::Second } | + tag!("second") => { |_| Unit::Second } | tag!("secs") => { |_| Unit::Second } | + tag!("sec") => { |_| Unit::Second } | tag!("s") => { |_| Unit::Second } | - tag!("minute") => { |_| Unit::Minute } | tag!("minutes") => { |_| Unit::Minute } | - tag!("min") => { |_| Unit::Minute } | + tag!("minute") => { |_| Unit::Minute } | tag!("mins") => { |_| Unit::Minute } | - tag!("hour") => { |_| Unit::Hour } | + tag!("min") => { |_| Unit::Minute } | tag!("hours") => { |_| Unit::Hour } | - tag!("hr") => { |_| Unit::Hour } | + tag!("hour") => { |_| Unit::Hour } | tag!("hrs") => { |_| Unit::Hour } | - tag!("day") => { |_| Unit::Day } | + tag!("hr") => { |_| Unit::Hour } | tag!("days") => { |_| Unit::Day } | + tag!("day") => { |_| Unit::Day } | tag!("d") => { |_| Unit::Day } | - tag!("week") => { |_| Unit::Week } | tag!("weeks") => { |_| Unit::Week } | + tag!("week") => { |_| Unit::Week } | tag!("w") => { |_| Unit::Week } | - tag!("month") => { |_| Unit::Month } | tag!("months") => { |_| Unit::Month } | - tag!("year") => { |_| Unit::Year } | + tag!("month") => { |_| Unit::Month } | tag!("years") => { |_| Unit::Year } | + tag!("year") => { |_| Unit::Year } | tag!("yrs") => { |_| Unit::Year } )); -- cgit v1.2.3 From 8cc947e67d99c6c00f17b9c443f8c1c2bc721410 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 28 Oct 2017 21:35:06 +0200 Subject: Add debug, partialeq, eq derive --- src/parser.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index b1d8955..16973e8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -77,6 +77,7 @@ named!(unit_parser, alt_complete!( tag!("yrs") => { |_| Unit::Year } )); +#[derive(Debug, PartialEq, Eq)] pub enum Unit { Second, Minute, @@ -92,6 +93,7 @@ named!(operator_parser, alt!( tag!("-") => { |_| Operator::Minus } )); +#[derive(Debug, PartialEq, Eq)] pub enum Operator { Plus, Minus, @@ -103,6 +105,7 @@ named!(amount_parser, do_parse!( (Amount(number, unit)) )); +#[derive(Debug, PartialEq, Eq)] pub struct Amount(i64, Unit); named!(iter_spec, alt!( @@ -121,6 +124,7 @@ named!(iter_spec, alt!( ) )); +#[derive(Debug, PartialEq, Eq)] pub enum Iterspec { Secondly, Minutely, @@ -138,6 +142,7 @@ named!(amount_expr, do_parse!( (AmountExpr { amount: amount, next: o, }) )); +#[derive(Debug, PartialEq, Eq)] pub struct AmountExpr { amount: Amount, next: Option<(Operator, Box)>, @@ -162,6 +167,7 @@ named!(exact_date_parser, alt!( do_parse!(d: parse_datetime >> (ExactDate::Iso8601DateTime(d))) )); +#[derive(Debug, PartialEq, Eq)] pub enum ExactDate { Today, Yesterday, @@ -176,6 +182,7 @@ named!(date, do_parse!( (Date(exact, o)) )); +#[derive(Debug, PartialEq, Eq)] pub struct Date(ExactDate, Option<(Operator, AmountExpr)>); named!(until_spec, alt!( @@ -191,6 +198,7 @@ named!(until_spec, alt!( ) )); +#[derive(Debug, PartialEq, Eq)] pub enum UntilSpec { Exact(ExactDate), Times(i64) -- cgit v1.2.3 From ffdac059cc0384b52758ce8eaba0dcd3bc5d276b Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 28 Oct 2017 21:35:52 +0200 Subject: Use alt_complete!() here --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 16973e8..66900a4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -108,7 +108,7 @@ named!(amount_parser, do_parse!( #[derive(Debug, PartialEq, Eq)] pub struct Amount(i64, Unit); -named!(iter_spec, alt!( +named!(iter_spec, alt_complete!( tag!("secondly") => { |_| Iterspec::Secondly } | tag!("minutely") => { |_| Iterspec::Minutely } | tag!("hourly") => { |_| Iterspec::Hourly } | -- cgit v1.2.3 From fd123adfcc517b0b31881086a4163c288191ed37 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 28 Oct 2017 21:36:01 +0200 Subject: Add optional whitespace --- src/parser.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 66900a4..01faef2 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -136,9 +136,12 @@ pub enum Iterspec { Every(i64, Unit), } +use nom::whitespace::sp; + named!(amount_expr, do_parse!( amount:amount_parser >> - o: opt!(do_parse!(op:operator_parser >> amexp:amount_expr >> ((op, Box::new(amexp))))) >> + opt!(sp) >> + o: opt!(do_parse!(op:operator_parser >> opt!(sp) >> amexp:amount_expr >> ((op, Box::new(amexp))))) >> (AmountExpr { amount: amount, next: o, }) )); -- cgit v1.2.3 From e74f49e712a0479c839dac4354ffb3b3073cfe86 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 28 Oct 2017 21:36:07 +0200 Subject: Add tests --- src/parser.rs | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 91 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index 01faef2..0a2ecb8 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -214,5 +214,96 @@ named!(iterator, do_parse!( (Iterator(d, spec, until)) )); +#[derive(Debug, PartialEq, Eq)] pub struct Iterator(Date, Iterspec, Option); + +#[cfg(test)] +mod tests { + use nom::IResult; + use super::*; + + #[test] + fn test_integer() { + assert_eq!(integer(&b"2"[..]), IResult::Done(&b""[..], 2)); + assert_eq!(integer(&b"217"[..]), IResult::Done(&b""[..], 217)); + } + + #[test] + fn test_unit() { + assert_eq!(unit_parser(&b"second"[..]), IResult::Done(&b""[..], Unit::Second)); + assert_eq!(unit_parser(&b"seconds"[..]), IResult::Done(&b""[..], Unit::Second)); + assert_eq!(unit_parser(&b"sec"[..]), IResult::Done(&b""[..], Unit::Second)); + assert_eq!(unit_parser(&b"secs"[..]), IResult::Done(&b""[..], Unit::Second)); + assert_eq!(unit_parser(&b"s"[..]), IResult::Done(&b""[..], Unit::Second)); + assert_eq!(unit_parser(&b"minute"[..]), IResult::Done(&b""[..], Unit::Minute)); + assert_eq!(unit_parser(&b"minutes"[..]), IResult::Done(&b""[..], Unit::Minute)); + assert_eq!(unit_parser(&b"min"[..]), IResult::Done(&b""[..], Unit::Minute)); + assert_eq!(unit_parser(&b"mins"[..]), IResult::Done(&b""[..], Unit::Minute)); + assert_eq!(unit_parser(&b"hour"[..]), IResult::Done(&b""[..], Unit::Hour)); + assert_eq!(unit_parser(&b"hours"[..]), IResult::Done(&b""[..], Unit::Hour)); + assert_eq!(unit_parser(&b"hr"[..]), IResult::Done(&b""[..], Unit::Hour)); + assert_eq!(unit_parser(&b"hrs"[..]), IResult::Done(&b""[..], Unit::Hour)); + assert_eq!(unit_parser(&b"day"[..]), IResult::Done(&b""[..], Unit::Day)); + assert_eq!(unit_parser(&b"days"[..]), IResult::Done(&b""[..], Unit::Day)); + assert_eq!(unit_parser(&b"d"[..]), IResult::Done(&b""[..], Unit::Day)); + assert_eq!(unit_parser(&b"week"[..]), IResult::Done(&b""[..], Unit::Week)); + assert_eq!(unit_parser(&b"weeks"[..]), IResult::Done(&b""[..], Unit::Week)); + assert_eq!(unit_parser(&b"w"[..]), IResult::Done(&b""[..], Unit::Week)); + assert_eq!(unit_parser(&b"month"[..]), IResult::Done(&b""[..], Unit::Month)); + assert_eq!(unit_parser(&b"months"[..]), IResult::Done(&b""[..], Unit::Month)); + assert_eq!(unit_parser(&b"year"[..]), IResult::Done(&b""[..], Unit::Year)); + assert_eq!(unit_parser(&b"years"[..]), IResult::Done(&b""[..], Unit::Year)); + assert_eq!(unit_parser(&b"yrs"[..]), IResult::Done(&b""[..], Unit::Year)); + } + + #[test] + fn test_operator() { + assert_eq!(operator_parser(&b"+"[..]), IResult::Done(&b""[..], Operator::Plus)); + assert_eq!(operator_parser(&b"-"[..]), IResult::Done(&b""[..], Operator::Minus)); + } + + #[test] + fn test_amount() { + assert_eq!(amount_parser(&b"5s"[..]), IResult::Done(&b""[..], Amount(5, Unit::Second))); + assert_eq!(amount_parser(&b"5min"[..]), IResult::Done(&b""[..], Amount(5, Unit::Minute))); + assert_eq!(amount_parser(&b"55hrs"[..]), IResult::Done(&b""[..], Amount(55, Unit::Hour))); + assert_eq!(amount_parser(&b"25days"[..]), IResult::Done(&b""[..], Amount(25, Unit::Day))); + assert_eq!(amount_parser(&b"15weeks"[..]), IResult::Done(&b""[..], Amount(15, Unit::Week))); + } + + #[test] + fn test_iterspec() { + assert_eq!(iter_spec(&b"secondly"[..]), IResult::Done(&b""[..], Iterspec::Secondly)); + assert_eq!(iter_spec(&b"minutely"[..]), IResult::Done(&b""[..], Iterspec::Minutely)); + assert_eq!(iter_spec(&b"hourly"[..]), IResult::Done(&b""[..], Iterspec::Hourly)); + assert_eq!(iter_spec(&b"daily"[..]), IResult::Done(&b""[..], Iterspec::Daily)); + assert_eq!(iter_spec(&b"weekly"[..]), IResult::Done(&b""[..], Iterspec::Weekly)); + assert_eq!(iter_spec(&b"monthly"[..]), IResult::Done(&b""[..], Iterspec::Monthly)); + assert_eq!(iter_spec(&b"yearly"[..]), IResult::Done(&b""[..], Iterspec::Yearly)); + assert_eq!(iter_spec(&b"every 5min"[..]), IResult::Done(&b""[..], Iterspec::Every(5, Unit::Minute))); + } + + #[test] + fn test_amountexpr() { + assert_eq!(amount_expr(&b"5minutes"[..]), + IResult::Done(&b""[..], + AmountExpr { + amount: Amount(5, Unit::Minute), + next: None + }) + ); + + assert_eq!(amount_expr(&b"5min + 12min"[..]), + IResult::Done(&b""[..], + AmountExpr { + amount: Amount(5, Unit::Minute), + next: Some((Operator::Plus, Box::new( + AmountExpr { + amount: Amount(12, Unit::Minute), + next: None + }))) + })); + } +} + -- cgit v1.2.3 From 645f6a332a649d5bad9c60f0b07d7084901e5cb9 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 29 Oct 2017 20:26:51 +0100 Subject: Break amount expression parsing into multiple parts --- src/parser.rs | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 0a2ecb8..9ca8f16 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -138,10 +138,17 @@ pub enum Iterspec { use nom::whitespace::sp; +named!(amount_expr_next<(Operator, Box)>, do_parse!( + op:operator_parser + >> opt!(sp) + >> amexp:amount_expr + >> ((op, Box::new(amexp))) +)); + named!(amount_expr, do_parse!( amount:amount_parser >> opt!(sp) >> - o: opt!(do_parse!(op:operator_parser >> opt!(sp) >> amexp:amount_expr >> ((op, Box::new(amexp))))) >> + o: opt!(amount_expr_next) >> (AmountExpr { amount: amount, next: o, }) )); @@ -284,6 +291,17 @@ mod tests { assert_eq!(iter_spec(&b"every 5min"[..]), IResult::Done(&b""[..], Iterspec::Every(5, Unit::Minute))); } + #[test] + fn test_amountexpr_next() { + assert_eq!(amount_expr_next(&b"+ 12minutes"[..]), + IResult::Done(&b""[..], + ( + Operator::Plus, + Box::new(AmountExpr { amount: Amount(12, Unit::Minute), next: None }) + ) + )); + } + #[test] fn test_amountexpr() { assert_eq!(amount_expr(&b"5minutes"[..]), -- cgit v1.2.3 From a2be48dcb8ac189e55f1fe7163d68d7056cfc9d5 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Mon, 30 Oct 2017 13:07:34 +0100 Subject: Fix: Parse complete next expression --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 9ca8f16..d016ee9 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -148,7 +148,7 @@ named!(amount_expr_next<(Operator, Box)>, do_parse!( named!(amount_expr, do_parse!( amount:amount_parser >> opt!(sp) >> - o: opt!(amount_expr_next) >> + o: opt!(complete!(amount_expr_next)) >> (AmountExpr { amount: amount, next: o, }) )); -- cgit v1.2.3 From 5d4670be62cb59900a026247795d7418905966d0 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Fri, 10 Nov 2017 18:31:41 +0100 Subject: Add into-conversions and simple test for it --- src/parser.rs | 174 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index d016ee9..bfa70e7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -39,6 +39,10 @@ use nom::{IResult, space, alpha, alphanumeric, digit}; use std::str; use std::str::FromStr; +use chrono::NaiveDate; + +use timetype::TimeType; + named!(integer, alt!( map_res!( map_res!( @@ -108,6 +112,20 @@ named!(amount_parser, do_parse!( #[derive(Debug, PartialEq, Eq)] pub struct Amount(i64, Unit); +impl Into for Amount { + fn into(self) -> TimeType { + match self.1 { + Unit::Second => TimeType::seconds(self.0), + Unit::Minute => TimeType::minutes(self.0), + Unit::Hour => TimeType::hours(self.0), + Unit::Day => TimeType::days(self.0), + Unit::Week => TimeType::weeks(self.0), + Unit::Month => TimeType::months(self.0), + Unit::Year => TimeType::years(self.0), + } + } +} + named!(iter_spec, alt_complete!( tag!("secondly") => { |_| Iterspec::Secondly } | tag!("minutely") => { |_| Iterspec::Minutely } | @@ -158,6 +176,25 @@ pub struct AmountExpr { next: Option<(Operator, Box)>, } +impl Into for AmountExpr { + fn into(self) -> TimeType { + let mut amount = self.amount.into(); + + if let Some((op, other_amonut_expr)) = self.next { + match op { + Operator::Plus => { + amount = amount + (*other_amonut_expr).into(); + }, + Operator::Minus => { + amount = amount - (*other_amonut_expr).into(); + }, + } + } + + amount + } +} + impl AmountExpr { fn new(amount: Amount, next: Option<(Operator, Box)>) -> AmountExpr { AmountExpr { @@ -186,6 +223,49 @@ pub enum ExactDate { Iso8601DateTime(::iso8601::DateTime) } +impl Into for ExactDate { + fn into(self) -> TimeType { + match self { + ExactDate::Today => TimeType::today(), + ExactDate::Yesterday => TimeType::today() - TimeType::days(1), + ExactDate::Tomorrow => TimeType::today() + TimeType::days(1), + ExactDate::Iso8601Date(date) => { + let (year, month, day) = match date { + ::iso8601::Date::YMD { year, month, day } => { + (year, month, day) + }, + ::iso8601::Date::Week { year, ww, d } => { + unimplemented!() + }, + ::iso8601::Date::Ordinal { year, ddd } => { + unimplemented!() + }, + }; + + let ndt = NaiveDate::from_ymd(year, month, day).and_hms(0, 0, 0); + TimeType::moment(ndt) + }, + ExactDate::Iso8601DateTime(::iso8601::DateTime { date, time }) => { + let (hour, minute, second) = (time.hour, time.minute, time.second); + let (year, month, day) = match date { + ::iso8601::Date::YMD { year, month, day } => { + (year, month, day) + }, + ::iso8601::Date::Week { year, ww, d } => { + unimplemented!() + }, + ::iso8601::Date::Ordinal { year, ddd } => { + unimplemented!() + }, + }; + + let ndt = NaiveDate::from_ymd(year, month, day).and_hms(hour, minute, second); + TimeType::moment(ndt) + }, + } + } +} + named!(date, do_parse!( exact:exact_date_parser >> o: opt!(do_parse!(op:operator_parser >> a:amount_expr >> (op, a))) >> @@ -195,6 +275,17 @@ named!(date, do_parse!( #[derive(Debug, PartialEq, Eq)] pub struct Date(ExactDate, Option<(Operator, AmountExpr)>); +impl Into for Date { + fn into(self) -> TimeType { + let base : TimeType = self.0.into(); + match self.1 { + Some((Operator::Plus, amount)) => base + amount.into(), + Some((Operator::Minus, amount)) => base - amount.into(), + None => base, + } + } +} + named!(until_spec, alt!( do_parse!( tag!("until") >> @@ -224,12 +315,20 @@ named!(iterator, do_parse!( #[derive(Debug, PartialEq, Eq)] pub struct Iterator(Date, Iterspec, Option); +impl Into for Iterator { + fn into(self) -> TimeType { + unimplemented!() + } +} #[cfg(test)] mod tests { use nom::IResult; use super::*; + use chrono::Timelike; + use chrono::Datelike; + #[test] fn test_integer() { assert_eq!(integer(&b"2"[..]), IResult::Done(&b""[..], 2)); @@ -323,5 +422,80 @@ mod tests { }))) })); } + + #[test] + fn test_expressions_to_date() { + let res = amount_expr(&b"5min + 12min"[..]); + assert!(res.is_done()); + let (_, o) = res.unwrap(); + + let calc_res : TimeType = o.into(); + let calc_res = calc_res.calculate(); + assert!(calc_res.is_ok()); + + let calc_res = calc_res.unwrap(); + assert_eq!(calc_res.get_seconds(), 17 * 60); + assert_eq!(calc_res.get_minutes(), 17); + assert_eq!(calc_res.get_hours(), 0); + assert_eq!(calc_res.get_days(), 0); + assert_eq!(calc_res.get_years(), 0); + } + + #[test] + fn test_expressions_to_date_2() { + let res = amount_expr(&b"5min + 12min + 15hours"[..]); + assert!(res.is_done()); + let (_, o) = res.unwrap(); + + let calc_res : TimeType = o.into(); + let calc_res = calc_res.calculate(); + assert!(calc_res.is_ok()); + + let calc_res = calc_res.unwrap(); + assert_eq!(calc_res.get_seconds(), 17 * 60 + (15 * 60 * 60)); + assert_eq!(calc_res.get_minutes(), 17 + (15 * 60)); + assert_eq!(calc_res.get_hours(), 15); + assert_eq!(calc_res.get_days(), 0); + assert_eq!(calc_res.get_years(), 0); + } + + #[test] + fn test_expressions_to_date_3() { + let res = date(&b"today + 5min + 12min"[..]); + assert!(res.is_done(), "Not done: {:?}", res.unwrap_err().description()); + let (_, o) = res.unwrap(); + + let calc_res : TimeType = o.into(); + let calc_res = calc_res.calculate(); + assert!(calc_res.is_ok()); + + // because this test is basically dependent on the current time, which is a baaaad use of + // state in a test, we rely on `test_expressions_to_date_4()` here and assume that the + // upper assertions are enough. + } + + #[test] + fn test_expressions_to_date_4() { + let res = date(&b"2017-01-01 + 5min + 12min"[..]); + assert!(res.is_done(), "Not done: {:?}", res.unwrap_err().description()); + let (_, o) = res.unwrap(); + + println!("{:#?}", o); + + let calc_res : TimeType = o.into(); + let calc_res = calc_res.calculate(); + assert!(calc_res.is_ok()); + + let calc_res = calc_res.unwrap(); + println!("{:#?}", calc_res); + + assert_eq!(calc_res.get_moment().unwrap().year() , 2017); + assert_eq!(calc_res.get_moment().unwrap().month() , 01); + assert_eq!(calc_res.get_moment().unwrap().day() , 01); + assert_eq!(calc_res.get_moment().unwrap().hour() , 00); + assert_eq!(calc_res.get_moment().unwrap().minute(), 17); + assert_eq!(calc_res.get_moment().unwrap().second(), 00); + } + } -- cgit v1.2.3 From e5ed730a4aa0413f05c9c0d0a6a1d903ba3b31dd Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 01:39:31 +0100 Subject: Add tests for iso8601 parsing --- src/parser.rs | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index bfa70e7..251ec31 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -206,12 +206,14 @@ impl AmountExpr { use iso8601::parsers::parse_date; use iso8601::parsers::parse_datetime; -named!(exact_date_parser, alt!( +// The order is relevant here, because datetime is longer than date, we must parse datetime before +// date. +named!(exact_date_parser, alt_complete!( tag!("today") => { |_| ExactDate::Today } | tag!("yesterday") => { |_| ExactDate::Yesterday } | tag!("tomorrow") => { |_| ExactDate::Tomorrow } | - do_parse!(d: parse_date >> (ExactDate::Iso8601Date(d))) | - do_parse!(d: parse_datetime >> (ExactDate::Iso8601DateTime(d))) + do_parse!(d: parse_datetime >> (ExactDate::Iso8601DateTime(d))) | + do_parse!(d: parse_date >> (ExactDate::Iso8601Date(d))) )); #[derive(Debug, PartialEq, Eq)] @@ -423,6 +425,57 @@ mod tests { })); } + #[test] + fn test_parse_expressions_date() { + use iso8601::Date; + let res = exact_date_parser(&b"2017-01-01"[..]); + assert!(res.is_done()); + + match res.unwrap().1 { + ExactDate::Iso8601DateTime(_) => assert!(false), + ExactDate::Iso8601Date(d) => { + match d { + Date::YMD { year, month, day } => { + assert_eq!(year, 2017); + assert_eq!(month, 1); + assert_eq!(day, 1) + }, + _ => assert!(false), + } + }, + ExactDate::Tomorrow => assert!(false), + ExactDate::Yesterday => assert!(false), + ExactDate::Today => assert!(false), + }; + } + + #[test] + fn test_parse_expressions_datetime() { + use iso8601::Date; + let res = exact_date_parser(&b"2017-01-01T22:00:11"[..]); + assert!(res.is_done()); + + match res.unwrap().1 { + ExactDate::Iso8601DateTime(obj) => { + match obj.date { + Date::YMD { year, month, day } => { + assert_eq!(year, 2017); + assert_eq!(month, 1); + assert_eq!(day, 1) + }, + _ => assert!(false), + } + assert_eq!(obj.time.hour, 22); + assert_eq!(obj.time.minute, 0); + assert_eq!(obj.time.second, 11); + }, + ExactDate::Iso8601Date(_) => assert!(false), + ExactDate::Tomorrow => assert!(false), + ExactDate::Yesterday => assert!(false), + ExactDate::Today => assert!(false), + }; + } + #[test] fn test_expressions_to_date() { let res = amount_expr(&b"5min + 12min"[..]); -- cgit v1.2.3 From 26329eaf112690048e6bfb94f1e4e9d4056e09aa Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 02:34:45 +0100 Subject: Fix: There must we whitespace! --- src/parser.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/parser.rs b/src/parser.rs index 251ec31..27396dc 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -270,7 +270,7 @@ impl Into for ExactDate { named!(date, do_parse!( exact:exact_date_parser >> - o: opt!(do_parse!(op:operator_parser >> a:amount_expr >> (op, a))) >> + o: opt!(do_parse!(sp >> op:operator_parser >> a:amount_expr >> (op, a))) >> (Date(exact, o)) )); -- cgit v1.2.3 From 2f1a28a0b5058fea346fbf668a249d04900f5ae1 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 02:48:00 +0100 Subject: Add internal timetype which resolves to either AmountExpr or Date --- src/parser.rs | 110 +++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 82 insertions(+), 28 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index 27396dc..f23d244 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -41,7 +41,7 @@ use std::str::FromStr; use chrono::NaiveDate; -use timetype::TimeType; +use timetype; named!(integer, alt!( map_res!( @@ -112,16 +112,16 @@ named!(amount_parser, do_parse!( #[derive(Debug, PartialEq, Eq)] pub struct Amount(i64, Unit); -impl Into for Amount { - fn into(self) -> TimeType { +impl Into for Amount { + fn into(self) -> timetype::TimeType { match self.1 { - Unit::Second => TimeType::seconds(self.0), - Unit::Minute => TimeType::minutes(self.0), - Unit::Hour => TimeType::hours(self.0), - Unit::Day => TimeType::days(self.0), - Unit::Week => TimeType::weeks(self.0), - Unit::Month => TimeType::months(self.0), - Unit::Year => TimeType::years(self.0), + Unit::Second => timetype::TimeType::seconds(self.0), + Unit::Minute => timetype::TimeType::minutes(self.0), + Unit::Hour => timetype::TimeType::hours(self.0), + Unit::Day => timetype::TimeType::days(self.0), + Unit::Week => timetype::TimeType::weeks(self.0), + Unit::Month => timetype::TimeType::months(self.0), + Unit::Year => timetype::TimeType::years(self.0), } } } @@ -176,8 +176,8 @@ pub struct AmountExpr { next: Option<(Operator, Box)>, } -impl Into for AmountExpr { - fn into(self) -> TimeType { +impl Into for AmountExpr { + fn into(self) -> timetype::TimeType { let mut amount = self.amount.into(); if let Some((op, other_amonut_expr)) = self.next { @@ -225,12 +225,12 @@ pub enum ExactDate { Iso8601DateTime(::iso8601::DateTime) } -impl Into for ExactDate { - fn into(self) -> TimeType { +impl Into for ExactDate { + fn into(self) -> timetype::TimeType { match self { - ExactDate::Today => TimeType::today(), - ExactDate::Yesterday => TimeType::today() - TimeType::days(1), - ExactDate::Tomorrow => TimeType::today() + TimeType::days(1), + ExactDate::Today => timetype::TimeType::today(), + ExactDate::Yesterday => timetype::TimeType::today() - timetype::TimeType::days(1), + ExactDate::Tomorrow => timetype::TimeType::today() + timetype::TimeType::days(1), ExactDate::Iso8601Date(date) => { let (year, month, day) = match date { ::iso8601::Date::YMD { year, month, day } => { @@ -245,7 +245,7 @@ impl Into for ExactDate { }; let ndt = NaiveDate::from_ymd(year, month, day).and_hms(0, 0, 0); - TimeType::moment(ndt) + timetype::TimeType::moment(ndt) }, ExactDate::Iso8601DateTime(::iso8601::DateTime { date, time }) => { let (hour, minute, second) = (time.hour, time.minute, time.second); @@ -262,7 +262,7 @@ impl Into for ExactDate { }; let ndt = NaiveDate::from_ymd(year, month, day).and_hms(hour, minute, second); - TimeType::moment(ndt) + timetype::TimeType::moment(ndt) }, } } @@ -277,9 +277,9 @@ named!(date, do_parse!( #[derive(Debug, PartialEq, Eq)] pub struct Date(ExactDate, Option<(Operator, AmountExpr)>); -impl Into for Date { - fn into(self) -> TimeType { - let base : TimeType = self.0.into(); +impl Into for Date { + fn into(self) -> timetype::TimeType { + let base : timetype::TimeType = self.0.into(); match self.1 { Some((Operator::Plus, amount)) => base + amount.into(), Some((Operator::Minus, amount)) => base - amount.into(), @@ -288,6 +288,27 @@ impl Into for Date { } } + +named!(timetype, alt!( + do_parse!(d: date >> (TimeType::Date(d))) | + do_parse!(a: amount_expr >> (TimeType::AmountExpr(a))) +)); + +#[derive(Debug, PartialEq, Eq)] +pub enum TimeType { + Date(Date), + AmountExpr(AmountExpr), +} + +impl Into for TimeType { + fn into(self) -> timetype::TimeType { + match self { + TimeType::Date(d) => d.into(), + TimeType::AmountExpr(a) => a.into(), + } + } +} + named!(until_spec, alt!( do_parse!( tag!("until") >> @@ -317,8 +338,8 @@ named!(iterator, do_parse!( #[derive(Debug, PartialEq, Eq)] pub struct Iterator(Date, Iterspec, Option); -impl Into for Iterator { - fn into(self) -> TimeType { +impl Into for Iterator { + fn into(self) -> timetype::TimeType { unimplemented!() } } @@ -482,7 +503,7 @@ mod tests { assert!(res.is_done()); let (_, o) = res.unwrap(); - let calc_res : TimeType = o.into(); + let calc_res : timetype::TimeType = o.into(); let calc_res = calc_res.calculate(); assert!(calc_res.is_ok()); @@ -500,7 +521,7 @@ mod tests { assert!(res.is_done()); let (_, o) = res.unwrap(); - let calc_res : TimeType = o.into(); + let calc_res : timetype::TimeType = o.into(); let calc_res = calc_res.calculate(); assert!(calc_res.is_ok()); @@ -518,7 +539,7 @@ mod tests { assert!(res.is_done(), "Not done: {:?}", res.unwrap_err().description()); let (_, o) = res.unwrap(); - let calc_res : TimeType = o.into(); + let calc_res : timetype::TimeType = o.into(); let calc_res = calc_res.calculate(); assert!(calc_res.is_ok()); @@ -535,7 +556,7 @@ mod tests { println!("{:#?}", o); - let calc_res : TimeType = o.into(); + let calc_res : timetype::TimeType = o.into(); let calc_res = calc_res.calculate(); assert!(calc_res.is_ok()); @@ -550,5 +571,38 @@ mod tests { assert_eq!(calc_res.get_moment().unwrap().second(), 00); } + #[test] + fn test_expressions_to_timetype() { + let res = timetype(&b"5min + 12min + 15hours"[..]); + assert!(res.is_done()); + let (_, o) = res.unwrap(); + + let calc_res : timetype::TimeType = o.into(); + let calc_res = calc_res.calculate(); + assert!(calc_res.is_ok()); + + let calc_res = calc_res.unwrap(); + assert_eq!(calc_res.get_seconds(), 17 * 60 + (15 * 60 * 60)); + assert_eq!(calc_res.get_minutes(), 17 + (15 * 60)); + assert_eq!(calc_res.get_hours(), 15); + assert_eq!(calc_res.get_days(), 0); + assert_eq!(calc_res.get_years(), 0); + } + + #[test] + fn test_expressions_to_timetype_2() { + let res = timetype(&b"today + 5min + 12min"[..]); + assert!(res.is_done(), "Not done: {:?}", res.unwrap_err().description()); + let (_, o) = res.unwrap(); + + let calc_res : timetype::TimeType = o.into(); + let calc_res = calc_res.calculate(); + assert!(calc_res.is_ok()); + + // because this test is basically dependent on the current time, which is a baaaad use of + // state in a test, we rely on `test_expressions_to_date_4()` here and assume that the + // upper assertions are enough. + } + } -- cgit v1.2.3 From 8013983ba94135494079dc607580985717d8a4d3 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 02:57:18 +0100 Subject: Make parser::timetype() pub, add documentation about interface --- src/parser.rs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/parser.rs b/src/parser.rs index f23d244..1b87bc4 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -34,6 +34,14 @@ //! Date = ( )? //! Iterator = ("until" | "times")? //! +//! # Warning +//! +//! This module is not intended for public use... it is still public, so you can use it, but you +//! should know that these interfaces are considered private and I will not follow semver and +//! update the minor or major semver numbers of the interface of this module changes. +//! +//! Be warned! +//! use nom::{IResult, space, alpha, alphanumeric, digit}; use std::str; @@ -288,8 +296,14 @@ impl Into for Date { } } - -named!(timetype, alt!( +/// Main entry function for timetype parser +/// +/// # Notice +/// +/// Note that this function returns a parser::TimeType, not a timetype::TimeType. Though, the +/// parser::TimeType can be `Into::into()`ed. +/// +named!(pub timetype, alt!( do_parse!(d: date >> (TimeType::Date(d))) | do_parse!(a: amount_expr >> (TimeType::AmountExpr(a))) )); -- cgit v1.2.3 From 8f46938f9947c3f487bd0ba6c02a19c0de672a6d Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 02:57:31 +0100 Subject: Error-chain setup for nom errors --- src/error.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/error.rs b/src/error.rs index 39d482f..f9f1efc 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,6 +9,7 @@ error_chain! { } foreign_links { + NomError(::nom::IError); } errors { -- cgit v1.2.3 From 55678c5a6fba270f6bb050377ebc9bad19873e36 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 02:57:40 +0100 Subject: Add TimeType::parse() --- src/error.rs | 7 ++++++- src/timetype.rs | 9 +++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index f9f1efc..653b368 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,7 +9,7 @@ error_chain! { } foreign_links { - NomError(::nom::IError); + NomError(::nom::simple_errors::Err); } errors { @@ -59,6 +59,11 @@ error_chain! { display("Cannot compare Month to non-Moment TimeType: {:?}", tt_rep) } + UnknownParserError { + description("Unknown parser error") + display("Unknown parser error") + } + } } diff --git a/src/timetype.rs b/src/timetype.rs index e8df4c8..8a33aa0 100644 --- a/src/timetype.rs +++ b/src/timetype.rs @@ -435,6 +435,15 @@ impl TimeType { pub fn calculate(self) -> Result { do_calculate(self) } + + pub fn parse(s: &str) -> Result { + use parser; + match parser::timetype(s.as_bytes()) { + ::nom::IResult::Done(_, o) => Ok(o.into()), + ::nom::IResult::Error(e) => Err(KE::from(e)), + ::nom::IResult::Incomplete(_) => Err(KEK::UnknownParserError.into()), + } + } } fn do_calculate(tt: TimeType) -> Result { -- cgit v1.2.3 From dca50088be139bd87a297c2b5513f923a34a024b Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 03:14:10 +0100 Subject: Add simple example CLI app for using kairos from commandline --- examples/main.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 examples/main.rs diff --git a/examples/main.rs b/examples/main.rs new file mode 100644 index 0000000..920accc --- /dev/null +++ b/examples/main.rs @@ -0,0 +1,20 @@ +extern crate kairos; + +fn main() { + // not sure whether this is actually fast or something, but we don't care here, do we? + let s = ::std::env::args().skip(1).fold(String::new(), |acc, obj| format!("{} {}", acc, obj)); + let s = s.trim(); // because kairos is not yet whitespace tolerant + + println!("Parsing: '{}'", s); + match kairos::timetype::TimeType::parse(&s) { + Ok(tt) => { + println!("{:?}", tt); + + match tt.calculate() { + Ok(r) => println!("{:?}", r), + Err(e) => println!("Error calculating: {:?}", e), + } + }, + Err(e) => println!("Error -> {:?}", e), + } +} -- cgit v1.2.3 From 43f00a146355ed234570542324cf2d20c51436cb Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 03:38:45 +0100 Subject: Fix Bug: Called subtraction fn instead of addition fn --- src/timetype.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/timetype.rs b/src/timetype.rs index 8a33aa0..7de608c 100644 --- a/src/timetype.rs +++ b/src/timetype.rs @@ -680,7 +680,7 @@ fn add_to_minutes(amount: i64, tt: TimeType) -> Result { TT::EndOfHour(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Minutes(amount), TT::EndOfHour(e)))), TT::EndOfMinute(e) => Err(KE::from_kind(KEK::CannotAdd(TT::Minutes(amount), TT::EndOfMinute(e)))), TT::Addition(b, c) => add_to_minutes(amount, try!(add(b, c))), - TT::Subtraction(b, c) => sub_from_minutes(amount, try!(sub(b, c))), + TT::Subtraction(b, c) => add_to_minutes(amount, try!(sub(b, c))), } } -- cgit v1.2.3 From 2053d956af43d26238f7be9ae6acf0d60b85acad Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sat, 11 Nov 2017 03:39:25 +0100 Subject: Add test for parser which contains subtraction --- src/parser.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/parser.rs b/src/parser.rs index 1b87bc4..2cbf046 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -618,5 +618,28 @@ mod tests { // upper assertions are enough. } + #[test] + fn test_expressions_to_timetype_subtract() { + let res = timetype(&b"5min + 12min + 15hours - 1hour"[..]); + assert!(res.is_done()); + let (_, o) = res.unwrap(); + + println!("{:#?}", o); + + let calc_res : timetype::TimeType = o.into(); + println!("{:#?}", calc_res); + + let calc_res = calc_res.calculate(); + assert!(calc_res.is_ok()); + println!("{:#?}", calc_res); + + let calc_res = calc_res.unwrap(); + assert_eq!(calc_res.get_seconds(), 17 * 60 + (14 * 60 * 60)); + assert_eq!(calc_res.get_minutes(), 17 + (14 * 60)); + assert_eq!(calc_res.get_hours(), 14); + assert_eq!(calc_res.get_days(), 0); + assert_eq!(calc_res.get_years(), 0); + } + } -- cgit v1.2.3