codebiber/parse_file/parser/
line.rs

1use super::*;
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4pub enum Line<'a>
5{
6  CODE(&'a str),
7  BEGIN_CODEGEN{marker: Marker<'a>, identifier: &'a str,},
8  END_CODEGEN{marker: Marker<'a>, checksum: &'a str,},
9}
10
11pub type Result<T=(), E=Syntax_Error> = std::result::Result<T, E>;
12
13pub fn parse<'a>(line: &'a str) -> Result<Line<'a>>
14{
15  const BEGIN : &'static str = "<< codegen ";
16  const END : &'static str = "<< /codegen";
17  const TAG_END : &'static str = ">>";
18
19  debug_assert_eq!(line.contains('\n'), false);
20
21  if let Some(index) = line.find(BEGIN)
22  {
23    let mut code = &line[..index];
24    let indentation = Indentation::parse(&mut code);
25    let before_marker = code;
26
27    code = &line[index+BEGIN.len() .. ];
28    let identifier = parse_identifier(&mut code)?;
29    skip_while(&mut code, |x| x==' ');
30    expect(&mut code, TAG_END)?;
31    let after_marker = code;
32
33    let marker = Marker{indentation, before_marker, after_marker};
34    return Ok(Line::BEGIN_CODEGEN{marker, identifier});
35  }else if let Some(index) = line.find(END)
36  {
37    let mut code = &line[..index];
38    let indentation = Indentation::parse(&mut code);
39    let before_marker = code;
40
41    code = &line[index+END.len() .. ];
42    skip_while(&mut code, |x| x==' ');
43    let checksum = eat_while(&mut code, |x| char::is_ascii_hexdigit(&x));
44    skip_while(&mut code, |x| x==' ');
45    expect(&mut code, TAG_END)?;
46    let after_marker = code;
47
48    let marker = Marker{indentation, before_marker, after_marker};
49    return Ok(Line::END_CODEGEN{marker, checksum});
50  }else
51  {
52    return Ok(Line::CODE(line));
53  }
54
55}
56
57fn parse_identifier<'a>(code: &mut &'a str) -> Result<&'a str>
58{
59  let index = code.rfind(">>").unwrap_or(code.len());
60  let ident = &(*code)[..index].trim_end();
61  let rest = &(*code)[ident.len()..];
62  *code = rest;
63  return Ok(ident);
64}
65
66fn expect(code: &mut &str, snippet: &'static str) -> Result<>
67{
68  if !code.starts_with(snippet)
69  {
70    return Err(Syntax_Error::EXPECTED_SNIPPET(snippet))
71  }
72  skip(code, snippet.len());
73  return Ok(())
74}
75
76fn skip(code: &mut &str, len: usize)
77{
78  let _ = eat(code, len);
79}
80
81fn eat<'a>(code: &mut &'a str, len: usize) -> &'a str
82{
83  assert!(code.len() >= len);
84  let x = &code[.. len];
85  *code = &code[len..];
86  return x;
87}
88
89fn skip_while<P: Fn(char)->bool>(code: &mut &str, pred: P)
90{
91  let _ = eat_while(code, pred);
92}
93
94fn eat_while<'a, P: Fn(char)->bool>(code: &mut &'a str, pred: P) -> &'a str
95{
96  let len = code.find(|x| !pred(x)).unwrap_or(code.len());
97  return eat(code, len);
98}
99
100#[cfg(test)]
101mod test
102{
103  use super::*;
104  #[test]
105  fn identifier()
106  {
107    fn parse(mut code: &str) -> Result<(&str, &str), Syntax_Error>
108    {
109      let ident = parse_identifier(&mut code)?;
110      return Ok((ident, code));
111    }
112
113    assert_eq!(parse(""), Ok(("", "")));
114    assert_eq!(parse(" "), Ok(("", " ")));
115    assert_eq!(parse("! "), Ok(("!", " ")));
116    assert_eq!(parse("x"), Ok(("x", "")));
117    assert_eq!(parse("x "), Ok(("x", " ")));
118    assert_eq!(parse("x, y"), Ok(("x, y", "")));
119    assert_eq!(parse("x >>"), Ok(("x", " >>")));
120    assert_eq!(parse("x >> y >>"), Ok(("x >> y", " >>")));
121  }
122
123  #[test]
124  fn lines() -> Result
125  {
126    let indentation = Indentation(2);
127
128    assert_eq!(parse_line("")?, Line::CODE(""));
129    assert_eq!(parse_line("xyz")?, Line::CODE("xyz"));
130    assert_eq!(parse_line("  // << codegen foo >> let's go!")?, Line::BEGIN_CODEGEN{identifier: "foo", marker: Marker{indentation, before_marker: "// ", after_marker: " let's go!"}});
131    assert_eq!(parse_line("  // << /codegen f00baa >> nice!")?, Line::END_CODEGEN{checksum: "f00baa", marker: Marker{indentation, before_marker: "// ", after_marker: " nice!"}});
132    assert_eq!(parse_line("  # << /codegen 0123465789abcdef00112233445566778899aabbccddeefffedcba9876543210 >>")?, Line::END_CODEGEN{checksum: "0123465789abcdef00112233445566778899aabbccddeefffedcba9876543210", marker: Marker{indentation, before_marker: "# ", after_marker: ""}});
133    assert_eq!(parse_line("  // << /codegen >> nice!")?, Line::END_CODEGEN{checksum: "", marker: Marker{indentation, before_marker: "// ", after_marker: " nice!"}});
134    assert_eq!(parse_line("  // << /codegen>> nice!")?, Line::END_CODEGEN{checksum: "", marker: Marker{indentation, before_marker: "// ", after_marker: " nice!"}});
135
136    Ok(())
137  }
138
139  fn parse_line<'a>(code: &'a str) -> Result<Line<'a>>
140  {
141    return super::parse(code);
142  }
143}