2023-10-17 18:33:07 +00:00
|
|
|
//! Converts a text file into tokens
|
2023-10-22 23:28:20 +00:00
|
|
|
use crate::token::{Keyword, Token, TokenData, Type};
|
|
|
|
use std::{
|
|
|
|
iter::Peekable,
|
|
|
|
str::{Chars, FromStr},
|
|
|
|
};
|
|
|
|
use unicode_xid::UnicodeXID;
|
2023-10-17 18:33:07 +00:00
|
|
|
|
2023-10-22 23:28:20 +00:00
|
|
|
pub mod lexer_iter {
|
|
|
|
use super::{
|
|
|
|
error::{LResult, Reason},
|
|
|
|
Lexer, Token,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// Fallible iterator over a [Lexer], returning optional [LResult<Token>]s
|
|
|
|
pub struct LexerIter<'t> {
|
|
|
|
lexer: Lexer<'t>,
|
|
|
|
}
|
|
|
|
impl<'t> Iterator for LexerIter<'t> {
|
|
|
|
type Item = LResult<Token>;
|
|
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
|
|
match self.lexer.scan() {
|
|
|
|
Ok(v) => Some(Ok(v)),
|
|
|
|
Err(e) => {
|
|
|
|
if e.reason == Reason::EndOfFile {
|
|
|
|
None
|
|
|
|
} else {
|
|
|
|
Some(Err(e))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
impl<'t> IntoIterator for Lexer<'t> {
|
|
|
|
type Item = LResult<Token>;
|
|
|
|
type IntoIter = LexerIter<'t>;
|
|
|
|
fn into_iter(self) -> Self::IntoIter {
|
|
|
|
LexerIter { lexer: self }
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub struct Lexer<'t> {
|
2023-10-22 23:28:20 +00:00
|
|
|
iter: Peekable<Chars<'t>>,
|
|
|
|
start: usize,
|
|
|
|
start_loc: (u32, u32),
|
|
|
|
current: usize,
|
|
|
|
current_loc: (u32, u32),
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
|
2023-10-17 18:33:07 +00:00
|
|
|
impl<'t> Lexer<'t> {
|
|
|
|
pub fn new(text: &'t str) -> Self {
|
2023-10-22 23:28:20 +00:00
|
|
|
Self {
|
|
|
|
iter: text.chars().peekable(),
|
|
|
|
start: 0,
|
|
|
|
start_loc: (1, 1),
|
|
|
|
current: 0,
|
|
|
|
current_loc: (1, 1),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
pub fn scan(&mut self) -> LResult<Token> {
|
|
|
|
match self.skip_whitespace().peek()? {
|
|
|
|
'{' => self.consume()?.produce(Type::LCurly, ()),
|
|
|
|
'}' => self.consume()?.produce(Type::RCurly, ()),
|
|
|
|
'[' => self.consume()?.produce(Type::LBrack, ()),
|
|
|
|
']' => self.consume()?.produce(Type::RBrack, ()),
|
|
|
|
'(' => self.consume()?.produce(Type::LParen, ()),
|
|
|
|
')' => self.consume()?.produce(Type::RParen, ()),
|
|
|
|
'&' => self.consume()?.amp(),
|
|
|
|
'@' => self.consume()?.produce(Type::At, ()),
|
|
|
|
'\\' => self.consume()?.produce(Type::Backslash, ()),
|
|
|
|
'!' => self.consume()?.bang(),
|
|
|
|
'|' => self.consume()?.bar(),
|
|
|
|
':' => self.consume()?.produce(Type::Colon, ()),
|
|
|
|
',' => self.consume()?.produce(Type::Comma, ()),
|
|
|
|
'.' => self.consume()?.dot(),
|
|
|
|
'=' => self.consume()?.equal(),
|
|
|
|
'`' => self.consume()?.produce(Type::Grave, ()),
|
|
|
|
'>' => self.consume()?.greater(),
|
|
|
|
'#' => self.consume()?.produce(Type::Hash, ()),
|
|
|
|
'<' => self.consume()?.less(),
|
|
|
|
'-' => self.consume()?.minus(),
|
|
|
|
'+' => self.consume()?.plus(),
|
|
|
|
'?' => self.consume()?.produce(Type::Question, ()),
|
|
|
|
'%' => self.consume()?.rem(),
|
|
|
|
';' => self.consume()?.produce(Type::Semi, ()),
|
|
|
|
'/' => self.consume()?.slash(),
|
|
|
|
'*' => self.consume()?.star(),
|
|
|
|
'~' => self.consume()?.produce(Type::Tilde, ()),
|
|
|
|
'^' => self.consume()?.xor(),
|
|
|
|
'0' => self.consume()?.int_with_base(),
|
|
|
|
'1'..='9' => self.digits::<10>(),
|
|
|
|
'"' => self.consume()?.string(),
|
|
|
|
'\'' => self.consume()?.character(),
|
|
|
|
'_' => self.identifier(),
|
|
|
|
i if i.is_xid_start() => self.identifier(),
|
|
|
|
e => Err(Error::unexpected_char(e, self.line(), self.col())),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
/// Gets the line of the next token
|
|
|
|
pub fn line(&self) -> u32 {
|
|
|
|
self.start_loc.0
|
|
|
|
}
|
|
|
|
/// Gets the column of the next token
|
|
|
|
pub fn col(&self) -> u32 {
|
|
|
|
self.start_loc.1
|
|
|
|
}
|
|
|
|
fn next(&mut self) -> LResult<char> {
|
|
|
|
let out = self.peek();
|
|
|
|
self.consume()?;
|
|
|
|
out
|
|
|
|
}
|
|
|
|
fn peek(&mut self) -> LResult<char> {
|
|
|
|
self.iter
|
|
|
|
.peek()
|
|
|
|
.copied()
|
|
|
|
.ok_or(Error::end_of_file(self.line(), self.col()))
|
|
|
|
}
|
2023-10-24 00:43:16 +00:00
|
|
|
fn produce(&mut self, ty: Type, data: impl Into<Data>) -> LResult<Token> {
|
2023-10-22 23:28:20 +00:00
|
|
|
let loc = self.start_loc;
|
|
|
|
self.start_loc = self.current_loc;
|
|
|
|
self.start = self.current;
|
|
|
|
Ok(Token::new(ty, data, loc.0, loc.1))
|
|
|
|
}
|
|
|
|
fn skip_whitespace(&mut self) -> &mut Self {
|
|
|
|
while let Ok(c) = self.peek() {
|
|
|
|
if !c.is_whitespace() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
let _ = self.consume();
|
|
|
|
}
|
|
|
|
self.start = self.current;
|
|
|
|
self.start_loc = self.current_loc;
|
|
|
|
self
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn consume(&mut self) -> LResult<&mut Self> {
|
|
|
|
self.current += 1;
|
|
|
|
match self.iter.next() {
|
|
|
|
Some('\n') => {
|
|
|
|
let (line, col) = &mut self.current_loc;
|
|
|
|
*line += 1;
|
|
|
|
*col = 1;
|
|
|
|
}
|
|
|
|
Some(_) => self.current_loc.1 += 1,
|
|
|
|
None => Err(Error::end_of_file(self.line(), self.col()))?,
|
|
|
|
}
|
|
|
|
Ok(self)
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
}
|
|
|
|
/// Digraphs and trigraphs
|
|
|
|
impl<'t> Lexer<'t> {
|
|
|
|
fn amp(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('&') => self.consume()?.produce(Type::AmpAmp, ()),
|
|
|
|
Ok('=') => self.consume()?.produce(Type::AmpEq, ()),
|
|
|
|
_ => self.produce(Type::Amp, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn bang(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('!') => self.consume()?.produce(Type::BangBang, ()),
|
|
|
|
Ok('=') => self.consume()?.produce(Type::BangEq, ()),
|
|
|
|
_ => self.produce(Type::Bang, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn bar(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('|') => self.consume()?.produce(Type::BarBar, ()),
|
|
|
|
Ok('=') => self.consume()?.produce(Type::BarEq, ()),
|
|
|
|
_ => self.produce(Type::Bar, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn dot(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('.') => {
|
|
|
|
if let Ok('=') = self.consume()?.peek() {
|
|
|
|
self.consume()?.produce(Type::DotDotEq, ())
|
|
|
|
} else {
|
|
|
|
self.produce(Type::DotDot, ())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => self.produce(Type::Dot, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn equal(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('=') => self.consume()?.produce(Type::EqEq, ()),
|
|
|
|
Ok('>') => self.consume()?.produce(Type::FatArrow, ()),
|
|
|
|
_ => self.produce(Type::Eq, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn greater(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('=') => self.consume()?.produce(Type::GtEq, ()),
|
|
|
|
Ok('>') => {
|
|
|
|
if let Ok('=') = self.consume()?.peek() {
|
|
|
|
self.consume()?.produce(Type::GtGtEq, ())
|
|
|
|
} else {
|
|
|
|
self.produce(Type::GtGt, ())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => self.produce(Type::Gt, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn less(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('=') => self.consume()?.produce(Type::LtEq, ()),
|
|
|
|
Ok('<') => {
|
|
|
|
if let Ok('=') = self.consume()?.peek() {
|
|
|
|
self.consume()?.produce(Type::LtLtEq, ())
|
|
|
|
} else {
|
|
|
|
self.produce(Type::LtLt, ())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => self.produce(Type::Lt, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn minus(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('=') => self.consume()?.produce(Type::MinusEq, ()),
|
|
|
|
Ok('>') => self.consume()?.produce(Type::Arrow, ()),
|
|
|
|
_ => self.produce(Type::Minus, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn plus(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('=') => self.consume()?.produce(Type::PlusEq, ()),
|
|
|
|
_ => self.produce(Type::Plus, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn rem(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('=') => self.consume()?.produce(Type::RemEq, ()),
|
|
|
|
_ => self.produce(Type::Rem, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn slash(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('=') => self.consume()?.produce(Type::SlashEq, ()),
|
|
|
|
Ok('/') => self.consume()?.line_comment(),
|
|
|
|
Ok('*') => self.consume()?.block_comment(),
|
|
|
|
_ => self.produce(Type::Slash, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn star(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('=') => self.consume()?.produce(Type::StarEq, ()),
|
|
|
|
_ => self.produce(Type::Star, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn xor(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('=') => self.consume()?.produce(Type::XorEq, ()),
|
|
|
|
Ok('^') => self.consume()?.produce(Type::XorXor, ()),
|
|
|
|
_ => self.produce(Type::Xor, ()),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
}
|
|
|
|
/// Comments
|
|
|
|
impl<'t> Lexer<'t> {
|
|
|
|
fn line_comment(&mut self) -> LResult<Token> {
|
|
|
|
while Ok('\n') != self.peek() {
|
|
|
|
self.consume()?;
|
|
|
|
}
|
|
|
|
self.produce(Type::Comment, ())
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn block_comment(&mut self) -> LResult<Token> {
|
|
|
|
while let Ok(c) = self.next() {
|
|
|
|
if '*' == c && Ok('/') == self.next() {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.produce(Type::Comment, ())
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
}
|
|
|
|
/// Identifiers
|
|
|
|
impl<'t> Lexer<'t> {
|
|
|
|
fn identifier(&mut self) -> LResult<Token> {
|
|
|
|
let mut out = String::from(self.xid_start()?);
|
|
|
|
while let Ok(c) = self.xid_continue() {
|
|
|
|
out.push(c)
|
|
|
|
}
|
|
|
|
if let Ok(keyword) = Keyword::from_str(&out) {
|
|
|
|
self.produce(Type::Keyword(keyword), ())
|
|
|
|
} else {
|
2023-10-24 00:43:16 +00:00
|
|
|
self.produce(Type::Identifier, Data::Identifier(out.into()))
|
2023-10-22 23:28:20 +00:00
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn xid_start(&mut self) -> LResult<char> {
|
|
|
|
match self.peek()? {
|
|
|
|
xid if xid == '_' || xid.is_xid_start() => {
|
|
|
|
self.consume()?;
|
|
|
|
Ok(xid)
|
|
|
|
}
|
|
|
|
bad => Err(Error::not_identifier(bad, self.line(), self.col())),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn xid_continue(&mut self) -> LResult<char> {
|
|
|
|
match self.peek()? {
|
|
|
|
xid if xid.is_xid_continue() => {
|
|
|
|
self.consume()?;
|
|
|
|
Ok(xid)
|
|
|
|
}
|
|
|
|
bad => Err(Error::not_identifier(bad, self.line(), self.col())),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
/// Integers
|
|
|
|
impl<'t> Lexer<'t> {
|
|
|
|
fn int_with_base(&mut self) -> LResult<Token> {
|
|
|
|
match self.peek() {
|
|
|
|
Ok('x') => self.consume()?.digits::<16>(),
|
|
|
|
Ok('d') => self.consume()?.digits::<10>(),
|
|
|
|
Ok('o') => self.consume()?.digits::<8>(),
|
|
|
|
Ok('b') => self.consume()?.digits::<2>(),
|
|
|
|
Ok('0'..='9') => self.digits::<10>(),
|
|
|
|
_ => self.produce(Type::Integer, 0),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn digits<const B: u32>(&mut self) -> LResult<Token> {
|
|
|
|
let mut value = self.digit::<B>()? as u128;
|
|
|
|
while let Ok(true) = self.peek().as_ref().map(char::is_ascii_alphanumeric) {
|
|
|
|
value = value * B as u128 + self.digit::<B>()? as u128;
|
|
|
|
}
|
|
|
|
self.produce(Type::Integer, value)
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn digit<const B: u32>(&mut self) -> LResult<u32> {
|
|
|
|
let digit = self.peek()?;
|
|
|
|
self.consume()?;
|
|
|
|
digit
|
|
|
|
.to_digit(B)
|
|
|
|
.ok_or(Error::invalid_digit(digit, self.line(), self.col()))
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
/// Strings and characters
|
|
|
|
impl<'t> Lexer<'t> {
|
|
|
|
fn string(&mut self) -> LResult<Token> {
|
|
|
|
let mut value = String::new();
|
|
|
|
while '"'
|
|
|
|
!= self
|
|
|
|
.peek()
|
|
|
|
.map_err(|e| e.mask_reason(Reason::UnmatchedDelimiters('"')))?
|
|
|
|
{
|
|
|
|
value.push(self.unescape()?)
|
|
|
|
}
|
|
|
|
self.consume()?.produce(Type::String, value)
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
fn character(&mut self) -> LResult<Token> {
|
|
|
|
let out = self.unescape()?;
|
|
|
|
match self.peek()? {
|
|
|
|
'\'' => self.consume()?.produce(Type::Character, out),
|
|
|
|
_ => Err(Error::unmatched_delimiters('\'', self.line(), self.col())),
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
/// Unescape a single character
|
|
|
|
fn unescape(&mut self) -> LResult<char> {
|
|
|
|
match self.next() {
|
|
|
|
Ok('\\') => (),
|
|
|
|
other => return other,
|
|
|
|
}
|
|
|
|
Ok(match self.next()? {
|
|
|
|
'a' => '\x07',
|
|
|
|
'b' => '\x08',
|
|
|
|
'f' => '\x0c',
|
|
|
|
'n' => '\n',
|
|
|
|
'r' => '\r',
|
|
|
|
't' => '\t',
|
|
|
|
'x' => self.hex_escape()?,
|
|
|
|
'u' => self.unicode_escape()?,
|
|
|
|
'0' => '\0',
|
|
|
|
chr => chr,
|
2023-10-17 18:33:07 +00:00
|
|
|
})
|
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
/// unescape a single 2-digit hex escape
|
|
|
|
fn hex_escape(&mut self) -> LResult<char> {
|
|
|
|
let out = (self.digit::<16>()? << 4) + self.digit::<16>()?;
|
|
|
|
char::from_u32(out).ok_or(Error::bad_unicode(out, self.line(), self.col()))
|
|
|
|
}
|
|
|
|
/// unescape a single \u{} unicode escape
|
|
|
|
fn unicode_escape(&mut self) -> LResult<char> {
|
|
|
|
let mut out = 0;
|
|
|
|
let Ok('{') = self.peek() else {
|
|
|
|
return Err(Error::invalid_escape('u', self.line(), self.col()));
|
|
|
|
};
|
|
|
|
self.consume()?;
|
|
|
|
while let Ok(c) = self.peek() {
|
|
|
|
match c {
|
|
|
|
'}' => {
|
|
|
|
self.consume()?;
|
|
|
|
return char::from_u32(out).ok_or(Error::bad_unicode(
|
|
|
|
out,
|
|
|
|
self.line(),
|
|
|
|
self.col(),
|
|
|
|
));
|
|
|
|
}
|
|
|
|
_ => out = (out << 4) + self.digit::<16>()?,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(Error::invalid_escape('u', self.line(), self.col()))
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
|
|
|
|
2023-10-22 23:28:20 +00:00
|
|
|
use error::{Error, LResult, Reason};
|
|
|
|
pub mod error {
|
|
|
|
use std::fmt::Display;
|
|
|
|
|
|
|
|
pub type LResult<T> = Result<T, Error>;
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)]
|
|
|
|
pub struct Error {
|
|
|
|
pub reason: Reason,
|
|
|
|
pub line: u32,
|
|
|
|
pub col: u32,
|
|
|
|
}
|
|
|
|
/// The reason for the [Error]
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
pub enum Reason {
|
|
|
|
UnmatchedDelimiters(char),
|
|
|
|
UnexpectedChar(char),
|
|
|
|
NotIdentifier(char),
|
|
|
|
UnknownEscape(char),
|
|
|
|
InvalidEscape(char),
|
|
|
|
InvalidDigit(char),
|
|
|
|
UnknownBase(char),
|
|
|
|
BadUnicode(u32),
|
|
|
|
EndOfFile,
|
|
|
|
}
|
|
|
|
error_impl! {
|
|
|
|
unmatched_delimiters(c: char) => Reason::UnmatchedDelimiters(c),
|
|
|
|
unexpected_char(c: char) => Reason::UnexpectedChar(c),
|
|
|
|
not_identifier(c: char) => Reason::NotIdentifier(c),
|
|
|
|
unknown_escape(e: char) => Reason::UnknownEscape(e),
|
|
|
|
invalid_escape(e: char) => Reason::InvalidEscape(e),
|
|
|
|
invalid_digit(digit: char) => Reason::InvalidDigit(digit),
|
|
|
|
unknown_base(base: char) => Reason::UnknownBase(base),
|
|
|
|
bad_unicode(value: u32) => Reason::BadUnicode(value),
|
|
|
|
end_of_file => Reason::EndOfFile,
|
|
|
|
}
|
|
|
|
impl Error {
|
|
|
|
/// Changes the [Reason] of this error
|
|
|
|
pub(super) fn mask_reason(self, reason: Reason) -> Self {
|
|
|
|
Self { reason, ..self }
|
|
|
|
}
|
|
|
|
/// Gets the (line, col) where the error happened
|
|
|
|
pub fn location(&self) -> (u32, u32) {
|
|
|
|
(self.line, self.col)
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
macro error_impl ($($fn:ident$(( $($p:ident: $t:ty),* ))? => $reason:expr),*$(,)?) {
|
|
|
|
#[allow(dead_code)]
|
|
|
|
impl Error {
|
|
|
|
$(pub(super) fn $fn ($($($p: $t),*,)? line: u32, col: u32) -> Self {
|
|
|
|
Self { reason: $reason, line, col }
|
|
|
|
})*
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
2023-10-22 23:28:20 +00:00
|
|
|
impl Display for Error {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
write!(f, "{}:{}: {}", self.line, self.col, self.reason)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
impl Display for Reason {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
match self {
|
|
|
|
Reason::UnmatchedDelimiters(c) => write! {f, "Unmatched `{c}` in input"},
|
|
|
|
Reason::UnexpectedChar(c) => write!(f, "Character `{c}` not expected"),
|
|
|
|
Reason::NotIdentifier(c) => write!(f, "Character `{c}` not valid in identifiers"),
|
|
|
|
Reason::UnknownEscape(c) => write!(f, "`\\{c}` is not a known escape sequence"),
|
|
|
|
Reason::InvalidEscape(c) => write!(f, "Escape sequence `\\{c}`... is malformed"),
|
|
|
|
Reason::InvalidDigit(c) => write!(f, "`{c}` is not a valid digit"),
|
|
|
|
Reason::UnknownBase(c) => write!(f, "`0{c}`... is not a valid base"),
|
|
|
|
Reason::BadUnicode(c) => write!(f, "`{c}` is not a valid unicode code-point"),
|
|
|
|
Reason::EndOfFile => write!(f, "Reached end of input"),
|
|
|
|
}
|
|
|
|
}
|
2023-10-17 18:33:07 +00:00
|
|
|
}
|
|
|
|
}
|