aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/lexer/mod.rs
diff options
context:
space:
mode:
authorTolmachev Igor <me@igorek.dev>2026-05-08 15:25:55 +0300
committerTolmachev Igor <me@igorek.dev>2026-05-08 15:25:55 +0300
commit58b937521f3e459089c0d475551bf9a49f930657 (patch)
tree3c7e33c914445e3b6448ffc287cf70038c5e080c /compiler/src/lexer/mod.rs
parent558c5dcaf7bcc32cfe5672c4113962e3bcd19188 (diff)
downloadcrisp-58b937521f3e459089c0d475551bf9a49f930657.tar.gz
crisp-58b937521f3e459089c0d475551bf9a49f930657.zip
Fold unclosed string error into Token variant
UnclosedString was the only error variant, making lexer::Error redundant. Also removes Result from the Iterator impl.
Diffstat (limited to 'compiler/src/lexer/mod.rs')
-rw-r--r--compiler/src/lexer/mod.rs31
1 files changed, 18 insertions, 13 deletions
diff --git a/compiler/src/lexer/mod.rs b/compiler/src/lexer/mod.rs
index 2ef4922..ff7d51d 100644
--- a/compiler/src/lexer/mod.rs
+++ b/compiler/src/lexer/mod.rs
@@ -1,7 +1,4 @@
1mod error;
2
3use crate::span::{Pos, Span}; 1use crate::span::{Pos, Span};
4pub use error::{Error, Result};
5 2
6#[cfg(test)] 3#[cfg(test)]
7mod tests; 4mod tests;
@@ -17,6 +14,7 @@ pub enum Token<'a> {
17 Quote, 14 Quote,
18 Number(&'a str), 15 Number(&'a str),
19 String(&'a str), 16 String(&'a str),
17 UnclosedString(&'a str),
20 Symbol(&'a str), 18 Symbol(&'a str),
21} 19}
22 20
@@ -82,7 +80,7 @@ impl<'a> Lexer<'a> {
82 self.next_while(|ch| !is_terminator(ch)) 80 self.next_while(|ch| !is_terminator(ch))
83 } 81 }
84 82
85 fn next_string(&mut self) -> Result<&'a str> { 83 fn next_string(&mut self) -> Result<&'a str, &'a str> {
86 debug_assert_eq!(self.peek(), Some('"')); 84 debug_assert_eq!(self.peek(), Some('"'));
87 self.consume(); 85 self.consume();
88 86
@@ -95,7 +93,11 @@ impl<'a> Lexer<'a> {
95 self.consume(); 93 self.consume();
96 return Ok(string); 94 return Ok(string);
97 } 95 }
98 '\n' => return Err(Error::UnclosedString), 96 '\n' => {
97 let string = &self.input[start..self.cursor];
98 self.consume();
99 return Err(string);
100 }
99 '\\' => { 101 '\\' => {
100 self.consume(); 102 self.consume();
101 self.consume(); 103 self.consume();
@@ -106,12 +108,12 @@ impl<'a> Lexer<'a> {
106 } 108 }
107 } 109 }
108 110
109 Err(Error::UnclosedString) 111 Err(&self.input[start..self.cursor])
110 } 112 }
111} 113}
112 114
113impl<'a> Iterator for Lexer<'a> { 115impl<'a> Iterator for Lexer<'a> {
114 type Item = Span<Result<Token<'a>>>; 116 type Item = Span<Token<'a>>;
115 117
116 fn next(&mut self) -> Option<Self::Item> { 118 fn next(&mut self) -> Option<Self::Item> {
117 loop { 119 loop {
@@ -131,15 +133,15 @@ impl<'a> Iterator for Lexer<'a> {
131 let token = match self.peek()? { 133 let token = match self.peek()? {
132 '(' => { 134 '(' => {
133 self.consume(); 135 self.consume();
134 Ok(Token::LeftPar) 136 Token::LeftPar
135 } 137 }
136 ')' => { 138 ')' => {
137 self.consume(); 139 self.consume();
138 Ok(Token::RightPar) 140 Token::RightPar
139 } 141 }
140 '\'' => { 142 '\'' => {
141 self.consume(); 143 self.consume();
142 Ok(Token::Quote) 144 Token::Quote
143 } 145 }
144 146
145 // Number 147 // Number
@@ -147,14 +149,17 @@ impl<'a> Iterator for Lexer<'a> {
147 || matches!(ch, '+' | '-' | '.') 149 || matches!(ch, '+' | '-' | '.')
148 && self.peek_nth(1).is_some_and(|ch| ch.is_ascii_digit()) => 150 && self.peek_nth(1).is_some_and(|ch| ch.is_ascii_digit()) =>
149 { 151 {
150 Ok(Token::Number(self.next_atom())) 152 Token::Number(self.next_atom())
151 } 153 }
152 154
153 // String 155 // String
154 '"' => self.next_string().map(Token::String), 156 '"' => match self.next_string() {
157 Ok(string) => Token::String(string),
158 Err(string) => Token::UnclosedString(string),
159 },
155 160
156 // Symbol 161 // Symbol
157 _ => Ok(Token::Symbol(self.next_atom())), 162 _ => Token::Symbol(self.next_atom()),
158 }; 163 };
159 164
160 let end = Pos::new(self.line, self.column, self.cursor); 165 let end = Pos::new(self.line, self.column, self.cursor);