diff --git a/src/lexer.cc b/src/lexer.cc index bc3d3b8..a943c88 100644 --- a/src/lexer.cc +++ b/src/lexer.cc @@ -1,5 +1,7 @@ #include +constexpr std::string_view Notes_Symbols = "abcedefgh"; + auto Lexer::next_token() -> Result { while (consume_if(unicode::is_space)) {} @@ -40,6 +42,51 @@ auto Lexer::next_token() -> Result return { Token::Type::Numeric, finish(), token_location }; } + if (consume_if([](u32 ch) { return Notes_Symbols.find(ch) != std::string_view::npos; })) { + // chord declaration + constexpr u8 Expect_Number = 0b01; + constexpr u8 Expect_Move = 0b10; + constexpr u8 Expect_Number_Or_Move = 0b11; + + auto current = Expect_Number; + std::string_view accepted_digits = "12357"; + usize digit_cursor = 0; + + for (;;) { + if ((current & Expect_Move) == Expect_Move + && consume_if([](u32 c) { return c == ',' || c == '\''; }) + ) { + current = Expect_Number; + continue; + } + + if ((current & Expect_Number) == Expect_Number) { + bool found = false; + for (; digit_cursor < accepted_digits.size(); ++digit_cursor) { + if (consume_if([&](u32 c) { return u32(accepted_digits[digit_cursor]) == c; })) { + found = true; + break; + } + } + + if (found) { + current = digit_cursor < accepted_digits.size() + ? Expect_Number_Or_Move + : Expect_Move; + continue; + } + } + + break; + } + + if (unicode::is_letter(peek())) { + assert(false && "symbols are not implemented yet"); + } + + return { Token::Type::Chord, finish(), token_location }; + } + return errors::unrecognized_character(peek(), token_location); } diff --git a/src/musique.hh b/src/musique.hh index 89eebe3..00d57e4 100644 --- a/src/musique.hh +++ b/src/musique.hh @@ -113,6 +113,7 @@ namespace unicode bool is_digit(u32 digit); bool is_space(u32 space); + bool is_letter(u32 letter); } namespace utf8 @@ -183,7 +184,7 @@ struct Lexer // Finds next rune in source and returns it, advancing the string auto consume() -> u32; - inline auto consume_if(auto test) -> u32 + inline auto consume_if(auto test) -> bool { return test(peek()) && (consume(), true); } diff --git a/src/tests/lex.cc b/src/tests/lex.cc index 2319d11..4cfed50 100644 --- a/src/tests/lex.cc +++ b/src/tests/lex.cc @@ -27,9 +27,12 @@ static void expect_token_type_and_value( { Lexer lexer{source}; auto result = lexer.next_token(); - expect(result.has_value() >> fatal, sl) << "have not parsed any tokens"; - expect(eq(under(result->type), under(expected_type)), sl) << "different token type then expected"; - expect(eq(result->source, expected), sl) << "tokenized source is not equal to original"; + expect(result.has_value(), sl) << "have not parsed any tokens"; + + if (result.has_value()) { + expect(eq(under(result->type), under(expected_type)), sl) << "different token type then expected"; + expect(eq(result->source, expected), sl) << "tokenized source is not equal to original"; + } } static void expect_token_type_and_value( @@ -86,4 +89,13 @@ suite lexer_test = [] { expect_token_type_and_location(Token::Type::Numeric, "\n123", Location::at(2, 1)); expect_token_type_and_location(Token::Type::Numeric, "\n 123", Location::at(2, 3)); }; + + "Chord literals"_test = [] { + expect_token_type_and_value(Token::Type::Chord, "c1"); + expect_token_type_and_value(Token::Type::Chord, "d1257"); + expect_token_type_and_value(Token::Type::Chord, "e1'"); + expect_token_type_and_value(Token::Type::Chord, "g127"); + expect_token_type_and_value(Token::Type::Chord, "f1'2'3'5'7'"); + expect_token_type_and_value(Token::Type::Chord, "b1,2,5,7,"); + }; }; diff --git a/src/unicode.cc b/src/unicode.cc index 3c6dcaa..82b851d 100644 --- a/src/unicode.cc +++ b/src/unicode.cc @@ -97,3 +97,9 @@ bool unicode::is_space(u32 space) } return false; } + +bool unicode::is_letter(u32 letter) +{ + // TODO Unicode letters handling + return std::isalpha(letter); +}