codebiber/parse_file/parser/
line.rs1use 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}