diff --git a/Cargo.toml b/Cargo.toml index 61609161..3bbd25ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -23,6 +23,7 @@ debug_msgs = [] unchecked = [] no_stdlib = [] no_index = [] +no_float = [] [profile.release] lto = "fat" diff --git a/README.md b/README.md index 6cf47caf..5488ec97 100644 --- a/README.md +++ b/README.md @@ -44,6 +44,7 @@ Optional features | `no_stdlib` | Exclude the standard library of utility functions in the build, and only include the minimum necessary functionalities. | | `unchecked` | Exclude arithmetic checking in the standard library. Beware that a bad script may panic the entire system! | | `no_index` | Disable arrays and indexing features | +| `no_float` | Disable floating-point numbers and math | Related ------- diff --git a/src/builtin.rs b/src/builtin.rs index 02e64581..106d647b 100644 --- a/src/builtin.rs +++ b/src/builtin.rs @@ -11,13 +11,16 @@ use crate::engine::Array; use std::{ fmt::{Debug, Display}, i32, i64, - ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Range, Rem, Sub}, + ops::{BitAnd, BitOr, BitXor, Range}, u32, }; #[cfg(feature = "unchecked")] use std::ops::{Shl, Shr}; +#[cfg(any(feature = "unchecked", not(feature = "no_float")))] +use std::ops::{Add, Div, Mul, Neg, Rem, Sub}; + #[cfg(not(feature = "unchecked"))] use { crate::{parser::Position, result::EvalAltResult, RegisterResultFn}, @@ -188,21 +191,27 @@ impl Engine<'_> { }) } } + #[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn add_u(x: T, y: T) -> ::Output { x + y } + #[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn sub_u(x: T, y: T) -> ::Output { x - y } + #[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn mul_u(x: T, y: T) -> ::Output { x * y } + #[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn div_u(x: T, y: T) -> ::Output { x / y } + #[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn neg_u(x: T) -> ::Output { -x } + #[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn abs_u>(x: T) -> T where ::Output: Into, @@ -298,6 +307,7 @@ impl Engine<'_> { ) }) } + #[cfg(any(feature = "unchecked", not(feature = "no_float")))] fn modulo_u(x: T, y: T) -> ::Output { x % y } @@ -321,10 +331,12 @@ impl Engine<'_> { fn pow_i64_i64(x: i64, y: i64) -> i64 { x.pow(y as u32) } + #[cfg(not(feature = "no_float"))] fn pow_f64_f64(x: f64, y: f64) -> f64 { x.powf(y) } #[cfg(not(feature = "unchecked"))] + #[cfg(not(feature = "no_float"))] fn pow_f64_i64_u(x: f64, y: i64) -> Result { if y > (i32::MAX as i64) { return Err(EvalAltResult::ErrorArithmetic( @@ -336,6 +348,7 @@ impl Engine<'_> { Ok(x.powi(y as i32)) } #[cfg(feature = "unchecked")] + #[cfg(not(feature = "no_float"))] fn pow_f64_i64(x: f64, y: i64) -> f64 { x.powi(y as i32) } @@ -356,21 +369,30 @@ impl Engine<'_> { reg_op!(self, "/", div_u, i8, u8, i16, u16, i32, i64, u32, u64); } - reg_op!(self, "+", add_u, f32, f64); - reg_op!(self, "-", sub_u, f32, f64); - reg_op!(self, "*", mul_u, f32, f64); - reg_op!(self, "/", div_u, f32, f64); + #[cfg(not(feature = "no_float"))] + { + reg_op!(self, "+", add_u, f32, f64); + reg_op!(self, "-", sub_u, f32, f64); + reg_op!(self, "*", mul_u, f32, f64); + reg_op!(self, "/", div_u, f32, f64); + } - reg_cmp!(self, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char); - reg_cmp!(self, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char); - reg_cmp!(self, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char); - reg_cmp!(self, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, String, char); - reg_cmp!( - self, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, bool, f32, f64, String, char - ); - reg_cmp!( - self, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, bool, f32, f64, String, char - ); + reg_cmp!(self, "<", lt, i8, u8, i16, u16, i32, i64, u32, u64, String, char); + reg_cmp!(self, "<=", lte, i8, u8, i16, u16, i32, i64, u32, u64, String, char); + reg_cmp!(self, ">", gt, i8, u8, i16, u16, i32, i64, u32, u64, String, char); + reg_cmp!(self, ">=", gte, i8, u8, i16, u16, i32, i64, u32, u64, String, char); + reg_cmp!(self, "==", eq, i8, u8, i16, u16, i32, i64, u32, u64, bool, String, char); + reg_cmp!(self, "!=", ne, i8, u8, i16, u16, i32, i64, u32, u64, bool, String, char); + + #[cfg(not(feature = "no_float"))] + { + reg_cmp!(self, "<", lt, f32, f64); + reg_cmp!(self, "<=", lte, f32, f64); + reg_cmp!(self, ">", gt, f32, f64); + reg_cmp!(self, ">=", gte, f32, f64); + reg_cmp!(self, "==", eq, f32, f64); + reg_cmp!(self, "!=", ne, f32, f64); + } //reg_op!(self, "||", or, bool); //reg_op!(self, "&&", and, bool); @@ -396,19 +418,25 @@ impl Engine<'_> { reg_op!(self, "%", modulo_u, i8, u8, i16, u16, i32, i64, u32, u64); } - reg_op!(self, "%", modulo_u, f32, f64); - - self.register_fn("~", pow_f64_f64); + #[cfg(not(feature = "no_float"))] + { + reg_op!(self, "%", modulo_u, f32, f64); + self.register_fn("~", pow_f64_f64); + } #[cfg(not(feature = "unchecked"))] { self.register_result_fn("~", pow_i64_i64_u); + + #[cfg(not(feature = "no_float"))] self.register_result_fn("~", pow_f64_i64_u); } #[cfg(feature = "unchecked")] { self.register_fn("~", pow_i64_i64); + + #[cfg(not(feature = "no_float"))] self.register_fn("~", pow_f64_i64); } @@ -424,8 +452,12 @@ impl Engine<'_> { reg_un!(self, "abs", abs_u, i8, i16, i32, i64); } - reg_un!(self, "-", neg_u, f32, f64); - reg_un!(self, "abs", abs_u, f32, f64); + #[cfg(not(feature = "no_float"))] + { + reg_un!(self, "-", neg_u, f32, f64); + reg_un!(self, "abs", abs_u, f32, f64); + } + reg_un!(self, "!", not, bool); self.register_fn("+", |x: String, y: String| x + &y); // String + String @@ -441,13 +473,19 @@ impl Engine<'_> { reg_func1!(self, "print", print, String, i8, u8, i16, u16); reg_func1!(self, "print", print, String, i32, i64, u32, u64); - reg_func1!(self, "print", print, String, f32, f64, bool, char, String); + reg_func1!(self, "print", print, String, bool, char, String); self.register_fn("print", || "".to_string()); self.register_fn("print", |_: ()| "".to_string()); reg_func1!(self, "debug", print_debug, String, i8, u8, i16, u16); reg_func1!(self, "debug", print_debug, String, i32, i64, u32, u64); - reg_func1!(self, "debug", print_debug, String, f32, f64, bool, char); + reg_func1!(self, "debug", print_debug, String, bool, char); + + #[cfg(not(feature = "no_float"))] + { + reg_func1!(self, "print", print, String, f32, f64); + reg_func1!(self, "debug", print_debug, String, f32, f64); + } #[cfg(not(feature = "no_index"))] { @@ -479,43 +517,46 @@ impl Engine<'_> { #[cfg(not(feature = "no_index"))] use crate::fn_register::RegisterDynamicFn; - // Advanced math functions - self.register_fn("sin", |x: f64| x.to_radians().sin()); - self.register_fn("cos", |x: f64| x.to_radians().cos()); - self.register_fn("tan", |x: f64| x.to_radians().tan()); - self.register_fn("sinh", |x: f64| x.to_radians().sinh()); - self.register_fn("cosh", |x: f64| x.to_radians().cosh()); - self.register_fn("tanh", |x: f64| x.to_radians().tanh()); - self.register_fn("asin", |x: f64| x.asin().to_degrees()); - self.register_fn("acos", |x: f64| x.acos().to_degrees()); - self.register_fn("atan", |x: f64| x.atan().to_degrees()); - self.register_fn("asinh", |x: f64| x.asinh().to_degrees()); - self.register_fn("acosh", |x: f64| x.acosh().to_degrees()); - self.register_fn("atanh", |x: f64| x.atanh().to_degrees()); - self.register_fn("sqrt", |x: f64| x.sqrt()); - self.register_fn("exp", |x: f64| x.exp()); - self.register_fn("ln", |x: f64| x.ln()); - self.register_fn("log", |x: f64, base: f64| x.log(base)); - self.register_fn("log10", |x: f64| x.log10()); - self.register_fn("floor", |x: f64| x.floor()); - self.register_fn("ceiling", |x: f64| x.ceil()); - self.register_fn("round", |x: f64| x.ceil()); - self.register_fn("int", |x: f64| x.trunc()); - self.register_fn("fraction", |x: f64| x.fract()); - self.register_fn("is_nan", |x: f64| x.is_nan()); - self.register_fn("is_finite", |x: f64| x.is_finite()); - self.register_fn("is_infinite", |x: f64| x.is_infinite()); + #[cfg(not(feature = "no_float"))] + { + // Advanced math functions + self.register_fn("sin", |x: f64| x.to_radians().sin()); + self.register_fn("cos", |x: f64| x.to_radians().cos()); + self.register_fn("tan", |x: f64| x.to_radians().tan()); + self.register_fn("sinh", |x: f64| x.to_radians().sinh()); + self.register_fn("cosh", |x: f64| x.to_radians().cosh()); + self.register_fn("tanh", |x: f64| x.to_radians().tanh()); + self.register_fn("asin", |x: f64| x.asin().to_degrees()); + self.register_fn("acos", |x: f64| x.acos().to_degrees()); + self.register_fn("atan", |x: f64| x.atan().to_degrees()); + self.register_fn("asinh", |x: f64| x.asinh().to_degrees()); + self.register_fn("acosh", |x: f64| x.acosh().to_degrees()); + self.register_fn("atanh", |x: f64| x.atanh().to_degrees()); + self.register_fn("sqrt", |x: f64| x.sqrt()); + self.register_fn("exp", |x: f64| x.exp()); + self.register_fn("ln", |x: f64| x.ln()); + self.register_fn("log", |x: f64, base: f64| x.log(base)); + self.register_fn("log10", |x: f64| x.log10()); + self.register_fn("floor", |x: f64| x.floor()); + self.register_fn("ceiling", |x: f64| x.ceil()); + self.register_fn("round", |x: f64| x.ceil()); + self.register_fn("int", |x: f64| x.trunc()); + self.register_fn("fraction", |x: f64| x.fract()); + self.register_fn("is_nan", |x: f64| x.is_nan()); + self.register_fn("is_finite", |x: f64| x.is_finite()); + self.register_fn("is_infinite", |x: f64| x.is_infinite()); - // Register conversion functions - self.register_fn("to_float", |x: i8| x as f64); - self.register_fn("to_float", |x: u8| x as f64); - self.register_fn("to_float", |x: i16| x as f64); - self.register_fn("to_float", |x: u16| x as f64); - self.register_fn("to_float", |x: i32| x as f64); - self.register_fn("to_float", |x: u32| x as f64); - self.register_fn("to_float", |x: i64| x as f64); - self.register_fn("to_float", |x: u64| x as f64); - self.register_fn("to_float", |x: f32| x as f64); + // Register conversion functions + self.register_fn("to_float", |x: i8| x as f64); + self.register_fn("to_float", |x: u8| x as f64); + self.register_fn("to_float", |x: i16| x as f64); + self.register_fn("to_float", |x: u16| x as f64); + self.register_fn("to_float", |x: i32| x as f64); + self.register_fn("to_float", |x: u32| x as f64); + self.register_fn("to_float", |x: i64| x as f64); + self.register_fn("to_float", |x: u64| x as f64); + self.register_fn("to_float", |x: f32| x as f64); + } self.register_fn("to_int", |x: i8| x as i64); self.register_fn("to_int", |x: u8| x as i64); @@ -526,34 +567,37 @@ impl Engine<'_> { self.register_fn("to_int", |x: u64| x as i64); self.register_fn("to_int", |ch: char| ch as i64); - #[cfg(not(feature = "unchecked"))] + #[cfg(not(feature = "no_float"))] { - self.register_result_fn("to_int", |x: f32| { - if x > (i64::MAX as f32) { - return Err(EvalAltResult::ErrorArithmetic( - format!("Integer overflow: to_int({})", x), - Position::none(), - )); - } + #[cfg(not(feature = "unchecked"))] + { + self.register_result_fn("to_int", |x: f32| { + if x > (i64::MAX as f32) { + return Err(EvalAltResult::ErrorArithmetic( + format!("Integer overflow: to_int({})", x), + Position::none(), + )); + } - Ok(x.trunc() as i64) - }); - self.register_result_fn("to_int", |x: f64| { - if x > (i64::MAX as f64) { - return Err(EvalAltResult::ErrorArithmetic( - format!("Integer overflow: to_int({})", x), - Position::none(), - )); - } + Ok(x.trunc() as i64) + }); + self.register_result_fn("to_int", |x: f64| { + if x > (i64::MAX as f64) { + return Err(EvalAltResult::ErrorArithmetic( + format!("Integer overflow: to_int({})", x), + Position::none(), + )); + } - Ok(x.trunc() as i64) - }); - } + Ok(x.trunc() as i64) + }); + } - #[cfg(feature = "unchecked")] - { - self.register_fn("to_int", |x: f32| x as i64); - self.register_fn("to_int", |x: f64| x as i64); + #[cfg(feature = "unchecked")] + { + self.register_fn("to_int", |x: f32| x as i64); + self.register_fn("to_int", |x: f64| x as i64); + } } #[cfg(not(feature = "no_index"))] @@ -572,14 +616,19 @@ impl Engine<'_> { reg_func2x!(self, "push", push, &mut Array, (), i8, u8, i16, u16); reg_func2x!(self, "push", push, &mut Array, (), i32, i64, u32, u64); - reg_func2x!(self, "push", push, &mut Array, (), f32, f64, bool, char); + reg_func2x!(self, "push", push, &mut Array, (), bool, char); reg_func2x!(self, "push", push, &mut Array, (), String, Array, ()); reg_func3!(self, "pad", pad, &mut Array, i64, (), i8, u8, i16, u16); - reg_func3!(self, "pad", pad, &mut Array, i64, (), i32, u32, f32); - reg_func3!(self, "pad", pad, &mut Array, i64, (), i64, u64, f64); + reg_func3!(self, "pad", pad, &mut Array, i64, (), i32, u32, i64, u64); reg_func3!(self, "pad", pad, &mut Array, i64, (), bool, char); reg_func3!(self, "pad", pad, &mut Array, i64, (), String, Array, ()); + #[cfg(not(feature = "no_float"))] + { + reg_func2x!(self, "push", push, &mut Array, (), f32, f64); + reg_func3!(self, "pad", pad, &mut Array, i64, (), f32, f64); + } + self.register_dynamic_fn("pop", |list: &mut Array| { list.pop().unwrap_or_else(|| ().into_dynamic()) }); @@ -605,17 +654,21 @@ impl Engine<'_> { } reg_func2x!( - self, "+", append, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, - bool, char + self, "+", append, String, String, i8, u8, i16, u16, i32, i64, u32, u64, bool, char ); self.register_fn("+", |x: String, _: ()| format!("{}", x)); reg_func2y!( - self, "+", prepend, String, String, i8, u8, i16, u16, i32, i64, u32, u64, f32, f64, - bool, char + self, "+", prepend, String, String, i8, u8, i16, u16, i32, i64, u32, u64, bool, char ); self.register_fn("+", |_: (), y: String| format!("{}", y)); + #[cfg(not(feature = "no_float"))] + { + reg_func2x!(self, "+", append, String, String, f32, f64); + reg_func2y!(self, "+", prepend, String, String, f32, f64); + } + #[cfg(not(feature = "no_index"))] { self.register_fn("+", |x: String, y: Array| format!("{}{:?}", x, y)); diff --git a/src/engine.rs b/src/engine.rs index 4ac2d67e..d13d45d3 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -768,8 +768,10 @@ impl Engine<'_> { /// Evaluate an expression fn eval_expr(&mut self, scope: &mut Scope, expr: &Expr) -> Result { match expr { - Expr::IntegerConstant(i, _) => Ok(i.into_dynamic()), + #[cfg(not(feature = "no_float"))] Expr::FloatConstant(f, _) => Ok(f.into_dynamic()), + + Expr::IntegerConstant(i, _) => Ok(i.into_dynamic()), Expr::StringConstant(s, _) => Ok(s.into_dynamic()), Expr::CharConstant(c, _) => Ok(c.into_dynamic()), Expr::Identifier(id, pos) => { diff --git a/src/optimize.rs b/src/optimize.rs index 0f2eb585..29f226f6 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -112,7 +112,6 @@ fn optimize_stmt(stmt: Stmt, changed: &mut bool) -> Stmt { fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr { match expr { Expr::IntegerConstant(_, _) - | Expr::FloatConstant(_, _) | Expr::Identifier(_, _) | Expr::CharConstant(_, _) | Expr::StringConstant(_, _) @@ -120,6 +119,9 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr { | Expr::False(_) | Expr::Unit(_) => expr, + #[cfg(not(feature = "no_float"))] + Expr::FloatConstant(_, _) => expr, + Expr::Stmt(stmt, pos) => match optimize_stmt(*stmt, changed) { Stmt::Noop(_) => { *changed = true; @@ -144,9 +146,9 @@ fn optimize_expr(expr: Expr, changed: &mut bool) -> Expr { (Expr::Array(mut items, _), Expr::IntegerConstant(i, _)) if i >= 0 && (i as usize) < items.len() - && !items.iter().any(|x| x.is_constant()) => + && !items.iter().any(|x| x.is_constant() || x.is_identifier()) => { - // Array where everything is a constant - promote the item + // Array where everything is a constant or identifier - promote the item *changed = true; items.remove(i as usize) } diff --git a/src/parser.rs b/src/parser.rs index 9009305b..9943a7f7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -159,6 +159,7 @@ impl Stmt { #[derive(Debug, Clone)] pub enum Expr { IntegerConstant(i64, Position), + #[cfg(not(feature = "no_float"))] FloatConstant(f64, Position), Identifier(String, Position), CharConstant(char, Position), @@ -182,7 +183,6 @@ impl Expr { pub fn position(&self) -> Position { match self { Expr::IntegerConstant(_, pos) - | Expr::FloatConstant(_, pos) | Expr::Identifier(_, pos) | Expr::CharConstant(_, pos) | Expr::StringConstant(_, pos) @@ -196,6 +196,9 @@ impl Expr { e.position() } + #[cfg(not(feature = "no_float"))] + Expr::FloatConstant(_, pos) => *pos, + #[cfg(not(feature = "no_index"))] Expr::Index(e, _, _) => e.position(), @@ -207,14 +210,22 @@ impl Expr { pub fn is_constant(&self) -> bool { match self { Expr::IntegerConstant(_, _) - | Expr::FloatConstant(_, _) - | Expr::Identifier(_, _) | Expr::CharConstant(_, _) | Expr::StringConstant(_, _) | Expr::True(_) | Expr::False(_) | Expr::Unit(_) => true, + #[cfg(not(feature = "no_float"))] + Expr::FloatConstant(_, _) => true, + + _ => false, + } + } + + pub fn is_identifier(&self) -> bool { + match self { + Expr::Identifier(_, _) => true, _ => false, } } @@ -223,6 +234,7 @@ impl Expr { #[derive(Debug, PartialEq, Clone)] pub enum Token { IntegerConstant(i64), + #[cfg(not(feature = "no_float"))] FloatConstant(f64), Identifier(String), CharConstant(char), @@ -293,6 +305,7 @@ impl Token { match *self { IntegerConstant(ref i) => i.to_string().into(), + #[cfg(not(feature = "no_float"))] FloatConstant(ref f) => f.to_string().into(), Identifier(ref s) => s.into(), CharConstant(ref c) => c.to_string().into(), @@ -622,6 +635,7 @@ impl<'a> TokenIterator<'a> { self.char_stream.next(); self.advance(); } + #[cfg(not(feature = "no_float"))] '.' => { result.push(next_char); self.char_stream.next(); @@ -707,6 +721,17 @@ impl<'a> TokenIterator<'a> { } else { let out: String = result.iter().filter(|&&c| c != '_').collect(); + #[cfg(feature = "no_float")] + return Some(( + i64::from_str(&out) + .map(Token::IntegerConstant) + .unwrap_or_else(|_| { + Token::LexError(LERR::MalformedNumber(result.iter().collect())) + }), + pos, + )); + + #[cfg(not(feature = "no_float"))] return Some(( i64::from_str(&out) .map(Token::IntegerConstant) @@ -1190,6 +1215,7 @@ fn parse_index_expr<'a>( *pos, )) } + #[cfg(not(feature = "no_float"))] Expr::FloatConstant(_, pos) => { return Err(ParseError::new( PERR::MalformedIndexExpr("Array access expects integer index, not a float".into()), @@ -1332,8 +1358,10 @@ fn parse_primary<'a>(input: &mut Peekable>) -> Result Ok(Expr::IntegerConstant(x, pos)), + #[cfg(not(feature = "no_float"))] Some((Token::FloatConstant(x), pos)) => Ok(Expr::FloatConstant(x, pos)), + + Some((Token::IntegerConstant(x), pos)) => Ok(Expr::IntegerConstant(x, pos)), Some((Token::CharConstant(c), pos)) => Ok(Expr::CharConstant(c, pos)), Some((Token::StringConst(s), pos)) => { can_be_indexed = true; @@ -1383,14 +1411,31 @@ fn parse_unary<'a>(input: &mut Peekable>) -> Result Ok(i + #[cfg(not(feature = "no_float"))] + Ok(Expr::IntegerConstant(i, _)) => Ok(i .checked_neg() .map(|x| Expr::IntegerConstant(x, pos)) .unwrap_or_else(|| Expr::FloatConstant(-(i as f64), pos))), + + // Negative integer + #[cfg(feature = "no_float")] + Ok(Expr::IntegerConstant(i, _)) => i + .checked_neg() + .map(|x| Expr::IntegerConstant(x, pos)) + .ok_or_else(|| { + ParseError::new( + PERR::BadInput(LERR::MalformedNumber(format!("-{}", i)).to_string()), + pos, + ) + }), + // Negative float + #[cfg(not(feature = "no_float"))] Ok(Expr::FloatConstant(f, pos)) => Ok(Expr::FloatConstant(-f, pos)), + // Call negative function Ok(expr) => Ok(Expr::FunctionCall("-".into(), vec![expr], None, pos)), + err @ Err(_) => err, } } @@ -1418,10 +1463,9 @@ fn parse_assignment(lhs: Expr, rhs: Expr, pos: Position) -> Result (true, *pos), #[cfg(not(feature = "no_index"))] - Expr::Index(idx_lhs, _, _) => match idx_lhs.as_ref() { - Expr::Identifier(_, _) => (true, idx_lhs.position()), - _ => (false, idx_lhs.position()), - }, + Expr::Index(idx_lhs, _, _) if idx_lhs.is_identifier() => (true, idx_lhs.position()), + #[cfg(not(feature = "no_index"))] + Expr::Index(idx_lhs, _, _) => (false, idx_lhs.position()), Expr::Dot(dot_lhs, dot_rhs, _) => match dot_lhs.as_ref() { Expr::Identifier(_, _) => valid_assignment_chain(dot_rhs), @@ -1460,29 +1504,6 @@ fn parse_op_assignment( Expr::FunctionCall(function.into(), vec![lhs_copy, rhs], None, pos), pos, ) - - /* - const LHS_VALUE: &'static str = "@LHS_VALUE@"; - - let lhs_pos = lhs.position(); - - Ok(Expr::Block( - Box::new(Stmt::Block(vec![ - Stmt::Let(LHS_VALUE.to_string(), Some(Box::new(lhs)), lhs_pos), - Stmt::Expr(Box::new(parse_assignment( - lhs, - Expr::FunctionCall( - function.into(), - vec![Expr::Identifier(LHS_VALUE.to_string(), lhs_pos), rhs], - None, - pos, - ), - pos, - )?)), - ])), - pos, - )) - */ } fn parse_binary_op<'a>( @@ -1532,6 +1553,7 @@ fn parse_binary_op<'a>( Token::Equals => parse_assignment(current_lhs, rhs, pos)?, Token::PlusAssign => parse_op_assignment("+", current_lhs, rhs, pos)?, Token::MinusAssign => parse_op_assignment("-", current_lhs, rhs, pos)?, + Token::Period => Expr::Dot(Box::new(current_lhs), Box::new(rhs), pos), // Comparison operators default to false when passed invalid operands