Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ISC Open Source Projects
Kea
Commits
dfb173ce
Unverified
Commit
dfb173ce
authored
Nov 26, 2012
by
Michal 'vorner' Vaner
Browse files
Merge
#2375
parents
c5eee92f
00256ac1
Changes
8
Hide whitespace changes
Inline
Side-by-side
src/lib/dns/master_lexer.cc
View file @
dfb173ce
...
...
@@ -33,9 +33,13 @@ typedef boost::shared_ptr<master_lexer_internal::InputSource> InputSourcePtr;
}
using
namespace
master_lexer_internal
;
struct
MasterLexer
::
MasterLexerImpl
{
MasterLexerImpl
()
:
source_
(
NULL
),
token_
(
Token
::
NOT_STARTED
),
paren_count_
(
0
),
last_was_eol_
(
false
)
paren_count_
(
0
),
last_was_eol_
(
false
),
has_previous_
(
false
),
previous_paren_count_
(
0
),
previous_was_eol_
(
false
)
{
separators_
.
set
(
'\r'
);
separators_
.
set
(
'\n'
);
...
...
@@ -91,6 +95,11 @@ struct MasterLexer::MasterLexerImpl {
// if escaped by a backslash. See isTokenEnd() for the bitmap size.
std
::
bitset
<
128
>
separators_
;
std
::
bitset
<
128
>
esc_separators_
;
// These are to allow restoring state before previous token.
bool
has_previous_
;
size_t
previous_paren_count_
;
bool
previous_was_eol_
;
};
MasterLexer
::
MasterLexer
()
:
impl_
(
new
MasterLexerImpl
)
{
...
...
@@ -116,6 +125,7 @@ MasterLexer::pushSource(const char* filename, std::string* error) {
}
impl_
->
source_
=
impl_
->
sources_
.
back
().
get
();
impl_
->
has_previous_
=
false
;
return
(
true
);
}
...
...
@@ -123,6 +133,7 @@ void
MasterLexer
::
pushSource
(
std
::
istream
&
input
)
{
impl_
->
sources_
.
push_back
(
InputSourcePtr
(
new
InputSource
(
input
)));
impl_
->
source_
=
impl_
->
sources_
.
back
().
get
();
impl_
->
has_previous_
=
false
;
}
void
...
...
@@ -134,6 +145,7 @@ MasterLexer::popSource() {
impl_
->
sources_
.
pop_back
();
impl_
->
source_
=
impl_
->
sources_
.
empty
()
?
NULL
:
impl_
->
sources_
.
back
().
get
();
impl_
->
has_previous_
=
false
;
}
std
::
string
...
...
@@ -152,12 +164,53 @@ MasterLexer::getSourceLine() const {
return
(
impl_
->
sources_
.
back
()
->
getCurrentLine
());
}
const
MasterLexer
::
Token
&
MasterLexer
::
getNextToken
(
Options
options
)
{
// If the source is not available
if
(
impl_
->
source_
==
NULL
)
{
isc_throw
(
isc
::
InvalidOperation
,
"No source to read tokens from"
);
}
// Store the current state so we can restore it in ungetToken
impl_
->
previous_paren_count_
=
impl_
->
paren_count_
;
impl_
->
previous_was_eol_
=
impl_
->
last_was_eol_
;
impl_
->
source_
->
mark
();
impl_
->
has_previous_
=
true
;
// Reset the token now. This is to check a token was actually produced.
// This is debugging aid.
impl_
->
token_
=
Token
(
Token
::
NO_TOKEN_PRODUCED
);
// And get the token
// This actually handles EOF internally too.
const
State
*
state
=
State
::
start
(
*
this
,
options
);
if
(
state
!=
NULL
)
{
state
->
handle
(
*
this
);
}
// Make sure a token was produced. Since this Can Not Happen, we assert
// here instead of throwing.
assert
(
impl_
->
token_
.
getType
()
!=
Token
::
ERROR
||
impl_
->
token_
.
getErrorCode
()
!=
Token
::
NO_TOKEN_PRODUCED
);
return
(
impl_
->
token_
);
}
void
MasterLexer
::
ungetToken
()
{
if
(
impl_
->
has_previous_
)
{
impl_
->
has_previous_
=
false
;
impl_
->
source_
->
ungetAll
();
impl_
->
last_was_eol_
=
impl_
->
previous_was_eol_
;
impl_
->
paren_count_
=
impl_
->
previous_paren_count_
;
}
else
{
isc_throw
(
isc
::
InvalidOperation
,
"No token to unget ready"
);
}
}
namespace
{
const
char
*
const
error_text
[]
=
{
"lexer not started"
,
// NOT_STARTED
"unbalanced parentheses"
,
// UNBALANCED_PAREN
"unexpected end of input"
,
// UNEXPECTED_END
"unbalanced quotes"
// UNBALANCED_QUOTES
"unbalanced quotes"
,
// UNBALANCED_QUOTES
"no token produced"
// NO_TOKEN_PRODUCED
};
const
size_t
error_text_max_count
=
sizeof
(
error_text
)
/
sizeof
(
error_text
[
0
]);
}
...
...
@@ -201,7 +254,7 @@ class CRLF : public State {
public:
CRLF
()
{}
virtual
~
CRLF
()
{}
// see the base class for the destructor
virtual
const
State
*
handle
(
MasterLexer
&
lexer
)
const
{
virtual
void
handle
(
MasterLexer
&
lexer
)
const
{
// We've just seen '\r'. If this is part of a sequence of '\r\n',
// we combine them as a single END-OF-LINE. Otherwise we treat the
// single '\r' as an EOL and continue tokeniziation from the character
...
...
@@ -218,7 +271,6 @@ public:
}
getLexerImpl
(
lexer
)
->
token_
=
Token
(
Token
::
END_OF_LINE
);
getLexerImpl
(
lexer
)
->
last_was_eol_
=
true
;
return
(
NULL
);
}
};
...
...
@@ -226,14 +278,14 @@ class String : public State {
public:
String
()
{}
virtual
~
String
()
{}
// see the base class for the destructor
virtual
const
State
*
handle
(
MasterLexer
&
lexer
)
const
;
virtual
void
handle
(
MasterLexer
&
lexer
)
const
;
};
class
QString
:
public
State
{
public:
QString
()
{}
virtual
~
QString
()
{}
// see the base class for the destructor
virtual
const
State
*
handle
(
MasterLexer
&
lexer
)
const
;
virtual
void
handle
(
MasterLexer
&
lexer
)
const
;
};
// We use a common instance of a each state in a singleton-like way to save
...
...
@@ -325,7 +377,7 @@ State::start(MasterLexer& lexer, MasterLexer::Options options) {
}
}
const
State
*
void
String
::
handle
(
MasterLexer
&
lexer
)
const
{
std
::
vector
<
char
>&
data
=
getLexerImpl
(
lexer
)
->
data_
;
data
.
clear
();
...
...
@@ -339,14 +391,14 @@ String::handle(MasterLexer& lexer) const {
getLexerImpl
(
lexer
)
->
source_
->
ungetChar
();
getLexerImpl
(
lexer
)
->
token_
=
MasterLexer
::
Token
(
&
data
.
at
(
0
),
data
.
size
());
return
(
NULL
)
;
return
;
}
escaped
=
(
c
==
'\\'
&&
!
escaped
);
data
.
push_back
(
c
);
}
}
const
State
*
void
QString
::
handle
(
MasterLexer
&
lexer
)
const
{
MasterLexer
::
Token
&
token
=
getLexerImpl
(
lexer
)
->
token_
;
std
::
vector
<
char
>&
data
=
getLexerImpl
(
lexer
)
->
data_
;
...
...
@@ -357,7 +409,7 @@ QString::handle(MasterLexer& lexer) const {
const
int
c
=
getLexerImpl
(
lexer
)
->
source_
->
getChar
();
if
(
c
==
InputSource
::
END_OF_STREAM
)
{
token
=
Token
(
Token
::
UNEXPECTED_END
);
return
(
NULL
)
;
return
;
}
else
if
(
c
==
'"'
)
{
if
(
escaped
)
{
// found escaped '"'. overwrite the preceding backslash.
...
...
@@ -366,12 +418,12 @@ QString::handle(MasterLexer& lexer) const {
data
.
back
()
=
'"'
;
}
else
{
token
=
MasterLexer
::
Token
(
&
data
.
at
(
0
),
data
.
size
(),
true
);
return
(
NULL
)
;
return
;
}
}
else
if
(
c
==
'\n'
&&
!
escaped
)
{
getLexerImpl
(
lexer
)
->
source_
->
ungetChar
();
token
=
Token
(
Token
::
UNBALANCED_QUOTES
);
return
(
NULL
)
;
return
;
}
else
{
escaped
=
(
c
==
'\\'
&&
!
escaped
);
data
.
push_back
(
c
);
...
...
src/lib/dns/master_lexer.h
View file @
dfb173ce
...
...
@@ -69,6 +69,14 @@ class State;
class
MasterLexer
{
friend
class
master_lexer_internal
::
State
;
public:
/// \brief Exception thrown when we fail to read from the input
/// stream or file.
struct
ReadError
:
public
Unexpected
{
ReadError
(
const
char
*
file
,
size_t
line
,
const
char
*
what
)
:
Unexpected
(
file
,
line
,
what
)
{}
};
class
Token
;
// we define it separately for better readability
/// \brief Options for getNextToken.
...
...
@@ -178,6 +186,52 @@ public:
/// \return The current line number of the source (see the description)
size_t
getSourceLine
()
const
;
/// \brief Parse and return another token from the input.
///
/// It reads a bit of the last opened source and produces another token
/// found in it.
///
/// This method does not provide the strong exception guarantee. Generally,
/// if it throws, the object should not be used any more and should be
/// discarded. It was decided all the exceptions thrown from here are
/// serious enough that aborting the loading process is the only reasonable
/// recovery anyway, so the strong exception guarantee is not needed.
///
/// \param options The options can be used to modify the tokenization.
/// The method can be made reporting things which are usually ignored
/// by this parameter. Multiple options can be passed at once by
/// bitwise or (eg. option1 | option 2). See description of available
/// options.
/// \return Next token found in the input. Note that the token refers to
/// some internal data in the lexer. It is valid only until
/// getNextToken or ungetToken is called. Also, the token becomes
/// invalid when the lexer is destroyed.
/// \throw isc::InvalidOperation in case the source is not available. This
/// may mean the pushSource() has not been called yet, or that the
/// current source has been read past the end.
/// \throw ReadError in case there's problem reading from the underlying
/// source (eg. I/O error in the file on the disk).
/// \throw std::bad_alloc in case allocation of some internal resources
/// or the token fail.
const
Token
&
getNextToken
(
Options
options
=
NONE
);
/// \brief Return the last token back to the lexer.
///
/// The method undoes the lasts call to getNextToken(). If you call the
/// getNextToken() again with the same options, it'll return the same
/// token. If the options are different, it may return a different token,
/// but it acts as if the previous getNextToken() was never called.
///
/// It is possible to return only one token back in time (you can't call
/// ungetToken() twice in a row without calling getNextToken() in between
/// successfully).
///
/// It does not work after change of source (by pushSource or popSource).
///
/// \throw isc::InvalidOperation If called second time in a row or if
/// getNextToken() was not called since the last change of the source.
void
ungetToken
();
private:
struct
MasterLexerImpl
;
MasterLexerImpl
*
impl_
;
...
...
@@ -234,8 +288,10 @@ public:
NOT_STARTED
,
///< The lexer is just initialized and has no token
UNBALANCED_PAREN
,
///< Unbalanced parentheses detected
UNEXPECTED_END
,
///< The lexer reaches the end of line or file
/// unexpectedly
/// unexpectedly
UNBALANCED_QUOTES
,
///< Unbalanced quotations detected
NO_TOKEN_PRODUCED
,
///< No token was produced. This means programmer
/// error and should never get out of the lexer.
MAX_ERROR_CODE
///< Max integer corresponding to valid error codes.
/// (excluding this one). Mainly for internal use.
};
...
...
src/lib/dns/master_lexer_inputsource.cc
View file @
dfb173ce
...
...
@@ -13,6 +13,7 @@
// PERFORMANCE OF THIS SOFTWARE.
#include <dns/master_lexer_inputsource.h>
#include <dns/master_lexer.h>
#include <cerrno>
#include <cstring>
...
...
@@ -94,7 +95,7 @@ InputSource::getChar() {
// This has to come after the .eof() check as some
// implementations seem to check the eofbit also in .fail().
if
(
input_
.
fail
())
{
isc_throw
(
ReadError
,
isc_throw
(
MasterLexer
::
ReadError
,
"Error reading from the input stream: "
<<
getName
());
}
buffer_
.
push_back
(
c
);
...
...
src/lib/dns/master_lexer_inputsource.h
View file @
dfb173ce
...
...
@@ -56,14 +56,6 @@ public:
{}
};
/// \brief Exception thrown when we fail to read from the input
/// stream or file.
struct
ReadError
:
public
Unexpected
{
ReadError
(
const
char
*
file
,
size_t
line
,
const
char
*
what
)
:
Unexpected
(
file
,
line
,
what
)
{}
};
/// \brief Exception thrown when we fail to open the input file.
struct
OpenError
:
public
Unexpected
{
OpenError
(
const
char
*
file
,
size_t
line
,
const
char
*
what
)
:
...
...
@@ -124,8 +116,8 @@ public:
/// \brief Returns a single character from the input source. If end
/// of file is reached, \c END_OF_STREAM is returned.
///
/// \throws ReadError when reading from the input stream or
file
/// fails.
/// \throws
MasterLexer::
ReadError when reading from the input stream or
///
file
fails.
int
getChar
();
/// \brief Skips backward a single character in the input
...
...
src/lib/dns/master_lexer_state.h
View file @
dfb173ce
...
...
@@ -17,6 +17,8 @@
#include <dns/master_lexer.h>
#include <boost/function.hpp>
namespace
isc
{
namespace
dns
{
...
...
@@ -67,7 +69,7 @@ public:
/// tokenization session. The lexer passes a reference to itself
/// and options given in \c getNextToken().
///
/// \throw
InputSource
::ReadError Unexpected I/O error
/// \throw
MasterLexer
::ReadError Unexpected I/O error
/// \throw std::bad_alloc Internal resource allocation failure
///
/// \param lexer The lexer object that holds the main context.
...
...
@@ -80,16 +82,16 @@ public:
/// \brief Handle the process of one specific state.
///
/// This method is expected to be called on the object returned by
/// start(), and keep called on the returned object until NULL is
/// returned. The call chain will form the complete state transition.
/// start(). In the usual state transition design pattern, it would
/// return the next state. But as we noticed, we never have another
/// state, so we simplify it by not returning anything instead of
/// returning NULL every time.
///
/// \throw
InputSource
::ReadError Unexpected I/O error
/// \throw
MasterLexer
::ReadError Unexpected I/O error
/// \throw std::bad_alloc Internal resource allocation failure
///
/// \param lexer The lexer object that holds the main context.
/// \return A pointer to the next state object or NULL if the transition
/// is completed.
virtual
const
State
*
handle
(
MasterLexer
&
lexer
)
const
=
0
;
virtual
void
handle
(
MasterLexer
&
lexer
)
const
=
0
;
/// \brief Types of states.
///
...
...
src/lib/dns/tests/master_lexer_state_unittest.cc
View file @
dfb173ce
...
...
@@ -227,7 +227,7 @@ TEST_F(MasterLexerStateTest, crlf) {
// 1. A sequence of \r, \n is recognized as a single 'end-of-line'
EXPECT_EQ
(
&
s_crlf
,
State
::
start
(
lexer
,
common_options
));
// recognize '\r'
EXPECT_EQ
(
s_null
,
s_crlf
.
handle
(
lexer
)
)
;
// recognize '\n'
s_crlf
.
handle
(
lexer
);
// recognize '\n'
EXPECT_EQ
(
Token
::
END_OF_LINE
,
s_crlf
.
getToken
(
lexer
).
getType
());
EXPECT_TRUE
(
s_crlf
.
wasLastEOL
(
lexer
));
...
...
@@ -235,22 +235,22 @@ TEST_F(MasterLexerStateTest, crlf) {
// 'end-of-line'. then there will be "initial WS"
EXPECT_EQ
(
&
s_crlf
,
State
::
start
(
lexer
,
common_options
));
// recognize '\r'
// see ' ', "unget" it
EXPECT_EQ
(
s_null
,
s_crlf
.
handle
(
lexer
)
)
;
s_crlf
.
handle
(
lexer
);
EXPECT_EQ
(
s_null
,
State
::
start
(
lexer
,
common_options
));
// recognize ' '
EXPECT_EQ
(
Token
::
INITIAL_WS
,
s_crlf
.
getToken
(
lexer
).
getType
());
// 3. comment between \r and \n
EXPECT_EQ
(
&
s_crlf
,
State
::
start
(
lexer
,
common_options
));
// recognize '\r'
// skip comments, recognize '\n'
EXPECT_EQ
(
s_null
,
s_crlf
.
handle
(
lexer
)
)
;
s_crlf
.
handle
(
lexer
);
EXPECT_EQ
(
Token
::
END_OF_LINE
,
s_crlf
.
getToken
(
lexer
).
getType
());
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// skip 'a'
s_string
.
handle
(
lexer
);
// skip 'a'
// 4. \r then EOF
EXPECT_EQ
(
&
s_crlf
,
State
::
start
(
lexer
,
common_options
));
// recognize '\r'
// see EOF, then "unget" it
EXPECT_EQ
(
s_null
,
s_crlf
.
handle
(
lexer
)
)
;
s_crlf
.
handle
(
lexer
);
EXPECT_EQ
(
s_null
,
State
::
start
(
lexer
,
common_options
));
// recognize EOF
EXPECT_EQ
(
Token
::
END_OF_FILE
,
s_crlf
.
getToken
(
lexer
).
getType
());
}
...
...
@@ -281,41 +281,41 @@ TEST_F(MasterLexerStateTest, string) {
lexer
.
pushSource
(
ss
);
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see \n
s_string
.
handle
(
lexer
);
// recognize str, see \n
EXPECT_FALSE
(
s_string
.
wasLastEOL
(
lexer
));
stringTokenCheck
(
"followed-by-EOL"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
s_null
,
State
::
start
(
lexer
,
common_options
));
// skip \n
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see \r
s_string
.
handle
(
lexer
);
// recognize str, see \r
stringTokenCheck
(
"followed-by-CR"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
&
s_crlf
,
State
::
start
(
lexer
,
common_options
));
// handle \r...
EXPECT_EQ
(
s_null
,
s_crlf
.
handle
(
lexer
)
)
;
// ...and skip it
s_crlf
.
handle
(
lexer
);
// ...and skip it
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see ' '
s_string
.
handle
(
lexer
);
// recognize str, see ' '
stringTokenCheck
(
"followed-by-space"
,
s_string
.
getToken
(
lexer
));
// skip ' ', then recognize the next string
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see \t
s_string
.
handle
(
lexer
);
// recognize str, see \t
stringTokenCheck
(
"followed-by-tab"
,
s_string
.
getToken
(
lexer
));
// skip \t, then recognize the next string
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see comment
s_string
.
handle
(
lexer
);
// recognize str, see comment
stringTokenCheck
(
"followed-by-comment"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
s_null
,
State
::
start
(
lexer
,
common_options
));
// skip \n after it
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see '('
s_string
.
handle
(
lexer
);
// recognize str, see '('
stringTokenCheck
(
"followed-by-paren"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
// str in ()
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize the str, see ')'
s_string
.
handle
(
lexer
);
// recognize the str, see ')'
stringTokenCheck
(
"closing"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see EOF
s_string
.
handle
(
lexer
);
// recognize str, see EOF
stringTokenCheck
(
"followed-by-EOF"
,
s_string
.
getToken
(
lexer
));
}
...
...
@@ -331,32 +331,32 @@ TEST_F(MasterLexerStateTest, stringEscape) {
lexer
.
pushSource
(
ss
);
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see ' ' at end
s_string
.
handle
(
lexer
);
// recognize str, see ' ' at end
stringTokenCheck
(
"escaped
\\
space"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see ' ' at end
s_string
.
handle
(
lexer
);
// recognize str, see ' ' at end
stringTokenCheck
(
"escaped
\\\t
tab"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see ' ' at end
s_string
.
handle
(
lexer
);
// recognize str, see ' ' at end
stringTokenCheck
(
"escaped
\\
(paren"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see ' ' at end
s_string
.
handle
(
lexer
);
// recognize str, see ' ' at end
stringTokenCheck
(
"escaped
\\
)close"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see ' ' at end
s_string
.
handle
(
lexer
);
// recognize str, see ' ' at end
stringTokenCheck
(
"escaped
\\
;comment"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see ' ' in mid
s_string
.
handle
(
lexer
);
// recognize str, see ' ' in mid
stringTokenCheck
(
"escaped
\\\\
"
,
s_string
.
getToken
(
lexer
));
// Confirm the word that follows the escaped '\' is correctly recognized.
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see ' ' at end
s_string
.
handle
(
lexer
);
// recognize str, see ' ' at end
stringTokenCheck
(
"backslash"
,
s_string
.
getToken
(
lexer
));
}
...
...
@@ -376,7 +376,7 @@ TEST_F(MasterLexerStateTest, quotedString) {
// by default, '"' doesn't have any special meaning and part of string
EXPECT_EQ
(
&
s_string
,
State
::
start
(
lexer
,
common_options
));
EXPECT_EQ
(
s_null
,
s_string
.
handle
(
lexer
)
)
;
// recognize str, see \n
s_string
.
handle
(
lexer
);
// recognize str, see \n
stringTokenCheck
(
"
\"
ignore-quotes
\"
"
,
s_string
.
getToken
(
lexer
));
EXPECT_EQ
(
s_null
,
State
::
start
(
lexer
,
common_options
));
// skip \n after it
EXPECT_TRUE
(
s_string
.
wasLastEOL
(
lexer
));
...
...
@@ -386,35 +386,35 @@ TEST_F(MasterLexerStateTest, quotedString) {
const
MasterLexer
::
Options
options
=
common_options
|
MasterLexer
::
QSTRING
;
EXPECT_EQ
(
&
s_qstring
,
State
::
start
(
lexer
,
options
));
EXPECT_FALSE
(
s_string
.
wasLastEOL
(
lexer
));
// EOL is canceled due to '"'
EXPECT_EQ
(
s_null
,
s_qstring
.
handle
(
lexer
)
)
;
s_qstring
.
handle
(
lexer
);
stringTokenCheck
(
"quoted string"
,
s_string
.
getToken
(
lexer
),
true
);
// Also checks other separator characters within a qstring
EXPECT_EQ
(
&
s_qstring
,
State
::
start
(
lexer
,
options
));
EXPECT_EQ
(
s_null
,
s_qstring
.
handle
(
lexer
)
)
;
s_qstring
.
handle
(
lexer
);
stringTokenCheck
(
"quoted()
\t\r
string"
,
s_string
.
getToken
(
lexer
),
true
);
// escape character mostly doesn't have any effect in the qstring
// processing
EXPECT_EQ
(
&
s_qstring
,
State
::
start
(
lexer
,
options
));
EXPECT_EQ
(
s_null
,
s_qstring
.
handle
(
lexer
)
)
;
s_qstring
.
handle
(
lexer
);
stringTokenCheck
(
"escape
\\
in quote"
,
s_string
.
getToken
(
lexer
),
true
);
// The only exception is the quotation mark itself. Note that the escape
// only works on the quotation mark immediately after it.
EXPECT_EQ
(
&
s_qstring
,
State
::
start
(
lexer
,
options
));
EXPECT_EQ
(
s_null
,
s_qstring
.
handle
(
lexer
)
)
;
s_qstring
.
handle
(
lexer
);
stringTokenCheck
(
"escaped
\"
"
,
s_string
.
getToken
(
lexer
),
true
);
// quoted '\' then '"'. Unlike the previous case '"' shouldn't be
// escaped.
EXPECT_EQ
(
&
s_qstring
,
State
::
start
(
lexer
,
options
));
EXPECT_EQ
(
s_null
,
s_qstring
.
handle
(
lexer
)
)
;
s_qstring
.
handle
(
lexer
);
stringTokenCheck
(
"escaped backslash
\\\\
"
,
s_string
.
getToken
(
lexer
),
true
);
// ';' has no meaning in a quoted string (not indicating a comment)
EXPECT_EQ
(
&
s_qstring
,
State
::
start
(
lexer
,
options
));
EXPECT_EQ
(
s_null
,
s_qstring
.
handle
(
lexer
)
)
;
s_qstring
.
handle
(
lexer
);
stringTokenCheck
(
"no;comment"
,
s_string
.
getToken
(
lexer
),
true
);
}
...
...
@@ -427,7 +427,7 @@ TEST_F(MasterLexerStateTest, brokenQuotedString) {
// EOL is encountered without closing the quote
const
MasterLexer
::
Options
options
=
common_options
|
MasterLexer
::
QSTRING
;
EXPECT_EQ
(
&
s_qstring
,
State
::
start
(
lexer
,
options
));
EXPECT_EQ
(
s_null
,
s_qstring
.
handle
(
lexer
)
)
;
s_qstring
.
handle
(
lexer
);
ASSERT_EQ
(
Token
::
ERROR
,
s_qstring
.
getToken
(
lexer
).
getType
());
EXPECT_EQ
(
Token
::
UNBALANCED_QUOTES
,
s_qstring
.
getToken
(
lexer
).
getErrorCode
());
...
...
@@ -437,12 +437,12 @@ TEST_F(MasterLexerStateTest, brokenQuotedString) {
// \n is okay in a quoted string if escaped
EXPECT_EQ
(
&
s_qstring
,
State
::
start
(
lexer
,
options
));
EXPECT_EQ
(
s_null
,
s_qstring
.
handle
(
lexer
)
)
;
s_qstring
.
handle
(
lexer
);
stringTokenCheck
(
"quoted
\\\n
"
,
s_string
.
getToken
(
lexer
),
true
);
// EOF is encountered without closing the quote
EXPECT_EQ
(
&
s_qstring
,
State
::
start
(
lexer
,
options
));
EXPECT_EQ
(
s_null
,
s_qstring
.
handle
(
lexer
)
)
;
s_qstring
.
handle
(
lexer
);
ASSERT_EQ
(
Token
::
ERROR
,
s_qstring
.
getToken
(
lexer
).
getType
());
EXPECT_EQ
(
Token
::
UNEXPECTED_END
,
s_qstring
.
getToken
(
lexer
).
getErrorCode
());
// If we continue we'll simply see the EOF
...
...
src/lib/dns/tests/master_lexer_token_unittest.cc
View file @
dfb173ce
...
...
@@ -142,15 +142,18 @@ TEST_F(MasterLexerTokenTest, errors) {
EXPECT_EQ
(
"unbalanced quotes"
,
MasterLexer
::
Token
(
MasterLexer
::
Token
::
UNBALANCED_QUOTES
).
getErrorText
());
EXPECT_EQ
(
"no token produced"
,
MasterLexer
::
Token
(
MasterLexer
::
Token
::
NO_TOKEN_PRODUCED
).
getErrorText
());
// getErrorCode/Text() isn't allowed for non number types
EXPECT_THROW
(
token_num
.
getErrorCode
(),
isc
::
InvalidOperation
);
EXPECT_THROW
(
token_num
.
getErrorText
(),
isc
::
InvalidOperation
);
// Only the pre-defined error code is accepted. Hardcoding '
4
' (max code
// Only the pre-defined error code is accepted. Hardcoding '
5
' (max code
// + 1) is intentional; it'd be actually better if we notice it when we
// update the enum list (which shouldn't happen too often).
EXPECT_THROW
(
MasterLexer
::
Token
(
MasterLexer
::
Token
::
ErrorCode
(
4
)),
EXPECT_THROW
(
MasterLexer
::
Token
(
MasterLexer
::
Token
::
ErrorCode
(
5
)),
isc
::
InvalidParameter
);
// Check the coexistence of "from number" and "from error-code"
...
...
src/lib/dns/tests/master_lexer_unittest.cc
View file @
dfb173ce
...
...
@@ -15,10 +15,14 @@
#include <exceptions/exceptions.h>
#include <dns/master_lexer.h>
#include <dns/master_lexer_state.h>
#include <gtest/gtest.h>
#include <boost/lexical_cast.hpp>
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/bind.hpp>
#include <string>
#include <sstream>
...
...
@@ -27,6 +31,8 @@ using namespace isc::dns;
using
std
::
string
;
using
std
::
stringstream
;
using
boost
::
lexical_cast
;
using
boost
::
scoped_ptr
;
using
master_lexer_internal
::
State
;
namespace
{
...
...
@@ -124,4 +130,158 @@ TEST_F(MasterLexerTest, invalidPop) {
EXPECT_THROW
(
lexer
.
popSource
(),
isc
::
InvalidOperation
);
}
// Test it is not possible to get token when no source is available.
TEST_F
(
MasterLexerTest
,
noSource
)
{
EXPECT_THROW
(
lexer
.
getNextToken
(),
isc
::
InvalidOperation
);
}
// Test getting some tokens