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