logic_eval/parse/
text.rs

1use super::{
2    CloseParenToken, CommaToken, DotToken, HornToken, Ident, NegationToken, OpenParenToken, Parse,
3    ParseBuffer, VAR_PREFIX,
4    repr::{Clause, ClauseDataset, Expr, Term},
5};
6use crate::{Error, Result, Str};
7use std::{borrow, fmt, ops};
8
9impl Parse for ClauseDataset<Name> {
10    fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
11        let clauses = buf.parse()?;
12        Ok(Self(clauses))
13    }
14}
15
16impl Parse for Vec<Clause<Name>> {
17    fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
18        let mut v = Vec::new();
19        while let Some((clause, moved_buf)) = buf.peek_parse::<Clause<Name>>() {
20            v.push(clause);
21            *buf = moved_buf;
22        }
23        Ok(v)
24    }
25}
26
27impl Parse for Clause<Name> {
28    fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
29        let head = buf.parse::<Term<Name>>()?;
30
31        let body = if let Some((_, mut body_buf)) = buf.peek_parse::<HornToken>() {
32            let dot = body_buf
33                .cur_text()
34                .find('.')
35                .ok_or(Error::from("clause must end with `.`"))?;
36            body_buf.end = body_buf.start + dot;
37            let body = body_buf.parse::<Expr<Name>>()?;
38
39            buf.start = body_buf.end + 1; // Next to the dot
40            Some(body)
41        } else {
42            let _ = buf.parse::<DotToken>()?;
43            None
44        };
45
46        Ok(Self { head, body })
47    }
48}
49
50// Precedence: `Paren ()` -> `Not \+` -> `And ,` -> `Or ;`
51impl Expr<Name> {
52    fn parse_or(buf: ParseBuffer<'_>) -> Result<Self> {
53        Self::parse_by_delimiter(buf, ';', Self::parse_and, Self::Or)
54    }
55
56    fn parse_and(buf: ParseBuffer<'_>) -> Result<Self> {
57        Self::parse_by_delimiter(buf, ',', Self::parse_not, Self::And)
58    }
59
60    fn parse_not(buf: ParseBuffer<'_>) -> Result<Self> {
61        if let Some((_, moved_buf)) = buf.peek_parse::<NegationToken>() {
62            let inner = Self::parse_not(moved_buf)?;
63            Ok(Self::Not(Box::new(inner)))
64        } else {
65            Self::parse_paren(buf)
66        }
67    }
68
69    fn parse_paren(buf: ParseBuffer<'_>) -> Result<Self> {
70        if let Some((_, mut moved_buf)) = buf.peek_parse::<OpenParenToken>() {
71            let r = moved_buf.cur_text().rfind(')').unwrap();
72            moved_buf.end = moved_buf.start + r;
73            moved_buf.parse::<Self>()
74        } else {
75            Self::parse_term(buf)
76        }
77    }
78
79    fn parse_term(mut buf: ParseBuffer<'_>) -> Result<Self> {
80        if buf.cur_text().chars().all(|c: char| c.is_whitespace()) {
81            Err("expected a non-empty term expression".into())
82        } else {
83            buf.parse::<Term<Name>>().map(Self::Term)
84        }
85    }
86
87    fn parse_by_delimiter(
88        buf: ParseBuffer<'_>,
89        del: char,
90        parse_partial: fn(ParseBuffer<'_>) -> Result<Self>,
91        wrap_vec: fn(Vec<Self>) -> Self,
92    ) -> Result<Self> {
93        let mut v = Vec::new();
94        let (mut l, mut r) = (buf.start, buf.start);
95        let mut open = 0;
96
97        for c in buf.cur_text().chars() {
98            if c == del && open == 0 {
99                let partial = parse_partial(ParseBuffer {
100                    text: buf.text,
101                    start: l,
102                    end: r,
103                })?;
104                v.push(partial);
105                l = r + c.len_utf8();
106            } else if c == '(' {
107                open += 1;
108            } else if c == ')' {
109                open -= 1;
110            }
111            r += c.len_utf8();
112        }
113
114        let last = parse_partial(ParseBuffer {
115            text: buf.text,
116            start: l,
117            end: r,
118        })?;
119
120        if !v.is_empty() {
121            v.push(last);
122            Ok(wrap_vec(v))
123        } else {
124            Ok(last)
125        }
126    }
127}
128
129impl Parse for Expr<Name> {
130    fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
131        // The buffer range is not being moved by this call.
132        Self::parse_or(*buf)
133    }
134}
135
136impl Term<Name> {
137    fn parse_args(buf: &mut ParseBuffer<'_>) -> Result<Box<[Term<Name>]>> {
138        let mut open = 0;
139        while let Some((_, moved_buf)) = buf.peek_parse::<OpenParenToken>() {
140            *buf = moved_buf;
141            open += 1;
142        }
143
144        if open == 0 {
145            return Ok([].into());
146        }
147
148        let mut args = Vec::new();
149        let mut well_seperated = true;
150        loop {
151            if let Some((_, moved_buf)) = buf.peek_parse::<CloseParenToken>() {
152                *buf = moved_buf;
153                open -= 1;
154                break;
155            }
156
157            if !well_seperated {
158                return Err(format!("expected `,` from {}", buf.cur_text()).into());
159            }
160
161            let arg = buf.parse::<Term<Name>>()?;
162            args.push(arg);
163
164            let comma = buf.parse::<CommaToken>();
165            well_seperated = comma.is_ok();
166        }
167
168        for _ in 0..open {
169            buf.parse::<CloseParenToken>()?;
170        }
171
172        Ok(args.into())
173    }
174}
175
176impl Parse for Term<Name> {
177    fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
178        let functor = Name::parse(buf)?;
179        let args = Self::parse_args(buf)?;
180        Ok(Self { functor, args })
181    }
182}
183
184#[derive(Default, Clone, PartialEq, Eq, Hash)]
185pub struct Name(Str /* Non-empty string */);
186
187impl Name {
188    pub(crate) fn is_variable(&self) -> bool {
189        let first = self.0.chars().next().unwrap();
190        first == VAR_PREFIX // btw, prolog style is `is_uppercase() or '_'`
191    }
192}
193
194impl Parse for Name {
195    fn parse(buf: &mut ParseBuffer<'_>) -> Result<Self> {
196        let ident = buf.parse::<Ident>()?;
197        let s: Str = ident.to_text(buf.text).into();
198        debug_assert!(!s.is_empty());
199        Ok(Self(s))
200    }
201}
202
203impl<T: AsRef<str> + ?Sized> PartialEq<T> for Name {
204    fn eq(&self, other: &T) -> bool {
205        (**self).eq(other.as_ref())
206    }
207}
208
209impl fmt::Display for Name {
210    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211        (**self).fmt(f)
212    }
213}
214
215impl fmt::Debug for Name {
216    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217        (**self).fmt(f)
218    }
219}
220
221impl ops::Deref for Name {
222    type Target = str;
223
224    fn deref(&self) -> &Self::Target {
225        &self.0
226    }
227}
228
229impl borrow::Borrow<str> for Name {
230    fn borrow(&self) -> &str {
231        self
232    }
233}
234
235impl<T: Into<Str>> From<T> for Name {
236    fn from(value: T) -> Self {
237        Self(value.into())
238    }
239}