vihaco_parser_core/
lib.rs1pub mod container;
5pub mod impls;
6
7pub use impls::ident;
8
9use chumsky::error::Simple;
10use chumsky::extra;
11
12pub trait Parse<'src>: Sized {
17 fn parser() -> impl chumsky::Parser<'src, &'src str, Self, extra::Err<Simple<'src, char>>>;
18}
19
20#[cfg(test)]
21mod tests {
22 use super::*;
23 use chumsky::Parser;
24
25 fn parses<'src, T: Parse<'src>>(input: &'src str) -> T {
26 T::parser().parse(input).into_result().unwrap()
27 }
28
29 #[test]
30 fn i64_basic() {
31 assert_eq!(parses::<i64>("42"), 42);
32 }
33 #[test]
34 fn i32_basic() {
35 assert_eq!(parses::<i32>("7"), 7);
36 }
37 #[test]
38 fn u64_basic() {
39 assert_eq!(parses::<u64>("100"), 100);
40 }
41 #[test]
42 fn u32_basic() {
43 assert_eq!(parses::<u32>("0"), 0);
44 }
45 #[test]
46 fn usize_basic() {
47 assert_eq!(parses::<usize>("9"), 9);
48 }
49 #[test]
50 fn f64_int() {
51 assert_eq!(parses::<f64>("3"), 3.0);
52 }
53 #[test]
54 #[allow(clippy::approx_constant)]
55 fn f64_float() {
56 assert_eq!(parses::<f64>("3.14"), 3.14);
57 }
58 #[test]
59 fn f32_float() {
60 assert!((parses::<f32>("1.5") - 1.5f32).abs() < 1e-6);
61 }
62 #[test]
63 fn i64_negative() {
64 assert_eq!(parses::<i64>("-42"), -42);
65 }
66 #[test]
67 fn i32_negative() {
68 assert_eq!(parses::<i32>("-7"), -7);
69 }
70 #[test]
71 fn f64_negative() {
72 assert_eq!(parses::<f64>("-0.5"), -0.5);
73 }
74 #[test]
75 fn f64_negative_scientific() {
76 assert_eq!(parses::<f64>("-1.0e-3"), -1.0e-3);
77 }
78 #[test]
79 fn u64_rejects_negative() {
80 assert!(u64::parser().parse("-1").into_result().is_err());
81 }
82 #[test]
83 fn bool_true() {
84 assert!(parses::<bool>("true"));
85 }
86 #[test]
87 fn bool_false() {
88 assert!(!parses::<bool>("false"));
89 }
90 #[test]
91 fn string_word() {
92 assert_eq!(parses::<String>("hello"), "hello");
93 }
94
95 #[test]
96 fn string_stops_at_ws() {
97 let result = String::parser().parse("hello world").into_result();
101 assert!(result.is_err());
102 }
103
104 #[test]
105 fn ident_operand_with_colons() {
106 assert_eq!(
107 ident().parse("AOD0:T1:A").into_result().unwrap(),
108 "AOD0:T1:A"
109 );
110 }
111
112 #[test]
113 fn ident_stops_at_comma() {
114 let result = ident()
115 .then_ignore(chumsky::primitive::just(','))
116 .parse("foo,")
117 .into_result();
118 assert_eq!(result.unwrap(), "foo");
119 }
120
121 #[test]
122 fn ident_allows_dots() {
123 assert_eq!(ident().parse("a.b.c").into_result().unwrap(), "a.b.c");
124 }
125
126 #[test]
127 fn ident_digi_target() {
128 assert_eq!(ident().parse("DIGI:0").into_result().unwrap(), "DIGI:0");
129 }
130
131 #[test]
132 fn ident_rejects_empty() {
133 assert!(ident().parse("").into_result().is_err());
134 }
135
136 #[test]
137 fn ident_rejects_leading_ws() {
138 assert!(ident().parse(" hello").into_result().is_err());
139 }
140
141 #[test]
142 fn ident_stops_at_open_paren() {
143 let result = ident()
144 .then_ignore(chumsky::primitive::just('('))
145 .parse("foo(")
146 .into_result();
147 assert_eq!(result.unwrap(), "foo");
148 }
149
150 #[test]
151 fn ident_stops_at_brace() {
152 let result = ident()
153 .then_ignore(chumsky::primitive::just('{'))
154 .parse("device{")
155 .into_result();
156 assert_eq!(result.unwrap(), "device");
157 }
158}