mindustry logic execution, map- and schematic- parsing and rendering
do escapes properly
| -rw-r--r-- | lemu/Cargo.toml | 3 | ||||
| -rw-r--r-- | lemu/src/executor/mod.rs | 6 | ||||
| -rw-r--r-- | lemu/src/instructions/cop.rs | 10 | ||||
| -rw-r--r-- | lemu/src/instructions/draw.rs | 18 | ||||
| -rw-r--r-- | lemu/src/instructions/io.rs | 6 | ||||
| -rw-r--r-- | lemu/src/instructions/mod.rs | 42 | ||||
| -rw-r--r-- | lemu/src/instructions/mop.rs | 17 | ||||
| -rw-r--r-- | lemu/src/instructions/mop2.rs | 18 | ||||
| -rw-r--r-- | lemu/src/lexer.rs | 19 | ||||
| -rw-r--r-- | lemu/src/memory.rs | 31 | ||||
| -rw-r--r-- | lemu/src/parser.rs | 10 |
11 files changed, 91 insertions, 89 deletions
diff --git a/lemu/Cargo.toml b/lemu/Cargo.toml index 84f4a11..df6d6c5 100644 --- a/lemu/Cargo.toml +++ b/lemu/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "lemu" -version = "0.1.3" +version = "0.1.4" edition = "2021" description = "M-LOG runner" authors = ["bend-n <[email protected]>"] @@ -16,6 +16,7 @@ fimg = { version = "0.4", default-features = false } logos = "0.13.0" yumy = { version = "0.2.1", optional = true } rust-fuzzy-search = { version = "0.1.1", optional = true } +beef = "0.5" [features] bin = ["fimg/save", "diagnose"] diff --git a/lemu/src/executor/mod.rs b/lemu/src/executor/mod.rs index c900045..7d5fc60 100644 --- a/lemu/src/executor/mod.rs +++ b/lemu/src/executor/mod.rs @@ -159,11 +159,11 @@ impl<'s, W: Write> ExecutorContext<'s, W> { } } - pub fn set(&mut self, a: LAddress<'s>, b: LAddress<'s>) -> bool { + pub fn set(&mut self, a: &LAddress<'s>, b: LAddress<'s>) -> bool { self.memory.set(a, b) } - pub fn get_mut(&mut self, a: LAddress<'s>) -> Option<&mut LVar<'s>> { + pub fn get_mut(&mut self, a: &LAddress<'s>) -> Option<&mut LVar<'s>> { self.memory.get_mut(a) } @@ -172,7 +172,7 @@ impl<'s, W: Write> ExecutorContext<'s, W> { self.counter = n; } - pub fn get(&self, a: LAddress<'s>) -> LVar<'s> { + pub fn get<'a>(&'a self, a: &'a LAddress<'s>) -> &LVar<'s> { self.memory.get(a) } } diff --git a/lemu/src/instructions/cop.rs b/lemu/src/instructions/cop.rs index 9e32720..250a0f1 100644 --- a/lemu/src/instructions/cop.rs +++ b/lemu/src/instructions/cop.rs @@ -12,16 +12,16 @@ super::op_enum! { pub enum ConditionOp { macro_rules! op { ($name: ident $op:tt ) => { - fn $name<'v>(a: LVar<'v>, b: LVar<'v>) -> bool { - if let LVar::Num(a) = a && let LVar::Num(b) = b { a $op b } else { false } + fn $name<'v>(a: &LVar<'v>, b: &LVar<'v>) -> bool { + if let &LVar::Num(a) = a && let &LVar::Num(b) = b { a $op b } else { false } } }; } -fn eq<'v>(a: LVar<'v>, b: LVar<'v>) -> bool { +fn eq<'v>(a: &LVar<'v>, b: &LVar<'v>) -> bool { a == b } -fn ne<'v>(a: LVar<'v>, b: LVar<'v>) -> bool { +fn ne<'v>(a: &LVar<'v>, b: &LVar<'v>) -> bool { a != b } op!(lt <); @@ -30,7 +30,7 @@ op!(le <=); op!(ge >=); impl ConditionOp { - pub const fn get_fn(self) -> for<'f> fn(LVar<'f>, LVar<'f>) -> bool { + pub const fn get_fn(self) -> for<'f> fn(&LVar<'f>, &LVar<'f>) -> bool { match self { Self::Equal | Self::StrictEqual => eq, Self::NotEqual => ne, diff --git a/lemu/src/instructions/draw.rs b/lemu/src/instructions/draw.rs index 701fc55..fcd7dc6 100644 --- a/lemu/src/instructions/draw.rs +++ b/lemu/src/instructions/draw.rs @@ -46,7 +46,7 @@ impl<'v> DrawInstruction<'v> for Clear<'v> { fn draw(&self, mem: &mut LRegistry<'v>, image: &mut Image<&mut [u8], 4>, _: &mut DisplayState) { macro_rules! u8 { ($v:ident) => { - match mem.get(self.$v) { + match mem.get(&self.$v) { LVar::Num(n) => n.round() as u8, _ => return, } @@ -70,7 +70,7 @@ impl<'v> DrawInstruction<'v> for SetColorDyn<'v> { fn draw(&self, mem: &mut LRegistry<'v>, _: &mut Image<&mut [u8], 4>, state: &mut DisplayState) { macro_rules! u8 { ($v:ident) => { - match mem.get(self.$v) { + match mem.get(&self.$v) { LVar::Num(n) => n.round() as u8, _ => return, } @@ -99,7 +99,7 @@ pub struct SetStroke<'v> { } impl<'v> DrawInstruction<'v> for SetStroke<'v> { fn draw(&self, mem: &mut LRegistry<'v>, _: &mut Image<&mut [u8], 4>, state: &mut DisplayState) { - if let LVar::Num(n) = mem.get(self.size) { + if let &LVar::Num(n) = mem.get(&self.size) { state.stroke = n; } } @@ -109,8 +109,8 @@ pub type Point<'v> = (LAddress<'v>, LAddress<'v>); #[rustfmt::skip] macro_rules! point { ($mem:ident@$point:expr) => {{ - let LVar::Num(a) = $mem.get($point.0) else { return; }; - let LVar::Num(b) = $mem.get($point.1) else { return; }; + let &LVar::Num(a) = $mem.get(&$point.0) else { return; }; + let &LVar::Num(b) = $mem.get(&$point.1) else { return; }; (a,b) }} } @@ -161,8 +161,8 @@ impl<'v> DrawInstruction<'v> for RectFilled<'v> { state: &mut DisplayState, ) { let pos = map!(point!([email protected]), |n| n as u32); - let width = get_num!(mem.get(self.width), or ret) as u32; - let height = get_num!(mem.get(self.height), or ret) as u32; + let width = get_num!(mem.get(&self.width)) as u32; + let height = get_num!(mem.get(&self.height)) as u32; if unbounded!(image @ pos.0 + width => pos.1 + height) { return; } @@ -187,8 +187,8 @@ impl<'v> DrawInstruction<'v> for RectBordered<'v> { ) { // happily ignoring that state specifies box stroke width let pos = map!(point!([email protected]), |n| n as u32); - let width = get_num!(mem.get(self.width), or ret) as u32; - let height = get_num!(mem.get(self.height), or ret) as u32; + let width = get_num!(mem.get(&self.width)) as u32; + let height = get_num!(mem.get(&self.height)) as u32; if unbounded!(image @ pos.0 + width => pos.1 + height) { return; } diff --git a/lemu/src/instructions/io.rs b/lemu/src/instructions/io.rs index b6564a8..75b9c0e 100644 --- a/lemu/src/instructions/io.rs +++ b/lemu/src/instructions/io.rs @@ -16,7 +16,7 @@ pub struct Read<'v> { impl<'v> LInstruction<'v> for Read<'v> { fn run<W: Wr>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { let to = exec.mem(self.container)[self.index]; - let Some(out) = exec.get_mut(self.output) else { + let Some(out) = exec.get_mut(&self.output) else { return Flow::Continue; }; *out = LVar::from(to); @@ -34,7 +34,7 @@ pub struct Write<'v> { impl<'v> LInstruction<'v> for Write<'v> { fn run<W: Wr>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { - let LVar::Num(n) = exec.get(self.set) else { + let &LVar::Num(n) = exec.get(&self.set) else { return Flow::Continue; }; exec.mem(self.container)[self.index] = n; @@ -48,7 +48,7 @@ pub struct Print<'v> { } impl LInstruction<'_> for Print<'_> { fn run<W: Wr>(&self, exec: &mut ExecutorContext<'_, W>) -> Flow { - let v = exec.get(self.val); + let v = exec.get(&self.val).clone(); if let Some(o) = &mut exec.output { write!(o, "{v}").unwrap(); } diff --git a/lemu/src/instructions/mod.rs b/lemu/src/instructions/mod.rs index 514bc8e..8dbae7e 100644 --- a/lemu/src/instructions/mod.rs +++ b/lemu/src/instructions/mod.rs @@ -128,7 +128,7 @@ pub struct Set<'v> { } impl<'v> LInstruction<'v> for Set<'v> { fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { - exec.set(self.from, self.to); + exec.set(&self.from, self.to.clone()); Flow::Continue } } @@ -142,12 +142,12 @@ macro_rules! op_enum { $($variant),+ } - impl TryFrom<Token<'_>> for $name { - type Error = (); - fn try_from(value: Token<'_>) -> Result<Self, Self::Error> { + impl<'a> TryFrom<Token<'a>> for $name { + type Error = Token<'a>; + fn try_from(value: Token<'a>) -> Result<Self, Self::Error> { match value { $(Token::$variant => Ok(Self::$variant),)+ - _ => Err(()) + v => Err(v) } } } @@ -158,14 +158,8 @@ use op_enum; macro_rules! get_num { ($x:expr) => { match $x { - LVar::Num(x) => x, - _ => return LVar::null(), - } - }; - ($x:expr, or ret) => { - match $x { - LVar::Num(x) => x, - _ => return, + LVar::Num(x) => *x, + _ => return Default::default(), } }; } @@ -173,7 +167,7 @@ use get_num; #[derive(Debug)] pub struct Op1<'v> { - pub(crate) op: fn(LVar<'v>) -> LVar<'v>, + pub(crate) op: fn(&LVar<'v>) -> f64, pub(crate) x: LAddress<'v>, pub(crate) out: LAddress<'v>, } @@ -189,9 +183,9 @@ impl<'v> Op1<'v> { impl<'s> LInstruction<'s> for Op1<'s> { fn run<W: Write>(&self, exec: &mut ExecutorContext<'s, W>) -> Flow { - let x = (self.op)(exec.get(self.x)); - if let Some(y) = exec.get_mut(self.out) { - *y = x; + let x = (self.op)(exec.get(&self.x)); + if let Some(y) = exec.get_mut(&self.out) { + *y = LVar::Num(x); } Flow::Continue } @@ -199,7 +193,7 @@ impl<'s> LInstruction<'s> for Op1<'s> { #[derive(Debug)] pub struct Op2<'v> { - pub(crate) op: fn(LVar<'v>, LVar<'v>) -> LVar<'v>, + pub(crate) op: fn(&LVar<'v>, &LVar<'v>) -> f64, pub(crate) a: LAddress<'v>, pub(crate) b: LAddress<'v>, pub(crate) out: LAddress<'v>, @@ -222,9 +216,9 @@ impl<'v> Op2<'v> { impl<'v> LInstruction<'v> for Op2<'v> { fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { - let x = (self.op)(exec.get(self.a), exec.get(self.b)); - if let Some(y) = exec.get_mut(self.out) { - *y = x; + let x = (self.op)(exec.get(&self.a), exec.get(&self.b)); + if let Some(y) = exec.get_mut(&self.out) { + *y = LVar::from(x); } Flow::Continue } @@ -247,7 +241,7 @@ impl LInstruction<'_> for AlwaysJump { #[derive(Debug)] pub struct Jump<'v> { - pub(crate) op: fn(LVar<'v>, LVar<'v>) -> bool, + pub(crate) op: fn(&LVar<'v>, &LVar<'v>) -> bool, pub(crate) to: Instruction, pub(crate) a: LAddress<'v>, pub(crate) b: LAddress<'v>, @@ -271,7 +265,7 @@ pub struct DynJump<'v> { impl<'v> LInstruction<'v> for DynJump<'v> { fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { - if let LVar::Num(n) = exec.get(self.to) { + if let &LVar::Num(n) = exec.get(&self.to) { let i = n.round() as usize; if i < self.proglen { exec.jump(Instruction(i)); @@ -285,7 +279,7 @@ impl<'v> LInstruction<'v> for DynJump<'v> { impl<'v> LInstruction<'v> for Jump<'v> { #[allow(unused_variables)] fn run<W: Write>(&self, exec: &mut ExecutorContext<'v, W>) -> Flow { - if (self.op)(exec.get(self.a), exec.get(self.b)) { + if (self.op)(exec.get(&self.a), exec.get(&self.b)) { exec.jump(self.to); Flow::Stay } else { diff --git a/lemu/src/instructions/mop.rs b/lemu/src/instructions/mop.rs index 1710731..eb315eb 100644 --- a/lemu/src/instructions/mop.rs +++ b/lemu/src/instructions/mop.rs @@ -20,8 +20,8 @@ super::op_enum! { pub enum MathOp1 { macro_rules! num { ($fn: ident $c:expr) => { - fn $fn(x: LVar<'_>) -> LVar<'_> { - LVar::from($c(get_num!(x))) + fn $fn(x: &LVar<'_>) -> f64 { + f64::from($c(get_num!(x))) } }; } @@ -33,16 +33,13 @@ macro_rules! flbop { } num!(floor f64::floor); -fn not(x: LVar<'_>) -> LVar<'_> { - match x { - LVar::Num(n) => LVar::Num(flbop!(n, |n: u64| !n)), - LVar::String(_) => LVar::null(), - } +fn not(x: &LVar<'_>) -> f64 { + flbop!(get_num!(x), |n: u64| !n) } num!(log f64::ln); num!(abs f64::abs); -const fn rand(_: LVar<'_>) -> LVar<'_> { - LVar::Num(4.0) +const fn rand(_: &LVar<'_>) -> f64 { + 4.0 } num!(ceil f64::ceil); num!(sqrt f64::sqrt); @@ -55,7 +52,7 @@ num!(atan f64::atan); num!(log10 f64::log10); impl MathOp1 { - pub const fn get_fn(self) -> for<'f> fn(LVar<'f>) -> LVar<'f> { + pub const fn get_fn(self) -> for<'f> fn(&LVar<'f>) -> f64 { match self { Self::Floor => floor, Self::Not => not, diff --git a/lemu/src/instructions/mop2.rs b/lemu/src/instructions/mop2.rs index 2eacd91..9fb208f 100644 --- a/lemu/src/instructions/mop2.rs +++ b/lemu/src/instructions/mop2.rs @@ -33,29 +33,29 @@ super::op_enum! { pub enum MathOp2 { macro_rules! num { ($fn:ident $closure:expr) => { - fn $fn<'v>(a: LVar<'v>, b: LVar<'v>) -> LVar<'v> { - LVar::from($closure(get_num!(a), get_num!(b))) + fn $fn<'v>(a: &LVar<'v>, b: &LVar<'v>) -> f64 { + f64::from($closure(get_num!(a), get_num!(b))) } }; } macro_rules! op { ($fn:ident $op:tt) => { - fn $fn<'v>(a: LVar<'v>, b: LVar<'v>) -> LVar<'v> { - LVar::from(get_num!(a) $op get_num!(b)) + fn $fn<'v>(a: &LVar<'v>, b: &LVar<'v>) -> f64 { + f64::from(get_num!(a) $op get_num!(b)) } } } macro_rules! bop { ($fn: ident $op: tt) => { - fn $fn<'v>(a: LVar<'v>, b: LVar<'v>) -> LVar<'v> { - LVar::from(((get_num!(a) as u64) $op (get_num!(b) as u64)) as f64) + fn $fn<'v>(a: &LVar<'v>, b:& LVar<'v>) -> f64 { + f64::from(((get_num!(a) as u64) $op (get_num!(b) as u64)) as f64) } }; } macro_rules! nofun { ($fn:ident $closure:expr) => { - fn $fn<'v>(a: LVar<'v>, b: LVar<'v>) -> LVar<'v> { - LVar::from($closure(a, b)) + fn $fn<'v>(a: &LVar<'v>, b: &LVar<'v>) -> f64 { + f64::from($closure(a, b)) } }; } @@ -100,7 +100,7 @@ num!(angle |a: f64, b: f64| { }); impl MathOp2 { - pub const fn get_fn(self) -> for<'f> fn(LVar<'f>, LVar<'f>) -> LVar<'f> { + pub const fn get_fn(self) -> for<'f> fn(&LVar<'f>, &LVar<'f>) -> f64 { match self { // we kind of interpret strings as numbers so yeah Self::Equal | Self::StrictEqual => eq, diff --git a/lemu/src/lexer.rs b/lemu/src/lexer.rs index 1a5be66..4d85822 100644 --- a/lemu/src/lexer.rs +++ b/lemu/src/lexer.rs @@ -1,7 +1,8 @@ +use beef::lean::Cow; use logos::{Lexer as RealLexer, Logos, Span}; macro_rules! instrs { ($($z:literal => $v:ident,)+) => { - #[derive(Logos, Debug, PartialEq, Copy, Clone)] + #[derive(Logos, Debug, PartialEq, Clone)] #[logos(skip r"[ \t]+")] pub enum Token<'strings> { #[token("\n")] @@ -10,11 +11,12 @@ macro_rules! instrs { 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))] - #[regex(r#""[0-9]+(\.[0-9]+)?""#, |lex| lex.slice()[1..lex.slice().len()-1].parse().ok())] + #[regex(r#""[0-9]+(\.[0-9]+)?""#, callback = |lex| lex.slice()[1..lex.slice().len()-1].parse().ok(), priority = 6)] Num(f64), - #[regex(r#""[^"]*""#, |lex| &lex.slice()[1..lex.slice().len()-1])] - #[regex(r#"@[^ "\n]*"#, |lex| &lex.slice()[1..])] - String(&'strings str), + #[regex(r#""([^\\"\n])*""#, callback = |lex| Cow::from(&lex.slice()[1..lex.slice().len()-1]), priority = 5)] + #[regex(r#"@[^ "\n]*"#, |lex| Cow::from(&lex.slice()[1..]))] + #[regex(r#""[^"]*""#, |lex| Cow::from(lex.slice()[1..lex.slice().len()-1].replace(r"\n", "\n")))] + String(Cow<'strings, str>), #[regex("[^0-9 \t\n]+")] Ident(&'strings str), @@ -25,7 +27,8 @@ macro_rules! instrs { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { match self { $(Self::$v => write!(f, $z,),)+ - Self::String(s) | Self::Ident(s)| Self::Comment(s) => write!(f, "{s}"), + 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"), } @@ -134,7 +137,7 @@ fn lexer() { set x "4""#); macro_rules! test { ($($tok:ident$(($var:literal))?),+ $(,)?) => {{ - $(assert_eq!(lex.next(), Some(Token::$tok$(($var))?));)+ + $(assert_eq!(lex.next(), Some(Token::$tok$(($var.into()))?));)+ assert_eq!(lex.next(), None); }} } @@ -151,6 +154,6 @@ fn lexer() { Newline, Set, Ident("x"), - Num(4.0), + Num(4), ]; } diff --git a/lemu/src/memory.rs b/lemu/src/memory.rs index b350110..561569c 100644 --- a/lemu/src/memory.rs +++ b/lemu/src/memory.rs @@ -1,7 +1,8 @@ -#[derive(Copy, Clone, Debug)] +use beef::lean::Cow; +#[derive(Clone, Debug)] pub enum LVar<'string> { Num(f64), - String(&'string str), + String(Cow<'string, str>), } impl PartialEq for LVar<'_> { @@ -21,7 +22,7 @@ impl LVar<'_> { } } -#[derive(Copy, Clone)] +#[derive(Clone)] pub enum LAddress<'str> { Const(LVar<'str>), Address(usize, Priv), @@ -58,7 +59,7 @@ impl std::fmt::Display for LVar<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Num(n) => write!(f, "{n}"), - Self::String(s) => write!(f, r#"{}"#, s.replace(r"\n", "\n")), + Self::String(s) => write!(f, r#"{s}"#), } } } @@ -77,6 +78,12 @@ impl From<bool> for LVar<'_> { impl<'s> From<&'s str> for LVar<'s> { fn from(value: &'s str) -> Self { + Self::String(value.into()) + } +} + +impl<'s> From<Cow<'s, str>> for LVar<'s> { + fn from(value: Cow<'s, str>) -> Self { Self::String(value) } } @@ -96,28 +103,28 @@ impl<'s> LRegistry<'s> { } } - pub fn get(&self, a: LAddress<'s>) -> LVar<'s> { + pub fn get<'a>(&'a self, a: &'a LAddress<'s>) -> &LVar<'s> { match a { // SAFETY: addr constructor requires bounds - LAddress::Address(n, ..) => unsafe { *self.0.get_unchecked(n) }, + LAddress::Address(n, ..) => unsafe { self.0.get_unchecked(*n) }, LAddress::Const(n) => n, } } - pub fn set(&mut self, a: LAddress<'s>, b: LAddress<'s>) -> bool { + pub fn set(&mut self, a: &LAddress<'s>, b: LAddress<'s>) -> bool { match a { LAddress::Const(_) => false, LAddress::Address(v, ..) => { match b { LAddress::Const(n) => { // SAFETY: v comes from Address, therefore safe - *unsafe { self.0.get_unchecked_mut(v) } = n; + *unsafe { self.0.get_unchecked_mut(*v) } = n; } LAddress::Address(n, ..) => { // SAFETY: n comes from Address, therefore safe - let b = *unsafe { self.0.get_unchecked(n) }; + let b = unsafe { self.0.get_unchecked(n).clone() }; // SAFETY: v comes from Addr, therefore safe - *unsafe { self.0.get_unchecked_mut(v) } = b; + *unsafe { self.0.get_unchecked_mut(*v) } = b; } }; true @@ -125,11 +132,11 @@ impl<'s> LRegistry<'s> { } } - pub fn get_mut(&mut self, a: LAddress<'s>) -> Option<&mut LVar<'s>> { + pub fn get_mut(&mut self, a: &LAddress<'s>) -> Option<&mut LVar<'s>> { match a { LAddress::Const(_) => None, // SAFETY: addr constructor requires bounds - LAddress::Address(n, ..) => Some(unsafe { self.0.get_unchecked_mut(n) }), + LAddress::Address(n, ..) => Some(unsafe { self.0.get_unchecked_mut(*n) }), } } } diff --git a/lemu/src/parser.rs b/lemu/src/parser.rs index f81c71b..04305ad 100644 --- a/lemu/src/parser.rs +++ b/lemu/src/parser.rs @@ -466,19 +466,19 @@ pub fn parse<'source, W: Wr>( executor.jmp(); unfinished_jumps.push((UJump::Always, i, executor.last())); } else { - let op = op.try_into().map_err(|()| err!(ExpectedOp(op)))?; + let op = op.try_into().map_err(|op| err!(ExpectedOp(op)))?; let a = take_var!(tok!()?)?; let b = take_var!(tok!()?)?; executor.jmp(); unfinished_jumps.push((UJump::Sometimes { a, b, op }, i, executor.last())); } - } else if let Ok(n) = take_int!(tok) { + } else if let Ok(n) = take_int!(tok.clone()) { let to = Instruction(n); let op = tok!()?; if op == Token::Always { executor.add(AlwaysJump { to }); } else { - let op = op.try_into().map_err(|()| err!(ExpectedOp(op)))?; + let op = op.try_into().map_err(|op| err!(ExpectedOp(op)))?; let a = take_var!(tok!()?)?; let b = take_var!(tok!()?)?; executor.add(Jump::new(op, to, a, b)); @@ -490,12 +490,12 @@ pub fn parse<'source, W: Wr>( // op add c 1 2 Token::Op => { let op = tok!()?; - if let Ok(op) = MathOp1::try_from(op) { + if let Ok(op) = MathOp1::try_from(op.clone()) { // assigning to a var is useless but legal let out = take_numvar!(tok!()?)?; let x = take_numvar!(tok!()?)?; executor.add(Op1::new(op, x, out)); - } else if let Ok(op) = MathOp2::try_from(op) { + } else if let Ok(op) = MathOp2::try_from(op.clone()) { let out = take_numvar!(tok!()?)?; let a = take_numvar!(tok!()?)?; let b = take_numvar!(tok!()?)?; |