xo-tokenizer: = and := tokens

This commit is contained in:
Roland Conybeare 2024-08-24 12:30:22 -04:00
commit bff6b7ce9b
3 changed files with 144 additions and 12 deletions

View file

@ -77,7 +77,7 @@ namespace xo {
static token doublecolon() { return token(tokentype::tk_doublecolon); } static token doublecolon() { return token(tokentype::tk_doublecolon); }
static token semicolon() { return token(tokentype::tk_semicolon); } static token semicolon() { return token(tokentype::tk_semicolon); }
static token singleassign() { return token(tokentype::tk_singleassign); } static token singleassign() { return token(tokentype::tk_singleassign); }
static token assign() { return token(tokentype::tk_assign); } static token assign_token() { return token(tokentype::tk_assign); }
static token yields() { return token(tokentype::tk_yields); } static token yields() { return token(tokentype::tk_yields); }
static token type() { return token(tokentype::tk_type); } static token type() { return token(tokentype::tk_type); }

View file

@ -61,10 +61,16 @@ namespace xo {
/** identifies punctuation chars. /** identifies punctuation chars.
* These are chars that are not permitted to appear within * These are chars that are not permitted to appear within
* a string/symbol token. Instead they force completion of * a symbol token. Instead they force completion of
* a preceding token, and start a new token with themselves * a preceding token, and start a new token with themselves
**/ **/
bool is_punctuation(CharT ch) const; bool is_1char_punctuation(CharT ch) const;
/** more-relazed version of is_1char_punctuation.
* Chars that are not permitted to appear within a symbol token,
* but may form token combined with next character
**/
bool is_2char_punctuation(CharT ch) const;
/** true if tokenizer contains stored prefix of /** true if tokenizer contains stored prefix of
* possibly-incomplete token * possibly-incomplete token
@ -115,7 +121,7 @@ namespace xo {
template <typename CharT> template <typename CharT>
bool bool
tokenizer<CharT>::is_punctuation(CharT ch) const { tokenizer<CharT>::is_1char_punctuation(CharT ch) const {
switch(ch) { switch(ch) {
case '<': case '<':
return true; return true;
@ -138,23 +144,36 @@ namespace xo {
case ';': case ';':
return true; return true;
case ':': case ':':
return true; /* can't be 1char punctuation -- can begin assignment token */
return false;
case '=': case '=':
return true; return true;
case '-': case '-':
/* can't be punctuation -- can appear inside f64 token */ /* can't be punctuation -- can appear inside f64 token: e.g. 1.23e-9 */
return false; return false;
case '+': case '+':
/* can't be punctuation -- can appear inside f64 token */ /* can't be punctuation -- can appear inside f64 token: e.g. 1.23e+4 */
return false; return false;
case '.': case '.':
/* can't be punctuation -- can appear inside f64 token */ /* can't be punctuation -- can appear inside f64 token: e.g. 1.23 */
return false; return false;
} }
return false; return false;
} }
template <typename CharT>
bool
tokenizer<CharT>::is_2char_punctuation(CharT ch) const {
switch(ch) {
case ':':
/* can begin := */
return true;
}
return false;
}
template <typename CharT> template <typename CharT>
auto auto
tokenizer<CharT>::assemble_token(const span_type & token_text) const -> token_type tokenizer<CharT>::assemble_token(const span_type & token_text) const -> token_type
@ -483,9 +502,19 @@ namespace xo {
++ix; ++ix;
break; break;
case ':': case ':':
tk_type = tokentype::tk_colon; {
++ix; log && log("colon or assignment token");
if (*(ix + 1) == '=') {
tk_type = tokentype::tk_assign;
++ix;
++ix;
} else {
tk_type = tokentype::tk_colon;
++ix;
}
break; break;
}
case '=': case '=':
tk_type = tokentype::tk_singleassign; tk_type = tokentype::tk_singleassign;
++ix; ++ix;
@ -575,9 +604,33 @@ namespace xo {
*/ */
const CharT * tk_start = ix; const CharT * tk_start = ix;
if (is_punctuation(*ix)) { if (is_1char_punctuation(*ix)) {
/* 1-character token */ /* 1-character token */
++ix; ++ix;
} else if (is_2char_punctuation(*ix)) {
CharT ch1 = *ix;
++ix;
if (ix == input.hi()) {
/* need more input to know if/when token complete */
this->prefix_ += std::string(tk_start, input.hi());
log && log(xtag("captured-prefix", this->prefix_));
} else {
CharT ch2 = *ix;
if (((ch2 >= '0') && (ch2 <= '9'))
|| ((ch2 >= 'A') && (ch2 <= 'Z'))
|| ((ch2 >= 'a') && (ch2 <= 'z')))
{
/* treat as 1 char punctuation */
;
} else {
/* include next char */
++ix;
}
}
} else if (*ix == '"') { } else if (*ix == '"') {
bool complete_flag = false; bool complete_flag = false;
@ -618,8 +671,12 @@ namespace xo {
* - punctuation * - punctuation
*/ */
for (; ix != input.hi(); ++ix) { for (; ix != input.hi(); ++ix) {
if (is_whitespace(*ix) || is_punctuation(*ix)) if (is_whitespace(*ix)
|| is_1char_punctuation(*ix)
|| is_2char_punctuation(*ix))
{
break; break;
}
} }
if (ix == input.hi()) { if (ix == input.hi()) {

View file

@ -99,6 +99,9 @@ namespace xo {
{"\"tab to the right [\\t], to the right [\\t]\"", false, {"\"tab to the right [\\t], to the right [\\t]\"", false,
token::string_token("tab to the right [\t], to the right [\t]"), true}, token::string_token("tab to the right [\t], to the right [\t]"), true},
{":", false, token::colon(), true},
{":=", false, token::assign_token(), true},
{"symbol", false, token::symbol_token("symbol"), true}, {"symbol", false, token::symbol_token("symbol"), true},
{"type", false, token::type(), true}, {"type", false, token::type(), true},
@ -162,6 +165,78 @@ namespace xo {
} }
} }
namespace {
struct testcase2_tkz {
std::string input_;
bool expect_throw_;
std::vector<token> expected_tk_v_;
};
std::vector<testcase2_tkz>
s_testcase2_v = {
{"def foo : f64 = 3.141;",
false,
{token::def(),
token::symbol_token("foo"),
token::colon(),
token::symbol_token("f64"),
token::singleassign(),
token::f64_token("3.141"),
token::semicolon()
}}
};
}
TEST_CASE("tokenizer2", "[tokenizer]") {
for (std::size_t i_tc = 0, n_tc = s_testcase2_v.size(); i_tc < n_tc; ++i_tc) {
const testcase2_tkz & testcase = s_testcase2_v[i_tc];
INFO(xtag("input", testcase.input_));
INFO(xtag("i_tc", i_tc));
using tokenizer
= xo::scm::tokenizer<char>;
tokenizer tkz;
tokenizer::span_type
in_span(testcase.input_.c_str(),
testcase.input_.c_str() + testcase.input_.size());
for (int i_tk = 0, n_tk = testcase.expected_tk_v_.size();
i_tk < n_tk; ++i_tk)
{
INFO(xtag("i_tk", i_tk));
auto res = tkz.scan2(in_span, in_span.empty());
const auto & tk = res.first;
if (tk.is_valid())
REQUIRE(tk.tk_type() == testcase.expected_tk_v_[i_tk].tk_type());
if (tk.tk_type() == tokentype::tk_i64)
{
REQUIRE(!tk.text().empty());
REQUIRE(tk.i64_value() == testcase.expected_tk_v_[i_tk].i64_value());
} else if (tk.tk_type() == tokentype::tk_f64)
{
REQUIRE(!tk.text().empty());
REQUIRE(tk.f64_value() == testcase.expected_tk_v_[i_tk].f64_value());
} else if(tk.tk_type() == tokentype::tk_string)
{
/* tk.text() can be empty, consider input "" */
REQUIRE(tk.text() == testcase.expected_tk_v_[i_tk].text());
} else if(tk.tk_type() == tokentype::tk_symbol)
{
REQUIRE(!tk.text().empty());
REQUIRE(tk.text() == testcase.expected_tk_v_[i_tk].text());
} else {
REQUIRE(tk.text().empty());
}
in_span = in_span.after_prefix(res.second);
}
}
} /*TEST_CASE(tokenizer2)*/
} /*namespace ut*/ } /*namespace ut*/
} /*namespace xo*/ } /*namespace xo*/