Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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
292 changes: 197 additions & 95 deletions compiler-core/src/parse/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,24 +196,50 @@ where
'=' => {
let tok_start = self.get_pos();
let _ = self.next_char();
match 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,
},
});
}
self.emit((tok_start, Token::EqualEqual, tok_end));

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;

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

if self.chr0 == Some('=') {
let _ = self.next_char();
seen_eq += 1;
} else {
// we've found at least three `=`s, but not
// enough to be a merge conflict indicator,
// so we just return the triple equals error
//
// TODO: provide a better error for 3 < '='s < 7
return Err(LexicalError {
error: LexicalErrorType::InvalidTripleEqual,
location: SrcSpan {
start: tok_start,
end: tok_start + 3,
},
});
}
}
}
Expand Down Expand Up @@ -353,84 +379,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 +788,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));
}
}
return Ok(());
}
// Lexer helper functions:
// this can be either a reserved word, or a name
fn lex_name(&mut self) -> LexResult {
Expand Down
Loading
Loading