Add yaml value coerce
This commit is contained in:
parent
0ab86869e7
commit
f49d6c0099
5 changed files with 113 additions and 30 deletions
|
@ -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 = "*"
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue