Skip to content
Snippets Groups Projects
tokens.rs 11 KiB
Newer Older
Louis's avatar
Louis committed
use crate::lexer::Span;
use std::error::Error;
use std::fmt::{format, Debug, Display, Formatter};

#[derive(PartialEq, Clone, Debug)]
pub enum ScriptTokenType<'a> {
	// Structural Tokens
	LeftParen,
	RightParen,
	LeftBrace,
	RightBrace,
	Comma,
	Dot,
	Semicolon,

	// Unary Operators
	Bang,
	Minus,

	// Binary Operators
	Asterisk,
	Slash,
	Plus,
	BangEqual,
	Equal,
	EqualEqual,
	Greater,
	GreaterEqual,
	Less,
	LessEqual,
	DoublePipe,
	DoubleAmpersand,
	Modulo,
	Caret,

	// Literals
	Identifier(&'a str),
	String(&'a str),
	OwnedString(String),
	Integer(i64),
	Float(f64),
	Boolean(bool),

	// Keywords
	Class,
	Else,
	Function,
	For,
	If,
	Null,
	Print,
	Return,
	Super,
	This,
	Let,
	While,
	Export,
	Import,
	Alias,
	From,
Louis's avatar
Louis committed

	// Misc
	Eof,
}

impl<'a> ScriptTokenType<'a> {
	pub fn len(&self) -> usize {
		match self {
			ScriptTokenType::LeftParen => 1,
			ScriptTokenType::RightParen => 1,
			ScriptTokenType::LeftBrace => 2,
			ScriptTokenType::RightBrace => 2,
			ScriptTokenType::Comma => 1,
			ScriptTokenType::Dot => 1,
			ScriptTokenType::Minus => 1,
			ScriptTokenType::Plus => 1,
			ScriptTokenType::Semicolon => 1,
			ScriptTokenType::Slash => 1,
			ScriptTokenType::Asterisk => 1,
			ScriptTokenType::Bang => 1,
			ScriptTokenType::BangEqual => 2,
			ScriptTokenType::Equal => 1,
			ScriptTokenType::EqualEqual => 2,
			ScriptTokenType::Greater => 1,
			ScriptTokenType::GreaterEqual => 2,
			ScriptTokenType::Less => 1,
			ScriptTokenType::LessEqual => 2,
			ScriptTokenType::DoublePipe => 2,
			ScriptTokenType::DoubleAmpersand => 2,
			ScriptTokenType::Modulo => 1,
			ScriptTokenType::Caret => 1,
			ScriptTokenType::Identifier(value) => value.len(),
			ScriptTokenType::String(value) => value.len() + 2,
			ScriptTokenType::OwnedString(value) => value.len() + 2,
			ScriptTokenType::Integer(value) => format!("{}", value).len(),
			ScriptTokenType::Float(value) => format!("{}", value).len(),
			ScriptTokenType::Boolean(value) => {
				if *value {
					4
				} else {
					5
				}
			}
			ScriptTokenType::Class => 6,
			ScriptTokenType::Else => 4,
			ScriptTokenType::Function => 2,
			ScriptTokenType::For => 3,
			ScriptTokenType::If => 2,
			ScriptTokenType::Null => 4,
			ScriptTokenType::Print => 5,
			ScriptTokenType::Return => 6,
			ScriptTokenType::Super => 5,
			ScriptTokenType::This => 4,
			ScriptTokenType::Let => 3,
			ScriptTokenType::While => 5,
			ScriptTokenType::Export => 6,
			ScriptTokenType::Import => 6,
			ScriptTokenType::Alias => 2,
			ScriptTokenType::From => 4,
			ScriptTokenType::Eof => 0,
			ScriptTokenType::Typeof => 6,
Louis's avatar
Louis committed
		}
	}
}

impl<'a> Display for ScriptTokenType<'a> {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		match self {
			ScriptTokenType::LeftParen => write!(f, "("),
			ScriptTokenType::RightParen => write!(f, ")"),
			ScriptTokenType::LeftBrace => write!(f, "{{"),
			ScriptTokenType::RightBrace => write!(f, "}}"),
			ScriptTokenType::Comma => write!(f, ","),
			ScriptTokenType::Dot => write!(f, "."),
			ScriptTokenType::Minus => write!(f, "-"),
			ScriptTokenType::Plus => write!(f, "+"),
			ScriptTokenType::Semicolon => write!(f, ";"),
			ScriptTokenType::Slash => write!(f, "/"),
			ScriptTokenType::Asterisk => write!(f, "*"),
			ScriptTokenType::Bang => write!(f, "!"),
			ScriptTokenType::BangEqual => write!(f, "!="),
			ScriptTokenType::Equal => write!(f, "="),
			ScriptTokenType::EqualEqual => write!(f, "=="),
			ScriptTokenType::Greater => write!(f, ">"),
			ScriptTokenType::GreaterEqual => write!(f, ">="),
			ScriptTokenType::Less => write!(f, "<"),
			ScriptTokenType::LessEqual => write!(f, "<="),
			ScriptTokenType::DoublePipe => write!(f, "||"),
			ScriptTokenType::DoubleAmpersand => write!(f, "&&"),
			ScriptTokenType::Modulo => write!(f, "%"),
			ScriptTokenType::Caret => write!(f, "^"),
			ScriptTokenType::Identifier(value) => write!(f, "{}", value),
			ScriptTokenType::String(value) => write!(f, "{}", value),
			ScriptTokenType::OwnedString(value) => write!(f, "{}", value),
			ScriptTokenType::Integer(value) => write!(f, "{}", value),
			ScriptTokenType::Float(value) => write!(f, "{}", value),
			ScriptTokenType::Boolean(value) => write!(f, "{}", value),
			ScriptTokenType::Class => write!(f, "struct"),
			ScriptTokenType::Else => write!(f, "else"),
			ScriptTokenType::Function => write!(f, "fn"),
			ScriptTokenType::For => write!(f, "for"),
			ScriptTokenType::If => write!(f, "if"),
			ScriptTokenType::Null => write!(f, "null"),
			ScriptTokenType::Print => write!(f, "print"),
			ScriptTokenType::Return => write!(f, "return"),
			ScriptTokenType::Super => write!(f, "super"),
			ScriptTokenType::This => write!(f, "this"),
			ScriptTokenType::Let => write!(f, "let"),
			ScriptTokenType::While => write!(f, "while"),
			ScriptTokenType::Export => write!(f, "export"),
			ScriptTokenType::Import => write!(f, "import"),
			ScriptTokenType::Alias => write!(f, "as"),
			ScriptTokenType::From => write!(f, "from"),
			ScriptTokenType::Eof => write!(f, ""),
			ScriptTokenType::Typeof => write!(f, "typeof"),
Louis's avatar
Louis committed
		}
	}
}

