diff --git a/Cargo.lock b/Cargo.lock index c8bf0f5..05bb6d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,78 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - [[package]] name = "leaf" version = "0.1.0" -dependencies = [ - "strum", -] - -[[package]] -name = "proc-macro2" -version = "1.0.95" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rustversion" -version = "1.0.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" - -[[package]] -name = "strum" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" -dependencies = [ - "strum_macros", -] - -[[package]] -name = "strum_macros" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "rustversion", - "syn", -] - -[[package]] -name = "syn" -version = "2.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "unicode-ident" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" diff --git a/Cargo.toml b/Cargo.toml index e309057..9be7182 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,3 @@ version = "0.1.0" edition = "2024" [dependencies] -strum = { version = "0.27", features = ["derive"] } diff --git a/src/kind.rs b/src/kind.rs new file mode 100644 index 0000000..a4fa438 --- /dev/null +++ b/src/kind.rs @@ -0,0 +1,36 @@ +pub trait Kind { + type Kinds; + + fn kind(&self) -> Self::Kinds; +} + +#[macro_export] +macro_rules! kinds { + ($b:ident, $k:ident, $( $v:ident $( ( $($vty:ty = $vval:expr),* ) )?),* $(,)?) => { + #[derive(Debug)] + pub enum $b { + $( $v $( ( $($vty),* ) )?, )* + } + impl $crate::kind::Kind for $b { + type Kinds = $k; + + fn kind(&self) -> $k { + $k (std::mem::discriminant(self)) + } + } + + #[derive(PartialEq, Eq, Clone, Copy)] + pub struct $k(std::mem::Discriminant<$b>); + + impl $k { + $( + #[allow(non_upper_case_globals, dead_code)] + pub const $v: Self = $k ( + std::mem::discriminant( + &( $b::$v $( ( $($vval),* ) )? ) + ) + ); + )* + } + }; +} diff --git a/src/lexer.rs b/src/lexer.rs index d1e4be8..dc690e8 100644 --- a/src/lexer.rs +++ b/src/lexer.rs @@ -1,6 +1,6 @@ -use std::{fmt, iter::Peekable}; +use std::{fmt, iter::Peekable, num::ParseIntError}; -use strum::EnumDiscriminants; +use crate::kinds; #[derive(Debug)] pub struct Ident(String); @@ -30,47 +30,38 @@ impl fmt::Display for Literal { } } -#[derive(Debug, EnumDiscriminants)] -#[strum_discriminants(name(TokenKind))] -pub enum Token { +kinds!( + Token, + TokenKind, Equals, - Plus, Minus, Star, Slash, Percent, Caret, - CurlyOpen, CurlyClose, - ParenOpen, ParenClose, - Comma, Semicolon, - + Eol, Func, If, Else, Return, - Not, - EqualTo, NotEqualTo, - And, Or, - LessThan, LessThanOrEqualTo, GreaterThan, GreaterThanOrEqualTo, - - Literal(Literal), -} + Literal(Literal = Literal::Nil), +); #[derive(PartialEq, Eq, PartialOrd, Ord, Clone, Copy)] pub enum Precedence { Min, @@ -116,6 +107,7 @@ impl Token { #[derive(Debug)] pub enum LexError { + InvalidInteger(ParseIntError), InvalidEscape(char), UnexpectedCharacter(char), UnexpectedEnd, @@ -123,12 +115,18 @@ pub enum LexError { impl fmt::Display for LexError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { + Self::InvalidInteger(err) => write!(f, "invalid integer: {err}"), Self::UnexpectedEnd => write!(f, "unexpected end of source"), Self::UnexpectedCharacter(c) => write!(f, "unexpected char '{c}'"), Self::InvalidEscape(c) => write!(f, "\"\\{c}\" is not a valid string escape"), } } } +impl From for LexError { + fn from(err: ParseIntError) -> Self { + Self::InvalidInteger(err) + } +} pub type Result = std::result::Result; @@ -139,6 +137,10 @@ where chars: Peekable, } +fn t(tk: Token) -> Option> { + Some(Ok(tk)) +} + impl Lexer where I: Iterator, @@ -209,7 +211,7 @@ where } } - fn lex_integer(&mut self) -> Token { + fn lex_integer(&mut self) -> Result { let mut n_str = String::new(); // we don't lex negatives. the impl for that is @@ -221,9 +223,9 @@ where // we can only read digits 0 to 9 so this should not fail // .. unless we overflow - let n = n_str.parse().unwrap(); + let n = n_str.parse()?; - Token::Literal(Literal::Integer(n)) + Ok(Token::Literal(Literal::Integer(n))) } fn lex_string(&mut self) -> Result { @@ -283,18 +285,21 @@ where // , comma ',' => self.eat_to(Token::Comma), + // ; semicolon + ';' => self.eat_to(Token::Semicolon), + // = equals // or == equal to '=' => match self.eat_peek() { Some('=') => self.eat_to(Token::EqualTo), - _ => Some(Ok(Token::Equals)), + _ => t(Token::Equals), }, // ! not // or != not equal to '!' => match self.eat_peek() { Some('=') => self.eat_to(Token::NotEqualTo), - _ => Some(Ok(Token::Not)), + _ => t(Token::Not), }, // && and @@ -307,21 +312,21 @@ where // or >= greater than/equal to '>' => match self.eat_peek() { Some('=') => self.eat_to(Token::GreaterThanOrEqualTo), - _ => Some(Ok(Token::GreaterThan)), + _ => t(Token::GreaterThan), }, // < less than // or <= less than/equal to '<' => match self.eat_peek() { Some('=') => self.eat_to(Token::LessThanOrEqualTo), - _ => Some(Ok(Token::LessThan)), + _ => t(Token::LessThan), }, // a-zA-Z_ start of word 'a'..='z' | 'A'..='Z' | '_' => Some(Ok(self.lex_word())), // 0-9 integer - '0'..='9' => Some(Ok(self.lex_integer())), + '0'..='9' => Some(self.lex_integer()), // " strings '"' => Some(self.lex_string()), @@ -336,9 +341,6 @@ where continue; } - // ; semicolon - ';' => self.eat_to(Token::Semicolon), - // unexpected character c => Some(Err(LexError::UnexpectedCharacter(c))), }; diff --git a/src/main.rs b/src/main.rs index 808d70e..1cb223b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,6 +2,7 @@ use std::time::Instant; use crate::{lexer::Lexer, parser::Parser}; +mod kind; mod lexer; mod parser; diff --git a/src/parser.rs b/src/parser.rs index 6e09db4..48b21b7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,8 +1,9 @@ use std::{fmt, iter::Peekable}; -use strum::IntoDiscriminant; - -use crate::lexer::{Associativity, LexError, Literal, Precedence, Token, TokenKind}; +use crate::{ + kind::Kind, + lexer::{Associativity, LexError, Literal, Precedence, Token, TokenKind}, +}; pub mod util; @@ -96,7 +97,7 @@ where fn expect_next(&mut self, kind: TokenKind) -> Result<()> { let t = self.try_next()?; - if t.discriminant() != kind { + if t.kind() != kind { return Err(ParseError::UnexpectedToken(t)); } @@ -104,7 +105,7 @@ where } fn is_next(&mut self, kind: Option) -> bool { match self.try_peek() { - Ok(t) if Some(t.discriminant()) == kind => true, + Ok(t) if Some(t.kind()) == kind => true, Ok(_) => false, Err(ParseError::UnexpectedEnd) if kind.is_none() => true,