1 #include "scanscalar.h"
4 #include "yaml-cpp/exceptions.h"
10 // . This is where the scalar magic happens.
12 // . We do the scanning in three phases:
13 // 1. Scan until newline
15 // 3. Scan leading blanks.
17 // . Depending on the parameters given, we store or stop
18 // and different places in the above flow.
19 std::string
ScanScalar(Stream
& INPUT
, ScanScalarParams
& params
)
21 bool foundNonEmptyLine
= false;
22 bool pastOpeningBreak
= (params
.fold
== FOLD_FLOW
);
23 bool emptyLine
= false, moreIndented
= false;
24 int foldedNewlineCount
= 0;
25 bool foldedNewlineStartedMoreIndented
= false;
26 std::size_t lastEscapedChar
= std::string::npos
;
28 params
.leadingSpaces
= false;
31 // ********************************
32 // Phase #1: scan until line ending
34 std::size_t lastNonWhitespaceChar
= scalar
.size();
35 bool escapedNewline
= false;
36 while(!params
.end
.Matches(INPUT
) && !Exp::Break().Matches(INPUT
)) {
40 // document indicator?
41 if(INPUT
.column() == 0 && Exp::DocIndicator().Matches(INPUT
)) {
42 if(params
.onDocIndicator
== BREAK
)
44 else if(params
.onDocIndicator
== THROW
)
45 throw ParserException(INPUT
.mark(), ErrorMsg::DOC_IN_SCALAR
);
48 foundNonEmptyLine
= true;
49 pastOpeningBreak
= true;
51 // escaped newline? (only if we're escaping on slash)
52 if(params
.escape
== '\\' && Exp::EscBreak().Matches(INPUT
)) {
53 // eat escape character and get out (but preserve trailing whitespace!)
55 lastNonWhitespaceChar
= scalar
.size();
56 lastEscapedChar
= scalar
.size();
57 escapedNewline
= true;
62 if(INPUT
.peek() == params
.escape
) {
63 scalar
+= Exp::Escape(INPUT
);
64 lastNonWhitespaceChar
= scalar
.size();
65 lastEscapedChar
= scalar
.size();
69 // otherwise, just add the damn character
70 char ch
= INPUT
.get();
72 if(ch
!= ' ' && ch
!= '\t')
73 lastNonWhitespaceChar
= scalar
.size();
76 // eof? if we're looking to eat something, then we throw
79 throw ParserException(INPUT
.mark(), ErrorMsg::EOF_IN_SCALAR
);
84 if(params
.onDocIndicator
== BREAK
&& INPUT
.column() == 0 && Exp::DocIndicator().Matches(INPUT
))
87 // are we done via character match?
88 int n
= params
.end
.Match(INPUT
);
95 // do we remove trailing whitespace?
96 if(params
.fold
== FOLD_FLOW
)
97 scalar
.erase(lastNonWhitespaceChar
);
99 // ********************************
100 // Phase #2: eat line ending
101 n
= Exp::Break().Match(INPUT
);
104 // ********************************
105 // Phase #3: scan initial spaces
107 // first the required indentation
108 while(INPUT
.peek() == ' ' && (INPUT
.column() < params
.indent
|| (params
.detectIndent
&& !foundNonEmptyLine
)))
111 // update indent if we're auto-detecting
112 if(params
.detectIndent
&& !foundNonEmptyLine
)
113 params
.indent
= std::max(params
.indent
, INPUT
.column());
115 // and then the rest of the whitespace
116 while(Exp::Blank().Matches(INPUT
)) {
117 // we check for tabs that masquerade as indentation
118 if(INPUT
.peek() == '\t'&& INPUT
.column() < params
.indent
&& params
.onTabInIndentation
== THROW
)
119 throw ParserException(INPUT
.mark(), ErrorMsg::TAB_IN_INDENTATION
);
121 if(!params
.eatLeadingWhitespace
)
127 // was this an empty line?
128 bool nextEmptyLine
= Exp::Break().Matches(INPUT
);
129 bool nextMoreIndented
= Exp::Blank().Matches(INPUT
);
130 if(params
.fold
== FOLD_BLOCK
&& foldedNewlineCount
== 0 && nextEmptyLine
)
131 foldedNewlineStartedMoreIndented
= moreIndented
;
133 // for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
134 if(pastOpeningBreak
) {
135 switch(params
.fold
) {
140 if(!emptyLine
&& !nextEmptyLine
&& !moreIndented
&& !nextMoreIndented
&& INPUT
.column() >= params
.indent
)
142 else if(nextEmptyLine
)
143 foldedNewlineCount
++;
147 if(!nextEmptyLine
&& foldedNewlineCount
> 0) {
148 scalar
+= std::string(foldedNewlineCount
- 1, '\n');
149 if(foldedNewlineStartedMoreIndented
|| nextMoreIndented
| !foundNonEmptyLine
)
151 foldedNewlineCount
= 0;
157 else if(!emptyLine
&& !nextEmptyLine
&& !escapedNewline
)
163 emptyLine
= nextEmptyLine
;
164 moreIndented
= nextMoreIndented
;
165 pastOpeningBreak
= true;
167 // are we done via indentation?
168 if(!emptyLine
&& INPUT
.column() < params
.indent
) {
169 params
.leadingSpaces
= true;
175 if(params
.trimTrailingSpaces
) {
176 std::size_t pos
= scalar
.find_last_not_of(' ');
177 if(lastEscapedChar
!= std::string::npos
) {
178 if(pos
< lastEscapedChar
|| pos
== std::string::npos
)
179 pos
= lastEscapedChar
;
181 if(pos
< scalar
.size())
182 scalar
.erase(pos
+ 1);
185 switch(params
.chomp
) {
187 std::size_t pos
= scalar
.find_last_not_of('\n');
188 if(lastEscapedChar
!= std::string::npos
) {
189 if(pos
< lastEscapedChar
|| pos
== std::string::npos
)
190 pos
= lastEscapedChar
;
192 if(pos
== std::string::npos
)
194 else if(pos
+ 1 < scalar
.size())
195 scalar
.erase(pos
+ 2);
198 std::size_t pos
= scalar
.find_last_not_of('\n');
199 if(lastEscapedChar
!= std::string::npos
) {
200 if(pos
< lastEscapedChar
|| pos
== std::string::npos
)
201 pos
= lastEscapedChar
;
203 if(pos
== std::string::npos
)
205 else if(pos
< scalar
.size())
206 scalar
.erase(pos
+ 1);