From 2e1416c509a9c2346468f9e0b2c7ed8d870c1dcf Mon Sep 17 00:00:00 2001 From: Yuheng Chen Date: Mon, 25 May 2015 01:34:18 +0800 Subject: [PATCH] Add yaml value coerce --- saphyr/Cargo.toml | 3 ++ saphyr/src/lib.rs | 2 ++ saphyr/src/parser.rs | 54 ++++++++++++++++------------- saphyr/src/scanner.rs | 4 +-- saphyr/src/yaml.rs | 80 ++++++++++++++++++++++++++++++++++++++++--- 5 files changed, 113 insertions(+), 30 deletions(-) diff --git a/saphyr/Cargo.toml b/saphyr/Cargo.toml index d7dd718..82b30d2 100644 --- a/saphyr/Cargo.toml +++ b/saphyr/Cargo.toml @@ -2,3 +2,6 @@ name = "yaml-rust" version = "0.1.0" authors = ["Yuheng Chen "] + +[dependencies] +regex = "*" diff --git a/saphyr/src/lib.rs b/saphyr/src/lib.rs index a824d7d..1241d22 100644 --- a/saphyr/src/lib.rs +++ b/saphyr/src/lib.rs @@ -1,3 +1,5 @@ +extern crate regex; + pub mod yaml; pub mod scanner; pub mod parser; diff --git a/saphyr/src/parser.rs b/saphyr/src/parser.rs index e1d18dc..ffe97d9 100644 --- a/saphyr/src/parser.rs +++ b/saphyr/src/parser.rs @@ -1,4 +1,5 @@ use scanner::*; +use yaml::*; #[derive(Clone, Copy, PartialEq, Debug, Eq)] pub enum State { @@ -67,7 +68,7 @@ impl> Parser { states: Vec::new(), state: State::StreamStart, marks: Vec::new(), - token: None + token: None, } } @@ -104,38 +105,35 @@ impl> Parser { ev } - pub fn load(&mut self) -> Result<(), ScanError> { + pub fn load(&mut self) -> Result { if !self.scanner.stream_started() { let ev = try!(self.parse()); assert_eq!(ev, Event::StreamStart); } if self.scanner.stream_ended() { - return Ok(()); + return Ok(Yaml::Null); } let ev = try!(self.parse()); if ev == Event::StreamEnd { - return Ok(()); + return Ok(Yaml::Null); } - try!(self.load_document(&ev)); - Ok(()) + self.load_document(&ev) } - fn load_document(&mut self, first_ev: &Event) -> Result<(), ScanError> { + fn load_document(&mut self, first_ev: &Event) -> Result { assert_eq!(first_ev, &Event::DocumentStart); let ev = try!(self.parse()); - try!(self.load_node(&ev)); - - Ok(()) + self.load_node(&ev) } - fn load_node(&mut self, first_ev: &Event) -> Result<(), ScanError> { + fn load_node(&mut self, first_ev: &Event) -> Result { match *first_ev { - Event::Scalar(_) => { + Event::Scalar(ref v) => { // TODO scalar println!("Scalar: {:?}", first_ev); - Ok(()) + Ok(Yaml::String(v.clone())) }, Event::SequenceStart => { self.load_sequence(first_ev) @@ -147,31 +145,36 @@ impl> Parser { } } - fn load_mapping(&mut self, first_ev: &Event) -> Result<(), ScanError> { + fn load_mapping(&mut self, first_ev: &Event) -> Result { let mut ev = try!(self.parse()); + let mut map = Hash::new(); while ev != Event::MappingEnd { // key - try!(self.load_node(&ev)); + let key = try!(self.load_node(&ev)); // value ev = try!(self.parse()); - try!(self.load_node(&ev)); + let value = try!(self.load_node(&ev)); + + map.insert(key, value); // next event ev = try!(self.parse()); } - Ok(()) + Ok(Yaml::Hash(map)) } - fn load_sequence(&mut self, first_ev: &Event) -> Result<(), ScanError> { + fn load_sequence(&mut self, first_ev: &Event) -> Result { let mut ev = try!(self.parse()); + let mut vec = Vec::new(); while ev != Event::SequenceEnd { - try!(self.load_node(&ev)); + let entry = try!(self.load_node(&ev)); + vec.push(entry); // next event ev = try!(self.parse()); } - Ok(()) + Ok(Yaml::Array(vec)) } fn state_machine(&mut self) -> ParseResult { @@ -183,6 +186,10 @@ impl> Parser { State::DocumentStart => self.document_start(false), State::DocumentContent => self.document_content(), + State::BlockNode => self.parse_node(true, false), + State::BlockNodeOrIndentlessSequence => self.parse_node(true, true), + State::FlowNode => self.parse_node(false, false), + State::BlockMappingFirstKey => self.block_mapping_key(true), State::BlockMappingKey => self.block_mapping_key(false), State::BlockMappingValue => self.block_mapping_value(), @@ -240,7 +247,6 @@ impl> Parser { _ if implicit => { self.push_state(State::DocumentEnd); self.state = State::BlockNode; - self.skip(); Ok(Event::DocumentStart) }, _ => { @@ -460,7 +466,7 @@ mod test { use super::*; #[test] fn test_parser() { - let s: String = "--- + let s: String = " # comment a0 bb: val a1: @@ -474,8 +480,8 @@ a4: - 2 ".to_string(); let mut parser = Parser::new(s.chars()); - parser.load().unwrap(); - + let out = parser.load().unwrap(); + println!("DOC {:?}", out); } } diff --git a/saphyr/src/scanner.rs b/saphyr/src/scanner.rs index adf6518..02b6f65 100644 --- a/saphyr/src/scanner.rs +++ b/saphyr/src/scanner.rs @@ -540,7 +540,7 @@ impl> Scanner { // indicators ends a plain scalar match self.ch() { ':' if is_blankz(self.buffer[1]) => break, - ',' | ':' | '?' | '[' | ']' |'{' |'}' => break, + ',' | ':' | '?' | '[' | ']' |'{' |'}' if self.flow_level > 0 => break, _ => {} } @@ -718,7 +718,7 @@ a4: ".to_string(); let p = Scanner::new(s.chars()); for t in p { - // println!("{:?}", t); + println!("{:?}", t); } } } diff --git a/saphyr/src/yaml.rs b/saphyr/src/yaml.rs index d803bdc..4e5e4a1 100644 --- a/saphyr/src/yaml.rs +++ b/saphyr/src/yaml.rs @@ -1,11 +1,12 @@ use std::collections::{HashMap, BTreeMap}; use std::string; +use regex::Regex; -#[derive(Clone, PartialEq, PartialOrd, Debug)] +#[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord)] pub enum Yaml { I64(i64), - U64(u64), - F64(f64), + //U64(u64), + //F64(f64), String(string::String), Boolean(bool), Array(self::Array), @@ -14,7 +15,7 @@ pub enum Yaml { } pub type Array = Vec; -pub type Hash = BTreeMap; +pub type Hash = BTreeMap; /// The errors that can arise while parsing a YAML stream. #[derive(Clone, Copy, PartialEq, Debug)] @@ -37,3 +38,74 @@ pub enum ErrorCode { NotFourDigit, NotUtf8, } + +macro_rules! define_as ( + ($name:ident, $t:ident, $yt:ident) => ( +pub fn $name(&self) -> Option<$t> { + match *self { + Yaml::$yt(v) => Some(v), + _ => None + } +} + ); +); + +macro_rules! define_as_ref ( + ($name:ident, $t:ty, $yt:ident) => ( +pub fn $name(&self) -> Option<$t> { + match *self { + Yaml::$yt(ref v) => Some(v), + _ => None + } +} + ); +); + +// these regex are from libyaml-rust project +macro_rules! regex( + ($s:expr) => (Regex::new($s).unwrap()); +); +impl Yaml { + define_as!(as_i64, i64, I64); + define_as!(as_bool, bool, Boolean); + + define_as_ref!(as_str, &str, String); + define_as_ref!(as_hash, &Hash, Hash); + define_as_ref!(as_vec, &Array, Array); + + pub fn is_null(&self) -> bool { + match *self { + Yaml::Null => true, + _ => false + } + } + + pub fn as_f64(&self) -> Option { + // XXX(chenyh) precompile me + let float_pattern = regex!(r"^([-+]?)(\.[0-9]+|[0-9]+(\.[0-9]*)?([eE][-+]?[0-9]+)?)$"); + match *self { + Yaml::String(ref v) if float_pattern.is_match(v) => { + v.parse::().ok() + }, + _ => None + } + } +} + +#[cfg(test)] +mod test { + use parser::Parser; + use yaml::Yaml; + // #[test] + fn test_coerce() { + let s = "--- +a: 1 +b: 2.2 +c: [1, 2] +"; + let mut parser = Parser::new(s.chars()); + let out = parser.load().unwrap(); + //assert_eq!(out.as_hash().unwrap()[&Yaml::String("a".to_string())].as_i64().unwrap(), 1i64); + } +} +