1use nom::{IResult,Parser};
2use nom::bytes::complete::{tag,take,take_while,take_while1};
3use nom::branch::alt;
4use nom::multi::separated_list1;
5use nom::combinator::{map,opt,verify,recognize,success,value,iterator};
6use nom::character::complete::{digit1,char as nom_char,space1};
7use nom::character::{is_digit,is_alphanumeric};
8use nom::sequence::{preceded,terminated,separated_pair};
9use nom::number::complete::double;
10use nom::error::{Error, ErrorKind};
11
12use crate::{HVal, h_bool::HBool, h_null::HNull, h_na::HNA, h_marker::HMarker,
13 h_remove::HRemove, h_number::{NumTrait,HNumber,HUnit}, h_ref::HRef,
14 h_date::HDate, h_datetime::{HDateTime,HOffset}, h_time::HTime,
15 h_coord::HCoord, h_str::HStr, h_uri::HUri, h_dict::HDict,
16 h_list::HList, h_grid::HGrid};
17
18use crate::common::*;
19
20use std::collections::HashMap;
21use core::fmt::Display;
22use core::str::FromStr;
23use std::rc::Rc;
24use num::Float;
25
26pub type HBox<'a,T> = Rc<dyn HVal<'a,T> + 'a>;
27
28pub mod parse {
29 use nom::sequence::delimited;
30 use nom::combinator::map_res;
31 use super::*;
32
33 macro_rules! into_box {
34 ( $fn: expr, $num_type: ty, $lt: lifetime ) => {
35 map($fn,| hval | { Rc::new(hval) as HBox<$lt, $num_type>})
36 }
37 }
38
39 pub mod zinc {
40 use nom::{character::complete::newline, combinator::all_consuming, error::ParseError, AsChar, Err};
41
42 use super::*;
43
44 pub fn literal<'a,'b,T>(input: &'b str) -> IResult<&'b str, HBox<'a,T>>
45 where
46 T: NumTrait + 'a,
47 'a:'b
48 {
49 alt((
50 into_box!(na,T,'a),
51 into_box!(null,T,'a),
52 into_box!(marker,T,'a),
53 into_box!(remove,T,'a),
54 into_box!(boolean,T,'a),
55 into_box!(reference, T,'a),
56 into_box!(string,T,'a),
57 into_box!(uri,T,'a),
58 into_box!(datetime,T,'a),
59 into_box!(date,T,'a),
60 into_box!(time,T,'a),
61 into_box!(number,T,'a),
62 into_box!(coord,T,'a),
63 into_box!(dict,T,'a),
65 into_box!(list,T,'a),
66 into_box!(delimited(tag("<<"),grid::<T>,tag(">>")),T,'a),
67 )).parse(input)
69 }
70
71 pub fn boolean(input: &str) -> IResult<&str, HBool> {
72 alt((
73 value(HBool(true),tag("T")),
74 value(HBool(false),tag("F"))
75 )).parse(input)
76 }
77
78 pub fn null(input: &str) -> IResult<&str, HNull> {
79 use crate::h_null::NULL;
80 map(tag("N"), |_s: &str| { NULL }).parse(input)
81 }
82
83 pub fn na(input: &str) -> IResult<&str, HNA> {
84 use crate::h_na::NA;
85 map(tag("NA"), |_s: &str| { NA }).parse(input)
86 }
87
88 pub fn marker(input: &str) -> IResult<&str, HMarker> {
89 use crate::h_marker::MARKER;
90 map(tag("M"), |_s: &str| { MARKER }).parse(input)
91 }
92
93 pub fn remove(input: &str) -> IResult<&str, HRemove> {
94 use crate::h_remove::REMOVE;
95 map(tag("R"), |_s: &str| { REMOVE }).parse(input)
96 }
97
98 pub fn string(input: &str) -> IResult<&str, HStr> {
99 let (input,_) = tag("\"")(input)?;
100 let mut it = iterator(input, alt((
101 value("\x08", tag("\\b")),
102 value("\x0C", tag("\\f")),
103 value("\n", tag("\\n")),
104 value("\r", tag("\\r")),
105 value("\t", tag("\\t")),
106 value("\"", tag("\\\"")),
107 value("\\", tag("\\\\")),
108 value("$", tag("\\$")),
109 take_while1(unicode_char('"')),
110 )));
111
112 let string_literal = it.by_ref().fold(String::new(),|mut acc, input| { acc.push_str(input); acc });
113 let (input,()) = it.finish()?;
114 let (input,_) = tag("\"")(input)?;
115
116 Ok((input,HStr(string_literal)))
117 }
118
119 pub fn uri(input: &str) -> IResult<&str, HUri> {
120 let (input,_) = tag("`")(input)?;
121 let mut it = iterator(input, alt((
122 value(":", tag("\\:")),
123 value("/", tag("\\/")),
124 value("#", tag("\\#")),
125 value("\"", tag("\\\"")),
126 value("[", tag("\\[")),
127 value("]", tag("\\]")),
128 value("@", tag("\\@")),
129 value("`", tag("\\`")),
130 value("&", tag("\\&")),
131 value("=", tag("\\=")),
132 value(";", tag("\\;")),
133 value("\\", tag("\\\\")),
134 take_while1(unicode_char('`')),
135 )));
136
137 let url_literal = it.by_ref().fold(String::new(),|mut acc, input| { acc.push_str(input); acc });
138 let (input,()) = it.finish()?;
139 let (input,_) = tag("`")(input)?;
140
141 let uri_res = HUri::new(&url_literal);
142 let uri = uri_res.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
143 Ok((input,uri))
144 }
145
146 pub fn ref_chars_body<I>(prefix:char) -> impl FnMut(I) -> IResult<I,I>
147 where
148 I: nom::Input + Clone,
149 <I as nom::Input>::Item: nom::AsChar + Copy,
150 {
151
152 move |input| preceded(nom::character::complete::char::<I, nom::error::Error<I>>(prefix), take_while1(|c: <I as nom::Input>::Item| {
153 let c = c.as_char();
154 c.is_ascii_alphanumeric() || match c {
155 '_' | ':' | '-' | '.' | '~' => true,
156 _ => false
157 }
158 })).parse(input)
159 }
160
161 fn reference(input: &str) -> IResult<&str, HRef> {
162 let (input,(ref_str,dis_str)) = ((
163 ref_chars_body('@'),
164 opt(preceded(tag(" "), string)),
165 )).parse(input)?;
166 Ok((input,HRef::new(ref_str.to_owned(), dis_str.map(|s| s.into_string()))))
167 }
168
169 fn get_2_digits(input: &str) -> IResult<&str, &str> {
170 verify(take(2usize),|s: &str| s.chars().all(|c| char::is_digit(c,10))).parse(input)
171 }
172
173 fn get_offset(input: &str) -> IResult<&str, (i32, u32, u32)> {
174 ((
175 alt((value(-1,nom_char('-')),value(1,nom_char('+')))),
176 map(get_2_digits, |s| u32::from_str_radix(s,10).unwrap()),
177 preceded(tag(":"), map(get_2_digits, |s| u32::from_str_radix(s,10).unwrap()))
178 )).parse(input)
179 }
180
181 fn get_named_tz(input: &str) -> IResult<&str, &str> {
182 recognize(((
183 take_while1(|c: char| c.is_ascii_uppercase()),
184 take_while(|c: char| is_alphanumeric(c as u8) || c == '/' || c== '-' || c== '_' || c== '+')
185 ))).parse(input)
186 }
187
188 fn timezone(input: &str) -> IResult<&str, (String, HOffset)> {
189 use chrono_tz::{Tz, UTC};
190 use chrono::offset::FixedOffset;
191
192 let (input, (first, second)) = alt((
193 (recognize(get_offset), preceded(tag(" "), get_named_tz)),
194 (tag("Z"), preceded(tag(" "), get_named_tz)),
195 (tag("Z"), success("")),
196 )).parse(input)?;
197
198 let timezone: (String, HOffset) = match first {
200 "Z" => match second {
201 "" => ("UTC".to_owned(), HOffset::Utc),
203 _ => {
204 (second.to_owned(), HOffset::Variable(chrono::Duration::minutes(1 * 60 + 1)))
207 }
208 },
209 _ => {
210 let (_,(sign,hours,minutes)) = get_offset(first)?;
211 (second.to_owned(), HOffset::Fixed(FixedOffset::east(sign * (hours as i32 * 3600 + minutes as i32 * 60))))
213 }
214 };
215
216 Ok((input,timezone))
217 }
218
219 pub fn datetime(input: &str) -> IResult<&str, HDateTime> {
220 let start = input;
221 let (input, (yr, mo, day, hr, min, sec, nano, tz)) = (
222 terminated(map(take(4usize), |s| i32::from_str_radix(s,10)),tag("-")),
223 terminated(map(take(2usize), |s| u32::from_str_radix(s,10)),tag("-")),
224 terminated(map(take(2usize), |s| u32::from_str_radix(s,10)),tag("T")),
225 terminated(map(take(2usize), |s| u32::from_str_radix(s,10)),tag(":")),
226 terminated(map(take(2usize), |s| u32::from_str_radix(s,10)),tag(":")),
227 map(take(2usize), |s| u32::from_str_radix(s,10)),
228 opt(preceded(tag("."),map(digit1, |s| u32::from_str_radix(s,10)))),
229 timezone
230 ).parse(start)?;
231
232 let yr = yr.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
233 let mo = mo.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
234 let day = day.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
235 let hr = hr.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
236 let min = min.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
237 let sec = sec.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?;
238
239 Ok((input, HDateTime::new(
240 yr, mo, day, hr, min, sec,
241 if let Some(nano) = nano {
242 nano.or(Err(nom::Err::Error(Error{ input: start, code: ErrorKind::Digit })))?
243 } else { 0 },
244 tz
245 )))
246 }
247
248 fn coord_deg<T: Float + Display + FromStr>(input: &str) -> IResult<&str, T> {
249 map_res(recognize(((opt(tag("-")),digit1,opt(((tag("."),digit1)))))),|s: &str| s.parse::<T>()).parse(input)
250 }
251
252 pub fn coord<T: Float + Display + FromStr>(input: &str) -> IResult<&str, HCoord<T>> {
253 let (input,(lat,long)) = (delimited(tag("C("),coord_deg,tag(",")),terminated(coord_deg,tag(")"))).parse(input)?;
254
255 Ok((input,HCoord::new(lat,long)))
256 }
257
258 fn tags<'a,'b,T>(input: &'b str) -> IResult<&'b str,Option<HashMap<String,HBox<'a,T>>>>
259 where
260 T: NumTrait + 'a,
261 'a:'b,
262 {
263 let (input,res_opt) = opt(separated_list1(
264 tag(" "),
265 (id, opt(preceded(tag(":"), literal::<'a,'b,T>)))
266 )).parse(input)?;
267
268 let mut map: HashMap<String, HBox<'a,T>> = HashMap::new();
269 let map_opt: Option<_>;
270 if let Some(res) = res_opt {
271 res.into_iter().for_each(|(k,v)| {
272 map.insert(k.to_owned(), v.unwrap_or(Rc::new(crate::h_marker::MARKER) as HBox<'a, T>));
273 });
274 map_opt = Some(map);
275 } else {
276 map_opt = None;
277 }
278
279 Ok((input,map_opt))
280 }
281
282 fn tags_list<'a,'b,T: NumTrait + 'a>(input: &'b str) -> IResult<&'b str,Option<Vec<HBox<'a,T>>>>
283 where
284 T: NumTrait + 'a,
285 'a:'b,
286 {
287 let (input,res) = opt(separated_list1((take_while(AsChar::is_space),tag(","),take_while(AsChar::is_space)),literal::<'a,'b,T>)).parse(input)?;
288
289 Ok((input,res))
290 }
291
292 pub fn dict<'a,'b,T>(input: &'b str) -> IResult<&'b str, HDict<'a,T>>
293 where
294 T: NumTrait + 'a,
295 'a:'b,
296 {
297 let (input,opt_dict) = delimited(tag("{"),tags::<'a,'b,T>,tag("}")).parse(input)?;
298
299 let dict = match opt_dict {
300 Some(dict) => dict,
301 None => HashMap::new()
302 };
303
304 Ok((input,HDict::from_map(dict)))
305 }
306
307 pub fn list<'a,'b, T>(input: &'b str) -> IResult<&'b str, HList<'a,T>>
308 where
309 T: NumTrait + 'a,
310 'a:'b,
311 {
312 let (input,opt_vec) = delimited(tag("["),tags_list::<'a,'b,T>,tag("]")).parse(input)?;
313
314 let vec = match opt_vec {
315 Some(vec) => vec,
316 None => Vec::new()
317 };
318
319 Ok((input,HList::from_vec(vec)))
320 }
321
322 pub fn grid_meta<'a,'b,T>(input: &'b str) -> IResult<&'b str, HashMap<String,HBox<'a,T>>>
323 where
324 T: NumTrait + 'a,
325 'a:'b,
326 {
327 let (input,opt_dict) = tags::<'a,'b,T>(input)?;
328
329 let dict = match opt_dict {
330 Some(dict) => dict,
331 None => Err(nom::Err::Error(Error{
332 input: input,
333 code: ErrorKind::Tag
334 }))?
335 };
336
337 Ok((input,dict))
338 }
339
340 pub fn cols<'a,'b,T>(input: &'b str) -> IResult<&'b str, Vec<(String,Option<HashMap<String,HBox<'a,T>>>)>>
341 where
342 T: NumTrait + 'a,
343 'a:'b,
344 {
345 let (input,columns): (_,Vec<(_, Option<HashMap<_,HBox<'a,T>>>)>) = separated_list1(tag(","), separated_pair(id, space1, tags::<T>)).parse(input)?;
346 let columns = columns.into_iter().map(|(id,meta)| (id.to_owned(),meta));
347 let columns = columns.collect();
348 Ok((input,columns))
349 }
350
351 pub fn grid_err<'a,'b,T>(input: &'b str) -> IResult<&'b str, HGrid<T>>
352 where
353 T: NumTrait + 'a,
354 'a: 'b,
355 {
356 let (input,_) = tag("ver:\"3.0\"")(input)?;
357 let (input,meta) = delimited(space1, grid_meta::<'a,'b,T>, tag("\n")).parse(input)?;
358 let (_, is_empty) = all_consuming(map(terminated(tag("empty"), take_while1(|c| c == '\n')), |_| true)).parse(input)?;
359 if is_empty || meta.contains_key("err") {
360 let dis = match meta.get("dis") {
362 Some(v) => {
363 if let Some(s) = v.get_string_val() {
365 s.clone_into_string()
366 } else {
367 return Err(nom::Err::Error(Error{
368 input: input,
369 code: ErrorKind::Tag
370 }))
371 }
372 },
373 None => return Err(nom::Err::Error(Error{
374 input: input,
375 code: ErrorKind::Tag
376 }))
377 };
378
379 let errTrace = match meta.get("errTrace") {
381 Some(v) => {
382 if let Some(s) = v.get_string_val() {
384 Some(s.clone_into_string())
385 } else {
386 return Err(nom::Err::Error(Error{
387 input: input,
388 code: ErrorKind::Tag
389 }))
390 }
391 },
392 None => return Err(nom::Err::Error(Error{
393 input: input,
394 code: ErrorKind::Tag
395 }))
396 };
397
398 let err = HGrid::Error {
399 dis, errTrace,
400 };
401 return Ok((input, err));
402
403 }
404
405 Err(nom::Err::Error(Error{
406 input: input,
407 code: ErrorKind::Tag
408 }))
409 }
410
411 pub fn grid<'a,'b, T>(input: &'b str) -> IResult<&'b str, HGrid<'a,T>>
412 where
413 'a: 'b,
414 T: NumTrait + 'a,
415 {
416 let (input,version) = delimited(tag("ver:\""), recognize(double), tag("\"")).parse(input)?;
417
418 let (input,meta) = opt(preceded(space1, grid_meta::<'a,'b,T>)).parse(input)?;
420 let (_, is_empty) = all_consuming(map(terminated(tag("\nempty"), take_while1(|c| c == '\n')), |_| true)).parse(input)?;
421 if is_empty {
422 return Ok((input, HGrid::Empty { meta }));
423 }
424
425 let (input,columns) = terminated(cols::<T>, tag("\n")).parse(input)?;
427
428 let row_width = columns.len();
430 let (input,rows) = separated_list1(
431 tag("\n"),
432 verify(
433 separated_list1(tag(","),opt(literal::<T>)),
434 |v: &Vec<Option<HBox<T>>>| v.len()==row_width
435 )
436 ).parse(input)?;
437
438 let mut grid = HGrid::from_row_vec(columns,rows);
439
440 if let Some(meta) = meta {
441 grid = grid.add_meta(meta).unwrap();
442 }
443
444 Ok((input,grid))
445 }
446
447 #[cfg(test)]
448 mod tests {
449 use super::*;
450 use crate::HCast;
451
452 #[test]
453 fn parse_unicode_char() {
454 let hello: IResult<&str,&str> = take_while(unicode_char('"'))("Hello\n\r\t\"\\");
455 assert_eq!(hello,Ok(("\n\r\t\"\\","Hello")));
456 }
457
458 #[test]
459 fn parse_tags() {
460 let input = "dis:\"Fri 31-Jul-2020\" view:\"chart\" title:\"Line\" chartNoScroll chartLegend:\"hide\" hisStart:2020-07-31T00:00:00-04:00 New_York hisEnd:2020-08-01T00:00:00-04:00 New_York hisLimit:10000";
461
462 let res = tags::<f64>(input);
463 if let Ok((_,e)) = res {
464 let e1_ref = e.as_ref();
465 let mut buf = String::new();
466 let boxed_element = e1_ref.unwrap();
467
468 let v = boxed_element.get("dis").unwrap();
469 v.to_zinc(&mut buf).unwrap();
470 let rhs = Rc::new(HStr("Fri 31-Jul-2020".to_owned())) as HBox<f64>;
471 assert_eq!(v,&rhs)
472 } else {
473 panic!("Failed to parse separated list")
474 }
475 }
476
477 #[test]
478 fn parse_string_02() {
479 assert_eq!(string("\"He\\tllo\""),Ok(("",HStr("He\tllo".to_owned()))));
480 }
481
482 #[test]
483 fn parse_bool() {
484 assert_eq!(boolean("T").unwrap(),("",HBool(true)));
485 assert_eq!(boolean("F").unwrap(),("",HBool(false)),);
486 }
487
488 #[test]
489 fn parse_null() {
490 assert_eq!(null("N").unwrap(),("",crate::h_null::NULL));
491 }
492
493 #[test]
494 fn parse_na() {
495 assert_eq!(na("NA").unwrap(),("",crate::h_na::NA));
496 }
497
498 #[test]
499 fn parse_marker() {
500 assert_eq!(marker("M").unwrap(),("",crate::h_marker::MARKER));
501 }
502
503 #[test]
504 fn parse_remove() {
505 assert_eq!(remove("R").unwrap(),("",crate::h_remove::REMOVE));
506 }
507
508 #[test]
509 fn parse_datetime() {
510 let tz_obj = ("America/Los_Angeles".to_owned(), HOffset::Fixed(chrono::offset::FixedOffset::east(-1 * 8 * 3600)));
513 assert_eq!(datetime("2010-11-28T07:23:02.773-08:00 America/Los_Angeles").unwrap(),("",HDateTime::new(2010,11,28,7,23,2,773,tz_obj)));
514 }
515
516 #[test]
517 fn parse_coord() {
518 assert_eq!(coord("C(1.5,-9)").unwrap(),("",HCoord::new(1.5,-9f64)));
519 }
520
521 #[test]
522 fn coerce_na2hval() {
523 use crate::h_na::NA;
524 let (_,v) = literal::<f64>("NA").unwrap();
525 let lhs = v.get_na();
526 assert_eq!(lhs,Some(&NA))
527 }
528
529 macro_rules! assert_literal {
530 ( $val: literal, $get: ident, $rhs: expr ) => {
531 let v = literal::<f64>($val).unwrap();
532 let lhs = v.1.$get();
533 assert_eq!(lhs,Some(&$rhs));
534 }
535 }
536
537 #[test]
538 fn coerce_hval() {
539 use crate::h_null::NULL;
540 use crate::h_marker::MARKER;
541 use crate::h_remove::REMOVE;
542 use crate::h_bool::HBool;
543 use crate::h_na::NA;
544 use crate::h_str::HStr;
545 use crate::h_uri::HUri;
546
547 assert_literal!("N",get_null,NULL);
548 assert_literal!("M",get_marker,MARKER);
549 assert_literal!("R",get_remove,REMOVE);
550 assert_literal!("T",get_bool,HBool(true));
551 assert_literal!("F",get_bool,HBool(false));
552 assert_literal!("NA",get_na,NA);
553 assert_literal!(r#""Hello\nSmidgen\"""#,get_string,HStr("Hello\nSmidgen\"".to_owned()));
554 assert_literal!("`http://www.google.com`",get_uri,HUri::new("http://www.google.com").unwrap());
555 assert_literal!("1.5kWh",get_number,HNumber::new(1.5f64,Some(HUnit::new("kWh".to_owned()))));
556 }
557
558 #[test]
559 fn parse_literal_na() {
560 assert_eq!(literal::<f64>("NA").unwrap().1.get_na(), Some(&crate::h_na::NA));
561 }
562
563 #[test]
564 fn parse_literal_null() {
565 assert_eq!(literal::<f64>("N").unwrap().1.get_null(), Some(&crate::h_null::NULL));
566 }
567
568 #[test]
569 fn parse_literal_marker() {
570 assert_eq!(literal::<f64>("M").unwrap().1.get_marker(), Some(&crate::h_marker::MARKER));
571 }
572
573 #[test]
574 fn parse_literal_remove() {
575 assert_eq!(literal::<f64>("R").unwrap().1.get_remove(), Some(&crate::h_remove::REMOVE));
576 }
577
578 #[test]
579 fn parse_literal_bool() {
580 assert_eq!(literal::<f64>("T").unwrap().1.get_bool(), Some(&HBool(true)));
581 assert_eq!(literal::<f64>("F").unwrap().1.get_bool(), Some(&HBool(false)));
582 }
583
584 #[test]
585 fn parse_literal_string() {
586 assert_eq!(
587 literal::<f64>(r#""Hello\nWorld""#).unwrap().1.get_string(),
588 Some(&HStr("Hello\nWorld".to_owned()))
589 );
590 }
591
592 #[test]
593 fn parse_literal_uri() {
594 assert_eq!(
595 literal::<f64>("`http://example.com`").unwrap().1.get_uri(),
596 Some(&HUri::new("http://example.com").unwrap())
597 );
598 }
599
600 #[test]
601 fn parse_literal_number() {
602 assert_eq!(
603 literal::<f64>("42.5").unwrap().1.get_number(),
604 Some(&HNumber::new(42.5, None))
605 );
606 assert_eq!(
607 literal::<f64>("1.5kWh").unwrap().1.get_number(),
608 Some(&HNumber::new(1.5, Some(HUnit::new("kWh".to_owned()))))
609 );
610 }
611
612 #[test]
613 fn parse_literal_coord() {
614 assert_eq!(
615 literal::<f64>("C(12.34,-56.78)").unwrap().1.get_coord_val(),
616 Some(&HCoord::new(12.34, -56.78))
617 );
618 }
619
620 #[test]
621 fn parse_literal_datetime() {
622 let tz_obj = ("America/New_York".to_owned(), HOffset::Fixed(chrono::offset::FixedOffset::east(-5 * 3600)));
623 assert_eq!(
624 literal::<f64>("2023-03-15T12:34:56.789-05:00 America/New_York")
625 .unwrap()
626 .1
627 .get_datetime(),
628 Some(&HDateTime::new(2023, 3, 15, 12, 34, 56, 789, tz_obj))
629 );
630 }
631
632 #[test]
633 fn parse_literal_dict() {
634 let input = r#"{key1:"value1" key2:42 key3:T}"#;
635 let result = dict::<f64>(input).unwrap().1;
636 assert_eq!(result.get("key1").unwrap().get_string(), Some(&HStr("value1".to_owned())));
637 assert_eq!(result.get("key2").unwrap().get_number(), Some(&HNumber::new(42.0, None)));
638 assert_eq!(result.get("key3").unwrap().get_bool(), Some(&HBool(true)));
639 }
640
641 #[test]
642 fn parse_literal_list() {
643 let input = r#"[42,"hello" , T]"#;
644 let result = list::<f64>(input).unwrap().1;
645 assert_eq!(result[0].get_number(), Some(&HNumber::new(42.0, None)));
646 assert_eq!(result[1].get_string(), Some(&HStr("hello".to_owned())));
647 assert_eq!(result[2].get_bool(), Some(&HBool(true)));
648 }
649
650 #[test]
651 fn parse_grid_empty() {
652 let input = "ver:\"3.0\"\nempty\n";
653 let empty_grid = grid::<f64>(input).unwrap().1;
654 if let HGrid::Empty { meta } = empty_grid {
655 assert!(meta.is_none());
656 } else {
657 panic!("Expected an empty grid");
658 }
659 }
661
662 #[test]
663 fn parse_empty_grid_with_meta() {
664 let input = "ver:\"3.0\" dis:\"Example Grid\"\nempty\n";
665 let grid: HGrid<'_, f64> = grid::<f64>(input).unwrap().1;
666 match grid {
667 HGrid::Empty { meta } => {
668 assert_eq!(meta.unwrap().get("dis").unwrap().get_string(), Some(&HStr("Example Grid".to_owned())));
669 },
670 _ => panic!("Expected an empty grid with metadata"),
671 }
672 }
673
674 }
691 }
692
693 pub fn is_digits(chr: char) -> bool {
694 is_digit(chr as u8) && chr=='_'
695 }
696
697 pub fn digits(input: &str) -> IResult<&str, (&str, &str)> {
698 use nom::branch::permutation;
699
700 permutation((digit1, take_while(is_digits))).parse(input)
701 }
702
703 pub fn exp(input: &str) -> IResult<&str, (bool, (&str, &str))> {
704 use nom::sequence::pair;
705
706 preceded(alt((tag("e"),tag("E"))),pair(
707 opt(alt((tag("+"),tag("-"))))
708 .map(|sign| if let Some(c) = sign { c!="-" } else { true } ),
709 digits
710 )).parse(input)
711 }
712
713 pub fn is_unit(c: char) -> bool {
714 c.is_ascii_alphabetic() || c>=(128 as char) || match c {
715 '%' | '_' | '/' | '$' => true,
716 _ => false
717 }
718 }
719
720 pub fn unit(input: &str) -> IResult<&str, HUnit> {
721 let (input,unit_str) = take_while1(is_unit)(input)?;
722 Ok((input,HUnit::new(unit_str.to_owned())))
723 }
724
725 pub fn number<T: Float + Display + FromStr>(input: &str) -> IResult<&str, HNumber<T>> {
726 use std::slice;
727
728 let start = input;
729 let (input, is_positive) = map(opt(tag("-")),|d| d.is_none()).parse(start)?;
730 let (input, integer) = digits(input)?;
731 let (input, decimals) = opt(preceded(tag("."),digits)).parse(input)?;
732 let (input, exponent) = opt(exp).parse(input)?;
733 let number_slice = unsafe {
734 slice::from_raw_parts(start.as_ptr(),
735 (input.as_ptr() as usize) - (start.as_ptr() as usize))
736 };
737
738 let number_ty: T;
739
740 if let Ok(number_str) = std::str::from_utf8(number_slice) {
741 let number_res = number_str.parse::<T>();
742 if let Ok(number_ty_ok) = number_res {
743 number_ty = number_ty_ok;
744 } else {
745 return Err(nom::Err::Error(nom::error::Error{ input: number_str, code: nom::error::ErrorKind::Float }));
746 }
747 } else {
748 return Err(nom::Err::Error(nom::error::Error{
750 input: unsafe { std::str::from_utf8_unchecked(number_slice) },
751 code: nom::error::ErrorKind::Float }));
752 }
753
754 let (input, unit_opt) = opt(unit).parse(input)?;
755
756 Ok((input, HNumber::new(number_ty, unit_opt)))
757 }
758
759 pub fn date(input: &str) -> IResult<&str, HDate> {
760 let (input, year) = map(take(4usize), |s| i32::from_str_radix(s, 10) ).parse(input)?;
761 let year = year.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
762
763 let (input, _) = tag("-")(input)?;
764 let (input, month) = map(take(2usize), |s| u32::from_str_radix(s, 10) ).parse(input)?;
765 let month = month.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
766
767 let (input, _) = tag("-")(input)?;
768 let (input, day) = map(take(2usize), |s| u32::from_str_radix(s, 10) ).parse(input)?;
769 let day = day.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
770
771 Ok((input,HDate::new(year, month, day)))
772 }
773
774 pub fn time(input: &str) -> IResult<&str, HTime> {
775 let (input, hour) = map(take(2usize), |s| {u32::from_str_radix(s, 10)}).parse(input)?;
776 let hour = hour.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
777
778 let (input, _) = tag(":")(input)?;
779 let (input, min) = map(take(2usize), |s| {u32::from_str_radix(s, 10)}).parse(input)?;
780 let min = min.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
781
782 let (input, _) = tag(":")(input)?;
783 let (input, sec) = map(take(2usize), |s| {u32::from_str_radix(s, 10)}).parse(input)?;
784 let sec = sec.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
785
786 let (input, _) = tag(".")(input)?;
787 let (input, nano) = map(opt(digit1), |s| {if let Some(s) = s {u32::from_str_radix(s, 10)} else {Ok(0)}}).parse(input)?;
788 let nano = nano.or(Err(nom::Err::Error(Error{ input: input, code: ErrorKind::Digit })))?;
789
790 Ok((input, HTime::new(hour, min, sec, nano)))
791 }
792
793 #[cfg(test)]
794 mod tests {
795 use super::*;
796
797 #[test]
798 fn parse_id() {
799 assert_eq!(id("asdasd1223_").unwrap(),("","asdasd1223_"));
800 }
801
802 #[test]
803 fn parse_date() {
804 assert_eq!(date("2010-11-28").unwrap(),("",HDate::new(2010,11,28)));
805 }
806
807 #[test]
808 fn parse_time() {
809 assert_eq!(time("07:23:02.773").unwrap(),("",HTime::new(07,23,02,773)));
810 }
811 }
812}