Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
gaoqiong
yaml-cpp
Commits
b43f8271
Commit
b43f8271
authored
Sep 03, 2008
by
jbeder
Browse files
Set the eol style to native for all files.
parent
859ac5e5
Changes
42
Hide whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
1858 additions
and
1858 deletions
+1858
-1858
src/regex.h
src/regex.h
+90
-90
src/scalar.cpp
src/scalar.cpp
+108
-108
src/scalar.h
src/scalar.h
+37
-37
src/scanner.cpp
src/scanner.cpp
+276
-276
src/scanner.h
src/scanner.h
+85
-85
src/scanscalar.cpp
src/scanscalar.cpp
+154
-154
src/scanscalar.h
src/scanscalar.h
+35
-35
src/scantoken.cpp
src/scantoken.cpp
+405
-405
src/sequence.cpp
src/sequence.cpp
+175
-175
src/sequence.h
src/sequence.h
+41
-41
src/simplekey.cpp
src/simplekey.cpp
+102
-102
src/stream.cpp
src/stream.cpp
+53
-53
src/stream.h
src/stream.h
+25
-25
src/token.h
src/token.h
+68
-68
yaml-reader/main.cpp
yaml-reader/main.cpp
+41
-41
yaml-reader/tests.cpp
yaml-reader/tests.cpp
+88
-88
yaml-reader/tests.h
yaml-reader/tests.h
+6
-6
yaml-reader/tests/directives.yaml
yaml-reader/tests/directives.yaml
+4
-4
yaml-reader/tests/mixed.yaml
yaml-reader/tests/mixed.yaml
+31
-31
yaml-reader/tests/scalars.yaml
yaml-reader/tests/scalars.yaml
+34
-34
No files found.
src/regex.h
View file @
b43f8271
#pragma once
#include <vector>
#include <string>
#include <ios>
namespace
YAML
{
struct
Stream
;
enum
REGEX_OP
{
REGEX_EMPTY
,
REGEX_MATCH
,
REGEX_RANGE
,
REGEX_OR
,
REGEX_AND
,
REGEX_NOT
,
REGEX_SEQ
};
// simplified regular expressions
// . Only straightforward matches (no repeated characters)
// . Only matches from start of string
class
RegEx
{
private:
struct
Operator
{
virtual
~
Operator
()
{}
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
=
0
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
=
0
;
};
struct
MatchOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
struct
RangeOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
struct
OrOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
struct
AndOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
struct
NotOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
struct
SeqOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
public:
friend
struct
Operator
;
RegEx
();
RegEx
(
char
ch
);
RegEx
(
char
a
,
char
z
);
RegEx
(
const
std
::
string
&
str
,
REGEX_OP
op
=
REGEX_SEQ
);
RegEx
(
const
RegEx
&
rhs
);
~
RegEx
();
RegEx
&
operator
=
(
const
RegEx
&
rhs
);
bool
Matches
(
char
ch
)
const
;
bool
Matches
(
const
std
::
string
&
str
)
const
;
bool
Matches
(
std
::
istream
&
in
)
const
;
bool
Matches
(
Stream
&
in
)
const
;
int
Match
(
const
std
::
string
&
str
)
const
;
int
Match
(
std
::
istream
&
in
)
const
;
int
Match
(
Stream
&
in
)
const
;
friend
RegEx
operator
!
(
const
RegEx
&
ex
);
friend
RegEx
operator
||
(
const
RegEx
&
ex1
,
const
RegEx
&
ex2
);
friend
RegEx
operator
&&
(
const
RegEx
&
ex1
,
const
RegEx
&
ex2
);
friend
RegEx
operator
+
(
const
RegEx
&
ex1
,
const
RegEx
&
ex2
);
private:
RegEx
(
REGEX_OP
op
);
void
SetOp
();
private:
REGEX_OP
m_op
;
Operator
*
m_pOp
;
char
m_a
,
m_z
;
std
::
vector
<
RegEx
>
m_params
;
};
}
#pragma once
#include <vector>
#include <string>
#include <ios>
namespace
YAML
{
struct
Stream
;
enum
REGEX_OP
{
REGEX_EMPTY
,
REGEX_MATCH
,
REGEX_RANGE
,
REGEX_OR
,
REGEX_AND
,
REGEX_NOT
,
REGEX_SEQ
};
// simplified regular expressions
// . Only straightforward matches (no repeated characters)
// . Only matches from start of string
class
RegEx
{
private:
struct
Operator
{
virtual
~
Operator
()
{}
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
=
0
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
=
0
;
};
struct
MatchOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
struct
RangeOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
struct
OrOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
struct
AndOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
struct
NotOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
struct
SeqOperator
:
public
Operator
{
virtual
int
Match
(
const
std
::
string
&
str
,
const
RegEx
&
regex
)
const
;
virtual
int
Match
(
std
::
istream
&
in
,
const
RegEx
&
regex
)
const
;
};
public:
friend
struct
Operator
;
RegEx
();
RegEx
(
char
ch
);
RegEx
(
char
a
,
char
z
);
RegEx
(
const
std
::
string
&
str
,
REGEX_OP
op
=
REGEX_SEQ
);
RegEx
(
const
RegEx
&
rhs
);
~
RegEx
();
RegEx
&
operator
=
(
const
RegEx
&
rhs
);
bool
Matches
(
char
ch
)
const
;
bool
Matches
(
const
std
::
string
&
str
)
const
;
bool
Matches
(
std
::
istream
&
in
)
const
;
bool
Matches
(
Stream
&
in
)
const
;
int
Match
(
const
std
::
string
&
str
)
const
;
int
Match
(
std
::
istream
&
in
)
const
;
int
Match
(
Stream
&
in
)
const
;
friend
RegEx
operator
!
(
const
RegEx
&
ex
);
friend
RegEx
operator
||
(
const
RegEx
&
ex1
,
const
RegEx
&
ex2
);
friend
RegEx
operator
&&
(
const
RegEx
&
ex1
,
const
RegEx
&
ex2
);
friend
RegEx
operator
+
(
const
RegEx
&
ex1
,
const
RegEx
&
ex2
);
private:
RegEx
(
REGEX_OP
op
);
void
SetOp
();
private:
REGEX_OP
m_op
;
Operator
*
m_pOp
;
char
m_a
,
m_z
;
std
::
vector
<
RegEx
>
m_params
;
};
}
src/scalar.cpp
View file @
b43f8271
#include "crt.h"
#include "scalar.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "node.h"
#include <sstream>
namespace
YAML
{
Scalar
::
Scalar
()
{
}
Scalar
::~
Scalar
()
{
}
void
Scalar
::
Parse
(
Scanner
*
pScanner
,
const
ParserState
&
state
)
{
Token
&
token
=
pScanner
->
peek
();
m_data
=
token
.
value
;
pScanner
->
pop
();
}
void
Scalar
::
Write
(
std
::
ostream
&
out
,
int
indent
,
bool
startedLine
,
bool
onlyOneCharOnLine
)
{
out
<<
"
\"
"
;
for
(
unsigned
i
=
0
;
i
<
m_data
.
size
();
i
++
)
{
switch
(
m_data
[
i
])
{
case
'\\'
:
out
<<
"
\\\\
"
;
break
;
case
'\t'
:
out
<<
"
\\
t"
;
break
;
case
'\n'
:
out
<<
"
\\
n"
;
break
;
case
'\r'
:
out
<<
"
\\
r"
;
break
;
default:
out
<<
m_data
[
i
];
break
;
}
}
out
<<
"
\"\n
"
;
}
void
Scalar
::
Read
(
std
::
string
&
s
)
{
s
=
m_data
;
}
void
Scalar
::
Read
(
int
&
i
)
{
std
::
stringstream
data
(
m_data
);
data
>>
i
;
if
(
!
data
)
throw
InvalidScalar
();
}
void
Scalar
::
Read
(
unsigned
&
u
)
{
std
::
stringstream
data
(
m_data
);
data
>>
u
;
if
(
!
data
)
throw
InvalidScalar
();
}
void
Scalar
::
Read
(
long
&
l
)
{
std
::
stringstream
data
(
m_data
);
data
>>
l
;
if
(
!
data
)
throw
InvalidScalar
();
}
void
Scalar
::
Read
(
float
&
f
)
{
std
::
stringstream
data
(
m_data
);
data
>>
f
;
if
(
!
data
)
throw
InvalidScalar
();
}
void
Scalar
::
Read
(
double
&
d
)
{
std
::
stringstream
data
(
m_data
);
data
>>
d
;
if
(
!
data
)
throw
InvalidScalar
();
}
void
Scalar
::
Read
(
char
&
c
)
{
std
::
stringstream
data
(
m_data
);
data
>>
c
;
if
(
!
data
)
throw
InvalidScalar
();
}
int
Scalar
::
Compare
(
Content
*
pContent
)
{
return
-
pContent
->
Compare
(
this
);
}
int
Scalar
::
Compare
(
Scalar
*
pScalar
)
{
if
(
m_data
<
pScalar
->
m_data
)
return
-
1
;
else
if
(
m_data
>
pScalar
->
m_data
)
return
1
;
else
return
0
;
}
}
#include "crt.h"
#include "scalar.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "node.h"
#include <sstream>
namespace
YAML
{
Scalar
::
Scalar
()
{
}
Scalar
::~
Scalar
()
{
}
void
Scalar
::
Parse
(
Scanner
*
pScanner
,
const
ParserState
&
state
)
{
Token
&
token
=
pScanner
->
peek
();
m_data
=
token
.
value
;
pScanner
->
pop
();
}
void
Scalar
::
Write
(
std
::
ostream
&
out
,
int
indent
,
bool
startedLine
,
bool
onlyOneCharOnLine
)
{
out
<<
"
\"
"
;
for
(
unsigned
i
=
0
;
i
<
m_data
.
size
();
i
++
)
{
switch
(
m_data
[
i
])
{
case
'\\'
:
out
<<
"
\\\\
"
;
break
;
case
'\t'
:
out
<<
"
\\
t"
;
break
;
case
'\n'
:
out
<<
"
\\
n"
;
break
;
case
'\r'
:
out
<<
"
\\
r"
;
break
;
default:
out
<<
m_data
[
i
];
break
;
}
}
out
<<
"
\"\n
"
;
}
void
Scalar
::
Read
(
std
::
string
&
s
)
{
s
=
m_data
;
}
void
Scalar
::
Read
(
int
&
i
)
{
std
::
stringstream
data
(
m_data
);
data
>>
i
;
if
(
!
data
)
throw
InvalidScalar
();
}
void
Scalar
::
Read
(
unsigned
&
u
)
{
std
::
stringstream
data
(
m_data
);
data
>>
u
;
if
(
!
data
)
throw
InvalidScalar
();
}
void
Scalar
::
Read
(
long
&
l
)
{
std
::
stringstream
data
(
m_data
);
data
>>
l
;
if
(
!
data
)
throw
InvalidScalar
();
}
void
Scalar
::
Read
(
float
&
f
)
{
std
::
stringstream
data
(
m_data
);
data
>>
f
;
if
(
!
data
)
throw
InvalidScalar
();
}
void
Scalar
::
Read
(
double
&
d
)
{
std
::
stringstream
data
(
m_data
);
data
>>
d
;
if
(
!
data
)
throw
InvalidScalar
();
}
void
Scalar
::
Read
(
char
&
c
)
{
std
::
stringstream
data
(
m_data
);
data
>>
c
;
if
(
!
data
)
throw
InvalidScalar
();
}
int
Scalar
::
Compare
(
Content
*
pContent
)
{
return
-
pContent
->
Compare
(
this
);
}
int
Scalar
::
Compare
(
Scalar
*
pScalar
)
{
if
(
m_data
<
pScalar
->
m_data
)
return
-
1
;
else
if
(
m_data
>
pScalar
->
m_data
)
return
1
;
else
return
0
;
}
}
src/scalar.h
View file @
b43f8271
#pragma once
#include "content.h"
#include <string>
namespace
YAML
{
class
Scalar
:
public
Content
{
public:
Scalar
();
virtual
~
Scalar
();
virtual
void
Parse
(
Scanner
*
pScanner
,
const
ParserState
&
state
);
virtual
void
Write
(
std
::
ostream
&
out
,
int
indent
,
bool
startedLine
,
bool
onlyOneCharOnLine
);
virtual
bool
IsScalar
()
const
{
return
true
;
}
// extraction
virtual
void
Read
(
std
::
string
&
s
);
virtual
void
Read
(
int
&
i
);
virtual
void
Read
(
unsigned
&
u
);
virtual
void
Read
(
long
&
l
);
virtual
void
Read
(
float
&
f
);
virtual
void
Read
(
double
&
d
);
virtual
void
Read
(
char
&
c
);
// ordering
virtual
int
Compare
(
Content
*
pContent
);
virtual
int
Compare
(
Scalar
*
pScalar
);
virtual
int
Compare
(
Sequence
*
pSeq
)
{
return
-
1
;
}
virtual
int
Compare
(
Map
*
pMap
)
{
return
-
1
;
}
protected:
std
::
string
m_data
;
};
}
#pragma once
#include "content.h"
#include <string>
namespace
YAML
{
class
Scalar
:
public
Content
{
public:
Scalar
();
virtual
~
Scalar
();
virtual
void
Parse
(
Scanner
*
pScanner
,
const
ParserState
&
state
);
virtual
void
Write
(
std
::
ostream
&
out
,
int
indent
,
bool
startedLine
,
bool
onlyOneCharOnLine
);
virtual
bool
IsScalar
()
const
{
return
true
;
}
// extraction
virtual
void
Read
(
std
::
string
&
s
);
virtual
void
Read
(
int
&
i
);
virtual
void
Read
(
unsigned
&
u
);
virtual
void
Read
(
long
&
l
);
virtual
void
Read
(
float
&
f
);
virtual
void
Read
(
double
&
d
);
virtual
void
Read
(
char
&
c
);
// ordering
virtual
int
Compare
(
Content
*
pContent
);
virtual
int
Compare
(
Scalar
*
pScalar
);
virtual
int
Compare
(
Sequence
*
pSeq
)
{
return
-
1
;
}
virtual
int
Compare
(
Map
*
pMap
)
{
return
-
1
;
}
protected:
std
::
string
m_data
;
};
}
src/scanner.cpp
View file @
b43f8271
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
#include <cassert>
namespace
YAML
{
Scanner
::
Scanner
(
std
::
istream
&
in
)
:
INPUT
(
in
),
m_startedStream
(
false
),
m_endedStream
(
false
),
m_simpleKeyAllowed
(
false
),
m_flowLevel
(
0
)
{
}
Scanner
::~
Scanner
()
{
}
// empty
// . Returns true if there are no more tokens to be read
bool
Scanner
::
empty
()
{
EnsureTokensInQueue
();
return
m_tokens
.
empty
();
}
// pop
// . Simply removes the next token on the queue.
void
Scanner
::
pop
()
{
EnsureTokensInQueue
();
if
(
!
m_tokens
.
empty
())
m_tokens
.
pop
();
}
// peek
// . Returns (but does not remove) the next token on the queue.
Token
&
Scanner
::
peek
()
{
EnsureTokensInQueue
();
assert
(
!
m_tokens
.
empty
());
// should we be asserting here? I mean, we really just be checking
// if it's empty before peeking.
return
m_tokens
.
front
();
}
// EnsureTokensInQueue
// . Scan until there's a valid token at the front of the queue,
// or we're sure the queue is empty.
void
Scanner
::
EnsureTokensInQueue
()
{
while
(
1
)
{
if
(
!
m_tokens
.
empty
())
{
Token
&
token
=
m_tokens
.
front
();
// if this guy's valid, then we're done
if
(
token
.
status
==
TS_VALID
)
return
;
// here's where we clean up the impossible tokens
if
(
token
.
status
==
TS_INVALID
)
{
m_tokens
.
pop
();
continue
;
}
// note: what's left are the unverified tokens
}
// no token? maybe we've actually finished
if
(
m_endedStream
)
return
;
// no? then scan...
ScanNextToken
();
}
}
// ScanNextToken
// . The main scanning function; here we branch out and
// scan whatever the next token should be.
void
Scanner
::
ScanNextToken
()
{
if
(
m_endedStream
)
return
;
if
(
!
m_startedStream
)
return
StartStream
();
// get rid of whitespace, etc. (in between tokens it should be irrelevent)
ScanToNextToken
();
// check the latest simple key
VerifySimpleKey
();
// maybe need to end some blocks
PopIndentTo
(
INPUT
.
column
);
// *****
// And now branch based on the next few characters!
// *****
// end of stream
if
(
INPUT
.
peek
()
==
EOF
)
return
EndStream
();
if
(
INPUT
.
column
==
0
&&
INPUT
.
peek
()
==
Keys
::
Directive
)
return
ScanDirective
();
// document token
if
(
INPUT
.
column
==
0
&&
Exp
::
DocStart
.
Matches
(
INPUT
))
return
ScanDocStart
();
if
(
INPUT
.
column
==
0
&&
Exp
::
DocEnd
.
Matches
(
INPUT
))
return
ScanDocEnd
();
// flow start/end/entry
if
(
INPUT
.
peek
()
==
Keys
::
FlowSeqStart
||
INPUT
.
peek
()
==
Keys
::
FlowMapStart
)
return
ScanFlowStart
();
if
(
INPUT
.
peek
()
==
Keys
::
FlowSeqEnd
||
INPUT
.
peek
()
==
Keys
::
FlowMapEnd
)
return
ScanFlowEnd
();
if
(
INPUT
.
peek
()
==
Keys
::
FlowEntry
)
return
ScanFlowEntry
();
// block/map stuff
if
(
Exp
::
BlockEntry
.
Matches
(
INPUT
))
return
ScanBlockEntry
();
if
((
m_flowLevel
==
0
?
Exp
::
Key
:
Exp
::
KeyInFlow
).
Matches
(
INPUT
))
return
ScanKey
();
if
((
m_flowLevel
==
0
?
Exp
::
Value
:
Exp
::
ValueInFlow
).
Matches
(
INPUT
))
return
ScanValue
();
// alias/anchor
if
(
INPUT
.
peek
()
==
Keys
::
Alias
||
INPUT
.
peek
()
==
Keys
::
Anchor
)
return
ScanAnchorOrAlias
();
// tag
if
(
INPUT
.
peek
()
==
Keys
::
Tag
)
return
ScanTag
();
// special scalars
if
(
m_flowLevel
==
0
&&
(
INPUT
.
peek
()
==
Keys
::
LiteralScalar
||
INPUT
.
peek
()
==
Keys
::
FoldedScalar
))
return
ScanBlockScalar
();
if
(
INPUT
.
peek
()
==
'\''
||
INPUT
.
peek
()
==
'\"'
)
return
ScanQuotedScalar
();
// plain scalars
if
((
m_flowLevel
==
0
?
Exp
::
PlainScalar
:
Exp
::
PlainScalarInFlow
).
Matches
(
INPUT
))
return
ScanPlainScalar
();
// don't know what it is!
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
UNKNOWN_TOKEN
);
}
// ScanToNextToken
// . Eats input until we reach the next token-like thing.
void
Scanner
::
ScanToNextToken
()
{
while
(
1
)
{
// first eat whitespace
while
(
IsWhitespaceToBeEaten
(
INPUT
.
peek
()))
INPUT
.
eat
(
1
);
// then eat a comment
if
(
Exp
::
Comment
.
Matches
(
INPUT
))
{
// eat until line break
while
(
INPUT
&&
!
Exp
::
Break
.
Matches
(
INPUT
))
INPUT
.
eat
(
1
);
}
// if it's NOT a line break, then we're done!
if
(
!
Exp
::
Break
.
Matches
(
INPUT
))
break
;
// otherwise, let's eat the line break and keep going
int
n
=
Exp
::
Break
.
Match
(
INPUT
);
INPUT
.
eat
(
n
);
// oh yeah, and let's get rid of that simple key
VerifySimpleKey
();
// new line - we may be able to accept a simple key now
if
(
m_flowLevel
==
0
)
m_simpleKeyAllowed
=
true
;
}
}
///////////////////////////////////////////////////////////////////////
// Misc. helpers
// IsWhitespaceToBeEaten
// . We can eat whitespace if:
// 1. It's a space
// 2. It's a tab, and we're either:
// a. In the flow context
// b. In the block context but not where a simple key could be allowed
// (i.e., not at the beginning of a line, or following '-', '?', or ':')
bool
Scanner
::
IsWhitespaceToBeEaten
(
char
ch
)
{
if
(
ch
==
' '
)
return
true
;
if
(
ch
==
'\t'
&&
(
m_flowLevel
>=
0
||
!
m_simpleKeyAllowed
))
return
true
;
return
false
;
}
// StartStream
// . Set the initial conditions for starting a stream.
void
Scanner
::
StartStream
()
{
m_startedStream
=
true
;
m_simpleKeyAllowed
=
true
;
m_indents
.
push
(
-
1
);
}
// EndStream
// . Close out the stream, finish up, etc.
void
Scanner
::
EndStream
()
{
// force newline
if
(
INPUT
.
column
>
0
)
INPUT
.
column
=
0
;
PopIndentTo
(
-
1
);
VerifyAllSimpleKeys
();
m_simpleKeyAllowed
=
false
;
m_endedStream
=
true
;
}
// PushIndentTo
// . Pushes an indentation onto the stack, and enqueues the
// proper token (sequence start or mapping start).
// . Returns the token it generates (if any).
Token
*
Scanner
::
PushIndentTo
(
int
column
,
bool
sequence
)
{
// are we in flow?
if
(
m_flowLevel
>
0
)
return
0
;
// is this actually an indentation?
if
(
column
<=
m_indents
.
top
())
return
0
;
// now push
m_indents
.
push
(
column
);
if
(
sequence
)
m_tokens
.
push
(
Token
(
TT_BLOCK_SEQ_START
,
INPUT
.
line
,
INPUT
.
column
));
else
m_tokens
.
push
(
Token
(
TT_BLOCK_MAP_START
,
INPUT
.
line
,
INPUT
.
column
));
return
&
m_tokens
.
back
();
}
// PopIndentTo
// . Pops indentations off the stack until we reach 'column' indentation,
// and enqueues the proper token each time.
void
Scanner
::
PopIndentTo
(
int
column
)
{
// are we in flow?
if
(
m_flowLevel
>
0
)
return
;
// now pop away
while
(
!
m_indents
.
empty
()
&&
m_indents
.
top
()
>
column
)
{
m_indents
.
pop
();
m_tokens
.
push
(
Token
(
TT_BLOCK_END
,
INPUT
.
line
,
INPUT
.
column
));
}
}
}
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
#include <cassert>
namespace
YAML
{
Scanner
::
Scanner
(
std
::
istream
&
in
)
:
INPUT
(
in
),
m_startedStream
(
false
),
m_endedStream
(
false
),
m_simpleKeyAllowed
(
false
),
m_flowLevel
(
0
)
{
}
Scanner
::~
Scanner
()
{
}
// empty
// . Returns true if there are no more tokens to be read
bool
Scanner
::
empty
()
{
EnsureTokensInQueue
();
return
m_tokens
.
empty
();
}
// pop
// . Simply removes the next token on the queue.
void
Scanner
::
pop
()
{
EnsureTokensInQueue
();
if
(
!
m_tokens
.
empty
())
m_tokens
.
pop
();
}
// peek
// . Returns (but does not remove) the next token on the queue.
Token
&
Scanner
::
peek
()
{
EnsureTokensInQueue
();
assert
(
!
m_tokens
.
empty
());
// should we be asserting here? I mean, we really just be checking
// if it's empty before peeking.
return
m_tokens
.
front
();
}
// EnsureTokensInQueue
// . Scan until there's a valid token at the front of the queue,
// or we're sure the queue is empty.
void
Scanner
::
EnsureTokensInQueue
()
{
while
(
1
)
{
if
(
!
m_tokens
.
empty
())
{
Token
&
token
=
m_tokens
.
front
();
// if this guy's valid, then we're done
if
(
token
.
status
==
TS_VALID
)
return
;
// here's where we clean up the impossible tokens
if
(
token
.
status
==
TS_INVALID
)
{
m_tokens
.
pop
();
continue
;
}
// note: what's left are the unverified tokens
}
// no token? maybe we've actually finished
if
(
m_endedStream
)
return
;
// no? then scan...
ScanNextToken
();
}
}
// ScanNextToken
// . The main scanning function; here we branch out and
// scan whatever the next token should be.
void
Scanner
::
ScanNextToken
()
{
if
(
m_endedStream
)
return
;
if
(
!
m_startedStream
)
return
StartStream
();
// get rid of whitespace, etc. (in between tokens it should be irrelevent)
ScanToNextToken
();
// check the latest simple key
VerifySimpleKey
();
// maybe need to end some blocks
PopIndentTo
(
INPUT
.
column
);
// *****
// And now branch based on the next few characters!
// *****
// end of stream
if
(
INPUT
.
peek
()
==
EOF
)
return
EndStream
();
if
(
INPUT
.
column
==
0
&&
INPUT
.
peek
()
==
Keys
::
Directive
)
return
ScanDirective
();
// document token
if
(
INPUT
.
column
==
0
&&
Exp
::
DocStart
.
Matches
(
INPUT
))
return
ScanDocStart
();
if
(
INPUT
.
column
==
0
&&
Exp
::
DocEnd
.
Matches
(
INPUT
))
return
ScanDocEnd
();
// flow start/end/entry
if
(
INPUT
.
peek
()
==
Keys
::
FlowSeqStart
||
INPUT
.
peek
()
==
Keys
::
FlowMapStart
)
return
ScanFlowStart
();
if
(
INPUT
.
peek
()
==
Keys
::
FlowSeqEnd
||
INPUT
.
peek
()
==
Keys
::
FlowMapEnd
)
return
ScanFlowEnd
();
if
(
INPUT
.
peek
()
==
Keys
::
FlowEntry
)
return
ScanFlowEntry
();
// block/map stuff
if
(
Exp
::
BlockEntry
.
Matches
(
INPUT
))
return
ScanBlockEntry
();
if
((
m_flowLevel
==
0
?
Exp
::
Key
:
Exp
::
KeyInFlow
).
Matches
(
INPUT
))
return
ScanKey
();
if
((
m_flowLevel
==
0
?
Exp
::
Value
:
Exp
::
ValueInFlow
).
Matches
(
INPUT
))
return
ScanValue
();
// alias/anchor
if
(
INPUT
.
peek
()
==
Keys
::
Alias
||
INPUT
.
peek
()
==
Keys
::
Anchor
)
return
ScanAnchorOrAlias
();
// tag
if
(
INPUT
.
peek
()
==
Keys
::
Tag
)
return
ScanTag
();
// special scalars
if
(
m_flowLevel
==
0
&&
(
INPUT
.
peek
()
==
Keys
::
LiteralScalar
||
INPUT
.
peek
()
==
Keys
::
FoldedScalar
))
return
ScanBlockScalar
();
if
(
INPUT
.
peek
()
==
'\''
||
INPUT
.
peek
()
==
'\"'
)
return
ScanQuotedScalar
();
// plain scalars
if
((
m_flowLevel
==
0
?
Exp
::
PlainScalar
:
Exp
::
PlainScalarInFlow
).
Matches
(
INPUT
))
return
ScanPlainScalar
();
// don't know what it is!
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
UNKNOWN_TOKEN
);
}
// ScanToNextToken
// . Eats input until we reach the next token-like thing.
void
Scanner
::
ScanToNextToken
()
{
while
(
1
)
{
// first eat whitespace
while
(
IsWhitespaceToBeEaten
(
INPUT
.
peek
()))
INPUT
.
eat
(
1
);
// then eat a comment
if
(
Exp
::
Comment
.
Matches
(
INPUT
))
{
// eat until line break
while
(
INPUT
&&
!
Exp
::
Break
.
Matches
(
INPUT
))
INPUT
.
eat
(
1
);
}
// if it's NOT a line break, then we're done!
if
(
!
Exp
::
Break
.
Matches
(
INPUT
))
break
;
// otherwise, let's eat the line break and keep going
int
n
=
Exp
::
Break
.
Match
(
INPUT
);
INPUT
.
eat
(
n
);
// oh yeah, and let's get rid of that simple key
VerifySimpleKey
();
// new line - we may be able to accept a simple key now
if
(
m_flowLevel
==
0
)
m_simpleKeyAllowed
=
true
;
}
}
///////////////////////////////////////////////////////////////////////
// Misc. helpers
// IsWhitespaceToBeEaten
// . We can eat whitespace if:
// 1. It's a space
// 2. It's a tab, and we're either:
// a. In the flow context
// b. In the block context but not where a simple key could be allowed
// (i.e., not at the beginning of a line, or following '-', '?', or ':')
bool
Scanner
::
IsWhitespaceToBeEaten
(
char
ch
)
{
if
(
ch
==
' '
)
return
true
;
if
(
ch
==
'\t'
&&
(
m_flowLevel
>=
0
||
!
m_simpleKeyAllowed
))
return
true
;
return
false
;
}
// StartStream
// . Set the initial conditions for starting a stream.
void
Scanner
::
StartStream
()
{
m_startedStream
=
true
;
m_simpleKeyAllowed
=
true
;
m_indents
.
push
(
-
1
);
}
// EndStream
// . Close out the stream, finish up, etc.
void
Scanner
::
EndStream
()
{
// force newline
if
(
INPUT
.
column
>
0
)
INPUT
.
column
=
0
;
PopIndentTo
(
-
1
);
VerifyAllSimpleKeys
();
m_simpleKeyAllowed
=
false
;
m_endedStream
=
true
;
}
// PushIndentTo
// . Pushes an indentation onto the stack, and enqueues the
// proper token (sequence start or mapping start).
// . Returns the token it generates (if any).
Token
*
Scanner
::
PushIndentTo
(
int
column
,
bool
sequence
)
{
// are we in flow?
if
(
m_flowLevel
>
0
)
return
0
;
// is this actually an indentation?
if
(
column
<=
m_indents
.
top
())
return
0
;
// now push
m_indents
.
push
(
column
);
if
(
sequence
)
m_tokens
.
push
(
Token
(
TT_BLOCK_SEQ_START
,
INPUT
.
line
,
INPUT
.
column
));
else
m_tokens
.
push
(
Token
(
TT_BLOCK_MAP_START
,
INPUT
.
line
,
INPUT
.
column
));
return
&
m_tokens
.
back
();
}
// PopIndentTo
// . Pops indentations off the stack until we reach 'column' indentation,
// and enqueues the proper token each time.
void
Scanner
::
PopIndentTo
(
int
column
)
{
// are we in flow?
if
(
m_flowLevel
>
0
)
return
;
// now pop away
while
(
!
m_indents
.
empty
()
&&
m_indents
.
top
()
>
column
)
{
m_indents
.
pop
();
m_tokens
.
push
(
Token
(
TT_BLOCK_END
,
INPUT
.
line
,
INPUT
.
column
));
}
}
}
src/scanner.h
View file @
b43f8271
#pragma once
#include <ios>
#include <string>
#include <queue>
#include <stack>
#include <set>
#include "stream.h"
#include "token.h"
namespace
YAML
{
class
Scanner
{
public:
Scanner
(
std
::
istream
&
in
);
~
Scanner
();
// token queue management (hopefully this looks kinda stl-ish)
bool
empty
();
void
pop
();
Token
&
peek
();
private:
// scanning
void
EnsureTokensInQueue
();
void
ScanNextToken
();
void
ScanToNextToken
();
void
StartStream
();
void
EndStream
();
Token
*
PushIndentTo
(
int
column
,
bool
sequence
);
void
PopIndentTo
(
int
column
);
// checking input
void
InsertSimpleKey
();
bool
VerifySimpleKey
();
void
VerifyAllSimpleKeys
();
bool
IsWhitespaceToBeEaten
(
char
ch
);
struct
SimpleKey
{
SimpleKey
(
int
pos_
,
int
line_
,
int
column_
,
int
flowLevel_
);
void
Validate
();
void
Invalidate
();
int
pos
,
line
,
column
,
flowLevel
;
Token
*
pMapStart
,
*
pKey
;
};
// and the tokens
void
ScanDirective
();
void
ScanDocStart
();
void
ScanDocEnd
();
void
ScanBlockSeqStart
();
void
ScanBlockMapSTart
();
void
ScanBlockEnd
();
void
ScanBlockEntry
();
void
ScanFlowStart
();
void
ScanFlowEnd
();
void
ScanFlowEntry
();
void
ScanKey
();
void
ScanValue
();
void
ScanAnchorOrAlias
();
void
ScanTag
();
void
ScanPlainScalar
();
void
ScanQuotedScalar
();
void
ScanBlockScalar
();
private:
// the stream
Stream
INPUT
;
// the output (tokens)
std
::
queue
<
Token
>
m_tokens
;
// state info
bool
m_startedStream
,
m_endedStream
;
bool
m_simpleKeyAllowed
;
int
m_flowLevel
;
// number of unclosed '[' and '{' indicators
bool
m_isLastKeyValid
;
std
::
stack
<
SimpleKey
>
m_simpleKeys
;
std
::
stack
<
int
>
m_indents
;
};
}
#pragma once
#include <ios>
#include <string>
#include <queue>
#include <stack>
#include <set>
#include "stream.h"
#include "token.h"
namespace
YAML
{
class
Scanner
{
public:
Scanner
(
std
::
istream
&
in
);
~
Scanner
();
// token queue management (hopefully this looks kinda stl-ish)
bool
empty
();
void
pop
();
Token
&
peek
();
private:
// scanning
void
EnsureTokensInQueue
();
void
ScanNextToken
();
void
ScanToNextToken
();
void
StartStream
();
void
EndStream
();
Token
*
PushIndentTo
(
int
column
,
bool
sequence
);
void
PopIndentTo
(
int
column
);
// checking input
void
InsertSimpleKey
();
bool
VerifySimpleKey
();
void
VerifyAllSimpleKeys
();
bool
IsWhitespaceToBeEaten
(
char
ch
);
struct
SimpleKey
{
SimpleKey
(
int
pos_
,
int
line_
,
int
column_
,
int
flowLevel_
);
void
Validate
();
void
Invalidate
();
int
pos
,
line
,
column
,
flowLevel
;
Token
*
pMapStart
,
*
pKey
;
};
// and the tokens
void
ScanDirective
();
void
ScanDocStart
();
void
ScanDocEnd
();
void
ScanBlockSeqStart
();
void
ScanBlockMapSTart
();
void
ScanBlockEnd
();
void
ScanBlockEntry
();
void
ScanFlowStart
();
void
ScanFlowEnd
();
void
ScanFlowEntry
();
void
ScanKey
();
void
ScanValue
();
void
ScanAnchorOrAlias
();
void
ScanTag
();
void
ScanPlainScalar
();
void
ScanQuotedScalar
();
void
ScanBlockScalar
();
private:
// the stream
Stream
INPUT
;
// the output (tokens)
std
::
queue
<
Token
>
m_tokens
;
// state info
bool
m_startedStream
,
m_endedStream
;
bool
m_simpleKeyAllowed
;
int
m_flowLevel
;
// number of unclosed '[' and '{' indicators
bool
m_isLastKeyValid
;
std
::
stack
<
SimpleKey
>
m_simpleKeys
;
std
::
stack
<
int
>
m_indents
;
};
}
src/scanscalar.cpp
View file @
b43f8271
#include "crt.h"
#include "scanscalar.h"
#include "scanner.h"
#include "exp.h"
#include "exceptions.h"
#include "token.h"
namespace
YAML
{
// ScanScalar
// . This is where the scalar magic happens.
//
// . We do the scanning in three phases:
// 1. Scan until newline
// 2. Eat newline
// 3. Scan leading blanks.
//
// . Depending on the parameters given, we store or stop
// and different places in the above flow.
std
::
string
ScanScalar
(
Stream
&
INPUT
,
ScanScalarParams
&
params
)
{
bool
foundNonEmptyLine
=
false
,
pastOpeningBreak
=
false
;
bool
emptyLine
=
false
,
moreIndented
=
false
;
std
::
string
scalar
;
params
.
leadingSpaces
=
false
;
while
(
INPUT
)
{
// ********************************
// Phase #1: scan until line ending
while
(
!
params
.
end
.
Matches
(
INPUT
)
&&
!
Exp
::
Break
.
Matches
(
INPUT
))
{
if
(
INPUT
.
peek
()
==
EOF
)
break
;
// document indicator?
if
(
INPUT
.
column
==
0
&&
Exp
::
DocIndicator
.
Matches
(
INPUT
))
{
if
(
params
.
onDocIndicator
==
BREAK
)
break
;
else
if
(
params
.
onDocIndicator
==
THROW
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
DOC_IN_SCALAR
);
}
foundNonEmptyLine
=
true
;
pastOpeningBreak
=
true
;
// escaped newline? (only if we're escaping on slash)
if
(
params
.
escape
==
'\\'
&&
Exp
::
EscBreak
.
Matches
(
INPUT
))
{
int
n
=
Exp
::
EscBreak
.
Match
(
INPUT
);
INPUT
.
eat
(
n
);
continue
;
}
// escape this?
if
(
INPUT
.
peek
()
==
params
.
escape
)
{
scalar
+=
Exp
::
Escape
(
INPUT
);
continue
;
}
// otherwise, just add the damn character
scalar
+=
INPUT
.
get
();
}
// eof? if we're looking to eat something, then we throw
if
(
INPUT
.
peek
()
==
EOF
)
{
if
(
params
.
eatEnd
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
EOF_IN_SCALAR
);
break
;
}
// doc indicator?
if
(
params
.
onDocIndicator
==
BREAK
&&
INPUT
.
column
==
0
&&
Exp
::
DocIndicator
.
Matches
(
INPUT
))
break
;
// are we done via character match?
int
n
=
params
.
end
.
Match
(
INPUT
);
if
(
n
>=
0
)
{
if
(
params
.
eatEnd
)
INPUT
.
eat
(
n
);
break
;
}
// ********************************
// Phase #2: eat line ending
n
=
Exp
::
Break
.
Match
(
INPUT
);
INPUT
.
eat
(
n
);
// ********************************
// Phase #3: scan initial spaces
// first the required indentation
while
(
INPUT
.
peek
()
==
' '
&&
(
INPUT
.
column
<
params
.
indent
||
(
params
.
detectIndent
&&
!
foundNonEmptyLine
)))
INPUT
.
eat
(
1
);
// update indent if we're auto-detecting
if
(
params
.
detectIndent
&&
!
foundNonEmptyLine
)
params
.
indent
=
std
::
max
(
params
.
indent
,
INPUT
.
column
);
// and then the rest of the whitespace
while
(
Exp
::
Blank
.
Matches
(
INPUT
))
{
// we check for tabs that masquerade as indentation
if
(
INPUT
.
peek
()
==
'\t'
&&
INPUT
.
column
<
params
.
indent
&&
params
.
onTabInIndentation
==
THROW
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
TAB_IN_INDENTATION
);
if
(
!
params
.
eatLeadingWhitespace
)
break
;
INPUT
.
eat
(
1
);
}
// was this an empty line?
bool
nextEmptyLine
=
Exp
::
Break
.
Matches
(
INPUT
);
bool
nextMoreIndented
=
(
INPUT
.
peek
()
==
' '
);
// for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
bool
useNewLine
=
pastOpeningBreak
;
// and for folded scalars, we don't fold the very last newline to a space
if
(
params
.
fold
&&
!
emptyLine
&&
INPUT
.
column
<
params
.
indent
)
useNewLine
=
false
;
if
(
useNewLine
)
{
if
(
params
.
fold
&&
!
emptyLine
&&
!
nextEmptyLine
&&
!
moreIndented
&&
!
nextMoreIndented
)
scalar
+=
" "
;
else
scalar
+=
"
\n
"
;
}
emptyLine
=
nextEmptyLine
;
moreIndented
=
nextMoreIndented
;
pastOpeningBreak
=
true
;
// are we done via indentation?
if
(
!
emptyLine
&&
INPUT
.
column
<
params
.
indent
)
{
params
.
leadingSpaces
=
true
;
break
;
}
}
// post-processing
if
(
params
.
trimTrailingSpaces
)
{
unsigned
pos
=
scalar
.
find_last_not_of
(
' '
);
if
(
pos
<
scalar
.
size
())
scalar
.
erase
(
pos
+
1
);
}
if
(
params
.
chomp
<=
0
)
{
unsigned
pos
=
scalar
.
find_last_not_of
(
'\n'
);
if
(
params
.
chomp
==
0
&&
pos
+
1
<
scalar
.
size
())
scalar
.
erase
(
pos
+
2
);
else
if
(
params
.
chomp
==
-
1
&&
pos
<
scalar
.
size
())
scalar
.
erase
(
pos
+
1
);
}
return
scalar
;
}
}
#include "crt.h"
#include "scanscalar.h"
#include "scanner.h"
#include "exp.h"
#include "exceptions.h"
#include "token.h"
namespace
YAML
{
// ScanScalar
// . This is where the scalar magic happens.
//
// . We do the scanning in three phases:
// 1. Scan until newline
// 2. Eat newline
// 3. Scan leading blanks.
//
// . Depending on the parameters given, we store or stop
// and different places in the above flow.
std
::
string
ScanScalar
(
Stream
&
INPUT
,
ScanScalarParams
&
params
)
{
bool
foundNonEmptyLine
=
false
,
pastOpeningBreak
=
false
;
bool
emptyLine
=
false
,
moreIndented
=
false
;
std
::
string
scalar
;
params
.
leadingSpaces
=
false
;
while
(
INPUT
)
{
// ********************************
// Phase #1: scan until line ending
while
(
!
params
.
end
.
Matches
(
INPUT
)
&&
!
Exp
::
Break
.
Matches
(
INPUT
))
{
if
(
INPUT
.
peek
()
==
EOF
)
break
;
// document indicator?
if
(
INPUT
.
column
==
0
&&
Exp
::
DocIndicator
.
Matches
(
INPUT
))
{
if
(
params
.
onDocIndicator
==
BREAK
)
break
;
else
if
(
params
.
onDocIndicator
==
THROW
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
DOC_IN_SCALAR
);
}
foundNonEmptyLine
=
true
;
pastOpeningBreak
=
true
;
// escaped newline? (only if we're escaping on slash)
if
(
params
.
escape
==
'\\'
&&
Exp
::
EscBreak
.
Matches
(
INPUT
))
{
int
n
=
Exp
::
EscBreak
.
Match
(
INPUT
);
INPUT
.
eat
(
n
);
continue
;
}
// escape this?
if
(
INPUT
.
peek
()
==
params
.
escape
)
{
scalar
+=
Exp
::
Escape
(
INPUT
);
continue
;
}
// otherwise, just add the damn character
scalar
+=
INPUT
.
get
();
}
// eof? if we're looking to eat something, then we throw
if
(
INPUT
.
peek
()
==
EOF
)
{
if
(
params
.
eatEnd
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
EOF_IN_SCALAR
);
break
;
}
// doc indicator?
if
(
params
.
onDocIndicator
==
BREAK
&&
INPUT
.
column
==
0
&&
Exp
::
DocIndicator
.
Matches
(
INPUT
))
break
;
// are we done via character match?
int
n
=
params
.
end
.
Match
(
INPUT
);
if
(
n
>=
0
)
{
if
(
params
.
eatEnd
)
INPUT
.
eat
(
n
);
break
;
}
// ********************************
// Phase #2: eat line ending
n
=
Exp
::
Break
.
Match
(
INPUT
);
INPUT
.
eat
(
n
);
// ********************************
// Phase #3: scan initial spaces
// first the required indentation
while
(
INPUT
.
peek
()
==
' '
&&
(
INPUT
.
column
<
params
.
indent
||
(
params
.
detectIndent
&&
!
foundNonEmptyLine
)))
INPUT
.
eat
(
1
);
// update indent if we're auto-detecting
if
(
params
.
detectIndent
&&
!
foundNonEmptyLine
)
params
.
indent
=
std
::
max
(
params
.
indent
,
INPUT
.
column
);
// and then the rest of the whitespace
while
(
Exp
::
Blank
.
Matches
(
INPUT
))
{
// we check for tabs that masquerade as indentation
if
(
INPUT
.
peek
()
==
'\t'
&&
INPUT
.
column
<
params
.
indent
&&
params
.
onTabInIndentation
==
THROW
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
TAB_IN_INDENTATION
);
if
(
!
params
.
eatLeadingWhitespace
)
break
;
INPUT
.
eat
(
1
);
}
// was this an empty line?
bool
nextEmptyLine
=
Exp
::
Break
.
Matches
(
INPUT
);
bool
nextMoreIndented
=
(
INPUT
.
peek
()
==
' '
);
// for block scalars, we always start with a newline, so we should ignore it (not fold or keep)
bool
useNewLine
=
pastOpeningBreak
;
// and for folded scalars, we don't fold the very last newline to a space
if
(
params
.
fold
&&
!
emptyLine
&&
INPUT
.
column
<
params
.
indent
)
useNewLine
=
false
;
if
(
useNewLine
)
{
if
(
params
.
fold
&&
!
emptyLine
&&
!
nextEmptyLine
&&
!
moreIndented
&&
!
nextMoreIndented
)
scalar
+=
" "
;
else
scalar
+=
"
\n
"
;
}
emptyLine
=
nextEmptyLine
;
moreIndented
=
nextMoreIndented
;
pastOpeningBreak
=
true
;
// are we done via indentation?
if
(
!
emptyLine
&&
INPUT
.
column
<
params
.
indent
)
{
params
.
leadingSpaces
=
true
;
break
;
}
}
// post-processing
if
(
params
.
trimTrailingSpaces
)
{
unsigned
pos
=
scalar
.
find_last_not_of
(
' '
);
if
(
pos
<
scalar
.
size
())
scalar
.
erase
(
pos
+
1
);
}
if
(
params
.
chomp
<=
0
)
{
unsigned
pos
=
scalar
.
find_last_not_of
(
'\n'
);
if
(
params
.
chomp
==
0
&&
pos
+
1
<
scalar
.
size
())
scalar
.
erase
(
pos
+
2
);
else
if
(
params
.
chomp
==
-
1
&&
pos
<
scalar
.
size
())
scalar
.
erase
(
pos
+
1
);
}
return
scalar
;
}
}
src/scanscalar.h
View file @
b43f8271
#pragma once
#include <string>
#include "regex.h"
#include "stream.h"
namespace
YAML
{
enum
CHOMP
{
STRIP
=
-
1
,
CLIP
,
KEEP
};
enum
ACTION
{
NONE
,
BREAK
,
THROW
};
struct
ScanScalarParams
{
ScanScalarParams
()
:
eatEnd
(
false
),
indent
(
0
),
detectIndent
(
false
),
eatLeadingWhitespace
(
0
),
escape
(
0
),
fold
(
false
),
trimTrailingSpaces
(
0
),
chomp
(
CLIP
),
onDocIndicator
(
NONE
),
onTabInIndentation
(
NONE
),
leadingSpaces
(
false
)
{}
// input:
RegEx
end
;
// what condition ends this scalar?
bool
eatEnd
;
// should we eat that condition when we see it?
int
indent
;
// what level of indentation should be eaten and ignored?
bool
detectIndent
;
// should we try to autodetect the indent?
bool
eatLeadingWhitespace
;
// should we continue eating this delicious indentation after 'indent' spaces?
char
escape
;
// what character do we escape on (i.e., slash or single quote) (0 for none)
bool
fold
;
// do we fold line ends?
bool
trimTrailingSpaces
;
// do we remove all trailing spaces (at the very end)
CHOMP
chomp
;
// do we strip, clip, or keep trailing newlines (at the very end)
// Note: strip means kill all, clip means keep at most one, keep means keep all
ACTION
onDocIndicator
;
// what do we do if we see a document indicator?
ACTION
onTabInIndentation
;
// what do we do if we see a tab where we should be seeing indentation spaces
// output:
bool
leadingSpaces
;
};
std
::
string
ScanScalar
(
Stream
&
INPUT
,
ScanScalarParams
&
info
);
}
#pragma once
#include <string>
#include "regex.h"
#include "stream.h"
namespace
YAML
{
enum
CHOMP
{
STRIP
=
-
1
,
CLIP
,
KEEP
};
enum
ACTION
{
NONE
,
BREAK
,
THROW
};
struct
ScanScalarParams
{
ScanScalarParams
()
:
eatEnd
(
false
),
indent
(
0
),
detectIndent
(
false
),
eatLeadingWhitespace
(
0
),
escape
(
0
),
fold
(
false
),
trimTrailingSpaces
(
0
),
chomp
(
CLIP
),
onDocIndicator
(
NONE
),
onTabInIndentation
(
NONE
),
leadingSpaces
(
false
)
{}
// input:
RegEx
end
;
// what condition ends this scalar?
bool
eatEnd
;
// should we eat that condition when we see it?
int
indent
;
// what level of indentation should be eaten and ignored?
bool
detectIndent
;
// should we try to autodetect the indent?
bool
eatLeadingWhitespace
;
// should we continue eating this delicious indentation after 'indent' spaces?
char
escape
;
// what character do we escape on (i.e., slash or single quote) (0 for none)
bool
fold
;
// do we fold line ends?
bool
trimTrailingSpaces
;
// do we remove all trailing spaces (at the very end)
CHOMP
chomp
;
// do we strip, clip, or keep trailing newlines (at the very end)
// Note: strip means kill all, clip means keep at most one, keep means keep all
ACTION
onDocIndicator
;
// what do we do if we see a document indicator?
ACTION
onTabInIndentation
;
// what do we do if we see a tab where we should be seeing indentation spaces
// output:
bool
leadingSpaces
;
};
std
::
string
ScanScalar
(
Stream
&
INPUT
,
ScanScalarParams
&
info
);
}
src/scantoken.cpp
View file @
b43f8271
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
#include "scanscalar.h"
#include <sstream>
namespace
YAML
{
///////////////////////////////////////////////////////////////////////
// Specialization for scanning specific tokens
// Directive
// . Note: no semantic checking is done here (that's for the parser to do)
void
Scanner
::
ScanDirective
()
{
std
::
string
name
;
std
::
vector
<
std
::
string
>
params
;
// pop indents and simple keys
PopIndentTo
(
-
1
);
VerifyAllSimpleKeys
();
m_simpleKeyAllowed
=
false
;
// store pos and eat indicator
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
1
);
// read name
while
(
INPUT
.
peek
()
!=
EOF
&&
!
Exp
::
BlankOrBreak
.
Matches
(
INPUT
))
name
+=
INPUT
.
get
();
// read parameters
while
(
1
)
{
// first get rid of whitespace
while
(
Exp
::
Blank
.
Matches
(
INPUT
))
INPUT
.
eat
(
1
);
// break on newline or comment
if
(
INPUT
.
peek
()
==
EOF
||
Exp
::
Break
.
Matches
(
INPUT
)
||
Exp
::
Comment
.
Matches
(
INPUT
))
break
;
// now read parameter
std
::
string
param
;
while
(
INPUT
.
peek
()
!=
EOF
&&
!
Exp
::
BlankOrBreak
.
Matches
(
INPUT
))
param
+=
INPUT
.
get
();
params
.
push_back
(
param
);
}
Token
token
(
TT_DIRECTIVE
,
line
,
column
);
token
.
value
=
name
;
token
.
params
=
params
;
m_tokens
.
push
(
token
);
}
// DocStart
void
Scanner
::
ScanDocStart
()
{
PopIndentTo
(
INPUT
.
column
);
VerifyAllSimpleKeys
();
m_simpleKeyAllowed
=
false
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
3
);
m_tokens
.
push
(
Token
(
TT_DOC_START
,
line
,
column
));
}
// DocEnd
void
Scanner
::
ScanDocEnd
()
{
PopIndentTo
(
-
1
);
VerifyAllSimpleKeys
();
m_simpleKeyAllowed
=
false
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
3
);
m_tokens
.
push
(
Token
(
TT_DOC_END
,
line
,
column
));
}
// FlowStart
void
Scanner
::
ScanFlowStart
()
{
// flows can be simple keys
InsertSimpleKey
();
m_flowLevel
++
;
m_simpleKeyAllowed
=
true
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
char
ch
=
INPUT
.
get
();
TOKEN_TYPE
type
=
(
ch
==
Keys
::
FlowSeqStart
?
TT_FLOW_SEQ_START
:
TT_FLOW_MAP_START
);
m_tokens
.
push
(
Token
(
type
,
line
,
column
));
}
// FlowEnd
void
Scanner
::
ScanFlowEnd
()
{
if
(
m_flowLevel
==
0
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
FLOW_END
);
m_flowLevel
--
;
m_simpleKeyAllowed
=
false
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
char
ch
=
INPUT
.
get
();
TOKEN_TYPE
type
=
(
ch
==
Keys
::
FlowSeqEnd
?
TT_FLOW_SEQ_END
:
TT_FLOW_MAP_END
);
m_tokens
.
push
(
Token
(
type
,
line
,
column
));
}
// FlowEntry
void
Scanner
::
ScanFlowEntry
()
{
m_simpleKeyAllowed
=
true
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
1
);
m_tokens
.
push
(
Token
(
TT_FLOW_ENTRY
,
line
,
column
));
}
// BlockEntry
void
Scanner
::
ScanBlockEntry
()
{
// we better be in the block context!
if
(
m_flowLevel
>
0
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
BLOCK_ENTRY
);
// can we put it here?
if
(
!
m_simpleKeyAllowed
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
BLOCK_ENTRY
);
PushIndentTo
(
INPUT
.
column
,
true
);
m_simpleKeyAllowed
=
true
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
1
);
m_tokens
.
push
(
Token
(
TT_BLOCK_ENTRY
,
line
,
column
));
}
// Key
void
Scanner
::
ScanKey
()
{
// handle keys diffently in the block context (and manage indents)
if
(
m_flowLevel
==
0
)
{
if
(
!
m_simpleKeyAllowed
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
MAP_KEY
);
PushIndentTo
(
INPUT
.
column
,
false
);
}
// can only put a simple key here if we're in block context
if
(
m_flowLevel
==
0
)
m_simpleKeyAllowed
=
true
;
else
m_simpleKeyAllowed
=
false
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
1
);
m_tokens
.
push
(
Token
(
TT_KEY
,
line
,
column
));
}
// Value
void
Scanner
::
ScanValue
()
{
// does this follow a simple key?
if
(
m_isLastKeyValid
)
{
// can't follow a simple key with another simple key (dunno why, though - it seems fine)
m_simpleKeyAllowed
=
false
;
}
else
{
// handle values diffently in the block context (and manage indents)
if
(
m_flowLevel
==
0
)
{
if
(
!
m_simpleKeyAllowed
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
MAP_VALUE
);
PushIndentTo
(
INPUT
.
column
,
false
);
}
// can only put a simple key here if we're in block context
if
(
m_flowLevel
==
0
)
m_simpleKeyAllowed
=
true
;
else
m_simpleKeyAllowed
=
false
;
}
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
1
);
m_tokens
.
push
(
Token
(
TT_VALUE
,
line
,
column
));
}
// AnchorOrAlias
void
Scanner
::
ScanAnchorOrAlias
()
{
bool
alias
;
std
::
string
name
;
// insert a potential simple key
if
(
m_simpleKeyAllowed
)
InsertSimpleKey
();
m_simpleKeyAllowed
=
false
;
// eat the indicator
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
char
indicator
=
INPUT
.
get
();
alias
=
(
indicator
==
Keys
::
Alias
);
// now eat the content
while
(
Exp
::
AlphaNumeric
.
Matches
(
INPUT
))
name
+=
INPUT
.
get
();
// we need to have read SOMETHING!
if
(
name
.
empty
())
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
alias
?
ErrorMsg
::
ALIAS_NOT_FOUND
:
ErrorMsg
::
ANCHOR_NOT_FOUND
);
// and needs to end correctly
if
(
INPUT
.
peek
()
!=
EOF
&&
!
Exp
::
AnchorEnd
.
Matches
(
INPUT
))
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
alias
?
ErrorMsg
::
CHAR_IN_ALIAS
:
ErrorMsg
::
CHAR_IN_ANCHOR
);
// and we're done
Token
token
(
alias
?
TT_ALIAS
:
TT_ANCHOR
,
line
,
column
);
token
.
value
=
name
;
m_tokens
.
push
(
token
);
}
// Tag
void
Scanner
::
ScanTag
()
{
std
::
string
handle
,
suffix
;
// insert a potential simple key
if
(
m_simpleKeyAllowed
)
InsertSimpleKey
();
m_simpleKeyAllowed
=
false
;
// eat the indicator
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
handle
+=
INPUT
.
get
();
// read the handle
while
(
INPUT
.
peek
()
!=
EOF
&&
INPUT
.
peek
()
!=
Keys
::
Tag
&&
!
Exp
::
BlankOrBreak
.
Matches
(
INPUT
))
handle
+=
INPUT
.
get
();
// is there a suffix?
if
(
INPUT
.
peek
()
==
Keys
::
Tag
)
{
// eat the indicator
handle
+=
INPUT
.
get
();
// then read it
while
(
INPUT
.
peek
()
!=
EOF
&&
!
Exp
::
BlankOrBreak
.
Matches
(
INPUT
))
suffix
+=
INPUT
.
get
();
}
else
{
// this is a bit weird: we keep just the '!' as the handle and move the rest to the suffix
suffix
=
handle
.
substr
(
1
);
handle
=
"!"
;
}
Token
token
(
TT_TAG
,
line
,
column
);
token
.
value
=
handle
;
token
.
params
.
push_back
(
suffix
);
m_tokens
.
push
(
token
);
}
// PlainScalar
void
Scanner
::
ScanPlainScalar
()
{
std
::
string
scalar
;
// set up the scanning parameters
ScanScalarParams
params
;
params
.
end
=
(
m_flowLevel
>
0
?
Exp
::
EndScalarInFlow
:
Exp
::
EndScalar
)
||
(
Exp
::
BlankOrBreak
+
Exp
::
Comment
);
params
.
eatEnd
=
false
;
params
.
indent
=
(
m_flowLevel
>
0
?
0
:
m_indents
.
top
()
+
1
);
params
.
fold
=
true
;
params
.
eatLeadingWhitespace
=
true
;
params
.
trimTrailingSpaces
=
true
;
params
.
chomp
=
CLIP
;
params
.
onDocIndicator
=
BREAK
;
params
.
onTabInIndentation
=
THROW
;
// insert a potential simple key
if
(
m_simpleKeyAllowed
)
InsertSimpleKey
();
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
scalar
=
ScanScalar
(
INPUT
,
params
);
// can have a simple key only if we ended the scalar by starting a new line
m_simpleKeyAllowed
=
params
.
leadingSpaces
;
// finally, check and see if we ended on an illegal character
//if(Exp::IllegalCharInScalar.Matches(INPUT))
// throw ParserException(INPUT.line, INPUT.column, ErrorMsg::CHAR_IN_SCALAR);
Token
token
(
TT_SCALAR
,
line
,
column
);
token
.
value
=
scalar
;
m_tokens
.
push
(
token
);
}
// QuotedScalar
void
Scanner
::
ScanQuotedScalar
()
{
std
::
string
scalar
;
// eat single or double quote
char
quote
=
INPUT
.
get
();
bool
single
=
(
quote
==
'\''
);
// setup the scanning parameters
ScanScalarParams
params
;
params
.
end
=
(
single
?
RegEx
(
quote
)
&&
!
Exp
::
EscSingleQuote
:
RegEx
(
quote
));
params
.
eatEnd
=
true
;
params
.
escape
=
(
single
?
'\''
:
'\\'
);
params
.
indent
=
0
;
params
.
fold
=
true
;
params
.
eatLeadingWhitespace
=
true
;
params
.
trimTrailingSpaces
=
false
;
params
.
chomp
=
CLIP
;
params
.
onDocIndicator
=
THROW
;
// insert a potential simple key
if
(
m_simpleKeyAllowed
)
InsertSimpleKey
();
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
scalar
=
ScanScalar
(
INPUT
,
params
);
m_simpleKeyAllowed
=
false
;
Token
token
(
TT_SCALAR
,
line
,
column
);
token
.
value
=
scalar
;
m_tokens
.
push
(
token
);
}
// BlockScalarToken
// . These need a little extra processing beforehand.
// . We need to scan the line where the indicator is (this doesn't count as part of the scalar),
// and then we need to figure out what level of indentation we'll be using.
void
Scanner
::
ScanBlockScalar
()
{
std
::
string
scalar
;
ScanScalarParams
params
;
params
.
indent
=
1
;
params
.
detectIndent
=
true
;
// eat block indicator ('|' or '>')
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
char
indicator
=
INPUT
.
get
();
params
.
fold
=
(
indicator
==
Keys
::
FoldedScalar
);
// eat chomping/indentation indicators
int
n
=
Exp
::
Chomp
.
Match
(
INPUT
);
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
char
ch
=
INPUT
.
get
();
if
(
ch
==
'+'
)
params
.
chomp
=
KEEP
;
else
if
(
ch
==
'-'
)
params
.
chomp
=
STRIP
;
else
if
(
Exp
::
Digit
.
Matches
(
ch
))
{
if
(
ch
==
'0'
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
ZERO_INDENT_IN_BLOCK
);
params
.
indent
=
ch
-
'0'
;
params
.
detectIndent
=
false
;
}
}
// now eat whitespace
while
(
Exp
::
Blank
.
Matches
(
INPUT
))
INPUT
.
eat
(
1
);
// and comments to the end of the line
if
(
Exp
::
Comment
.
Matches
(
INPUT
))
while
(
INPUT
&&
!
Exp
::
Break
.
Matches
(
INPUT
))
INPUT
.
eat
(
1
);
// if it's not a line break, then we ran into a bad character inline
if
(
INPUT
&&
!
Exp
::
Break
.
Matches
(
INPUT
))
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
CHAR_IN_BLOCK
);
// set the initial indentation
if
(
m_indents
.
top
()
>=
0
)
params
.
indent
+=
m_indents
.
top
();
params
.
eatLeadingWhitespace
=
false
;
params
.
trimTrailingSpaces
=
false
;
params
.
onTabInIndentation
=
THROW
;
scalar
=
ScanScalar
(
INPUT
,
params
);
// simple keys always ok after block scalars (since we're gonna start a new line anyways)
m_simpleKeyAllowed
=
true
;
Token
token
(
TT_SCALAR
,
line
,
column
);
token
.
value
=
scalar
;
m_tokens
.
push
(
token
);
}
}
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
#include "scanscalar.h"
#include <sstream>
namespace
YAML
{
///////////////////////////////////////////////////////////////////////
// Specialization for scanning specific tokens
// Directive
// . Note: no semantic checking is done here (that's for the parser to do)
void
Scanner
::
ScanDirective
()
{
std
::
string
name
;
std
::
vector
<
std
::
string
>
params
;
// pop indents and simple keys
PopIndentTo
(
-
1
);
VerifyAllSimpleKeys
();
m_simpleKeyAllowed
=
false
;
// store pos and eat indicator
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
1
);
// read name
while
(
INPUT
.
peek
()
!=
EOF
&&
!
Exp
::
BlankOrBreak
.
Matches
(
INPUT
))
name
+=
INPUT
.
get
();
// read parameters
while
(
1
)
{
// first get rid of whitespace
while
(
Exp
::
Blank
.
Matches
(
INPUT
))
INPUT
.
eat
(
1
);
// break on newline or comment
if
(
INPUT
.
peek
()
==
EOF
||
Exp
::
Break
.
Matches
(
INPUT
)
||
Exp
::
Comment
.
Matches
(
INPUT
))
break
;
// now read parameter
std
::
string
param
;
while
(
INPUT
.
peek
()
!=
EOF
&&
!
Exp
::
BlankOrBreak
.
Matches
(
INPUT
))
param
+=
INPUT
.
get
();
params
.
push_back
(
param
);
}
Token
token
(
TT_DIRECTIVE
,
line
,
column
);
token
.
value
=
name
;
token
.
params
=
params
;
m_tokens
.
push
(
token
);
}
// DocStart
void
Scanner
::
ScanDocStart
()
{
PopIndentTo
(
INPUT
.
column
);
VerifyAllSimpleKeys
();
m_simpleKeyAllowed
=
false
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
3
);
m_tokens
.
push
(
Token
(
TT_DOC_START
,
line
,
column
));
}
// DocEnd
void
Scanner
::
ScanDocEnd
()
{
PopIndentTo
(
-
1
);
VerifyAllSimpleKeys
();
m_simpleKeyAllowed
=
false
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
3
);
m_tokens
.
push
(
Token
(
TT_DOC_END
,
line
,
column
));
}
// FlowStart
void
Scanner
::
ScanFlowStart
()
{
// flows can be simple keys
InsertSimpleKey
();
m_flowLevel
++
;
m_simpleKeyAllowed
=
true
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
char
ch
=
INPUT
.
get
();
TOKEN_TYPE
type
=
(
ch
==
Keys
::
FlowSeqStart
?
TT_FLOW_SEQ_START
:
TT_FLOW_MAP_START
);
m_tokens
.
push
(
Token
(
type
,
line
,
column
));
}
// FlowEnd
void
Scanner
::
ScanFlowEnd
()
{
if
(
m_flowLevel
==
0
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
FLOW_END
);
m_flowLevel
--
;
m_simpleKeyAllowed
=
false
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
char
ch
=
INPUT
.
get
();
TOKEN_TYPE
type
=
(
ch
==
Keys
::
FlowSeqEnd
?
TT_FLOW_SEQ_END
:
TT_FLOW_MAP_END
);
m_tokens
.
push
(
Token
(
type
,
line
,
column
));
}
// FlowEntry
void
Scanner
::
ScanFlowEntry
()
{
m_simpleKeyAllowed
=
true
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
1
);
m_tokens
.
push
(
Token
(
TT_FLOW_ENTRY
,
line
,
column
));
}
// BlockEntry
void
Scanner
::
ScanBlockEntry
()
{
// we better be in the block context!
if
(
m_flowLevel
>
0
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
BLOCK_ENTRY
);
// can we put it here?
if
(
!
m_simpleKeyAllowed
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
BLOCK_ENTRY
);
PushIndentTo
(
INPUT
.
column
,
true
);
m_simpleKeyAllowed
=
true
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
1
);
m_tokens
.
push
(
Token
(
TT_BLOCK_ENTRY
,
line
,
column
));
}
// Key
void
Scanner
::
ScanKey
()
{
// handle keys diffently in the block context (and manage indents)
if
(
m_flowLevel
==
0
)
{
if
(
!
m_simpleKeyAllowed
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
MAP_KEY
);
PushIndentTo
(
INPUT
.
column
,
false
);
}
// can only put a simple key here if we're in block context
if
(
m_flowLevel
==
0
)
m_simpleKeyAllowed
=
true
;
else
m_simpleKeyAllowed
=
false
;
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
1
);
m_tokens
.
push
(
Token
(
TT_KEY
,
line
,
column
));
}
// Value
void
Scanner
::
ScanValue
()
{
// does this follow a simple key?
if
(
m_isLastKeyValid
)
{
// can't follow a simple key with another simple key (dunno why, though - it seems fine)
m_simpleKeyAllowed
=
false
;
}
else
{
// handle values diffently in the block context (and manage indents)
if
(
m_flowLevel
==
0
)
{
if
(
!
m_simpleKeyAllowed
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
MAP_VALUE
);
PushIndentTo
(
INPUT
.
column
,
false
);
}
// can only put a simple key here if we're in block context
if
(
m_flowLevel
==
0
)
m_simpleKeyAllowed
=
true
;
else
m_simpleKeyAllowed
=
false
;
}
// eat
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
INPUT
.
eat
(
1
);
m_tokens
.
push
(
Token
(
TT_VALUE
,
line
,
column
));
}
// AnchorOrAlias
void
Scanner
::
ScanAnchorOrAlias
()
{
bool
alias
;
std
::
string
name
;
// insert a potential simple key
if
(
m_simpleKeyAllowed
)
InsertSimpleKey
();
m_simpleKeyAllowed
=
false
;
// eat the indicator
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
char
indicator
=
INPUT
.
get
();
alias
=
(
indicator
==
Keys
::
Alias
);
// now eat the content
while
(
Exp
::
AlphaNumeric
.
Matches
(
INPUT
))
name
+=
INPUT
.
get
();
// we need to have read SOMETHING!
if
(
name
.
empty
())
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
alias
?
ErrorMsg
::
ALIAS_NOT_FOUND
:
ErrorMsg
::
ANCHOR_NOT_FOUND
);
// and needs to end correctly
if
(
INPUT
.
peek
()
!=
EOF
&&
!
Exp
::
AnchorEnd
.
Matches
(
INPUT
))
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
alias
?
ErrorMsg
::
CHAR_IN_ALIAS
:
ErrorMsg
::
CHAR_IN_ANCHOR
);
// and we're done
Token
token
(
alias
?
TT_ALIAS
:
TT_ANCHOR
,
line
,
column
);
token
.
value
=
name
;
m_tokens
.
push
(
token
);
}
// Tag
void
Scanner
::
ScanTag
()
{
std
::
string
handle
,
suffix
;
// insert a potential simple key
if
(
m_simpleKeyAllowed
)
InsertSimpleKey
();
m_simpleKeyAllowed
=
false
;
// eat the indicator
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
handle
+=
INPUT
.
get
();
// read the handle
while
(
INPUT
.
peek
()
!=
EOF
&&
INPUT
.
peek
()
!=
Keys
::
Tag
&&
!
Exp
::
BlankOrBreak
.
Matches
(
INPUT
))
handle
+=
INPUT
.
get
();
// is there a suffix?
if
(
INPUT
.
peek
()
==
Keys
::
Tag
)
{
// eat the indicator
handle
+=
INPUT
.
get
();
// then read it
while
(
INPUT
.
peek
()
!=
EOF
&&
!
Exp
::
BlankOrBreak
.
Matches
(
INPUT
))
suffix
+=
INPUT
.
get
();
}
else
{
// this is a bit weird: we keep just the '!' as the handle and move the rest to the suffix
suffix
=
handle
.
substr
(
1
);
handle
=
"!"
;
}
Token
token
(
TT_TAG
,
line
,
column
);
token
.
value
=
handle
;
token
.
params
.
push_back
(
suffix
);
m_tokens
.
push
(
token
);
}
// PlainScalar
void
Scanner
::
ScanPlainScalar
()
{
std
::
string
scalar
;
// set up the scanning parameters
ScanScalarParams
params
;
params
.
end
=
(
m_flowLevel
>
0
?
Exp
::
EndScalarInFlow
:
Exp
::
EndScalar
)
||
(
Exp
::
BlankOrBreak
+
Exp
::
Comment
);
params
.
eatEnd
=
false
;
params
.
indent
=
(
m_flowLevel
>
0
?
0
:
m_indents
.
top
()
+
1
);
params
.
fold
=
true
;
params
.
eatLeadingWhitespace
=
true
;
params
.
trimTrailingSpaces
=
true
;
params
.
chomp
=
CLIP
;
params
.
onDocIndicator
=
BREAK
;
params
.
onTabInIndentation
=
THROW
;
// insert a potential simple key
if
(
m_simpleKeyAllowed
)
InsertSimpleKey
();
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
scalar
=
ScanScalar
(
INPUT
,
params
);
// can have a simple key only if we ended the scalar by starting a new line
m_simpleKeyAllowed
=
params
.
leadingSpaces
;
// finally, check and see if we ended on an illegal character
//if(Exp::IllegalCharInScalar.Matches(INPUT))
// throw ParserException(INPUT.line, INPUT.column, ErrorMsg::CHAR_IN_SCALAR);
Token
token
(
TT_SCALAR
,
line
,
column
);
token
.
value
=
scalar
;
m_tokens
.
push
(
token
);
}
// QuotedScalar
void
Scanner
::
ScanQuotedScalar
()
{
std
::
string
scalar
;
// eat single or double quote
char
quote
=
INPUT
.
get
();
bool
single
=
(
quote
==
'\''
);
// setup the scanning parameters
ScanScalarParams
params
;
params
.
end
=
(
single
?
RegEx
(
quote
)
&&
!
Exp
::
EscSingleQuote
:
RegEx
(
quote
));
params
.
eatEnd
=
true
;
params
.
escape
=
(
single
?
'\''
:
'\\'
);
params
.
indent
=
0
;
params
.
fold
=
true
;
params
.
eatLeadingWhitespace
=
true
;
params
.
trimTrailingSpaces
=
false
;
params
.
chomp
=
CLIP
;
params
.
onDocIndicator
=
THROW
;
// insert a potential simple key
if
(
m_simpleKeyAllowed
)
InsertSimpleKey
();
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
scalar
=
ScanScalar
(
INPUT
,
params
);
m_simpleKeyAllowed
=
false
;
Token
token
(
TT_SCALAR
,
line
,
column
);
token
.
value
=
scalar
;
m_tokens
.
push
(
token
);
}
// BlockScalarToken
// . These need a little extra processing beforehand.
// . We need to scan the line where the indicator is (this doesn't count as part of the scalar),
// and then we need to figure out what level of indentation we'll be using.
void
Scanner
::
ScanBlockScalar
()
{
std
::
string
scalar
;
ScanScalarParams
params
;
params
.
indent
=
1
;
params
.
detectIndent
=
true
;
// eat block indicator ('|' or '>')
int
line
=
INPUT
.
line
,
column
=
INPUT
.
column
;
char
indicator
=
INPUT
.
get
();
params
.
fold
=
(
indicator
==
Keys
::
FoldedScalar
);
// eat chomping/indentation indicators
int
n
=
Exp
::
Chomp
.
Match
(
INPUT
);
for
(
int
i
=
0
;
i
<
n
;
i
++
)
{
char
ch
=
INPUT
.
get
();
if
(
ch
==
'+'
)
params
.
chomp
=
KEEP
;
else
if
(
ch
==
'-'
)
params
.
chomp
=
STRIP
;
else
if
(
Exp
::
Digit
.
Matches
(
ch
))
{
if
(
ch
==
'0'
)
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
ZERO_INDENT_IN_BLOCK
);
params
.
indent
=
ch
-
'0'
;
params
.
detectIndent
=
false
;
}
}
// now eat whitespace
while
(
Exp
::
Blank
.
Matches
(
INPUT
))
INPUT
.
eat
(
1
);
// and comments to the end of the line
if
(
Exp
::
Comment
.
Matches
(
INPUT
))
while
(
INPUT
&&
!
Exp
::
Break
.
Matches
(
INPUT
))
INPUT
.
eat
(
1
);
// if it's not a line break, then we ran into a bad character inline
if
(
INPUT
&&
!
Exp
::
Break
.
Matches
(
INPUT
))
throw
ParserException
(
INPUT
.
line
,
INPUT
.
column
,
ErrorMsg
::
CHAR_IN_BLOCK
);
// set the initial indentation
if
(
m_indents
.
top
()
>=
0
)
params
.
indent
+=
m_indents
.
top
();
params
.
eatLeadingWhitespace
=
false
;
params
.
trimTrailingSpaces
=
false
;
params
.
onTabInIndentation
=
THROW
;
scalar
=
ScanScalar
(
INPUT
,
params
);
// simple keys always ok after block scalars (since we're gonna start a new line anyways)
m_simpleKeyAllowed
=
true
;
Token
token
(
TT_SCALAR
,
line
,
column
);
token
.
value
=
scalar
;
m_tokens
.
push
(
token
);
}
}
src/sequence.cpp
View file @
b43f8271
#include "crt.h"
#include "sequence.h"
#include "node.h"
#include "scanner.h"
#include "token.h"
#include <iostream>
namespace
YAML
{
Sequence
::
Sequence
()
{
}
Sequence
::~
Sequence
()
{
Clear
();
}
void
Sequence
::
Clear
()
{
for
(
unsigned
i
=
0
;
i
<
m_data
.
size
();
i
++
)
delete
m_data
[
i
];
m_data
.
clear
();
}
bool
Sequence
::
GetBegin
(
std
::
vector
<
Node
*>::
const_iterator
&
it
)
const
{
it
=
m_data
.
begin
();
return
true
;
}
bool
Sequence
::
GetEnd
(
std
::
vector
<
Node
*>::
const_iterator
&
it
)
const
{
it
=
m_data
.
end
();
return
true
;
}
Node
*
Sequence
::
GetNode
(
unsigned
i
)
const
{
if
(
i
<
m_data
.
size
())
return
m_data
[
i
];
return
0
;
}
unsigned
Sequence
::
GetSize
()
const
{
return
m_data
.
size
();
}
void
Sequence
::
Parse
(
Scanner
*
pScanner
,
const
ParserState
&
state
)
{
Clear
();
// split based on start token
switch
(
pScanner
->
peek
().
type
)
{
case
TT_BLOCK_SEQ_START
:
ParseBlock
(
pScanner
,
state
);
break
;
case
TT_BLOCK_ENTRY
:
ParseImplicit
(
pScanner
,
state
);
break
;
case
TT_FLOW_SEQ_START
:
ParseFlow
(
pScanner
,
state
);
break
;
}
}
void
Sequence
::
ParseBlock
(
Scanner
*
pScanner
,
const
ParserState
&
state
)
{
// eat start token
pScanner
->
pop
();
while
(
1
)
{
if
(
pScanner
->
empty
())
throw
ParserException
(
-
1
,
-
1
,
ErrorMsg
::
END_OF_SEQ
);
Token
token
=
pScanner
->
peek
();
if
(
token
.
type
!=
TT_BLOCK_ENTRY
&&
token
.
type
!=
TT_BLOCK_END
)
throw
ParserException
(
token
.
line
,
token
.
column
,
ErrorMsg
::
END_OF_SEQ
);
pScanner
->
pop
();
if
(
token
.
type
==
TT_BLOCK_END
)
break
;
Node
*
pNode
=
new
Node
;
m_data
.
push_back
(
pNode
);
pNode
->
Parse
(
pScanner
,
state
);
}
}
void
Sequence
::
ParseImplicit
(
Scanner
*
pScanner
,
const
ParserState
&
state
)
{
while
(
1
)
{
// we're actually *allowed* to have no tokens at some point
if
(
pScanner
->
empty
())
break
;
// and we end at anything other than a block entry
Token
&
token
=
pScanner
->
peek
();
if
(
token
.
type
!=
TT_BLOCK_ENTRY
)
break
;
pScanner
->
pop
();
Node
*
pNode
=
new
Node
;
m_data
.
push_back
(
pNode
);
pNode
->
Parse
(
pScanner
,
state
);
}
}
void
Sequence
::
ParseFlow
(
Scanner
*
pScanner
,
const
ParserState
&
state
)
{
// eat start token
pScanner
->
pop
();
while
(
1
)
{
if
(
pScanner
->
empty
())
throw
ParserException
(
-
1
,
-
1
,
ErrorMsg
::
END_OF_SEQ_FLOW
);
// first check for end
if
(
pScanner
->
peek
().
type
==
TT_FLOW_SEQ_END
)
{
pScanner
->
pop
();
break
;
}
// then read the node
Node
*
pNode
=
new
Node
;
m_data
.
push_back
(
pNode
);
pNode
->
Parse
(
pScanner
,
state
);
// now eat the separator (or could be a sequence end, which we ignore - but if it's neither, then it's a bad node)
Token
&
token
=
pScanner
->
peek
();
if
(
token
.
type
==
TT_FLOW_ENTRY
)
pScanner
->
pop
();
else
if
(
token
.
type
!=
TT_FLOW_SEQ_END
)
throw
ParserException
(
token
.
line
,
token
.
column
,
ErrorMsg
::
END_OF_SEQ_FLOW
);
}
}
void
Sequence
::
Write
(
std
::
ostream
&
out
,
int
indent
,
bool
startedLine
,
bool
onlyOneCharOnLine
)
{
if
(
startedLine
&&
!
onlyOneCharOnLine
)
out
<<
"
\n
"
;
for
(
unsigned
i
=
0
;
i
<
m_data
.
size
();
i
++
)
{
if
((
startedLine
&&
!
onlyOneCharOnLine
)
||
i
>
0
)
{
for
(
int
j
=
0
;
j
<
indent
;
j
++
)
out
<<
" "
;
}
out
<<
"- "
;
m_data
[
i
]
->
Write
(
out
,
indent
+
1
,
true
,
i
>
0
||
!
startedLine
||
onlyOneCharOnLine
);
}
if
(
m_data
.
empty
())
out
<<
"
\n
"
;
}
int
Sequence
::
Compare
(
Content
*
pContent
)
{
return
-
pContent
->
Compare
(
this
);
}
int
Sequence
::
Compare
(
Sequence
*
pSeq
)
{
unsigned
n
=
m_data
.
size
(),
m
=
pSeq
->
m_data
.
size
();
if
(
n
<
m
)
return
-
1
;
else
if
(
n
>
m
)
return
1
;
for
(
unsigned
i
=
0
;
i
<
n
;
i
++
)
{
int
cmp
=
m_data
[
i
]
->
Compare
(
*
pSeq
->
m_data
[
i
]);
if
(
cmp
!=
0
)
return
cmp
;
}
return
0
;
}
}
#include "crt.h"
#include "sequence.h"
#include "node.h"
#include "scanner.h"
#include "token.h"
#include <iostream>
namespace
YAML
{
Sequence
::
Sequence
()
{
}
Sequence
::~
Sequence
()
{
Clear
();
}
void
Sequence
::
Clear
()
{
for
(
unsigned
i
=
0
;
i
<
m_data
.
size
();
i
++
)
delete
m_data
[
i
];
m_data
.
clear
();
}
bool
Sequence
::
GetBegin
(
std
::
vector
<
Node
*>::
const_iterator
&
it
)
const
{
it
=
m_data
.
begin
();
return
true
;
}
bool
Sequence
::
GetEnd
(
std
::
vector
<
Node
*>::
const_iterator
&
it
)
const
{
it
=
m_data
.
end
();
return
true
;
}
Node
*
Sequence
::
GetNode
(
unsigned
i
)
const
{
if
(
i
<
m_data
.
size
())
return
m_data
[
i
];
return
0
;
}
unsigned
Sequence
::
GetSize
()
const
{
return
m_data
.
size
();
}
void
Sequence
::
Parse
(
Scanner
*
pScanner
,
const
ParserState
&
state
)
{
Clear
();
// split based on start token
switch
(
pScanner
->
peek
().
type
)
{
case
TT_BLOCK_SEQ_START
:
ParseBlock
(
pScanner
,
state
);
break
;
case
TT_BLOCK_ENTRY
:
ParseImplicit
(
pScanner
,
state
);
break
;
case
TT_FLOW_SEQ_START
:
ParseFlow
(
pScanner
,
state
);
break
;
}
}
void
Sequence
::
ParseBlock
(
Scanner
*
pScanner
,
const
ParserState
&
state
)
{
// eat start token
pScanner
->
pop
();
while
(
1
)
{
if
(
pScanner
->
empty
())
throw
ParserException
(
-
1
,
-
1
,
ErrorMsg
::
END_OF_SEQ
);
Token
token
=
pScanner
->
peek
();
if
(
token
.
type
!=
TT_BLOCK_ENTRY
&&
token
.
type
!=
TT_BLOCK_END
)
throw
ParserException
(
token
.
line
,
token
.
column
,
ErrorMsg
::
END_OF_SEQ
);
pScanner
->
pop
();
if
(
token
.
type
==
TT_BLOCK_END
)
break
;
Node
*
pNode
=
new
Node
;
m_data
.
push_back
(
pNode
);
pNode
->
Parse
(
pScanner
,
state
);
}
}
void
Sequence
::
ParseImplicit
(
Scanner
*
pScanner
,
const
ParserState
&
state
)
{
while
(
1
)
{
// we're actually *allowed* to have no tokens at some point
if
(
pScanner
->
empty
())
break
;
// and we end at anything other than a block entry
Token
&
token
=
pScanner
->
peek
();
if
(
token
.
type
!=
TT_BLOCK_ENTRY
)
break
;
pScanner
->
pop
();
Node
*
pNode
=
new
Node
;
m_data
.
push_back
(
pNode
);
pNode
->
Parse
(
pScanner
,
state
);
}
}
void
Sequence
::
ParseFlow
(
Scanner
*
pScanner
,
const
ParserState
&
state
)
{
// eat start token
pScanner
->
pop
();
while
(
1
)
{
if
(
pScanner
->
empty
())
throw
ParserException
(
-
1
,
-
1
,
ErrorMsg
::
END_OF_SEQ_FLOW
);
// first check for end
if
(
pScanner
->
peek
().
type
==
TT_FLOW_SEQ_END
)
{
pScanner
->
pop
();
break
;
}
// then read the node
Node
*
pNode
=
new
Node
;
m_data
.
push_back
(
pNode
);
pNode
->
Parse
(
pScanner
,
state
);
// now eat the separator (or could be a sequence end, which we ignore - but if it's neither, then it's a bad node)
Token
&
token
=
pScanner
->
peek
();
if
(
token
.
type
==
TT_FLOW_ENTRY
)
pScanner
->
pop
();
else
if
(
token
.
type
!=
TT_FLOW_SEQ_END
)
throw
ParserException
(
token
.
line
,
token
.
column
,
ErrorMsg
::
END_OF_SEQ_FLOW
);
}
}
void
Sequence
::
Write
(
std
::
ostream
&
out
,
int
indent
,
bool
startedLine
,
bool
onlyOneCharOnLine
)
{
if
(
startedLine
&&
!
onlyOneCharOnLine
)
out
<<
"
\n
"
;
for
(
unsigned
i
=
0
;
i
<
m_data
.
size
();
i
++
)
{
if
((
startedLine
&&
!
onlyOneCharOnLine
)
||
i
>
0
)
{
for
(
int
j
=
0
;
j
<
indent
;
j
++
)
out
<<
" "
;
}
out
<<
"- "
;
m_data
[
i
]
->
Write
(
out
,
indent
+
1
,
true
,
i
>
0
||
!
startedLine
||
onlyOneCharOnLine
);
}
if
(
m_data
.
empty
())
out
<<
"
\n
"
;
}
int
Sequence
::
Compare
(
Content
*
pContent
)
{
return
-
pContent
->
Compare
(
this
);
}
int
Sequence
::
Compare
(
Sequence
*
pSeq
)
{
unsigned
n
=
m_data
.
size
(),
m
=
pSeq
->
m_data
.
size
();
if
(
n
<
m
)
return
-
1
;
else
if
(
n
>
m
)
return
1
;
for
(
unsigned
i
=
0
;
i
<
n
;
i
++
)
{
int
cmp
=
m_data
[
i
]
->
Compare
(
*
pSeq
->
m_data
[
i
]);
if
(
cmp
!=
0
)
return
cmp
;
}
return
0
;
}
}
src/sequence.h
View file @
b43f8271
#pragma once
#include "content.h"
#include <vector>
namespace
YAML
{
class
Node
;
class
Sequence
:
public
Content
{
public:
Sequence
();
virtual
~
Sequence
();
void
Clear
();
virtual
bool
GetBegin
(
std
::
vector
<
Node
*>::
const_iterator
&
it
)
const
;
virtual
bool
GetEnd
(
std
::
vector
<
Node
*>::
const_iterator
&
it
)
const
;
virtual
Node
*
GetNode
(
unsigned
i
)
const
;
virtual
unsigned
GetSize
()
const
;
virtual
void
Parse
(
Scanner
*
pScanner
,
const
ParserState
&
state
);
virtual
void
Write
(
std
::
ostream
&
out
,
int
indent
,
bool
startedLine
,
bool
onlyOneCharOnLine
);
virtual
bool
IsSequence
()
const
{
return
true
;
}
// ordering
virtual
int
Compare
(
Content
*
pContent
);
virtual
int
Compare
(
Scalar
*
pScalar
)
{
return
1
;
}
virtual
int
Compare
(
Sequence
*
pSeq
);
virtual
int
Compare
(
Map
*
pMap
)
{
return
-
1
;
}
private:
void
ParseBlock
(
Scanner
*
pScanner
,
const
ParserState
&
state
);
void
ParseImplicit
(
Scanner
*
pScanner
,
const
ParserState
&
state
);
void
ParseFlow
(
Scanner
*
pScanner
,
const
ParserState
&
state
);
protected:
std
::
vector
<
Node
*>
m_data
;
};
}
#pragma once
#include "content.h"
#include <vector>
namespace
YAML
{
class
Node
;
class
Sequence
:
public
Content
{
public:
Sequence
();
virtual
~
Sequence
();
void
Clear
();
virtual
bool
GetBegin
(
std
::
vector
<
Node
*>::
const_iterator
&
it
)
const
;
virtual
bool
GetEnd
(
std
::
vector
<
Node
*>::
const_iterator
&
it
)
const
;
virtual
Node
*
GetNode
(
unsigned
i
)
const
;
virtual
unsigned
GetSize
()
const
;
virtual
void
Parse
(
Scanner
*
pScanner
,
const
ParserState
&
state
);
virtual
void
Write
(
std
::
ostream
&
out
,
int
indent
,
bool
startedLine
,
bool
onlyOneCharOnLine
);
virtual
bool
IsSequence
()
const
{
return
true
;
}
// ordering
virtual
int
Compare
(
Content
*
pContent
);
virtual
int
Compare
(
Scalar
*
pScalar
)
{
return
1
;
}
virtual
int
Compare
(
Sequence
*
pSeq
);
virtual
int
Compare
(
Map
*
pMap
)
{
return
-
1
;
}
private:
void
ParseBlock
(
Scanner
*
pScanner
,
const
ParserState
&
state
);
void
ParseImplicit
(
Scanner
*
pScanner
,
const
ParserState
&
state
);
void
ParseFlow
(
Scanner
*
pScanner
,
const
ParserState
&
state
);
protected:
std
::
vector
<
Node
*>
m_data
;
};
}
src/simplekey.cpp
View file @
b43f8271
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
namespace
YAML
{
Scanner
::
SimpleKey
::
SimpleKey
(
int
pos_
,
int
line_
,
int
column_
,
int
flowLevel_
)
:
pos
(
pos_
),
line
(
line_
),
column
(
column_
),
flowLevel
(
flowLevel_
),
pMapStart
(
0
),
pKey
(
0
)
{
}
void
Scanner
::
SimpleKey
::
Validate
()
{
if
(
pMapStart
)
pMapStart
->
status
=
TS_VALID
;
if
(
pKey
)
pKey
->
status
=
TS_VALID
;
}
void
Scanner
::
SimpleKey
::
Invalidate
()
{
if
(
pMapStart
)
pMapStart
->
status
=
TS_INVALID
;
if
(
pKey
)
pKey
->
status
=
TS_INVALID
;
}
// InsertSimpleKey
// . Adds a potential simple key to the queue,
// and saves it on a stack.
void
Scanner
::
InsertSimpleKey
()
{
SimpleKey
key
(
INPUT
.
pos
(),
INPUT
.
line
,
INPUT
.
column
,
m_flowLevel
);
// first add a map start, if necessary
key
.
pMapStart
=
PushIndentTo
(
INPUT
.
column
,
false
);
if
(
key
.
pMapStart
)
key
.
pMapStart
->
status
=
TS_UNVERIFIED
;
// then add the (now unverified) key
m_tokens
.
push
(
Token
(
TT_KEY
,
INPUT
.
line
,
INPUT
.
column
));
key
.
pKey
=
&
m_tokens
.
back
();
key
.
pKey
->
status
=
TS_UNVERIFIED
;
m_simpleKeys
.
push
(
key
);
}
// VerifySimpleKey
// . Determines whether the latest simple key to be added is valid,
// and if so, makes it valid.
bool
Scanner
::
VerifySimpleKey
()
{
m_isLastKeyValid
=
false
;
if
(
m_simpleKeys
.
empty
())
return
m_isLastKeyValid
;
// grab top key
SimpleKey
key
=
m_simpleKeys
.
top
();
// only validate if we're in the correct flow level
if
(
key
.
flowLevel
!=
m_flowLevel
)
return
false
;
m_simpleKeys
.
pop
();
bool
isValid
=
true
;
// needs to be followed immediately by a value
if
(
m_flowLevel
>
0
&&
!
Exp
::
ValueInFlow
.
Matches
(
INPUT
))
isValid
=
false
;
if
(
m_flowLevel
==
0
&&
!
Exp
::
Value
.
Matches
(
INPUT
))
isValid
=
false
;
// also needs to be less than 1024 characters and inline
if
(
INPUT
.
line
!=
key
.
line
||
INPUT
.
pos
()
-
key
.
pos
>
1024
)
isValid
=
false
;
// invalidate key
if
(
isValid
)
key
.
Validate
();
else
key
.
Invalidate
();
// In block style, remember that we've pushed an indent for this potential simple key (if it was starting).
// If it was invalid, then we need to pop it off.
// Note: we're guaranteed to be popping the right one (i.e., there couldn't have been anything in
// between) since keys have to be inline, and will be invalidated immediately on a newline.
if
(
!
isValid
&&
m_flowLevel
==
0
)
m_indents
.
pop
();
m_isLastKeyValid
=
isValid
;
return
isValid
;
}
void
Scanner
::
VerifyAllSimpleKeys
()
{
while
(
!
m_simpleKeys
.
empty
())
VerifySimpleKey
();
}
}
#include "crt.h"
#include "scanner.h"
#include "token.h"
#include "exceptions.h"
#include "exp.h"
namespace
YAML
{
Scanner
::
SimpleKey
::
SimpleKey
(
int
pos_
,
int
line_
,
int
column_
,
int
flowLevel_
)
:
pos
(
pos_
),
line
(
line_
),
column
(
column_
),
flowLevel
(
flowLevel_
),
pMapStart
(
0
),
pKey
(
0
)
{
}
void
Scanner
::
SimpleKey
::
Validate
()
{
if
(
pMapStart
)
pMapStart
->
status
=
TS_VALID
;
if
(
pKey
)
pKey
->
status
=
TS_VALID
;
}
void
Scanner
::
SimpleKey
::
Invalidate
()
{
if
(
pMapStart
)
pMapStart
->
status
=
TS_INVALID
;
if
(
pKey
)
pKey
->
status
=
TS_INVALID
;
}
// InsertSimpleKey
// . Adds a potential simple key to the queue,
// and saves it on a stack.
void
Scanner
::
InsertSimpleKey
()
{
SimpleKey
key
(
INPUT
.
pos
(),
INPUT
.
line
,
INPUT
.
column
,
m_flowLevel
);
// first add a map start, if necessary
key
.
pMapStart
=
PushIndentTo
(
INPUT
.
column
,
false
);
if
(
key
.
pMapStart
)
key
.
pMapStart
->
status
=
TS_UNVERIFIED
;
// then add the (now unverified) key
m_tokens
.
push
(
Token
(
TT_KEY
,
INPUT
.
line
,
INPUT
.
column
));
key
.
pKey
=
&
m_tokens
.
back
();
key
.
pKey
->
status
=
TS_UNVERIFIED
;
m_simpleKeys
.
push
(
key
);
}
// VerifySimpleKey
// . Determines whether the latest simple key to be added is valid,
// and if so, makes it valid.
bool
Scanner
::
VerifySimpleKey
()
{
m_isLastKeyValid
=
false
;
if
(
m_simpleKeys
.
empty
())
return
m_isLastKeyValid
;
// grab top key
SimpleKey
key
=
m_simpleKeys
.
top
();
// only validate if we're in the correct flow level
if
(
key
.
flowLevel
!=
m_flowLevel
)
return
false
;
m_simpleKeys
.
pop
();
bool
isValid
=
true
;
// needs to be followed immediately by a value
if
(
m_flowLevel
>
0
&&
!
Exp
::
ValueInFlow
.
Matches
(
INPUT
))
isValid
=
false
;
if
(
m_flowLevel
==
0
&&
!
Exp
::
Value
.
Matches
(
INPUT
))
isValid
=
false
;
// also needs to be less than 1024 characters and inline
if
(
INPUT
.
line
!=
key
.
line
||
INPUT
.
pos
()
-
key
.
pos
>
1024
)
isValid
=
false
;
// invalidate key
if
(
isValid
)
key
.
Validate
();
else
key
.
Invalidate
();
// In block style, remember that we've pushed an indent for this potential simple key (if it was starting).
// If it was invalid, then we need to pop it off.
// Note: we're guaranteed to be popping the right one (i.e., there couldn't have been anything in
// between) since keys have to be inline, and will be invalidated immediately on a newline.
if
(
!
isValid
&&
m_flowLevel
==
0
)
m_indents
.
pop
();
m_isLastKeyValid
=
isValid
;
return
isValid
;
}
void
Scanner
::
VerifyAllSimpleKeys
()
{
while
(
!
m_simpleKeys
.
empty
())
VerifySimpleKey
();
}
}
src/stream.cpp
View file @
b43f8271
#include "crt.h"
#include "stream.h"
#include <iostream>
namespace
YAML
{
int
Stream
::
pos
()
const
{
return
input
.
tellg
();
}
char
Stream
::
peek
()
{
return
input
.
peek
();
}
Stream
::
operator
bool
()
{
return
input
.
good
();
}
// get
// . Extracts a character from the stream and updates our position
char
Stream
::
get
()
{
char
ch
=
input
.
get
();
column
++
;
if
(
ch
==
'\n'
)
{
column
=
0
;
line
++
;
}
return
ch
;
}
// get
// . Extracts 'n' characters from the stream and updates our position
std
::
string
Stream
::
get
(
int
n
)
{
std
::
string
ret
;
for
(
int
i
=
0
;
i
<
n
;
i
++
)
ret
+=
get
();
return
ret
;
}
// eat
// . Eats 'n' characters and updates our position.
void
Stream
::
eat
(
int
n
)
{
for
(
int
i
=
0
;
i
<
n
;
i
++
)
get
();
}
}
#include "crt.h"
#include "stream.h"
#include <iostream>
namespace
YAML
{
int
Stream
::
pos
()
const
{
return
input
.
tellg
();
}
char
Stream
::
peek
()
{
return
input
.
peek
();
}
Stream
::
operator
bool
()
{
return
input
.
good
();
}
// get
// . Extracts a character from the stream and updates our position
char
Stream
::
get
()
{
char
ch
=
input
.
get
();
column
++
;
if
(
ch
==
'\n'
)
{
column
=
0
;
line
++
;
}
return
ch
;
}
// get
// . Extracts 'n' characters from the stream and updates our position
std
::
string
Stream
::
get
(
int
n
)
{
std
::
string
ret
;
for
(
int
i
=
0
;
i
<
n
;
i
++
)
ret
+=
get
();
return
ret
;
}
// eat
// . Eats 'n' characters and updates our position.
void
Stream
::
eat
(
int
n
)
{
for
(
int
i
=
0
;
i
<
n
;
i
++
)
get
();
}
}
src/stream.h
View file @
b43f8271
#pragma once
#include <ios>
#include <string>
namespace
YAML
{
struct
Stream
{
Stream
(
std
::
istream
&
input_
)
:
input
(
input_
),
line
(
0
),
column
(
0
)
{}
int
pos
()
const
;
operator
bool
();
bool
operator
!
()
{
return
!
(
*
this
);
}
std
::
istream
&
stream
()
const
{
return
input
;
}
char
peek
();
char
get
();
std
::
string
get
(
int
n
);
void
eat
(
int
n
=
1
);
std
::
istream
&
input
;
int
line
,
column
;
};
}
#pragma once
#include <ios>
#include <string>
namespace
YAML
{
struct
Stream
{
Stream
(
std
::
istream
&
input_
)
:
input
(
input_
),
line
(
0
),
column
(
0
)
{}
int
pos
()
const
;
operator
bool
();
bool
operator
!
()
{
return
!
(
*
this
);
}
std
::
istream
&
stream
()
const
{
return
input
;
}
char
peek
();
char
get
();
std
::
string
get
(
int
n
);
void
eat
(
int
n
=
1
);
std
::
istream
&
input
;
int
line
,
column
;
};
}
src/token.h
View file @
b43f8271
#pragma once
#include <ios>
#include <string>
#include <vector>
namespace
YAML
{
enum
TOKEN_STATUS
{
TS_VALID
,
TS_INVALID
,
TS_UNVERIFIED
};
enum
TOKEN_TYPE
{
TT_DIRECTIVE
,
TT_DOC_START
,
TT_DOC_END
,
TT_BLOCK_SEQ_START
,
TT_BLOCK_MAP_START
,
TT_BLOCK_END
,
TT_BLOCK_ENTRY
,
TT_FLOW_SEQ_START
,
TT_FLOW_MAP_START
,
TT_FLOW_SEQ_END
,
TT_FLOW_MAP_END
,
TT_FLOW_ENTRY
,
TT_KEY
,
TT_VALUE
,
TT_ANCHOR
,
TT_ALIAS
,
TT_TAG
,
TT_SCALAR
,
};
const
std
::
string
TokenNames
[]
=
{
"DIRECTIVE"
,
"DOC_START"
,
"DOC_END"
,
"BLOCK_SEQ_START"
,
"BLOCK_MAP_START"
,
"BLOCK_END"
,
"BLOCK_ENTRY"
,
"FLOW_SEQ_START"
,
"FLOW_MAP_START"
,
"FLOW_SEQ_END"
,
"FLOW_MAP_END"
,
"FLOW_ENTRY"
,
"KEY"
,
"VALUE"
,
"ANCHOR"
,
"ALIAS"
,
"TAG"
,
"SCALAR"
,
};
struct
Token
{
Token
(
TOKEN_TYPE
type_
,
int
line_
,
int
column_
)
:
status
(
TS_VALID
),
type
(
type_
),
line
(
line_
),
column
(
column_
)
{}
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
out
,
const
Token
&
token
)
{
out
<<
TokenNames
[
token
.
type
]
<<
std
::
string
(
": "
)
<<
token
.
value
;
for
(
unsigned
i
=
0
;
i
<
token
.
params
.
size
();
i
++
)
out
<<
std
::
string
(
" "
)
<<
token
.
params
[
i
];
return
out
;
}
TOKEN_STATUS
status
;
TOKEN_TYPE
type
;
int
line
,
column
;
std
::
string
value
;
std
::
vector
<
std
::
string
>
params
;
};
}
#pragma once
#include <ios>
#include <string>
#include <vector>
namespace
YAML
{
enum
TOKEN_STATUS
{
TS_VALID
,
TS_INVALID
,
TS_UNVERIFIED
};
enum
TOKEN_TYPE
{
TT_DIRECTIVE
,
TT_DOC_START
,
TT_DOC_END
,
TT_BLOCK_SEQ_START
,
TT_BLOCK_MAP_START
,
TT_BLOCK_END
,
TT_BLOCK_ENTRY
,
TT_FLOW_SEQ_START
,
TT_FLOW_MAP_START
,
TT_FLOW_SEQ_END
,
TT_FLOW_MAP_END
,
TT_FLOW_ENTRY
,
TT_KEY
,
TT_VALUE
,
TT_ANCHOR
,
TT_ALIAS
,
TT_TAG
,
TT_SCALAR
,
};
const
std
::
string
TokenNames
[]
=
{
"DIRECTIVE"
,
"DOC_START"
,
"DOC_END"
,
"BLOCK_SEQ_START"
,
"BLOCK_MAP_START"
,
"BLOCK_END"
,
"BLOCK_ENTRY"
,
"FLOW_SEQ_START"
,
"FLOW_MAP_START"
,
"FLOW_SEQ_END"
,
"FLOW_MAP_END"
,
"FLOW_ENTRY"
,
"KEY"
,
"VALUE"
,
"ANCHOR"
,
"ALIAS"
,
"TAG"
,
"SCALAR"
,
};
struct
Token
{
Token
(
TOKEN_TYPE
type_
,
int
line_
,
int
column_
)
:
status
(
TS_VALID
),
type
(
type_
),
line
(
line_
),
column
(
column_
)
{}
friend
std
::
ostream
&
operator
<<
(
std
::
ostream
&
out
,
const
Token
&
token
)
{
out
<<
TokenNames
[
token
.
type
]
<<
std
::
string
(
": "
)
<<
token
.
value
;
for
(
unsigned
i
=
0
;
i
<
token
.
params
.
size
();
i
++
)
out
<<
std
::
string
(
" "
)
<<
token
.
params
[
i
];
return
out
;
}
TOKEN_STATUS
status
;
TOKEN_TYPE
type
;
int
line
,
column
;
std
::
string
value
;
std
::
vector
<
std
::
string
>
params
;
};
}
yaml-reader/main.cpp
View file @
b43f8271
#include "yaml.h"
#include "tests.h"
#include <fstream>
#include <iostream>
#ifdef _MSC_VER
#ifdef _DEBUG
#pragma comment(lib, "yamlcppd.lib")
#else
#pragma comment(lib, "yamlcpp.lib")
#endif // _DEBUG
#endif // _MSC_VER
void
run
()
{
std
::
ifstream
fin
(
"tests/test.yaml"
);
try
{
YAML
::
Parser
parser
(
fin
);
parser
.
PrintTokens
(
std
::
cout
);
}
catch
(
YAML
::
Exception
&
)
{
std
::
cout
<<
"Error parsing the yaml!
\n
"
;
}
}
int
main
(
int
argc
,
char
**
argv
)
{
bool
verbose
=
false
;
for
(
int
i
=
1
;
i
<
argc
;
i
++
)
{
if
(
strcmp
(
argv
[
i
],
"-v"
)
==
0
)
verbose
=
true
;
}
#ifdef WINDOWS
_CrtSetDbgFlag
(
_CRTDBG_LEAK_CHECK_DF
|
_CRTDBG_ALLOC_MEM_DF
);
#endif // WINDOWS
Test
::
RunAll
(
verbose
);
run
();
return
0
;
}
#include "yaml.h"
#include "tests.h"
#include <fstream>
#include <iostream>
#ifdef _MSC_VER
#ifdef _DEBUG
#pragma comment(lib, "yamlcppd.lib")
#else
#pragma comment(lib, "yamlcpp.lib")
#endif // _DEBUG
#endif // _MSC_VER
void
run
()
{
std
::
ifstream
fin
(
"tests/test.yaml"
);
try
{
YAML
::
Parser
parser
(
fin
);
parser
.
PrintTokens
(
std
::
cout
);
}
catch
(
YAML
::
Exception
&
)
{
std
::
cout
<<
"Error parsing the yaml!
\n
"
;
}
}
int
main
(
int
argc
,
char
**
argv
)
{
bool
verbose
=
false
;
for
(
int
i
=
1
;
i
<
argc
;
i
++
)
{
if
(
strcmp
(
argv
[
i
],
"-v"
)
==
0
)
verbose
=
true
;
}
#ifdef WINDOWS
_CrtSetDbgFlag
(
_CRTDBG_LEAK_CHECK_DF
|
_CRTDBG_ALLOC_MEM_DF
);
#endif // WINDOWS
Test
::
RunAll
(
verbose
);
run
();
return
0
;
}
yaml-reader/tests.cpp
View file @
b43f8271
#include "yaml.h"
#include "tests.h"
#include "parser.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
namespace
Test
{
// runs all the tests on all data we have
void
RunAll
(
bool
verbose
)
{
std
::
vector
<
std
::
string
>
files
;
files
.
push_back
(
"tests/simple.yaml"
);
files
.
push_back
(
"tests/mixed.yaml"
);
files
.
push_back
(
"tests/scalars.yaml"
);
files
.
push_back
(
"tests/directives.yaml"
);
bool
passed
=
true
;
for
(
unsigned
i
=
0
;
i
<
files
.
size
();
i
++
)
{
if
(
!
Inout
(
files
[
i
],
verbose
))
{
std
::
cout
<<
"Inout test failed on "
<<
files
[
i
]
<<
"
\n
"
;
passed
=
false
;
}
else
std
::
cout
<<
"Inout test passed: "
<<
files
[
i
]
<<
"
\n
"
;
}
if
(
passed
)
std
::
cout
<<
"All tests passed!
\n
"
;
}
// loads the given YAML file, outputs it, and then loads the outputted file,
// outputs again, and makes sure that the two outputs are the same
bool
Inout
(
const
std
::
string
&
file
,
bool
verbose
)
{
std
::
ifstream
fin
(
file
.
c_str
());
try
{
// read and output
YAML
::
Parser
parser
(
fin
);
if
(
!
parser
)
return
false
;
YAML
::
Node
doc
;
parser
.
GetNextDocument
(
doc
);
std
::
stringstream
out
;
out
<<
doc
;
// and save
std
::
string
firstTry
=
out
.
str
();
// and now again
parser
.
Load
(
out
);
if
(
!
parser
)
return
false
;
parser
.
GetNextDocument
(
doc
);
std
::
stringstream
out2
;
out2
<<
doc
;
// and save
std
::
string
secondTry
=
out2
.
str
();
// now compare
if
(
firstTry
==
secondTry
)
return
true
;
std
::
ofstream
fout
(
"tests/out.yaml"
);
fout
<<
"---
\n
"
;
fout
<<
firstTry
<<
std
::
endl
;
fout
<<
"---
\n
"
;
fout
<<
secondTry
<<
std
::
endl
;
}
catch
(
YAML
::
ParserException
&
e
)
{
std
::
cout
<<
file
<<
" (line "
<<
e
.
line
+
1
<<
", col "
<<
e
.
column
+
1
<<
"): "
<<
e
.
msg
<<
std
::
endl
;
if
(
verbose
)
{
std
::
cout
<<
"Token queue:
\n
"
;
std
::
ifstream
f
(
file
.
c_str
());
YAML
::
Parser
p
(
f
);
p
.
PrintTokens
(
std
::
cout
);
}
return
false
;
}
return
true
;
}
}
#include "yaml.h"
#include "tests.h"
#include "parser.h"
#include <fstream>
#include <sstream>
#include <vector>
#include <iostream>
namespace
Test
{
// runs all the tests on all data we have
void
RunAll
(
bool
verbose
)
{
std
::
vector
<
std
::
string
>
files
;
files
.
push_back
(
"tests/simple.yaml"
);
files
.
push_back
(
"tests/mixed.yaml"
);
files
.
push_back
(
"tests/scalars.yaml"
);
files
.
push_back
(
"tests/directives.yaml"
);
bool
passed
=
true
;
for
(
unsigned
i
=
0
;
i
<
files
.
size
();
i
++
)
{
if
(
!
Inout
(
files
[
i
],
verbose
))
{
std
::
cout
<<
"Inout test failed on "
<<
files
[
i
]
<<
"
\n
"
;
passed
=
false
;
}
else
std
::
cout
<<
"Inout test passed: "
<<
files
[
i
]
<<
"
\n
"
;
}
if
(
passed
)
std
::
cout
<<
"All tests passed!
\n
"
;
}
// loads the given YAML file, outputs it, and then loads the outputted file,
// outputs again, and makes sure that the two outputs are the same
bool
Inout
(
const
std
::
string
&
file
,
bool
verbose
)
{
std
::
ifstream
fin
(
file
.
c_str
());
try
{
// read and output
YAML
::
Parser
parser
(
fin
);
if
(
!
parser
)
return
false
;
YAML
::
Node
doc
;
parser
.
GetNextDocument
(
doc
);
std
::
stringstream
out
;
out
<<
doc
;
// and save
std
::
string
firstTry
=
out
.
str
();
// and now again
parser
.
Load
(
out
);
if
(
!
parser
)
return
false
;
parser
.
GetNextDocument
(
doc
);
std
::
stringstream
out2
;
out2
<<
doc
;
// and save
std
::
string
secondTry
=
out2
.
str
();
// now compare
if
(
firstTry
==
secondTry
)
return
true
;
std
::
ofstream
fout
(
"tests/out.yaml"
);
fout
<<
"---
\n
"
;
fout
<<
firstTry
<<
std
::
endl
;
fout
<<
"---
\n
"
;
fout
<<
secondTry
<<
std
::
endl
;
}
catch
(
YAML
::
ParserException
&
e
)
{
std
::
cout
<<
file
<<
" (line "
<<
e
.
line
+
1
<<
", col "
<<
e
.
column
+
1
<<
"): "
<<
e
.
msg
<<
std
::
endl
;
if
(
verbose
)
{
std
::
cout
<<
"Token queue:
\n
"
;
std
::
ifstream
f
(
file
.
c_str
());
YAML
::
Parser
p
(
f
);
p
.
PrintTokens
(
std
::
cout
);
}
return
false
;
}
return
true
;
}
}
yaml-reader/tests.h
View file @
b43f8271
#include <string>
namespace
Test
{
void
RunAll
(
bool
verbose
);
bool
Inout
(
const
std
::
string
&
file
,
bool
verbose
);
}
#include <string>
namespace
Test
{
void
RunAll
(
bool
verbose
);
bool
Inout
(
const
std
::
string
&
file
,
bool
verbose
);
}
yaml-reader/tests/directives.yaml
View file @
b43f8271
%YAML
1.2
%TAG
!
!
howdy
---
-
basic node
%YAML
1.2
%TAG
!
!
howdy
---
-
basic node
-
!
yeah baby
\ No newline at end of file
yaml-reader/tests/mixed.yaml
View file @
b43f8271
-
the main thing is a sequence
-
here's a key
:
value
and another
:
value
-
let's inline
:
[
1
,
2
,
3
]
and an inline map
:
{
key
:
value
,
243
:
101
}
-
and multiple indents
:
-
here's
-
a
-
list
and another
:
-
list
-
of
-
things
-
maybe now
:
let's
:
get
pretty
:
deep
:
here
in
:
the
:
nesting
just
:
to
confuse
:
the
:
heck
out
:
-
of
-
the
:
parser
if
:
-
we
-
can
-
do
:
that
what
:
do
you
:
think?
-
the main thing is a sequence
-
here's a key
:
value
and another
:
value
-
let's inline
:
[
1
,
2
,
3
]
and an inline map
:
{
key
:
value
,
243
:
101
}
-
and multiple indents
:
-
here's
-
a
-
list
and another
:
-
list
-
of
-
things
-
maybe now
:
let's
:
get
pretty
:
deep
:
here
in
:
the
:
nesting
just
:
to
confuse
:
the
:
heck
out
:
-
of
-
the
:
parser
if
:
-
we
-
can
-
do
:
that
what
:
do
you
:
think?
\ No newline at end of file
yaml-reader/tests/scalars.yaml
View file @
b43f8271
-
normal scalar, but
over several lines
-
|
literal scalar - so we can draw ASCII:
- -
| - |
------
-
>
and a folded scalar... so we
can just keep writing various
things. And if we want to keep indentation:
we just indent a little
see, this stays indented
-
>-
Here's a folded scalar
that gets chomped.
-
|-
And here's a literal scalar
that gets chomped.
-
>
2
Here's a folded scalar
that starts with some indentation.
-
::vector
-
"
:
-
()"
-
Up, up, and away!
-
-123
-
http://example.com/foo#bar
# Inside flow collection:
-
[
::
vector
,
"
:
-
()"
,
"
Up,
up
and
away!"
,
-123
,
-
normal scalar, but
over several lines
-
|
literal scalar - so we can draw ASCII:
- -
| - |
------
-
>
and a folded scalar... so we
can just keep writing various
things. And if we want to keep indentation:
we just indent a little
see, this stays indented
-
>-
Here's a folded scalar
that gets chomped.
-
|-
And here's a literal scalar
that gets chomped.
-
>2
Here's a folded scalar
that starts with some indentation.
-
::vector
-
"
:
-
()"
-
Up, up, and away!
-
-123
-
http://example.com/foo#bar
# Inside flow collection:
-
[
::
vector
,
"
:
-
()"
,
"
Up,
up
and
away!"
,
-123
,
http
:
//example.com/foo#bar
]
\ No newline at end of file
Prev
1
2
3
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment