Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@
when building large projects.
([John Downey](https://github.com/jtdowney))

- The compiler now provides a more specific error message when encountering a
merge conflict indicator
([0xda157](https://github.com/0xda157))

### Language server

- The language server now supports go-to-definition, find-references and rename
Expand Down
5 changes: 5 additions & 0 deletions compiler-core/src/parse/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub enum LexicalErrorType {
tok: char,
},
InvalidTripleEqual,
MergeConflictIndicator,
/// A character was encountered that visually looks like a correct
/// character, but in reality it's some other unicode characters.
/// For example, a non-breaking-space instead of a regular space.
Expand Down Expand Up @@ -952,6 +953,10 @@ impl LexicalError {
"See: https://tour.gleam.run/basics/equality".into(),
],
),
LexicalErrorType::MergeConflictIndicator => (
"I don't know how to handle this",
vec!["Hint: resolve merge conflicts".into()],
Comment on lines +957 to +958

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't really have any good ideas for the error message, suggestions would be appreciated.

),
LexicalErrorType::VisuallySimilarInvalidCharacter { name, correct } => (
"Unexpected character",
vec![wrap(&format!(
Expand Down
299 changes: 204 additions & 95 deletions compiler-core/src/parse/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,26 +194,59 @@ where
self.emit(string);
}
'=' => {
let tok_start = self.get_pos();
let mut tok_start = self.get_pos();
let _ = self.next_char();
match self.chr0 {
Some('=') => {

if self.chr0 != Some('=') {
let tok_end = self.get_pos();
self.emit((tok_start, Token::Equal, tok_end));
return Ok(());
}

let _ = self.next_char();

if self.chr0 != Some('=') {
self.emit((tok_start, Token::EqualEqual, self.get_pos()));
return Ok(());
}

let _ = self.next_char();
let mut seen_eq = 3;

if self.chr0 != Some('=') {
return Err(LexicalError {
error: LexicalErrorType::InvalidTripleEqual,
location: SrcSpan {
start: tok_start,
end: self.get_pos(),
},
});
}

loop {
if seen_eq >= 7 {
return Err(LexicalError {
error: LexicalErrorType::MergeConflictIndicator,
location: SrcSpan {
start: tok_start,
end: self.get_pos(),
},
});
}

if self.chr0 == Some('=') {
let _ = self.next_char();
let tok_end = self.get_pos();
if let Some('=') = self.chr0 {
return Err(LexicalError {
error: LexicalErrorType::InvalidTripleEqual,
location: SrcSpan {
start: tok_start,
end: tok_end + 1,
},
});
seen_eq += 1;
} else {
while seen_eq > 1 {
self.emit((tok_start, Token::EqualEqual, tok_start + 2));
tok_start += 2;
seen_eq -= 2;
}
self.emit((tok_start, Token::EqualEqual, tok_end));
}
_ => {
let tok_end = self.get_pos();
self.emit((tok_start, Token::Equal, tok_end));
if seen_eq > 0 {
self.emit((tok_start, Token::Equal, self.get_pos()));
}
break;
}
}
}
Expand Down Expand Up @@ -353,84 +386,8 @@ where
':' => {
self.eat_single_char(Token::Colon);
}
'<' => {
let tok_start = self.get_pos();
let _ = self.next_char();
match self.chr0 {
Some('>') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::Concatenate, tok_end));
}
Some('<') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::LtLt, tok_end));
}
Some('.') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::LessDot, tok_end));
}
Some('-') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::LArrow, tok_end));
}
Some('=') => {
let _ = self.next_char();
match self.chr0 {
Some('.') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::LessEqualDot, tok_end));
}
_ => {
let tok_end = self.get_pos();
self.emit((tok_start, Token::LessEqual, tok_end));
}
}
}
_ => {
let tok_end = self.get_pos();
self.emit((tok_start, Token::Less, tok_end));
}
}
}
'>' => {
let tok_start = self.get_pos();
let _ = self.next_char();
match self.chr0 {
Some('>') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::GtGt, tok_end));
}
Some('.') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::GreaterDot, tok_end));
}
Some('=') => {
let _ = self.next_char();
match self.chr0 {
Some('.') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::GreaterEqualDot, tok_end));
}
_ => {
let tok_end = self.get_pos();
self.emit((tok_start, Token::GreaterEqual, tok_end));
}
}
}
_ => {
let tok_end = self.get_pos();
self.emit((tok_start, Token::Greater, tok_end));
}
}
}
'<' => return self.lex_lt(),
'>' => return self.lex_gt(),
',' => {
self.eat_single_char(Token::Comma);
}
Expand Down Expand Up @@ -838,6 +795,158 @@ where
Ok(())
}

