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