Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'lemu/src/lexer.rs')
| -rw-r--r-- | lemu/src/lexer.rs | 213 |
1 files changed, 213 insertions, 0 deletions
diff --git a/lemu/src/lexer.rs b/lemu/src/lexer.rs new file mode 100644 index 0000000..e6b85c6 --- /dev/null +++ b/lemu/src/lexer.rs @@ -0,0 +1,213 @@ +use atools::prelude::*; +use beef::lean::Cow; +use fimg::Pack; +use logos::{Lexer as RealLexer, Logos, Span}; + +macro_rules! instrs { + ($($z:literal => $v:ident,)+) => { + #[derive(Logos, Debug, PartialEq, Clone)] + #[logos(skip r"[ \t]+")] + pub enum Token<'strings> { + #[token("\n")] + #[token(";")] + Newline, + #[regex("#[^\n]+", priority = 8)] + Comment(&'strings str), + #[regex(r"-?[0-9]+(\.[0-9]+)?", |lex| lex.slice().parse().ok())] + #[regex(r"(true)|(false)", |lex| lex.slice().parse::<bool>().ok().map(f64::from), priority = 10)] + #[regex(r#"0[xX][0-9a-fA-F]+"#, |lex| u64::from_str_radix(&lex.slice()[2..], 16).map(|v| v as f64).ok())] + #[regex(r#"0[bB][01]+"#, |lex| u64::from_str_radix(&lex.slice()[2..], 2).map(|v| v as f64).ok())] + #[regex(r#""[0-9]+(\.[0-9]+)?""#, callback = |lex| lex.slice()[1..lex.slice().len()-1].parse().ok(), priority = 13)] + #[regex(r#"%\[([a-z]+)\]"#, |lex| COLORS.get(dbg!(&lex.slice()[2..lex.slice().len()-1])).copied().unwrap_or_default().join(255).pack() as f64)] + #[regex(r#"%[0-9a-fA-F]+"#, callback = |lex| { + let mut x = lex.slice()[1..].bytes() + .map(|b| (b & 0xF) + 9 * (b >> 6)) + .array_chunks::<2>() + .map(|[a, b]| a * 16 + b); + if lex.slice()[1..].len() == 6 { x.carr::<3>().join(255) } + else if lex.slice()[1..].len() == 8 { x.carr::<4>() } + else { [255; 4] }.pack() as f64 + })] + Num(f64), // TODO have bool and integer tokens, and parser converts + #[regex(r#""([^\\"\n])*""#, callback = |lex| Cow::from(&lex.slice()[1..lex.slice().len()-1]), priority = 12)] + #[regex(r#"@[^ "\n]*"#, |lex| Cow::from(lex.slice()))] + #[regex(r#""[^"]*""#, callback = |lex| Cow::from(lex.slice()[1..lex.slice().len()-1].replace(r"\n", "\n")), priority = 8)] + String(Cow<'strings, str>), + #[regex("[^%0-9- \t\n][^ \t\n]*", priority = 7)] + Ident(&'strings str), + + $(#[token($z, priority = 8)] $v,)+ + } + + impl std::fmt::Display for Token<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { + match self { + $(Self::$v => write!(f, $z,),)+ + Self::Ident(s)| Self::Comment(s) => write!(f, "{s}"), + Self::String(s) => write!(f, "{s}"), + Self::Num(n) => write!(f, "{n}"), + Self::Newline => write!(f, "\n"), + } + } + } + } +} + +instrs! { + "null" => Null, + "read" => Read, + "write" => Write, + "set" => Set, + "op" => Op, + "end" => End, + "packcolor" => PackColor, + "drawflush" => DrawFlush, + "draw" => Draw, + "select" => Select, + "print" => Print, + "jump" => Jump, + "stop" => Stop, + "equal" => Equal, + "notEqual" => NotEqual, + "lessThan" => LessThan, + "lessThanEq" => LessThanEq, + "greaterThan" => GreaterThan, + "greaterThanEq" => GreaterThanEq, + "strictEqual" => StrictEqual, + "always" => Always, + "add" => Add, + "sub" => Sub, + "mul" => Mul, + "div" => Div, + "idiv" => IDiv, + "mod" => Mod, + "pow" => Pow, + "land" => And, + "not" => Not, + "shl" => ShiftLeft, + "shr" => ShiftRight, + "or" => BitOr, + "and" => BitAnd, + "xor" => ExclusiveOr, + "max" => Max, + "min" => Min, + "angle" => Angle, + "angleDiff" => AngleDiff, + "len" => Len, + "noise" => Noise, + "abs" => Abs, + "log" => Log, + "log10" => Log10, + "floor" => Floor, + "ceil" => Ceil, + "sqrt" => Sqrt, + "rand" => Rand, + "sin" => Sin, + "cos" => Cos, + "tan" => Tan, + "asin" => ASin, + "acos" => ACos, + "atan" => ATan, +} + +pub fn lex(s: &str) -> Lexer<'_> { + Lexer { + inner: Token::lexer(s), + } +} + +pub struct Lexer<'s> { + inner: RealLexer<'s, Token<'s>>, +} + +impl<'s> Lexer<'s> { + pub fn next(&mut self) -> Option<Token<'s>> { + self.inner.find_map(Result::ok) + } + + pub fn span(&self) -> Span { + self.inner.span() + } +} + +#[allow(dead_code)] +pub fn print_stream(mut stream: Lexer) { + print!("["); + let Some(tok) = stream.next() else { + println!("]"); + return; + }; + print!("{tok:?}"); + while let Some(tok) = stream.next() { + print!(", {tok:?}"); + } + println!("]"); +} + +#[test] +fn lexer() { + let mut lex = lex(r#" + start: + print "xd" + jump start always + set x "4""#); + macro_rules! test { + ($($tok:ident$(($var:literal))?),+ $(,)?) => {{ + $(assert_eq!(lex.next(), Some(Token::$tok$(($var.into()))?));)+ + assert_eq!(lex.next(), None); + }} + } + test![ + Newline, + Ident("start:"), + Newline, + Print, + String("xd"), + Newline, + Jump, + Ident("start"), + Always, + Newline, + Set, + Ident("x"), + Num(4), + ]; +} + +const COLORS: phf::Map<&str, [u8; 3]> = phf::phf_map! { + "clear" => [0, 0, 0], + "black" => [0, 0, 0], + "white" => [255, 255, 255], + "lightgray" => [191, 191, 191], + "gray" => [127, 127, 127], + "darkgray" => [63, 63, 63], + "blue" => [0, 0, 255], + "navy" => [0, 0, 128], + "royal" => [65, 105, 225], + "slate" => [112, 128, 144], + "sky" => [135, 206, 235], + "cyan" => [0, 255, 255], + "teal" => [0, 128, 128], + "green" => [0, 255, 0], + "acid" => [127, 255, 0], + "lime" => [50, 205, 50], + "forest" => [34, 139, 34], + "olive" => [107, 142, 35], + "yellow" => [255, 255, 0], + "gold" => [255, 215, 0], + "goldenrod" => [218, 165, 32], + "orange" => [255, 165, 0], + "brown" => [139, 69, 19], + "tan" => [210, 180, 140], + "brick" => [178, 34, 34], + "red" => [255, 0, 0], + "scarlet" => [255, 52, 28], + "coral" => [255, 127, 80], + "salmon" => [250, 128, 114], + "pink" => [255, 105, 180], + "magenta" => [255, 0, 255], + "purple" => [160, 32, 240], + "violet" => [238, 130, 238], + "maroon" => [176, 48, 96], + "accent" => [255, 211, 127], +}; |