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;
27 params
.leadingSpaces
= false;
30 // ********************************
31 // Phase #1: scan until line ending
33 std::size_t lastNonWhitespaceChar
= scalar
.size();
34 bool escapedNewline
= false;
35 while(!params
.end
.Matches(INPUT
) && !Exp::Break().Matches(INPUT
)) {
39 // document indicator?
40 if(INPUT
.column() == 0 && Exp::DocIndicator().Matches(INPUT
)) {
41 if(params
.onDocIndicator
== BREAK
)
43 else if(params
.onDocIndicator
== THROW
)
44 throw ParserException(INPUT
.mark(), ErrorMsg::DOC_IN_SCALAR
);
47 foundNonEmptyLine
= true;
48 pastOpeningBreak
= true;
50 // escaped newline? (only if we're escaping on slash)
51 if(params
.escape
== '\\' && Exp::EscBreak().Matches(INPUT
)) {
52 // eat escape character and get out (but preserve trailing whitespace!)
54 lastNonWhitespaceChar
= scalar
.size();
55 escapedNewline
= true;
60 if(INPUT
.peek() == params
.escape
) {
61 scalar
+= Exp::Escape(INPUT
);
62 lastNonWhitespaceChar
= scalar
.size();
66 // otherwise, just add the damn character
67 char ch
= INPUT
.get();
69 if(ch
!= ' ' && ch
!= '\t')
70 lastNonWhitespaceChar
= scalar
.size();
73 // eof? if we're looking to eat something, then we throw
76 throw ParserException(INPUT
.mark(), ErrorMsg::EOF_IN_SCALAR
);
81 if(params
.onDocIndicator
== BREAK
&& INPUT
.column() == 0 && Exp::DocIndicator().Matches(INPUT
))
84 // are we done via character match?
85 int n
= params
.end
.Match(INPUT
);
92 // do we remove trailing whitespace?
93 if(params
.fold
== FOLD_FLOW
)
94 scalar
.erase(lastNonWhitespaceChar
);
96 // ********************************
97 // Phase #2: eat line ending
98 n
= Exp::Break().Match(INPUT
);
101 // ********************************
102 // Phase #3: scan initial spaces
104 // first the required indentation
105 while(INPUT
.peek() == ' ' && (INPUT
.column() < params
.indent
|| (params
.detectIndent
&& !foundNonEmptyLine
)))
108 // update indent if we're auto-detecting
109 if(params
.detectIndent
&& !foundNonEmptyLine
)
110 params
.indent
= std::max(params
.indent
, INPUT
.column());
112 // and then the rest of the whitespace
113 while(Exp::Blank().Matches(INPUT
)) {
114 // we check for tabs that masquerade as indentation
115 if(INPUT
.peek() == '\t'&& INPUT
.column() < params
.indent
&& params
.onTabInIndentation
== THROW
)
116 throw ParserException(INPUT
.mark(), ErrorMsg::TAB_IN_INDENTATION
);
118 if(!params
.eatLeadingWhitespace
)
124 // was this an empty line?
125 bool nextEmptyLine
= Exp::Break().Matches(INPUT
);
126 bool nextMoreIndented
= Exp::Blank().Matches(INPUT
);
127 if(params
.fold
== FOLD_BLOCK
&& foldedNewlineCount
== 0 && nextEmptyLine
)
128 foldedNewlineStartedMoreIndented
= moreIndented
;
130 // for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
131 if(pastOpeningBreak
) {
132 switch(params
.fold
) {
137 if(!emptyLine
&& !nextEmptyLine
&& !moreIndented
&& !nextMoreIndented
&& INPUT
.column() >= params
.indent
)
139 else if(nextEmptyLine
)
140 foldedNewlineCount
++;
144 if(!nextEmptyLine
&& foldedNewlineCount
> 0) {
145 scalar
+= std::string(foldedNewlineCount
- 1, '\n');
146 if(foldedNewlineStartedMoreIndented
|| nextMoreIndented
| !foundNonEmptyLine
)
148 foldedNewlineCount
= 0;
154 else if(!emptyLine
&& !nextEmptyLine
&& !escapedNewline
)
160 emptyLine
= nextEmptyLine
;
161 moreIndented
= nextMoreIndented
;
162 pastOpeningBreak
= true;
164 // are we done via indentation?
165 if(!emptyLine
&& INPUT
.column() < params
.indent
) {
166 params
.leadingSpaces
= true;
172 if(params
.trimTrailingSpaces
) {
173 std::size_t pos
= scalar
.find_last_not_of(' ');
174 if(pos
< scalar
.size())
175 scalar
.erase(pos
+ 1);
178 switch(params
.chomp
) {
180 const std::size_t pos
= scalar
.find_last_not_of('\n');
181 if(pos
== std::string::npos
)
183 else if(pos
+ 1 < scalar
.size())
184 scalar
.erase(pos
+ 2);
187 const std::size_t pos
= scalar
.find_last_not_of('\n');
188 if(pos
== std::string::npos
)
190 else if(pos
< scalar
.size())
191 scalar
.erase(pos
+ 1);