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