1extern crate regex;
2
3use regex::Regex;
4use std::collections::HashSet;
5use std::io::{self, Write};
6
7use super::{Compile, Name, ParseError, Path, Statement, Template};
8use self::runtime::RUNTIME;
9
10mod runtime;
11
12#[derive(Debug)]
18pub struct Program {
19 global: Scope,
20}
21
22impl Program {
23 fn new() -> Self {
24 Program { global: Scope::new(Name::new("global")) }
25 }
26
27 fn merge(&mut self, scope: Scope) -> &mut Self {
28 self.global.merge(scope);
29 self
30 }
31}
32
33impl Compile for Program {
34 fn emit(&self, buf: &mut Write) -> io::Result<()> {
40 writeln!(buf, "{}", RUNTIME)?;
42
43 for fun in &self.global.functions {
45 writeln!(buf, "{};", fun.decl)?;
46 }
47
48 writeln!(buf, "")?;
49
50 for fun in &self.global.functions {
52 fun.emit(buf)?
53 }
54
55 write!(buf,
57 r#"void Init_stache() {{
58 VALUE Stache = rb_define_module("Stache");
59 VALUE Templates = rb_define_class_under(Stache, "Templates", rb_cObject);
60 "#)?;
61
62 for fun in &self.global.functions {
63 if let Some(ref export) = fun.export {
64 writeln!(buf,
65 "rb_define_method(Templates, \"{}\", {}, 1);",
66 export,
67 fun.name)?;
68 }
69 }
70
71 writeln!(buf,
73 r#"rb_require("cgi");
74 cCGI = rb_const_get(rb_cObject, rb_intern("CGI"));
75 id_escape_html = rb_intern("escapeHTML");
76 id_key_p = rb_intern("key?");
77 id_to_s = rb_intern("to_s");
78 "#)?;
79
80 writeln!(buf, "}}")
81 }
82}
83
84#[derive(Debug)]
94struct Scope {
95 name: Name,
96 functions: Vec<Function>,
97}
98
99impl Scope {
100 fn new(name: Name) -> Self {
101 Scope {
102 name: name,
103 functions: Vec::new(),
104 }
105 }
106
107 fn merge(&mut self, mut other: Scope) -> &mut Self {
109 self.functions.append(&mut other.functions);
110 self
111 }
112
113 fn next(&mut self) -> &mut Self {
117 self.name.next();
118 self
119 }
120
121 fn register(&mut self, fun: Function) {
123 self.functions.push(fun);
124 }
125}
126
127#[derive(Debug)]
128struct Function {
129 name: String,
130 decl: String,
131 body: Vec<String>,
132 export: Option<String>,
133}
134
135impl Function {
136 fn emit(&self, buf: &mut Write) -> io::Result<()> {
137 writeln!(buf, "{} {{", self.decl)?;
138 for node in &self.body {
139 writeln!(buf, "{}", node)?;
140 }
141 writeln!(buf, "}}\n")
142 }
143}
144
145fn transform(scope: &mut Scope, node: &Statement) -> Option<String> {
153 match *node {
154 Statement::Program(ref block) => {
155 let id = scope.name.id();
156
157 let children = block.statements
159 .iter()
160 .filter_map(|stmt| transform(scope.next(), stmt))
161 .collect();
162
163 let internal = Function {
164 name: format!("render_{}", id),
165 decl: format!("static void render_{}(VALUE buf, VALUE stack)", id),
166 body: children,
167 export: None,
168 };
169
170 let body = format!("VALUE buf = rb_str_buf_new(0);
172 VALUE stack = rb_ary_new_from_args(1, context);
173 render_{}(buf, stack);
174 return buf;",
175 id);
176 let external = Function {
177 name: format!("{}_template", id),
178 decl: format!("static VALUE {}_template(VALUE self, VALUE context)", id),
179 body: vec![body],
180 export: Some(id),
181 };
182
183 scope.register(internal);
184 scope.register(external);
185
186 None
187 }
188 Statement::Section(ref path, ref block) => {
189 let children = block.statements
190 .iter()
191 .filter_map(|stmt| transform(scope.next(), stmt))
192 .collect();
193
194 let fun = Function {
195 name: format!("section_{}", scope.name),
196 decl: format!("static void section_{}(VALUE buf, VALUE stack)", scope.name),
197 body: children,
198 export: None,
199 };
200
201 let call = format!("{{ {} section(buf, stack, path, {}); }}",
202 path_ary(path),
203 fun.name);
204
205 scope.register(fun);
206 Some(call)
207 }
208 Statement::Inverted(ref path, ref block) => {
209 let children = block.statements
210 .iter()
211 .filter_map(|stmt| transform(scope.next(), stmt))
212 .collect();
213
214 let fun = Function {
215 name: format!("section_{}", scope.name),
216 decl: format!("static void section_{}(VALUE buf, VALUE stack)", scope.name),
217 body: children,
218 export: None,
219 };
220
221 let call = format!("{{ {} inverted(buf, stack, path, {}); }}",
222 path_ary(path),
223 fun.name);
224
225 scope.register(fun);
226 Some(call)
227 }
228 Statement::Partial(ref name) => {
229 let name = Name::new(name);
230 Some(format!("render_{}(buf, stack);", name.id()))
231 }
232 Statement::Comment(_) => None,
233 Statement::Content(ref text) => {
234 let text = clean(text);
235 Some(format!("rb_str_cat_cstr(buf, \"{}\");", text))
236 }
237 Statement::Variable(ref path) => {
238 let path = path_ary(path);
239 Some(format!("{{ {} append_value(buf, stack, path, true); }}", path))
240 }
241 Statement::Html(ref path) => {
242 let path = path_ary(path);
243 Some(format!("{{ {} append_value(buf, stack, path, false); }}", path))
244 }
245 }
246}
247
248pub fn link(templates: &Vec<Template>) -> Result<Program, ParseError> {
251 validate(templates)?;
252
253 let mut program = Program::new();
254 templates.iter()
255 .map(|template| {
256 let mut scope = Scope::new(template.name());
257 transform(&mut scope, &template.tree);
258 scope
259 })
260 .fold(&mut program, |program, scope| program.merge(scope));
261
262 Ok(program)
263}
264
265fn validate(templates: &Vec<Template>) -> Result<(), ParseError> {
273 let all: HashSet<_> = templates.iter().map(|temp| &temp.name).collect();
274
275 for template in templates {
276 let names: HashSet<_> = template.tree.partials().into_iter().collect();
277 let missing = &names - &all;
278 if !missing.is_empty() {
279 let name = missing.into_iter().next().unwrap();
280 return Err(ParseError::UnknownPartial(name.clone(), template.path.clone()));
281 }
282 }
283
284 Ok(())
285}
286
287fn clean(text: &str) -> String {
290 let re = Regex::new(r"\r").unwrap();
291 let value = re.replace_all(text, "\\r");
292
293 let re = Regex::new(r"\n").unwrap();
294 let value = re.replace_all(&value, "\\n");
295
296 let re = Regex::new(r#"["]"#).unwrap();
297 re.replace_all(&value, "\\\"")
298}
299
300fn path_ary(path: &Path) -> String {
304 let args = path.keys
305 .iter()
306 .map(|key| format!("rb_str_new_cstr(\"{}\")", key))
307 .collect::<Vec<String>>()
308 .join(", ");
309
310 format!("VALUE path = rb_ary_new_from_args({}, {});",
311 path.keys.len(),
312 args)
313}
314
315#[cfg(test)]
316mod tests {
317 use super::{link, transform, Scope};
318 use super::super::{Name, ParseError, Statement, Template};
319 use std::path::{Path, PathBuf};
320
321 #[test]
322 fn validates_valid_partial_reference() {
323 let base = PathBuf::from("app/templates");
324 let path = PathBuf::from("app/templates/machines/robots.mustache");
325 let tree = Statement::Partial(String::from("machines/robot"));
326 let master = Template::new(&base, path, tree);
327
328 let path = PathBuf::from("app/templates/machines/robot.mustache");
329 let tree = Statement::Content(String::from("hubot"));
330 let detail = Template::new(&base, path, tree);
331
332 let templates = vec![master, detail];
333 match link(&templates) {
334 Ok(_) => (),
335 Err(e) => panic!("Must link valid partials: {}", e),
336 }
337 }
338
339 #[test]
340 fn validates_invalid_partial_reference() {
341 let base = PathBuf::from("app/templates");
342 let path = PathBuf::from("app/templates/machines/robots.mustache");
343 let tree = Statement::Partial(String::from("machines/unknown"));
344 let master = Template::new(&base, path, tree);
345
346 let path = PathBuf::from("app/templates/machines/robot.mustache");
347 let tree = Statement::Content(String::from("hubot"));
348 let detail = Template::new(&base, path, tree);
349
350 let templates = vec![master, detail];
351 match link(&templates) {
352 Err(ParseError::UnknownPartial(ref name, ref path)) => {
353 assert_eq!("machines/unknown", name);
354 assert_eq!(Path::new("app/templates/machines/robots.mustache"), path);
355 }
356 _ => panic!("Must enforce partial references"),
357 }
358 }
359
360 #[test]
361 fn transforms_tree_into_functions() {
362 let text = "
363 {{> includes/header }}
364 <ul>
365 {{# robots}}
366 <li>{{ name.first }}</li>
367 {{/ robots}}
368 {{^ robots}}
369 {{! else clause }}
370 No robots
371 {{/ robots}}
372 </ul>
373 {{> includes/footer }}
374 {{{ unescaped.html }}}
375 ";
376
377 match Statement::parse(text) {
378 Ok(tree) => {
379 let mut scope = Scope::new(Name::new("machines/robot"));
380 transform(&mut scope, &tree);
381
382 let names: Vec<_> = scope.functions.iter().map(|fun| &fun.name).collect();
384 assert_eq!(vec!["section_machines_robot7",
385 "section_machines_robot12",
386 "render_machines_robot",
387 "machines_robot_template"],
388 names);
389
390 let exports: Vec<_> =
392 scope.functions.iter().filter_map(|fun| fun.export.as_ref()).collect();
393 assert_eq!(vec!["machines_robot"], exports);
394 }
395 Err(e) => panic!("Failed to parse tree: {}", e),
396 }
397 }
398}