Add function to tokenize strings

This commit is contained in:
Jonathan Müller 2017-10-19 19:02:27 +02:00
commit 1572920650
14 changed files with 798 additions and 83 deletions

View file

@ -1158,7 +1158,7 @@ void detail::write_template_arguments(
void detail::write_token_string(code_generator::output& output, const cpp_token_string& tokens)
{
auto last_kind = cpp_token_kind::unknown;
auto last_kind = cpp_token_kind::punctuation; // neutral regarding whitespace
for (auto& token : tokens)
{
switch (token.kind)
@ -1177,14 +1177,15 @@ void detail::write_token_string(code_generator::output& output, const cpp_token_
output << operator_ws;
break;
case cpp_token_kind::literal:
// determine kind of literal
if (token.spelling.front() == '\"')
output << string_literal(token.spelling);
else if (token.spelling.find('.') != std::string::npos)
output << float_literal(token.spelling);
else
output << int_literal(token.spelling);
case cpp_token_kind::int_literal:
output << int_literal(token.spelling);
break;
case cpp_token_kind::float_literal:
output << float_literal(token.spelling);
break;
case cpp_token_kind::char_literal:
case cpp_token_kind::string_literal:
output << string_literal(token.spelling);
break;
case cpp_token_kind::punctuation:
@ -1206,9 +1207,6 @@ void detail::write_token_string(code_generator::output& output, const cpp_token_
else
output << punctuation(token.spelling);
break;
case cpp_token_kind::unknown:
output << token_seq(token.spelling);
}
last_kind = token.kind;

View file

@ -6,6 +6,9 @@
#include <algorithm>
#include <cctype>
#include <cstring>
#include <type_safe/optional.hpp>
#include <cppast/detail/assert.hpp>
using namespace cppast;
@ -16,6 +19,586 @@ void cpp_token_string::builder::unmunch()
tokens_.back().spelling = ">";
}
namespace
{
template <std::size_t N>
bool starts_with(const char* ptr, const char (&str)[N])
{
return std::strncmp(ptr, str, N - 1u) == 0;
}
bool starts_with(const char* ptr, const std::string& str)
{
return std::strncmp(ptr, str.c_str(), str.size()) == 0;
}
template <std::size_t N>
bool bump_if(const char*& ptr, const char (&str)[N])
{
if (starts_with(ptr, str))
{
ptr += N - 1;
return true;
}
else
return false;
}
bool bump_if(const char*& ptr, const std::string& str)
{
if (starts_with(ptr, str))
{
ptr += str.size();
return true;
}
else
return false;
}
bool is_identifier_nondigit(char c)
{
// assume ASCII
if (c >= 'a' && c <= 'z')
return true;
else if (c >= 'A' && c <= 'Z')
return true;
else if (c == '_')
return true;
else
// technically \uXXX is allowed as well, but I haven't seen that used ever
return false;
}
bool is_digit(char c)
{
return c >= '0' && c <= '9';
}
bool is_hexadecimal_digit(char c)
{
return is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');
}
type_safe::optional<std::string> bump_identifier(const char*& ptr)
{
if (is_identifier_nondigit(*ptr))
{
std::string result;
result += *ptr++;
while (is_identifier_nondigit(*ptr) || is_digit(*ptr))
result += *ptr++;
return result;
}
else
return type_safe::nullopt;
}
type_safe::optional<cpp_token> identifier_token(const char*& ptr)
{
auto identifier = bump_identifier(ptr);
if (!identifier)
return type_safe::nullopt;
static constexpr const char* keywords[] = {"alignas",
"alignof",
"asm",
"auto",
"bool",
"break",
"case",
"catch",
"char",
"char16_t",
"char32_t",
"class",
"const",
"constexpr",
"const_cast",
"continue",
"decltype",
"default",
"delete",
"do",
"double",
"dynamic_cast",
"else",
"enum",
"explicit",
"export",
"extern",
"false",
"float",
"for",
"friend",
"goto",
"if",
"inline",
"int",
"long",
"mutable",
"namespace",
"new",
"noexcept",
"nullptr",
"operator",
"private",
"protected",
"public",
"register",
"reinterpret_cast",
"return",
"short",
"signed",
"sizeof",
"static",
"static_assert",
"static_cast",
"struct",
"switch",
"template",
"this",
"thread_local",
"throw",
"true",
"try",
"typedef",
"typeid",
"typename",
"union",
"unsigned",
"using",
"virtual",
"void",
"volatile",
"wchar_t",
"while"};
auto find_keyword = std::find(std::begin(keywords), std::end(keywords), identifier.value());
if (find_keyword != std::end(keywords))
return cpp_token(cpp_token_kind::keyword, identifier.value());
else if (identifier == "and")
return cpp_token(cpp_token_kind::punctuation, "&&");
else if (identifier == "and_eq")
return cpp_token(cpp_token_kind::punctuation, "&=");
else if (identifier == "bitand")
return cpp_token(cpp_token_kind::punctuation, "&");
else if (identifier == "bitor")
return cpp_token(cpp_token_kind::punctuation, "|");
else if (identifier == "compl")
return cpp_token(cpp_token_kind::punctuation, "~");
else if (identifier == "not")
return cpp_token(cpp_token_kind::punctuation, "!");
else if (identifier == "not_eq")
return cpp_token(cpp_token_kind::punctuation, "!=");
else if (identifier == "or")
return cpp_token(cpp_token_kind::punctuation, "||");
else if (identifier == "or_eq")
return cpp_token(cpp_token_kind::punctuation, "|=");
else if (identifier == "xor")
return cpp_token(cpp_token_kind::punctuation, "^");
else if (identifier == "xor_eq")
return cpp_token(cpp_token_kind::punctuation, "^=");
else
return cpp_token(cpp_token_kind::identifier, identifier.value());
}
void append_udl_suffix(std::string& literal, const char*& ptr)
{
if (auto id = identifier_token(ptr))
literal += id.value().spelling;
}
template <typename DigitPredicate>
std::string parse_digit_sequence(const char*& ptr, DigitPredicate is_digit)
{
std::string result;
for (; is_digit(*ptr) || *ptr == '\''; ++ptr)
if (*ptr != '\'')
result += *ptr;
DEBUG_ASSERT(result.back() != '\'', detail::assert_handler{});
return result;
}
void append_integer_suffix(std::string& literal, const char*& ptr)
{
auto append_unsigned_suffix = [](std::string& literal, const char*& ptr) {
if (*ptr == 'u' || *ptr == 'U')
{
literal += *ptr++;
return true;
}
else
return false;
};
auto append_long_suffix = [](std::string& literal, const char*& ptr) {
if (starts_with(ptr, "ll") || starts_with(ptr, "LL"))
{
literal += *ptr++;
literal += *ptr++;
return true;
}
else if (*ptr == 'l' || *ptr == 'L')
{
literal += *ptr++;
return true;
}
else
return false;
};
if (append_unsigned_suffix(literal, ptr))
append_long_suffix(literal, ptr);
else if (append_long_suffix(literal, ptr))
append_unsigned_suffix(literal, ptr);
else
append_udl_suffix(literal, ptr);
}
void append_floating_point_suffix(std::string& literal, const char*& ptr)
{
if (*ptr == 'f' || *ptr == 'F')
literal += *ptr++;
else if (*ptr == 'l' || *ptr == 'L')
literal += *ptr++;
else
append_udl_suffix(literal, ptr);
}
type_safe::optional<std::string> parse_floating_point_exponent(const char*& ptr)
{
if (*ptr == 'e' || *ptr == 'E' || *ptr == 'p' || *ptr == 'P')
{
std::string result;
result += *ptr++;
if (*ptr == '+' || *ptr == '-')
result += *ptr++;
result += parse_digit_sequence(ptr, &is_digit);
return result;
}
else
return type_safe::nullopt;
}
type_safe::optional<cpp_token> numeric_literal_token(const char*& ptr)
{
if (starts_with(ptr, "0b") || starts_with(ptr, "0B")) // binary integer literal
{
std::string result;
result += *ptr++;
result += *ptr++;
result += parse_digit_sequence(ptr, [](char c) { return c == '0' || c == '1'; });
append_integer_suffix(result, ptr);
return cpp_token(cpp_token_kind::int_literal, result);
}
else if (starts_with(ptr, "0x") || starts_with(ptr, "0X")) // hexadecimal literal
{
std::string result;
result += *ptr++;
result += *ptr++;
result += parse_digit_sequence(ptr, &is_hexadecimal_digit);
auto is_float = false;
if (*ptr == '.')
{
// floating point hexadecimal
is_float = true;
result += *ptr++;
result += parse_digit_sequence(ptr, &is_hexadecimal_digit);
}
if (auto exp = parse_floating_point_exponent(ptr))
{
is_float = true;
// floating point exponent
result += exp.value();
}
if (is_float)
append_floating_point_suffix(result, ptr);
else
append_integer_suffix(result, ptr);
return cpp_token(is_float ? cpp_token_kind::float_literal : cpp_token_kind::int_literal,
result);
}
else if (is_digit(*ptr)) // octal and decimal literals
{
std::string result;
result += parse_digit_sequence(ptr, &is_digit);
auto is_float = false;
if (*ptr == '.')
{
// floating point decimal
is_float = true;
result += *ptr++;
result += parse_digit_sequence(ptr, &is_hexadecimal_digit);
}
if (auto exp = parse_floating_point_exponent(ptr))
{
// floating point exponent
is_float = true;
result += exp.value();
}
if (is_float)
append_floating_point_suffix(result, ptr);
else
append_integer_suffix(result, ptr);
return cpp_token(is_float ? cpp_token_kind::float_literal : cpp_token_kind::int_literal,
result);
}
else if (*ptr == '.' && is_digit(ptr[1]))
{
std::string result;
// floating point fraction
result += *ptr++;
result += parse_digit_sequence(ptr, &is_digit);
if (auto exp = parse_floating_point_exponent(ptr))
result += exp.value();
append_floating_point_suffix(result, ptr);
return cpp_token(cpp_token_kind::float_literal, result);
}
else
return type_safe::nullopt;
}
type_safe::optional<std::string> parse_encoding_prefix(const char*& ptr)
{
if (bump_if(ptr, "u8"))
return "u8";
else if (bump_if(ptr, "u"))
return "u";
else if (bump_if(ptr, "U"))
return "U";
else if (bump_if(ptr, "L"))
return "L";
else
return type_safe::nullopt;
}
type_safe::optional<cpp_token> character_literal(const char*& ptr)
{
auto save = ptr;
auto prefix = parse_encoding_prefix(ptr);
if (*ptr != '\'')
{
ptr = save;
return type_safe::nullopt;
}
else
{
auto result = prefix.value_or("");
result += *ptr++;
while (*ptr != '\'')
{
DEBUG_ASSERT(*ptr, detail::assert_handler{});
if (*ptr == '\\')
result += *ptr++;
result += *ptr++;
}
result += *ptr++;
append_udl_suffix(result, ptr);
return cpp_token(cpp_token_kind::char_literal, result);
}
}
type_safe::optional<cpp_token> string_literal(const char*& ptr)
{
auto save = ptr;
auto prefix = parse_encoding_prefix(ptr);
if (starts_with(ptr, "R\""))
{
// raw string literal
auto result = prefix.value_or("");
result += *ptr++;
result += *ptr++;
std::string terminator;
terminator += ")";
while (*ptr != '(')
{
result += *ptr;
terminator += *ptr++;
}
result += *ptr++;
terminator += '"';
while (!bump_if(ptr, terminator))
{
DEBUG_ASSERT(ptr, detail::assert_handler{});
result += *ptr++;
}
result += terminator;
append_udl_suffix(result, ptr);
return cpp_token(cpp_token_kind::string_literal, result);
}
else if (starts_with(ptr, "\""))
{
// regular string literal
auto result = prefix.value_or("");
result += *ptr++;
while (*ptr != '"')
{
DEBUG_ASSERT(*ptr, detail::assert_handler{});
if (*ptr == '\\')
result += *ptr++;
result += *ptr++;
}
result += *ptr++;
append_udl_suffix(result, ptr);
return cpp_token(cpp_token_kind::string_literal, result);
}
else
{
ptr = save;
return type_safe::nullopt;
}
}
type_safe::optional<cpp_token> digraph_token(const char*& ptr)
{
if (bump_if(ptr, "<%"))
return cpp_token(cpp_token_kind::punctuation, "{");
else if (bump_if(ptr, "%>"))
return cpp_token(cpp_token_kind::punctuation, "}");
else if (starts_with(ptr, "<::") && ptr[3] != ':' && ptr[3] != '>')
// don't detect digraph in std::vector<::std::string>
return type_safe::nullopt;
else if (bump_if(ptr, "<:"))
return cpp_token(cpp_token_kind::punctuation, "[");
else if (bump_if(ptr, ":>"))
return cpp_token(cpp_token_kind::punctuation, "]");
else if (bump_if(ptr, "%:%:"))
return cpp_token(cpp_token_kind::punctuation, "##");
else if (bump_if(ptr, "%:"))
return cpp_token(cpp_token_kind::punctuation, "#");
else
return type_safe::nullopt;
}
type_safe::optional<cpp_token> punctuation_token(const char*& ptr)
{
static constexpr const char* punctuations[] = {
// tokens staring with #
"##",
"#",
// tokens starting with .
"...",
".*",
".",
// tokens starting with :
"::",
":",
// tokens starting with +
"+=",
"++",
"+",
// tokens starting with -
"->*",
"->",
"--",
"-=",
"-",
// tokens starting with *
"*=",
"*",
// tokens starting with /
"/=",
"/",
// tokens starting with %
"%=",
"%",
// tokens starting with ^
"^=",
"^",
// tokens starting with &
"&=",
"&&",
"&",
// tokens starting with |
"|=",
"||",
"|",
// tokens starting with <
"<<=",
"<<",
"<=",
"<",
// tokens starting with >
">>=",
">>",
">=",
">",
// tokens starting with !
"!=",
"!",
// tokens starting with =
"==",
"=",
// single tokens
"~",
";",
"?",
",",
"{",
"}",
"[",
"]",
"(",
")",
};
for (auto punct : punctuations)
if (bump_if(ptr, punct))
return cpp_token(cpp_token_kind::punctuation, punct);
return type_safe::nullopt;
}
}
cpp_token_string cpp_token_string::tokenize(std::string str)
{
cpp_token_string::builder builder;
auto ptr = str.c_str();
while (*ptr)
{
if (auto num = numeric_literal_token(ptr))
builder.add_token(num.value());
else if (auto char_lit = character_literal(ptr))
builder.add_token(char_lit.value());
else if (auto str_lit = string_literal(ptr))
builder.add_token(str_lit.value());
else if (auto digraphs = digraph_token(ptr))
builder.add_token(digraphs.value());
else if (auto punct = punctuation_token(ptr))
builder.add_token(punct.value());
else if (auto id = identifier_token(ptr))
builder.add_token(id.value());
else if (*ptr == ' ' || *ptr == '\t' || *ptr == '\n' || *ptr == '\r')
++ptr;
else
DEBUG_UNREACHABLE(detail::assert_handler{});
}
return builder.finish();
}
namespace
{
bool is_identifier(char c)

View file

@ -412,9 +412,9 @@ bool detail::skip_attribute(detail::cxtoken_stream& stream)
namespace
{
cpp_token_kind get_kind(CXTokenKind kind)
cpp_token_kind get_kind(const detail::cxtoken& token)
{
switch (kind)
switch (token.kind())
{
case CXToken_Punctuation:
return cpp_token_kind::punctuation;
@ -422,14 +422,26 @@ namespace
return cpp_token_kind::keyword;
case CXToken_Identifier:
return cpp_token_kind::identifier;
case CXToken_Literal:
return cpp_token_kind::literal;
{
auto spelling = token.value().std_str();
if (spelling.find('.') != std::string::npos)
return cpp_token_kind::float_literal;
else if (std::isdigit(spelling.front()))
return cpp_token_kind::int_literal;
else if (spelling.back() == '\'')
return cpp_token_kind::char_literal;
else
return cpp_token_kind::string_literal;
}
case CXToken_Comment:
break;
}
DEBUG_UNREACHABLE(detail::assert_handler{});
return cpp_token_kind ::literal;
return cpp_token_kind::punctuation;
}
}
@ -440,7 +452,7 @@ cpp_token_string detail::to_string(cxtoken_stream& stream, cxtoken_iterator end)
while (stream.cur() != end)
{
auto& token = stream.get();
builder.add_token(cpp_token(get_kind(token.kind()), token.c_str()));
builder.add_token(cpp_token(get_kind(token), token.c_str()));
}
if (stream.unmunch())

View file

@ -246,7 +246,7 @@ namespace
return size_expr.empty() ?
nullptr :
cpp_unexposed_expression::build(cpp_builtin_type::build(cpp_ulonglong),
cpp_token_string::from_string(
cpp_token_string::tokenize(
std::string(size_expr.rbegin(),
size_expr.rend())));
}
@ -488,7 +488,7 @@ namespace
return cpp_decltype_type::build(
cpp_unexposed_expression::build(cpp_unexposed_type::build("<decltype>"),
cpp_token_string::from_string(spelling)));
cpp_token_string::tokenize(spelling)));
});
}