// Once we've found a `<`, proceed from there
fn lex_lt(&mut self) -> Result<(), LexicalError> {
let mut tok_start = self.get_pos();
let _ = self.next_char();
match self.chr0 {
Some('>') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::Concatenate, tok_end));
}
Some('<') => {
let _ = self.next_char();

if self.chr0 != Some('<') {
self.emit((tok_start, Token::LtLt, self.get_pos()));
return Ok(());
}

let _ = self.next_char();
let mut seen_lt = 3;

loop {
if seen_lt >= 7 {
return Err(LexicalError {
error: LexicalErrorType::MergeConflictIndicator,
location: SrcSpan {
start: tok_start,
end: self.get_pos(),
},
});
}

if self.chr0 == Some('<') {
let _ = self.next_char();
seen_lt += 1;
} else {
while seen_lt > 1 {
self.emit((tok_start, Token::LtLt, tok_start + 2));
tok_start += 2;
seen_lt -= 2;
}
if seen_lt == 1 {
return self.lex_lt();
} else {
return Ok(());
}
}
}
}
Some('.') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::LessDot, tok_end));
}
Some('-') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::LArrow, tok_end));
}
Some('=') => {
let _ = self.next_char();
match self.chr0 {
Some('.') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::LessEqualDot, tok_end));
}
_ => {
let tok_end = self.get_pos();
self.emit((tok_start, Token::LessEqual, tok_end));
}
}
}
_ => {
let tok_end = self.get_pos();
self.emit((tok_start, Token::Less, tok_end));
}
}
Ok(())
}

// once we've found a `>`
fn lex_gt(&mut self) -> Result<(), LexicalError> {
let mut tok_start = self.get_pos();
let _ = self.next_char();
match self.chr0 {
Some('>') => {
let _ = self.next_char();
let tok_end = self.get_pos();

if self.chr0 != Some('>') {
self.emit((tok_start, Token::GtGt, tok_end));
return Ok(());
}

let _ = self.next_char();
let mut seen_gt = 3;

loop {
if seen_gt >= 7 {
return Err(LexicalError {
error: LexicalErrorType::MergeConflictIndicator,
location: SrcSpan {
start: tok_start,
end: self.get_pos(),
},
});
}

if self.chr0 == Some('>') {
let _ = self.next_char();
seen_gt += 1;
} else {
while seen_gt > 1 {
self.emit((tok_start, Token::GtGt, tok_start + 2));
tok_start += 2;
seen_gt -= 2;
}
if seen_gt > 0 {
return self.lex_lt();
} else {
return Ok(());
}
}
}
}
Some('.') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::GreaterDot, tok_end));
}
Some('=') => {
let _ = self.next_char();
match self.chr0 {
Some('.') => {
let _ = self.next_char();
let tok_end = self.get_pos();
self.emit((tok_start, Token::GreaterEqualDot, tok_end));
}
_ => {
let tok_end = self.get_pos();
self.emit((tok_start, Token::GreaterEqual, tok_end));
}
}
}
_ => {
let tok_end = self.get_pos();
self.emit((tok_start, Token::Greater, tok_end));
}
}
Ok(())
}
// Lexer helper functions:
// this can be either a reserved word, or a name
fn lex_name(&mut self) -> LexResult {
Expand Down
Loading
Loading