Skip to main content

ogma_libs/
clause.rs

1//! Clause parsing utilities
2
3use core::fmt;
4
5/// A token parsed from a clause string
6#[derive(Clone, Debug, Eq, PartialEq)]
7pub enum Token<'a> {
8    /// A static one word token
9    Static(&'a str),
10    /// A query variable
11    QueryVar(&'a str),
12    /// A data variable
13    DataVar(&'a str),
14}
15
16/// An error which may occur during parsing
17#[derive(Debug)]
18pub enum ParseError {
19    /// Variable name is invalid
20    InvalidVariableName,
21    /// Prefix is invalid. May only be `q` or `d`
22    InvalidVariablePrefix,
23}
24
25impl<'a> Token<'a> {
26    /// Is the token a static token
27    pub fn is_static(&self) -> bool {
28        match self {
29            Self::Static(_) => true,
30            _ => false,
31        }
32    }
33
34    /// Is the token a query variable
35    pub fn is_query_var(&self) -> bool {
36        match self {
37            Self::QueryVar(_) => true,
38            _ => false,
39        }
40    }
41
42    /// Is the token a data variable
43    pub fn is_data_var(&self) -> bool {
44        match self {
45            Self::DataVar(_) => true,
46            _ => false,
47        }
48    }
49}
50
51fn parse_next(src: &str) -> Result<Option<(Token, &str)>, ParseError> {
52    let (tok, rest) = if let Ok((_, tok, rest)) = nl_parser::parse_token(src) {
53        (tok, rest)
54    } else {
55        return Ok(None);
56    };
57    if tok.contains('`') {
58        if !tok.ends_with('`') {
59            return Err(ParseError::InvalidVariableName);
60        } else if tok.starts_with("q`") {
61            // TODO check variable name format?
62            let var = &tok[2..tok.len() - 1];
63            return Ok(Some((Token::QueryVar(var), rest)));
64        } else if tok.starts_with("d`") {
65            // TODO check variable name format?
66            let var = &tok[2..tok.len() - 1];
67            return Ok(Some((Token::DataVar(var), rest)));
68        } else {
69            return Err(ParseError::InvalidVariablePrefix);
70        }
71    } else {
72        return Ok(Some((Token::Static(tok), rest)));
73    }
74}
75
76/// A parser for a clause. Iterates over tokens
77pub struct Parser<'a> {
78    src: &'a str,
79}
80
81impl<'a> Parser<'a> {
82    /// Create a new Parser for a string
83    #[inline]
84    pub fn new(src: &'a str) -> Self {
85        Self { src }
86    }
87}
88
89impl<'a> Iterator for Parser<'a> {
90    type Item = Result<Token<'a>, ParseError>;
91    fn next(&mut self) -> Option<Self::Item> {
92        match parse_next(self.src) {
93            Ok(Some((tok, rest))) => {
94                self.src = rest;
95                Some(Ok(tok))
96            }
97            Ok(None) => None,
98            Err(err) => Some(Err(err)),
99        }
100    }
101}
102
103impl fmt::Display for ParseError {
104    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
105        match self {
106            ParseError::InvalidVariablePrefix => f.write_str("invalid variable prefix"),
107            ParseError::InvalidVariableName => f.write_str("invalid variable name"),
108        }
109    }
110}
111
112/// Parse a string into tokens
113#[inline]
114pub fn parse<'a>(string: &'a str) -> impl Iterator<Item = Result<Token<'a>, ParseError>> {
115    Parser::new(string)
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121    use alloc::vec;
122    use alloc::vec::Vec;
123
124    #[test]
125    fn test_parse_next() -> Result<(), ParseError> {
126        assert_eq!(parse_next("the")?, Some((Token::Static("the"), "")));
127        assert_eq!(
128            parse_next("the token")?,
129            Some((Token::Static("the"), "token"))
130        );
131        assert_eq!(
132            parse_next("q`variable`")?,
133            Some((Token::QueryVar("variable"), ""))
134        );
135        assert_eq!(
136            parse_next("q`variable` token")?,
137            Some((Token::QueryVar("variable"), "token"))
138        );
139        assert_eq!(
140            parse_next("d`variable`")?,
141            Some((Token::DataVar("variable"), ""))
142        );
143        assert_eq!(
144            parse_next("d`variable` token")?,
145            Some((Token::DataVar("variable"), "token"))
146        );
147        Ok(())
148    }
149
150    #[test]
151    fn test_parse() -> Result<(), ParseError> {
152        assert_eq!(
153            parse("the token string").collect::<Result<Vec<Token>, ParseError>>()?,
154            vec![
155                Token::Static("the"),
156                Token::Static("token"),
157                Token::Static("string")
158            ]
159        );
160        assert_eq!(
161            parse("the q`variable` token").collect::<Result<Vec<Token>, ParseError>>()?,
162            vec![
163                Token::Static("the"),
164                Token::QueryVar("variable"),
165                Token::Static("token")
166            ]
167        );
168        assert_eq!(
169            parse("the d`variable` token").collect::<Result<Vec<Token>, ParseError>>()?,
170            vec![
171                Token::Static("the"),
172                Token::DataVar("variable"),
173                Token::Static("token")
174            ]
175        );
176        Ok(())
177    }
178}