diff --git a/parser/Cargo.toml b/parser/Cargo.toml index 0c79598..a4e2fda 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "yaml-rust" -version = "0.3.5" +version = "0.3.6" authors = ["Yuheng Chen "] homepage = "http://chyh1990.github.io/yaml-rust/" documentation = "http://chyh1990.github.io/yaml-rust/doc/yaml_rust/" diff --git a/parser/src/emitter.rs b/parser/src/emitter.rs index 2ed20c2..ef3cdc7 100644 --- a/parser/src/emitter.rs +++ b/parser/src/emitter.rs @@ -302,17 +302,20 @@ fn need_quotes(string: &str) -> bool { _ => false, } }) - || string == "true" - || string == "false" - || string == "null" - || string == "~" + || [// http://yaml.org/type/bool.html + "y","Y","yes","Yes","YES","n","N","no","No","NO", + "True", "TRUE", "true", "False", "FALSE", "false", + "on","On","ON","off","Off","OFF", + // http://yaml.org/type/null.html + "null","Null","NULL", "~" + ].contains(&string) || string.starts_with('.') || string.parse::().is_ok() || string.parse::().is_ok() } #[cfg(test)] -mod tests { +mod test { use super::*; use YamlLoader; @@ -327,15 +330,8 @@ a1: a2: 4 # i'm comment a3: [1, 2, 3] a4: - - - a1 - - a2 + - [a1, a2] - 2 - - [] - - {} -a5: 'single_quoted' -a6: \"double_quoted\" -a7: 你好 -'key 1': \"ddd\\\tbbb\" "; @@ -346,7 +342,12 @@ a7: 你好 let mut emitter = YamlEmitter::new(&mut writer); emitter.dump(doc).unwrap(); } - let docs_new = YamlLoader::load_from_str(&s).unwrap(); + println!("original:\n{}", s); + println!("emitted:\n{}", writer); + let docs_new = match YamlLoader::load_from_str(&writer) { + Ok(y) => y, + Err(e) => panic!(format!("{}", e)) + }; let doc_new = &docs_new[0]; assert_eq!(doc, doc_new); @@ -380,7 +381,10 @@ products: let mut emitter = YamlEmitter::new(&mut writer); emitter.dump(doc).unwrap(); } - let docs_new = YamlLoader::load_from_str(&s).unwrap(); + let docs_new = match YamlLoader::load_from_str(&writer) { + Ok(y) => y, + Err(e) => panic!(format!("{}", e)) + }; let doc_new = &docs_new[0]; assert_eq!(doc, doc_new); } @@ -417,7 +421,8 @@ products: "true": bool key "{}": empty hash key x: test -y: string with spaces"#; +"y": "can't avoid quoting here" +z: string with spaces"#; let docs = YamlLoader::load_from_str(&s).unwrap(); let doc = &docs[0]; @@ -430,6 +435,64 @@ y: string with spaces"#; assert_eq!(s, writer, "actual:\n\n{}\n", writer); } + #[test] + fn emit_quoted_bools() { + let input = r#"--- +string0: yes +string1: no +string2: "true" +string3: "false" +string4: "~" +null0: ~ +[true, false]: real_bools +[True, TRUE, False, FALSE, y,Y,yes,Yes,YES,n,N,no,No,NO,on,On,ON,off,Off,OFF]: false_bools +bool0: true +bool1: false"#; + let expected = r#"--- +string0: "yes" +string1: "no" +string2: "true" +string3: "false" +string4: "~" +null0: ~ +? - true + - false +: real_bools +? - "True" + - "TRUE" + - "False" + - "FALSE" + - "y" + - "Y" + - "yes" + - "Yes" + - "YES" + - "n" + - "N" + - "no" + - "No" + - "NO" + - "on" + - "On" + - "ON" + - "off" + - "Off" + - "OFF" +: false_bools +bool0: true +bool1: false"#; + + let docs = YamlLoader::load_from_str(&input).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + + assert_eq!(expected, writer, "actual:\n\n{}\n", writer); + } + #[test] fn test_empty_and_nested() { test_empty_and_nested_flag(false) @@ -471,4 +534,74 @@ e: assert_eq!(s, writer); } + + #[test] + fn test_nested_arrays() { + let s = r#"--- +a: + - b + - - c + - d + - - e + - f"#; + + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{}", s); + println!("emitted:\n{}", writer); + + assert_eq!(s, writer); + } + + #[test] + fn test_deeply_nested_arrays() { + let s = r#"--- +a: + - b + - - c + - d + - - e + - - f + - - e"#; + + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{}", s); + println!("emitted:\n{}", writer); + + assert_eq!(s, writer); + } + + #[test] + fn test_nested_hashes() { + let s = r#"--- +a: + b: + c: + d: + e: f"#; + + let docs = YamlLoader::load_from_str(&s).unwrap(); + let doc = &docs[0]; + let mut writer = String::new(); + { + let mut emitter = YamlEmitter::new(&mut writer); + emitter.dump(doc).unwrap(); + } + println!("original:\n{}", s); + println!("emitted:\n{}", writer); + + assert_eq!(s, writer); + } + } diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 7d147e7..8b511e9 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -76,22 +76,20 @@ pub struct Parser { pub trait EventReceiver { - fn on_event(&mut self, ev: &Event); + fn on_event(&mut self, ev: Event); } pub trait MarkedEventReceiver { - fn on_event(&mut self, ev: &Event, _mark: Marker); + fn on_event(&mut self, ev: Event, _mark: Marker); } impl MarkedEventReceiver for R { - fn on_event(&mut self, ev: &Event, _mark: Marker) { + fn on_event(&mut self, ev: Event, _mark: Marker) { self.on_event(ev) } } - - pub type ParseResult = Result<(Event, Marker), ScanError>; impl> Parser { @@ -136,38 +134,37 @@ impl> Parser { self.states.push(state); } - fn parse(&mut self, recv: &mut R) - -> Result { + fn parse(&mut self) -> ParseResult { if self.state == State::End { - return Ok(Event::StreamEnd); + return Ok((Event::StreamEnd, self.scanner.mark())); } let (ev, mark) = try!(self.state_machine()); // println!("EV {:?}", ev); - recv.on_event(&ev, mark); - Ok(ev) + Ok((ev, mark)) } pub fn load(&mut self, recv: &mut R, multi: bool) -> Result<(), ScanError> { if !self.scanner.stream_started() { - let ev = try!(self.parse(recv)); + let (ev, mark) = try!(self.parse()); assert_eq!(ev, Event::StreamStart); + recv.on_event(ev, mark); } if self.scanner.stream_ended() { // XXX has parsed? - recv.on_event(&Event::StreamEnd, self.scanner.mark()); + recv.on_event(Event::StreamEnd, self.scanner.mark()); return Ok(()); } loop { - let ev = try!(self.parse(recv)); + let (ev, mark) = try!(self.parse()); if ev == Event::StreamEnd { - recv.on_event(&Event::StreamEnd, self.scanner.mark()); + recv.on_event(ev, mark); return Ok(()); } // clear anchors before a new document self.anchors.clear(); - try!(self.load_document(&ev, recv)); + try!(self.load_document(ev, mark, recv)); if !multi { break; } @@ -175,63 +172,75 @@ impl> Parser { Ok(()) } - fn load_document(&mut self, first_ev: &Event, recv: &mut R) + fn load_document(&mut self, first_ev: Event, mark: Marker, recv: &mut R) -> Result<(), ScanError> { - assert_eq!(first_ev, &Event::DocumentStart); + assert_eq!(first_ev, Event::DocumentStart); + recv.on_event(first_ev, mark); - let ev = try!(self.parse(recv)); - try!(self.load_node(&ev, recv)); + let (ev, mark) = try!(self.parse()); + try!(self.load_node(ev, mark, recv)); // DOCUMENT-END is expected. - let ev = try!(self.parse(recv)); + let (ev, mark) = try!(self.parse()); assert_eq!(ev, Event::DocumentEnd); + recv.on_event(ev, mark); Ok(()) } - fn load_node(&mut self, first_ev: &Event, recv: &mut R) + fn load_node(&mut self, first_ev: Event, mark: Marker, recv: &mut R) -> Result<(), ScanError> { - match *first_ev { + match first_ev { Event::Alias(..) | Event::Scalar(..) => { + recv.on_event(first_ev, mark); Ok(()) }, Event::SequenceStart(_) => { - self.load_sequence(first_ev, recv) + recv.on_event(first_ev, mark); + self.load_sequence(recv) }, Event::MappingStart(_) => { - self.load_mapping(first_ev, recv) + recv.on_event(first_ev, mark); + self.load_mapping(recv) }, _ => { println!("UNREACHABLE EVENT: {:?}", first_ev); unreachable!(); } } } - fn load_mapping(&mut self, _first_ev: &Event, recv: &mut R) + fn load_mapping(&mut self, recv: &mut R) -> Result<(), ScanError> { - let mut ev = try!(self.parse(recv)); - while ev != Event::MappingEnd { + let (mut key_ev, mut key_mark) = try!(self.parse()); + while key_ev != Event::MappingEnd { // key - try!(self.load_node(&ev, recv)); + try!(self.load_node(key_ev, key_mark, recv)); // value - ev = try!(self.parse(recv)); - try!(self.load_node(&ev, recv)); + let (ev, mark) = try!(self.parse()); + try!(self.load_node(ev, mark, recv)); // next event - ev = try!(self.parse(recv)); + let (ev, mark) = try!(self.parse()); + key_ev = ev; + key_mark = mark; + } + recv.on_event(key_ev, key_mark); Ok(()) } - fn load_sequence(&mut self, _first_ev: &Event, recv: &mut R) + fn load_sequence(&mut self, recv: &mut R) -> Result<(), ScanError> { - let mut ev = try!(self.parse(recv)); + let (mut ev, mut mark) = try!(self.parse()); while ev != Event::SequenceEnd { - try!(self.load_node(&ev, recv)); + try!(self.load_node(ev, mark, recv)); // next event - ev = try!(self.parse(recv)); + let (next_ev, next_mark) = try!(self.parse()); + ev = next_ev; + mark = next_mark; } + recv.on_event(ev, mark); Ok(()) } diff --git a/parser/src/yaml.rs b/parser/src/yaml.rs index d745dc6..5d7ef36 100644 --- a/parser/src/yaml.rs +++ b/parser/src/yaml.rs @@ -76,9 +76,9 @@ pub struct YamlLoader { } impl MarkedEventReceiver for YamlLoader { - fn on_event(&mut self, ev: &Event, _: Marker) { + fn on_event(&mut self, ev: Event, _: Marker) { // println!("EV {:?}", ev); - match *ev { + match ev { Event::DocumentStart => { // do nothing }, @@ -106,10 +106,10 @@ impl MarkedEventReceiver for YamlLoader { let node = self.doc_stack.pop().unwrap(); self.insert_new_node(node); }, - Event::Scalar(ref v, style, aid, ref tag) => { + Event::Scalar(v, style, aid, tag) => { let node = if style != TScalarStyle::Plain { - Yaml::String(v.clone()) - } else if let Some(TokenType::Tag(ref handle, ref suffix)) = *tag { + Yaml::String(v) + } else if let Some(TokenType::Tag(ref handle, ref suffix)) = tag { // XXX tag:yaml.org,2002: if handle == "!!" { match suffix.as_ref() { @@ -127,8 +127,8 @@ impl MarkedEventReceiver for YamlLoader { } }, "float" => { - match parse_f64(v) { - Some(_) => Yaml::Real(v.clone()), + match parse_f64(&v) { + Some(_) => Yaml::Real(v), None => Yaml::BadValue, } }, @@ -138,14 +138,14 @@ impl MarkedEventReceiver for YamlLoader { _ => Yaml::BadValue, } } - _ => Yaml::String(v.clone()), + _ => Yaml::String(v), } } else { - Yaml::String(v.clone()) + Yaml::String(v) } } else { // Datatype is not specified, or unrecognized - Yaml::from_str(v.as_ref()) + Yaml::from_str(&v) }; self.insert_new_node((node, aid)); @@ -266,6 +266,13 @@ impl Yaml { } } + pub fn is_array(&self) -> bool { + match *self { + Yaml::Array(_) => true, + _ => false + } + } + pub fn as_f64(&self) -> Option { match *self { Yaml::Real(ref v) => parse_f64(v), diff --git a/parser/tests/spec_test.rs b/parser/tests/spec_test.rs index 3e5d043..5e881fc 100644 --- a/parser/tests/spec_test.rs +++ b/parser/tests/spec_test.rs @@ -25,8 +25,8 @@ struct YamlChecker { } impl EventReceiver for YamlChecker { - fn on_event(&mut self, ev: &Event) { - let tev = match *ev { + fn on_event(&mut self, ev: Event) { + let tev = match ev { Event::DocumentStart => TestEvent::OnDocumentStart, Event::DocumentEnd => TestEvent::OnDocumentEnd, Event::SequenceStart(..) => TestEvent::OnSequenceStart,