- let token =
- match c {
- c if c.is_ascii_digit() || c == '-' => {
- let mut s = String::from(c);
- while let Some(c) = self
- .iter
- .next_if(|&c| c.is_ascii_digit() || c.is_alphabetic() || c == '.')
- {
- s.push(c);
- }
-
- if s == "-" {
- Token::Minus
- } else if !s.contains('.') {
- Token::Integer(s.parse().map_err(|msg| {
- self.error(format!("bad integer literal '{s}' ({msg})"))
- })?)
- } else {
- Token::Float(s.parse().map_err(|msg| {
- self.error(format!("bad float literal '{s}' ({msg})"))
- })?)
- }
- }
- '"' => {
- let mut s = String::new();
- loop {
- match self.iter.next() {
- None => Err(self.error(String::from("end-of-file inside string")))?,
- Some('\n') => Err(self.error(String::from("new-line inside string")))?,
- Some('"') => break,
- Some(c) => s.push(c),
- }
- }
- Token::String(s)
- }
- ';' => Token::Semicolon,
- '*' => Token::Asterisk,
- '+' => Token::Plus,
- '(' => Token::LParen,
- ')' => Token::RParen,
- c if c.is_alphabetic() || c == '@' || c == '_' => {
- let mut s = String::from(c);
- while let Some(c) = self.iter.next_if(|&c| {
- c.is_ascii_digit() || c.is_alphabetic() || c == '.' || c == '_'
- }) {
- s.push(c);
- }
- if self.iter.next_if_eq(&':').is_some() {
- Token::Label(s)
- } else if s.starts_with('@') {
- Token::At(s)
- } else if let Some(count) = s.strip_prefix('s') {
+ let start = s;
+ let mut iter = s.chars();
+ let Some(c) = iter.next() else {
+ return Ok(None);
+ };
+ let (token, rest) = match c {
+ c if c.is_ascii_digit() || c == '-' => {
+ let len = s
+ .find(|c: char| {
+ !(c.is_ascii_digit() || c.is_alphabetic() || c == '.' || c == '-')
+ })
+ .unwrap_or_else(|| s.len());
+ let (number, rest) = s.split_at(len);
+ let token = if number == "-" {
+ Token::Minus
+ } else if let Some(digits) = number.strip_prefix("0x") {
+ Token::Integer(i64::from_str_radix(digits, 16).map_err(|msg| {
+ self.error(format!("bad integer literal '{number}' ({msg})"))
+ })?)
+ } else if !number.contains('.') {
+ Token::Integer(number.parse().map_err(|msg| {
+ self.error(format!("bad integer literal '{number}' ({msg})"))
+ })?)
+ } else {
+ Token::Float(number.parse().map_err(|msg| {
+ self.error(format!("bad float literal '{number}' ({msg})"))
+ })?)
+ };
+ (token, rest)
+ }
+ '"' => {
+ let s = iter.as_str();
+ let Some(len) = s.find(['\n', '"']) else {
+ Err(self.error(String::from("end-of-file inside string")))?
+ };
+ let (string, rest) = s.split_at(len);
+ let Some(rest) = rest.strip_prefix('"') else {
+ Err(self.error(format!("new-line inside string ({string}...{rest})")))?
+ };
+ (Token::String(string.into()), rest)
+ }
+ ';' => (Token::Semicolon, iter.as_str()),
+ '*' => (Token::Asterisk, iter.as_str()),
+ '+' => (Token::Plus, iter.as_str()),
+ '(' => (Token::LParen, iter.as_str()),
+ ')' => (Token::RParen, iter.as_str()),
+ c if c.is_alphabetic() || c == '@' || c == '_' => {
+ let len = s
+ .find(|c: char| {
+ !(c.is_ascii_digit()
+ || c.is_alphabetic()
+ || c == '@'
+ || c == '.'
+ || c == '_')
+ })
+ .unwrap_or_else(|| s.len());
+ let (s, rest) = s.split_at(len);
+ if let Some(rest) = rest.strip_prefix(':') {
+ (Token::Label(s.into()), rest)
+ } else if let Some(name) = s.strip_prefix('@') {
+ (Token::At(name.into()), rest)
+ } else if let Some(count) = s.strip_prefix('s') {
+ let token =