logic_eval/parse/
inner.rs1use crate::{Error, Result};
2
3pub fn parse_str<T: Parse>(text: &str) -> Result<T> {
4 let mut buf = ParseBuffer::new(text);
5 T::parse(&mut buf)
6}
7
8pub trait Parse: Sized {
9 fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self>;
10}
11
12#[derive(Clone, Copy)]
13pub struct ParseBuffer<'a> {
14 pub(crate) text: &'a str,
15 pub(crate) start: usize,
17 pub(crate) end: usize,
19}
20
21impl<'a> ParseBuffer<'a> {
22 pub const fn new(text: &'a str) -> Self {
23 Self {
24 text,
25 start: 0,
26 end: text.len(),
27 }
28 }
29
30 pub(crate) fn cur_text(&self) -> &str {
31 &self.text[self.start..self.end]
32 }
33
34 pub(crate) fn parse<T: Parse>(&mut self) -> Result<T> {
35 T::parse(self)
36 }
37
38 pub(crate) fn peek_parse<T: Parse>(&self) -> Option<(T, Self)> {
39 let mut peek = *self;
40 T::parse(&mut peek).ok().map(|t| (t, peek))
43 }
44}
45
46pub(crate) struct Ident(Location);
47
48impl Ident {
49 pub(crate) fn to_text<'a>(&self, whole_text: &'a str) -> &'a str {
50 &whole_text[self.0.left..self.0.right]
51 }
52}
53
54impl Parse for Ident {
55 fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
56 fn is_allowed_first(c: char) -> bool {
57 c.is_alphabetic() || !(c.is_whitespace() || RESERVED.contains(&c))
58 }
59
60 fn is_allowed_rest(c: char) -> bool {
61 c.is_alphanumeric() || !(c.is_whitespace() || RESERVED.contains(&c) || c == VAR_PREFIX)
62 }
63
64 let s = buf.cur_text();
65
66 let Some(l) = s.find(|c: char| !c.is_whitespace()) else {
67 return Err("expected an ident, but input is empty".into());
68 };
69
70 let mut r = l;
71
72 let first = s[l..].chars().next().unwrap();
73 if is_allowed_first(first) {
74 r += first.len_utf8();
75 } else {
76 return Err(format!("expected an ident from {}", s).into());
77 }
78
79 for rest in s[l..].chars().skip(1) {
80 if is_allowed_rest(rest) {
81 r += rest.len_utf8();
82 } else {
83 break;
84 }
85 }
86
87 let loc = Location {
88 left: buf.start + l,
89 right: buf.start + r,
90 };
91 buf.start += r;
92 Ok(Ident(loc))
93 }
94}
95
96macro_rules! impl_parse_for_string {
97 ($str:literal, $ty:ident) => {
98 impl Parse for $ty {
99 fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
100 let s = buf.cur_text();
101
102 let Some(l) = s.find(|c: char| !c.is_whitespace()) else {
103 return Err(format!("expected `{}` from `{}`", $str, s).into());
104 };
105 let r = l + $str.len();
106
107 let substr = s
108 .get(l..r)
109 .ok_or(Error::from(format!("expected `{}` from `{s}`", $str)))?;
110
111 if substr == $str {
112 let loc = Location {
113 left: buf.start + l,
114 right: buf.start + r,
115 };
116 buf.start += r;
117 Ok($ty { _loc: loc })
118 } else {
119 Err(format!("expected `{}` from `{s}`", $str).into())
120 }
121 }
122 }
123 };
124}
125
126pub const VAR_PREFIX: char = '$';
127
128pub(crate) const RESERVED: &[char] = &[
129 ',', ';', '.', ':', '-', '\\', '+', '(', ')', '[', ']', '|', '+', '-', '*', '/', ];
146
147pub(crate) struct CommaToken {
148 pub(crate) _loc: Location,
149}
150impl_parse_for_string!(",", CommaToken);
151
152pub(crate) struct SemiToken {
153 pub(crate) _loc: Location,
154}
155impl_parse_for_string!(";", SemiToken);
156
157pub(crate) struct DotToken {
158 pub(crate) _loc: Location,
159}
160impl_parse_for_string!(".", DotToken);
161
162pub(crate) struct HornToken {
163 pub(crate) _loc: Location,
164}
165impl_parse_for_string!(":-", HornToken);
166
167pub(crate) struct NegationToken {
168 pub(crate) _loc: Location,
169}
170impl_parse_for_string!("\\+", NegationToken);
171
172pub(crate) struct OpenParenToken {
173 pub(crate) _loc: Location,
174}
175impl_parse_for_string!("(", OpenParenToken);
176
177pub(crate) struct CloseParenToken {
178 pub(crate) _loc: Location,
179}
180impl_parse_for_string!(")", CloseParenToken);
181
182#[derive(Debug, Clone, Copy)]
183pub(crate) struct Location {
184 left: usize,
186 right: usize,
188}