diff --git a/src/ast/printer.rs b/src/ast/printer/mod.rs similarity index 91% rename from src/ast/printer.rs rename to src/ast/printer/mod.rs index b56194e..9451da2 100644 --- a/src/ast/printer.rs +++ b/src/ast/printer/mod.rs @@ -87,18 +87,42 @@ where String::from_utf8_lossy(&indent).to_string() } + fn is_bareword(s: &str) -> bool { + match s.chars().nth(0) { + Some(c) => { + if !(c.is_ascii_alphabetic() || c == '_') { + return false; + } + } + None => return false, + }; + for c in s.chars() { + if !c.is_ascii_alphanumeric() { + return false; + } + } + return true; + } + fn render_list_def(&mut self, def: &ListDef) -> std::io::Result<()> { - write!(self.w, "[\n")?; + write!(self.w, "[")?; self.curr_indent += self.indent; // If the element list is just 1 we might be able to collapse the tuple. let indent = self.make_indent(); + let has_fields = def.elems.len() > 0; + if has_fields { + write!(self.w, "\n")?; + } for e in def.elems.iter() { // TODO(jwall): Now print out the elements write!(self.w, "{}", indent)?; self.render_expr(e)?; - write!(self.w, "\n")?; + write!(self.w, ",\n")?; } self.curr_indent -= self.indent; + if has_fields { + write!(self.w, "{}", self.make_indent())?; + } self.w.write(&[']' as u8])?; Ok(()) } @@ -108,14 +132,25 @@ where // If the field list is just 1 we might be able to collapse the tuple. self.curr_indent += self.indent; let indent = self.make_indent(); + let has_fields = def.len() > 0; + if has_fields { + write!(self.w, "\n")?; + } for &(ref t, ref expr) in def.iter() { write!(self.w, "{}", indent)?; - // TODO(jwall): Detect if there are strings and render as a quoted string. - write!(&mut self.w, "{} = ", t.fragment)?; + if Self::is_bareword(&t.fragment) { + write!(&mut self.w, "{} = ", t.fragment)?; + } else { + write!(self.w, "\"{}\" = ", Self::escape_quotes(&t.fragment))?; + } self.render_expr(expr)?; write!(&mut self.w, ",")?; write!(self.w, "\n")?; } + self.curr_indent -= self.indent; + if has_fields { + write!(self.w, "{}", self.make_indent())?; + } self.w.write(&['}' as u8])?; Ok(()) } @@ -352,7 +387,7 @@ where Ok(()) } - pub fn render(&mut self, stmts: Vec<&mut Statement>) { + pub fn render(&mut self, stmts: &Vec) { for v in stmts { if let Err(e) = self.render_stmt(v) { self.err = Some(e); @@ -361,3 +396,6 @@ where } } } + +#[cfg(test)] +mod test; diff --git a/src/ast/printer/test.rs b/src/ast/printer/test.rs new file mode 100644 index 0000000..b9a7896 --- /dev/null +++ b/src/ast/printer/test.rs @@ -0,0 +1,167 @@ +// Copyright 2019 Jeremy Wall +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::ast::printer::*; +use crate::iter::OffsetStrIter; +use crate::parse::*; + +fn assert_parse(input: &str) -> Vec { + parse(OffsetStrIter::new(input)).unwrap() +} + +#[test] +fn test_simple_value_printing() { + let input = "1;"; + let stmts = assert_parse("1;"); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(0, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_simple_quoted_printing() { + let input = "\"foo\";"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(0, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_escaped_quoted_printing() { + let input = "\"f\\\\o\\\"o\";"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(0, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_empty_tuple_printing() { + let input = "{};"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(2, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_empty_list_printing() { + let input = "[];"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(2, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_non_empty_tuple_printing() { + let input = "{\n foo = 1,\n};"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(2, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_nested_empty_tuple_printing() { + let input = "{\n foo = {},\n};"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(2, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_list_nested_empty_tuple_printing() { + let input = "[\n {},\n];"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(2, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_nested_non_empty_tuple_printing() { + let input = "{\n foo = {\n bar = 1,\n },\n};"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(2, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_nested_non_empty_list_printing() { + let input = "[\n [\n 1,\n ],\n];"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(2, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_simple_quoted_field_tuple_printing() { + let input = "{\n \"foo\" = {\n bar = 1,\n },\n};"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(2, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!( + String::from_utf8(buffer).unwrap(), + format!("{}\n", "{\n foo = {\n bar = 1,\n },\n};") + ); +} + +#[test] +fn test_special_quoted_field_tuple_printing() { + let input = "{\n \"foo bar\" = {\n bar = 1,\n },\n};"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(2, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +} + +#[test] +fn test_let_statement_printing() { + let input = "let tpl = {\n \"foo bar\" = {\n bar = 1,\n },\n};"; + let stmts = assert_parse(input); + let mut buffer: Vec = Vec::new(); + let mut printer = AstPrinter::new(2, &mut buffer); + printer.render(&stmts); + assert!(printer.err.is_none()); + assert_eq!(String::from_utf8(buffer).unwrap(), format!("{}\n", input)); +}