#[derive(Copy, Clone, Debug, PartialEq)]
pub struct TokenFromStringError<'a> {
	source: &'a str,
}
impl<'a> Display for TokenFromStringError<'a> {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		write!(f, "Failed to parse into token; value {}", self.source)
	}
}
impl<'a> Error for TokenFromStringError<'a> {}

impl<'a> TryFrom<&'a str> for ScriptTokenType<'a> {
	type Error = TokenFromStringError<'a>;

	fn try_from(value: &'a str) -> Result<Self, Self::Error> {
		match value {
			"(" => Ok(ScriptTokenType::LeftParen),
			")" => Ok(ScriptTokenType::RightParen),
			"{" => Ok(ScriptTokenType::LeftBrace),
			"}" => Ok(ScriptTokenType::RightBrace),
			"," => Ok(ScriptTokenType::Comma),
			"." => Ok(ScriptTokenType::Dot),
			"-" => Ok(ScriptTokenType::Minus),
			"+" => Ok(ScriptTokenType::Plus),
			";" => Ok(ScriptTokenType::Semicolon),
			"/" => Ok(ScriptTokenType::Slash),
			"*" => Ok(ScriptTokenType::Asterisk),
			"!" => Ok(ScriptTokenType::Bang),
			"!=" => Ok(ScriptTokenType::BangEqual),
			"=" => Ok(ScriptTokenType::Equal),
			"==" => Ok(ScriptTokenType::EqualEqual),
			">" => Ok(ScriptTokenType::Greater),
			">=" => Ok(ScriptTokenType::GreaterEqual),
			"<" => Ok(ScriptTokenType::Less),
			"<=" => Ok(ScriptTokenType::LessEqual),
			"||" => Ok(ScriptTokenType::DoublePipe),
			"&&" => Ok(ScriptTokenType::DoubleAmpersand),
			"%" => Ok(ScriptTokenType::Modulo),
			"^" => Ok(ScriptTokenType::Caret),
			"struct" => Ok(ScriptTokenType::Class),
			"else" => Ok(ScriptTokenType::Else),
			"fn" => Ok(ScriptTokenType::Function),
			"for" => Ok(ScriptTokenType::For),
			"if" => Ok(ScriptTokenType::If),
			"null" => Ok(ScriptTokenType::Null),
			"print" => Ok(ScriptTokenType::Print),
			"return" => Ok(ScriptTokenType::Return),
			"super" => Ok(ScriptTokenType::Super),
			"this" => Ok(ScriptTokenType::This),
			"let" => Ok(ScriptTokenType::Let),
			"while" => Ok(ScriptTokenType::While),
			"export" => Ok(ScriptTokenType::Export),
			"import" => Ok(ScriptTokenType::Import),
			"as" => Ok(ScriptTokenType::Alias),
			"from" => Ok(ScriptTokenType::From),
			"typeof" => Ok(ScriptTokenType::Typeof),
Louis's avatar
Louis committed
			"false" => Ok(ScriptTokenType::Boolean(false)),
			"true" => Ok(ScriptTokenType::Boolean(true)),
			_ => Err(TokenFromStringError { source: value }),
		}
	}
}

#[derive(Clone)]
pub struct ScriptToken<'a> {
	pub position: Span<'a>,
	pub token_type: ScriptTokenType<'a>,
}

