Add yaml value coerce

This commit is contained in:
Yuheng Chen 2015-05-25 01:34:18 +08:00
parent 0ab86869e7
commit f49d6c0099
5 changed files with 113 additions and 30 deletions

View file

@ -2,3 +2,6 @@
name = "yaml-rust" name = "yaml-rust"
version = "0.1.0" version = "0.1.0"
authors = ["Yuheng Chen <yuhengchen@sensetime.com>"] authors = ["Yuheng Chen <yuhengchen@sensetime.com>"]
[dependencies]
regex = "*"

View file

@ -1,3 +1,5 @@
extern crate regex;
pub mod yaml; pub mod yaml;
pub mod scanner; pub mod scanner;
pub mod parser; pub mod parser;

View file

@ -1,4 +1,5 @@
use scanner::*; use scanner::*;
use yaml::*;
#[derive(Clone, Copy, PartialEq, Debug, Eq)] #[derive(Clone, Copy, PartialEq, Debug, Eq)]
pub enum State { pub enum State {
@ -67,7 +68,7 @@ impl<T: Iterator<Item=char>> Parser<T> {
states: Vec::new(), states: Vec::new(),
state: State::StreamStart, state: State::StreamStart,
marks: Vec::new(), marks: Vec::new(),
token: None token: None,
} }
} }
@ -104,38 +105,35 @@ impl<T: Iterator<Item=char>> Parser<T> {
ev ev
} }
pub fn load(&mut self) -> Result<(), ScanError> { pub fn load(&mut self) -> Result<Yaml, ScanError> {
if !self.scanner.stream_started() { if !self.scanner.stream_started() {
let ev = try!(self.parse()); let ev = try!(self.parse());
assert_eq!(ev, Event::StreamStart); assert_eq!(ev, Event::StreamStart);
} }
if self.scanner.stream_ended() { if self.scanner.stream_ended() {
return Ok(()); return Ok(Yaml::Null);
} }
let ev = try!(self.parse()); let ev = try!(self.parse());
if ev == Event::StreamEnd { if ev == Event::StreamEnd {
return Ok(()); return Ok(Yaml::Null);
} }
try!(self.load_document(&ev)); self.load_document(&ev)
Ok(())
} }
fn load_document(&mut self, first_ev: &Event) -> Result<(), ScanError> { fn load_document(&mut self, first_ev: &Event) -> Result<Yaml, ScanError> {
assert_eq!(first_ev, &Event::DocumentStart); assert_eq!(first_ev, &Event::DocumentStart);
let ev = try!(self.parse()); let ev = try!(self.parse());
try!(self.load_node(&ev)); self.load_node(&ev)
Ok(())
} }
fn load_node(&mut self, first_ev: &Event) -> Result<(), ScanError> { fn load_node(&mut self, first_ev: &Event) -> Result<Yaml, ScanError> {
match *first_ev { match *first_ev {
Event::Scalar(_) => { Event::Scalar(ref v) => {
// TODO scalar // TODO scalar
println!("Scalar: {:?}", first_ev); println!("Scalar: {:?}", first_ev);
Ok(()) Ok(Yaml::String(v.clone()))
}, },
Event::SequenceStart => { Event::SequenceStart => {
self.load_sequence(first_ev) self.load_sequence(first_ev)
@ -147,31 +145,36 @@ impl<T: Iterator<Item=char>> Parser<T> {
} }
} }
fn load_mapping(&mut self, first_ev: &Event) -> Result<(), ScanError> { fn load_mapping(&mut self, first_ev: &Event) -> Result<Yaml, ScanError> {
let mut ev = try!(self.parse()); let mut ev = try!(self.parse());
let mut map = Hash::new();
while ev != Event::MappingEnd { while ev != Event::MappingEnd {
// key // key
try!(self.load_node(&ev)); let key = try!(self.load_node(&ev));
// value // value
ev = try!(self.parse()); ev = try!(self.parse());
try!(self.load_node(&ev)); let value = try!(self.load_node(&ev));
map.insert(key, value);
// next event // next event
ev = try!(self.parse()); 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<Yaml, ScanError> {
let mut ev = try!(self.parse()); let mut ev = try!(self.parse());
let mut vec = Vec::new();
while ev != Event::SequenceEnd { while ev != Event::SequenceEnd {
try!(self.load_node(&ev)); let entry = try!(self.load_node(&ev));
vec.push(entry);
// next event // next event
ev = try!(self.parse()); ev = try!(self.parse());
} }
Ok(()) Ok(Yaml::Array(vec))
} }
fn state_machine(&mut self) -> ParseResult { fn state_machine(&mut self) -> ParseResult {
@ -183,6 +186,10 @@ impl<T: Iterator<Item=char>> Parser<T> {
State::DocumentStart => self.document_start(false), State::DocumentStart => self.document_start(false),
State::DocumentContent => self.document_content(), 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::BlockMappingFirstKey => self.block_mapping_key(true),
State::BlockMappingKey => self.block_mapping_key(false), State::BlockMappingKey => self.block_mapping_key(false),
State::BlockMappingValue => self.block_mapping_value(), State::BlockMappingValue => self.block_mapping_value(),
@ -240,7 +247,6 @@ impl<T: Iterator<Item=char>> Parser<T> {
_ if implicit => { _ if implicit => {
self.push_state(State::DocumentEnd); self.push_state(State::DocumentEnd);
self.state = State::BlockNode; self.state = State::BlockNode;
self.skip();
Ok(Event::DocumentStart) Ok(Event::DocumentStart)
}, },
_ => { _ => {
@ -460,7 +466,7 @@ mod test {
use super::*; use super::*;
#[test] #[test]
fn test_parser() { fn test_parser() {
let s: String = "--- let s: String = "
# comment # comment
a0 bb: val a0 bb: val
a1: a1:
@ -474,8 +480,8 @@ a4:
- 2 - 2
".to_string(); ".to_string();
let mut parser = Parser::new(s.chars()); let mut parser = Parser::new(s.chars());
parser.load().unwrap(); let out = parser.load().unwrap();
println!("DOC {:?}", out);
} }
} }

View file

@ -540,7 +540,7 @@ impl<T: Iterator<Item=char>> Scanner<T> {
// indicators ends a plain scalar // indicators ends a plain scalar
match self.ch() { match self.ch() {
':' if is_blankz(self.buffer[1]) => break, ':' if is_blankz(self.buffer[1]) => break,
',' | ':' | '?' | '[' | ']' |'{' |'}' => break, ',' | ':' | '?' | '[' | ']' |'{' |'}' if self.flow_level > 0 => break,
_ => {} _ => {}
} }
@ -718,7 +718,7 @@ a4:
".to_string(); ".to_string();
let p = Scanner::new(s.chars()); let p = Scanner::new(s.chars());
for t in p { for t in p {
// println!("{:?}", t); println!("{:?}", t);
} }
} }
} }

View file

@ -1,11 +1,12 @@
use std::collections::{HashMap, BTreeMap}; use std::collections::{HashMap, BTreeMap};
use std::string; use std::string;
use regex::Regex;
#[derive(Clone, PartialEq, PartialOrd, Debug)] #[derive(Clone, PartialEq, PartialOrd, Debug, Eq, Ord)]
pub enum Yaml { pub enum Yaml {
I64(i64), I64(i64),
U64(u64), //U64(u64),
F64(f64), //F64(f64),
String(string::String), String(string::String),
Boolean(bool), Boolean(bool),
Array(self::Array), Array(self::Array),
@ -14,7 +15,7 @@ pub enum Yaml {
} }
pub type Array = Vec<Yaml>; pub type Array = Vec<Yaml>;
pub type Hash = BTreeMap<string::String, Yaml>; pub type Hash = BTreeMap<Yaml, Yaml>;
/// The errors that can arise while parsing a YAML stream. /// The errors that can arise while parsing a YAML stream.
#[derive(Clone, Copy, PartialEq, Debug)] #[derive(Clone, Copy, PartialEq, Debug)]
@ -37,3 +38,74 @@ pub enum ErrorCode {
NotFourDigit, NotFourDigit,
NotUtf8, 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<f64> {
// 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::<f64>().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);
}
}