diff --git a/crates/lexing/src/lexer.rs b/crates/lexing/src/lexer.rs index d7e1df22..c47b8906 100644 --- a/crates/lexing/src/lexer.rs +++ b/crates/lexing/src/lexer.rs @@ -110,7 +110,7 @@ impl<'a> Lexer<'a> { if is_lower_start(i) { self.take_lower() } else if i.is_letter_uppercase() { - self.take_upper() + self.take_prefix_or_upper() } else if is_operator(i) { self.take_operator() } else if i.is_whitespace() { @@ -171,10 +171,54 @@ impl<'a> Lexer<'a> { } #[inline] - fn take_upper(&mut self) { - let position = self.position(); - self.take_while(|c| c.is_letter()); - self.lexed.push(SyntaxKind::UPPER, position, None) + fn take_prefix_or_upper(&mut self) { + let prefix = self.position(); + let mut has_prefix = false; + + // It's best to follow through this block with a few examples: + // + //>================================================================ + // + // Hooks.do => [PREFIX, LOWER] + // Main.main => [UPPER, LOWER] + // + // 1. (A) takes 'Hooks' + // 2. (B) takes '.' + // 3. (D) pushes [PREFIX] and finishes + // + //>================================================================ + // + // List.Cons => [PREFIX, UPPER] + // + // 1. (A) takes 'List' + // 2. (B) takes '.' + // 3. (A) takes 'Cons' + // 4. (B) takes nothing + // 5. (C) pushes [PREFIX, UPPER] and finishes + // + //>================================================================ + loop { + // (D) + if !self.first().is_letter_uppercase() && has_prefix { + return self.lexed.push(SyntaxKind::PREFIX, prefix, None); + } + + // (A) + let proper = self.position(); + self.take_while(|c| c.is_letter()); + + // (B) + if self.first() == '.' { + self.take(); + has_prefix = true; + } else { + // (C) + if has_prefix { + self.lexed.push(SyntaxKind::PREFIX, prefix, None); + } + return self.lexed.push(SyntaxKind::UPPER, proper, None); + } + } } #[inline] diff --git a/crates/lexing/tests/lexer.rs b/crates/lexing/tests/lexer.rs index cdda8740..023a43c8 100644 --- a/crates/lexing/tests/lexer.rs +++ b/crates/lexing/tests/lexer.rs @@ -13,6 +13,7 @@ macro_rules! lexer_tests { lexer_tests!( keyword => "ado as case class data derive do else false foreign hiding if import in infix infixl infixr instance let module newtype of then true type where", + prefixed => "Hooks.do Main.main List.Cons", operator_purs => "<- ← -> → => ⇒ :: ∷ ∀ = . \\ | @", operator_source => "=>> >>= >=> && || : ++", integer_leading_zero => "0", diff --git a/crates/lexing/tests/snapshots/lexer__prefixed.snap b/crates/lexing/tests/snapshots/lexer__prefixed.snap new file mode 100644 index 00000000..8185f5a9 --- /dev/null +++ b/crates/lexing/tests/snapshots/lexer__prefixed.snap @@ -0,0 +1,16 @@ +--- +source: crates/lexing/tests/lexer.rs +expression: tokens +snapshot_kind: text +--- +[ + PREFIX, + DO, + WHITESPACE, + PREFIX, + LOWER, + WHITESPACE, + PREFIX, + UPPER, + END_OF_FILE, +] diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index a9219220..d13a315e 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs @@ -8,6 +8,7 @@ pub enum SyntaxKind { BLOCK_COMMENT, // Names + PREFIX, UPPER, LOWER, OPERATOR,