impl<'a> Display for ScriptToken<'a> {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		write!(f, "{}", self.token_type)
	}
}

impl<'a> Debug for ScriptToken<'a> {
	fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
		write!(
			f,
			"[{}:{}] {:?}",
			self.position.location_line(),
			self.position.get_column(),
			self.token_type
		)
	}
}

#[cfg(test)]
mod token_tests {
	use super::ScriptTokenType;

	#[test]
	fn match_type_from_string() {
		assert_eq!(
			ScriptTokenType::try_from("("),
			Ok(ScriptTokenType::LeftParen)
		);
		assert_eq!(
			ScriptTokenType::try_from(")"),
			Ok(ScriptTokenType::RightParen)
		);
		assert_eq!(
			ScriptTokenType::try_from("{"),
			Ok(ScriptTokenType::LeftBrace)
		);
		assert_eq!(
			ScriptTokenType::try_from("}"),
			Ok(ScriptTokenType::RightBrace)
		);
		assert_eq!(ScriptTokenType::try_from(","), Ok(ScriptTokenType::Comma));
		assert_eq!(ScriptTokenType::try_from("."), Ok(ScriptTokenType::Dot));
		assert_eq!(ScriptTokenType::try_from("-"), Ok(ScriptTokenType::Minus));
		assert_eq!(ScriptTokenType::try_from("+"), Ok(ScriptTokenType::Plus));
		assert_eq!(
			ScriptTokenType::try_from(";"),
			Ok(ScriptTokenType::Semicolon)
		);
		assert_eq!(ScriptTokenType::try_from("/"), Ok(ScriptTokenType::Slash));
		assert_eq!(
			ScriptTokenType::try_from("*"),
			Ok(ScriptTokenType::Asterisk)
		);
		assert_eq!(ScriptTokenType::try_from("!"), Ok(ScriptTokenType::Bang));
		assert_eq!(
			ScriptTokenType::try_from("!="),
			Ok(ScriptTokenType::BangEqual)
		);
		assert_eq!(ScriptTokenType::try_from("="), Ok(ScriptTokenType::Equal));
		assert_eq!(
			ScriptTokenType::try_from("=="),
			Ok(ScriptTokenType::EqualEqual)
		);
		assert_eq!(ScriptTokenType::try_from(">"), Ok(ScriptTokenType::Greater));
		assert_eq!(
			ScriptTokenType::try_from(">="),
			Ok(ScriptTokenType::GreaterEqual)
		);
		assert_eq!(ScriptTokenType::try_from("<"), Ok(ScriptTokenType::Less));
		assert_eq!(
			ScriptTokenType::try_from("<="),
			Ok(ScriptTokenType::LessEqual)
		);
		assert_eq!(
			ScriptTokenType::try_from("||"),
			Ok(ScriptTokenType::DoublePipe)
		);
		assert_eq!(
			ScriptTokenType::try_from("&&"),
			Ok(ScriptTokenType::DoubleAmpersand)
		);
		assert_eq!(ScriptTokenType::try_from("%"), Ok(ScriptTokenType::Modulo));
		assert_eq!(ScriptTokenType::try_from("^"), Ok(ScriptTokenType::Caret));
		assert_eq!(
			ScriptTokenType::try_from("struct"),
			Ok(ScriptTokenType::Class)
		);
		assert_eq!(ScriptTokenType::try_from("else"), Ok(ScriptTokenType::Else));
		assert_eq!(
			ScriptTokenType::try_from("fn"),
			Ok(ScriptTokenType::Function)
		);
		assert_eq!(ScriptTokenType::try_from("for"), Ok(ScriptTokenType::For));
		assert_eq!(ScriptTokenType::try_from("if"), Ok(ScriptTokenType::If));
		assert_eq!(ScriptTokenType::try_from("null"), Ok(ScriptTokenType::Null));
		assert_eq!(
			ScriptTokenType::try_from("print"),
			Ok(ScriptTokenType::Print)
		);
		assert_eq!(
			ScriptTokenType::try_from("return"),
			Ok(ScriptTokenType::Return)
		);
		assert_eq!(
			ScriptTokenType::try_from("super"),
			Ok(ScriptTokenType::Super)
		);
		assert_eq!(ScriptTokenType::try_from("this"), Ok(ScriptTokenType::This));
		assert_eq!(ScriptTokenType::try_from("let"), Ok(ScriptTokenType::Let));
		assert_eq!(
			ScriptTokenType::try_from("while"),
			Ok(ScriptTokenType::While)
		);
		assert_eq!(
			ScriptTokenType::try_from("export"),
			Ok(ScriptTokenType::Export)
		);
		assert_eq!(
			ScriptTokenType::try_from("import"),
			Ok(ScriptTokenType::Import)
		);
		assert_eq!(ScriptTokenType::try_from("as"), Ok(ScriptTokenType::Alias));
		assert_eq!(ScriptTokenType::try_from("from"), Ok(ScriptTokenType::From));
		assert_eq!(
			ScriptTokenType::try_from("typeof"),
			Ok(ScriptTokenType::Typeof)
		);