From 8e43d42621a1e0bbbe20f8e662db15eb90cd475d Mon Sep 17 00:00:00 2001 From: Yuheng Chen Date: Wed, 27 May 2015 00:29:40 +0800 Subject: [PATCH] Add block_scalar --- parser/src/parser.rs | 4 + parser/src/scanner.rs | 198 +++++++++++++++++++++++++++++++++++++- parser/tests/spec_test.rs | 1 + 3 files changed, 201 insertions(+), 2 deletions(-) diff --git a/parser/src/parser.rs b/parser/src/parser.rs index 8fa31f4..cb95203 100644 --- a/parser/src/parser.rs +++ b/parser/src/parser.rs @@ -59,6 +59,10 @@ pub struct Parser { token: Option, } +pub trait EventReceiver { + fn on_event(&mut self, ev: &Event); +} + pub type ParseResult = Result; impl> Parser { diff --git a/parser/src/scanner.rs b/parser/src/scanner.rs index 7109bd1..5171f97 100644 --- a/parser/src/scanner.rs +++ b/parser/src/scanner.rs @@ -147,6 +147,10 @@ fn is_blank(c: char) -> bool { fn is_blankz(c: char) -> bool { is_blank(c) || is_breakz(c) } +#[inline] +fn is_digit(c: char) -> bool { + c >= '0' && c <= '9' +} pub type ScanResult = Result<(), ScanError>; @@ -312,8 +316,10 @@ impl> Scanner { '*' => unimplemented!(), '&' => unimplemented!(), '!' => unimplemented!(), - '|' if self.flow_level == 0 => unimplemented!(), - '>' if self.flow_level == 0 => unimplemented!(), + // Is it a literal scalar? + '|' if self.flow_level == 0 => try!(self.fetch_block_scalar(true)), + // Is it a folded scalar? + '>' if self.flow_level == 0 => try!(self.fetch_block_scalar(false)), '\'' => try!(self.fetch_flow_scalar(true)), '"' => try!(self.fetch_flow_scalar(false)), // plain scalar @@ -517,6 +523,194 @@ impl> Scanner { Ok(()) } + fn fetch_block_scalar(&mut self, literal: bool) -> ScanResult { + try!(self.save_simple_key()); + self.allow_simple_key(); + let tok = try!(self.scan_block_scalar(literal)); + + self.tokens.push_back(tok); + Ok(()) + } + + fn scan_block_scalar(&mut self, literal: bool) -> Result { + let start_mark = self.mark; + let mut chomping: i32 = 0; + let mut increment: usize = 0; + let mut indent: usize = 0; + let mut trailing_blank: bool = false; + let mut leading_blank: bool = false; + + let mut string = String::new(); + let mut leading_break = String::new(); + let mut trailing_breaks = String::new(); + + // skip '|' or '>' + self.skip(); + self.lookahead(1); + + if self.ch() == '+' || self.ch() == '-' { + if self.ch() == '+' { + chomping = 1; + } else { + chomping = -1; + } + self.skip(); + self.lookahead(1); + if is_digit(self.ch()) { + if self.ch() == '0' { + return Err(ScanError::new(start_mark, + "while scanning a block scalar, found an intendation indicator equal to 0")); + } + increment = (self.ch() as usize) - ('0' as usize); + self.skip(); + } + } else if is_digit(self.ch()) { + if self.ch() == '0' { + return Err(ScanError::new(start_mark, + "while scanning a block scalar, found an intendation indicator equal to 0")); + } + + increment = (self.ch() as usize) - ('0' as usize); + self.skip(); + self.lookahead(1); + if self.ch() == '+' || self.ch() == '-' { + if self.ch() == '+' { + chomping = 1; + } else { + chomping = -1; + } + self.skip(); + } + } + + // Eat whitespaces and comments to the end of the line. + self.lookahead(1); + + while is_blank(self.ch()) { + self.skip(); + self.lookahead(1); + } + + if self.ch() == '#' { + while !is_breakz(self.ch()) { + self.skip(); + self.lookahead(1); + } + } + + // Check if we are at the end of the line. + if !is_breakz(self.ch()) { + return Err(ScanError::new(start_mark, + "while scanning a block scalar, did not find expected comment or line break")); + } + + if is_break(self.ch()) { + self.lookahead(2); + self.skip_line(); + } + + if increment > 0 { + indent = if self.indent >= 0 { (self.indent + increment as isize) as usize } else { increment } + } + // Scan the leading line breaks and determine the indentation level if needed. + try!(self.block_scalar_breaks(&mut indent, &mut trailing_breaks)); + + self.lookahead(1); + + let start_mark = self.mark; + + while self.mark.col == indent && !is_z(self.ch()) { + println!("--> {:?}", self.ch()); + // We are at the beginning of a non-empty line. + trailing_blank = is_blank(self.ch()); + if !literal && !leading_break.is_empty() + && !leading_blank && !trailing_blank { + if trailing_breaks.is_empty() { + string.push(' '); + } + leading_break.clear(); + } else { + string.extend(leading_break.chars()); + leading_break.clear(); + } + + string.extend(trailing_breaks.chars()); + trailing_breaks.clear(); + + leading_blank = is_blank(self.ch()); + + while !is_breakz(self.ch()) { + println!("----> {:?}", self.ch()); + string.push(self.ch()); + self.skip(); + self.lookahead(1); + } + + self.lookahead(2); + self.skip_line(); + + // Eat the following intendation spaces and line breaks. + try!(self.block_scalar_breaks(&mut indent, &mut trailing_breaks)); + } + + // Chomp the tail. + if chomping != -1 { + string.extend(leading_break.chars()); + } + + if chomping == 1 { + string.extend(trailing_breaks.chars()); + } + + if literal { + Ok(Token(start_mark, TokenType::ScalarToken(TScalarStyle::Literal, string))) + } else { + Ok(Token(start_mark, TokenType::ScalarToken(TScalarStyle::Foled, string))) + } + } + + fn block_scalar_breaks(&mut self, indent: &mut usize, breaks: &mut String) -> ScanResult { + let mut max_indent = 0; + loop { + self.lookahead(1); + while (*indent == 0 || self.mark.col < *indent) + && self.buffer[0] == ' ' { + self.skip(); + self.lookahead(1); + } + + if self.mark.col > max_indent { + max_indent = self.mark.col; + } + + // Check for a tab character messing the intendation. + if (*indent == 0 || self.mark.col < *indent) + && self.buffer[0] == '\t' { + return Err(ScanError::new(self.mark, + "while scanning a block scalar, found a tab character where an intendation space is expected")); + } + + if !is_break(self.ch()) { + break; + } + + self.lookahead(2); + // Consume the line break. + self.read_break(breaks); + } + + if *indent == 0 { + *indent = max_indent; + if *indent < (self.indent + 1) as usize { + *indent = (self.indent + 1) as usize; + } + if *indent < 1 { + *indent = 1; + } + } + Ok(()) + } + fn fetch_flow_scalar(&mut self, single: bool) -> ScanResult { try!(self.save_simple_key()); self.disallow_simple_key(); diff --git a/parser/tests/spec_test.rs b/parser/tests/spec_test.rs index cd9f1c1..c5910b0 100644 --- a/parser/tests/spec_test.rs +++ b/parser/tests/spec_test.rs @@ -19,6 +19,7 @@ enum TestEvent { } fn yaml_to_test_events(docs: &Vec) -> Vec { + println!("DOCS {:?}", docs); fn next(root: &Yaml, evs: &mut Vec) { match *root { Yaml::BadValue => { panic!("unexpected BadValue"); },