Переглянути джерело

Add basic structure for command parsing

Mark Rousskov 6 роки тому
батько
коміт
8cccedd7d8
3 змінених файлів з 21 додано та 239 видалено
  1. 19 41
      parser/src/command.rs
  2. 0 198
      parser/src/label.rs
  3. 2 0
      parser/src/lib.rs

+ 19 - 41
parser/src/command.rs

@@ -1,49 +1,27 @@
 use crate::token::{Error, Token, Tokenizer};
 
-/// Returns the start of the invocation, or None if at the end of the stream
-pub fn eat_until_invocation<'a>(
-    tok: &mut Tokenizer<'a>,
-    bot: &str,
-) -> Result<Option<usize>, Error<'a>> {
-    let command = format!("@{}", bot);
-    while let Some(token) = tok.peek_token()? {
-        match token {
-            Token::Word(word) if word == command => {
-                // eat invocation of bot
-                let pos = tok.position();
-                tok.next_token().unwrap();
-                return Ok(Some(pos));
-            }
-            // unwrap is safe because we've successfully peeked above
-            _ => {
-                tok.next_token().unwrap();
-            }
-        }
-    }
-    Ok(None)
+pub fn find_commmand_start(input: &str, bot: &str) -> Option<usize> {
+    input.find(&format!("@{}", bot))
 }
 
-#[test]
-fn cs_1() {
-    let input = "testing @bot command";
-    let mut toks = Tokenizer::new(input);
-    assert_eq!(toks.peek_token().unwrap(), Some(Token::Word("testing")));
-    assert_eq!(eat_until_invocation(&mut toks, "bot").unwrap(), Some(7));
-    assert_eq!(toks.peek_token().unwrap(), Some(Token::Word("command")));
+#[derive(Debug)]
+pub enum Command {
+    Label(label::LabelCommand),
 }
 
-#[test]
-fn cs_2() {
-    let input = "@bot command";
-    let mut toks = Tokenizer::new(input);
-    assert_eq!(toks.peek_token().unwrap(), Some(Token::Word("@bot")));
-    assert_eq!(eat_until_invocation(&mut toks, "bot").unwrap(), Some(0));
-    assert_eq!(toks.peek_token().unwrap(), Some(Token::Word("command")));
-}
+pub fn parse_command<'a>(input: &'a str, bot: &str) -> Result<Option<Command>, Error<'a>> {
+    let start = match find_commmand_start(input, bot) {
+        Some(pos) => pos,
+        None => return Ok(None),
+    };
+    let input = &input[start..];
+    let mut tok = Tokenizer::new(input);
+    assert_eq!(
+        tok.next_token().unwrap(),
+        Some(Token::Word(&format!("@{}", bot)))
+    );
+
+    let cmd = Command::Label;
 
-#[test]
-fn cs_3() {
-    let input = "no command";
-    let mut toks = Tokenizer::new(input);
-    assert_eq!(eat_until_invocation(&mut toks, "bot").unwrap(), None);
+    Ok(Some(cmd))
 }

+ 0 - 198
parser/src/label.rs

@@ -1,198 +0,0 @@
-//! The labels command parser.
-//!
-//! This can parse arbitrary input, giving the list of labels added/removed.
-//!
-//! The grammar is as follows:
-//!
-//! ```text
-//! Command: `labels: <label-list>.`
-//!
-//! <label-list>:
-//!  - <label-delta>
-//!  - <label-delta> and <label-list>
-//!  - <label-delta>, <label-list>
-//!  - <label-delta>, and <label-list>
-//!
-//! <label-delta>:
-//!  - +<label>
-//!  - -<label>
-//!  this can start with a + or -, but then the only supported way of adding it
-//!  is with the previous two variants of this (i.e., ++label and -+label).
-//!  - <label>
-//!
-//! <label>: \S+
-//! ```
-
-#[derive(Debug, PartialEq, Eq)]
-pub enum LabelDelta<'a> {
-    Add(Label<'a>),
-    Remove(Label<'a>),
-}
-
-#[derive(Debug, PartialEq, Eq, Copy, Clone)]
-pub struct Label<'a>(&'a str);
-
-impl<'a> Label<'a> {
-    fn parse(input: &'a str) -> Result<Label<'a>, ParseError<'a>> {
-        if input.is_empty() {
-            Err(ParseError::EmptyLabel)
-        } else {
-            Ok(Label(input))
-        }
-    }
-
-    pub fn as_str(&self) -> &'a str {
-        self.0
-    }
-}
-
-impl<'a> std::ops::Deref for Label<'a> {
-    type Target = str;
-    fn deref(&self) -> &str {
-        self.0
-    }
-}
-
-impl<'a> LabelDelta<'a> {
-    fn parse(input: &mut TokenStream<'a>) -> Result<LabelDelta<'a>, ParseError<'a>> {
-        let delta = match input.current() {
-            Some(Token::Word(delta)) => {
-                input.advance()?;
-                delta
-            }
-            cur => {
-                return Err(ParseError::Unexpected {
-                    found: cur,
-                    expected: "label delta",
-                });
-            }
-        };
-        if delta.starts_with('+') {
-            Ok(LabelDelta::Add(Label::parse(&delta[1..])?))
-        } else if delta.starts_with('-') {
-            Ok(LabelDelta::Remove(Label(&delta[1..])))
-        } else {
-            Ok(LabelDelta::Add(Label::parse(delta)?))
-        }
-    }
-}
-
-#[derive(PartialEq, Eq, Debug)]
-pub enum ParseError<'a> {
-    EmptyLabel,
-    UnexpectedEnd,
-    Unexpected {
-        found: Option<Token<'a>>,
-        expected: &'static str,
-    },
-}
-
-fn parse_command<'a>(input: &mut TokenStream<'a>) -> Result<Vec<LabelDelta<'a>>, ParseError<'a>> {
-    input.eat(Token::Labels, "labels command start")?;
-
-    let mut deltas = Vec::new();
-
-    loop {
-        let delta = LabelDelta::parse(input)?;
-        deltas.push(delta);
-
-        // optional `, and` separator
-        let _ = input.eat(Token::Comma, "");
-        let _ = input.eat(Token::And, "");
-
-        if let Some(Token::Dot) = input.current() {
-            input.advance()?;
-            break;
-        }
-    }
-
-    Ok(deltas)
-}
-
-pub fn parse<'a>(input: &'a str) -> Result<Vec<LabelDelta<'a>>, ParseError<'a>> {
-    let mut toks = TokenStream::new(input);
-    let mut labels = Vec::new();
-    while !toks.at_end() {
-        if toks.current() == Some(Token::Labels) {
-            match parse_command(&mut toks) {
-                Ok(deltas) => {
-                    labels.extend(deltas);
-                }
-                Err(err) => return Err(err),
-            }
-        } else {
-            // not the labels command
-            toks.advance()?;
-        }
-    }
-    Ok(labels)
-}
-
-#[test]
-fn parse_simple() {
-    assert_eq!(
-        parse("labels: +T-compiler -T-lang bug."),
-        Ok(vec![
-            LabelDelta::Add(Label("T-compiler")),
-            LabelDelta::Remove(Label("T-lang")),
-            LabelDelta::Add(Label("bug")),
-        ])
-    );
-}
-
-#[test]
-fn parse_empty() {
-    assert_eq!(
-        TokenStream::new("labels:+,-,,,."),
-        [
-            Token::Labels,
-            Token::Word("+"),
-            Token::Comma,
-            Token::Word("-"),
-            Token::Comma,
-            Token::Word(""),
-            Token::Comma,
-            Token::Word(""),
-            Token::Comma,
-            Token::Dot
-        ]
-    );
-    assert_eq!(parse("labels:+,,."), Err(ParseError::EmptyLabel));
-}
-
-#[test]
-fn parse_no_label_paragraph() {
-    assert_eq!(
-        parse("Labels do in fact exist but this is not a label paragraph."),
-        Ok(vec![]),
-    );
-}
-
-#[test]
-fn parse_multi_label() {
-    let para = "
-        labels: T-compiler, T-core. However, we probably *don't* want the
-        labels: -T-lang and -T-libs.
-        This nolabels:bar foo should not be a label statement.
-    ";
-    assert_eq!(
-        parse(para),
-        Ok(vec![
-            LabelDelta::Add(Label("T-compiler")),
-            LabelDelta::Add(Label("T-core")),
-            LabelDelta::Remove(Label("T-lang")),
-            LabelDelta::Remove(Label("T-libs")),
-        ])
-    );
-}
-
-#[test]
-fn parse_no_end() {
-    assert_eq!(
-        parse("labels: +T-compiler -T-lang bug"),
-        Err(ParseError::Unexpected {
-            found: None,
-            expected: "label delta"
-        }),
-    );
-}

+ 2 - 0
parser/src/lib.rs

@@ -1,3 +1,5 @@
 pub mod code_block;
 pub mod command;
+//pub mod label;
+pub mod label {}
 pub mod token;