From ce70796581474326097ab28f049f4e53a2d2f166 Mon Sep 17 00:00:00 2001 From: simon brahan Date: Mon, 7 Oct 2019 23:40:08 +0100 Subject: [PATCH] Implement exponentiation operator (#130) * Exponentiation (**) now works. Added missing NumOp type, token match, and calculation. * Assignment exponentiation (**=) now works. Added missing AssignOp type, token match, and calculation. This fixes #89. --- src/lib/exec.rs | 2 ++ src/lib/js/value.rs | 4 ++++ src/lib/syntax/ast/op.rs | 7 +++++++ src/lib/syntax/parser.rs | 30 ++++++++++++++++++++++++++++++ 4 files changed, 43 insertions(+) diff --git a/src/lib/exec.rs b/src/lib/exec.rs index ceb7d9e07e..fd1382cc38 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -39,6 +39,7 @@ fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value { AssignOp::Add => v_a + v_b, AssignOp::Sub => v_a - v_b, AssignOp::Mul => v_a * v_b, + AssignOp::Pow => v_a.as_num_to_power(v_b), AssignOp::Div => v_a / v_b, AssignOp::Mod => v_a % v_b, AssignOp::And => v_a & v_b, @@ -227,6 +228,7 @@ impl Executor for Interpreter { NumOp::Add => v_a + v_b, NumOp::Sub => v_a - v_b, NumOp::Mul => v_a * v_b, + NumOp::Pow => v_a.as_num_to_power(v_b), NumOp::Div => v_a / v_b, NumOp::Mod => v_a % v_b, })) diff --git a/src/lib/js/value.rs b/src/lib/js/value.rs index a0bd699035..53a7d4bdec 100644 --- a/src/lib/js/value.rs +++ b/src/lib/js/value.rs @@ -551,6 +551,10 @@ impl ValueData { } } } + + pub fn as_num_to_power(&self, other: ValueData) -> ValueData { + ValueData::Number(self.to_num().powf(other.to_num())) + } } impl Default for ValueData { diff --git a/src/lib/syntax/ast/op.rs b/src/lib/syntax/ast/op.rs index e71ec44917..83a2858670 100644 --- a/src/lib/syntax/ast/op.rs +++ b/src/lib/syntax/ast/op.rs @@ -24,6 +24,8 @@ pub enum NumOp { Div, /// `a * b` - Multiplication Mul, + /// `a ** b` - Exponentiation + Pow, /// `a % b` - Modulus Mod, } @@ -38,6 +40,7 @@ impl Display for NumOp { NumOp::Sub => "-", NumOp::Div => "/", NumOp::Mul => "*", + NumOp::Pow => "**", NumOp::Mod => "%", } ) @@ -193,6 +196,7 @@ impl Operator for BinOp { } fn get_precedence(&self) -> u64 { match *self { + BinOp::Num(NumOp::Pow) => 4, BinOp::Num(NumOp::Mul) | BinOp::Num(NumOp::Div) | BinOp::Num(NumOp::Mod) => 5, BinOp::Num(NumOp::Add) | BinOp::Num(NumOp::Sub) => 6, BinOp::Bit(BitOp::Shl) | BinOp::Bit(BitOp::Shr) => 7, @@ -239,6 +243,8 @@ pub enum AssignOp { Sub, /// `a *= b` - Mul assign Mul, + /// `a **= b` - Exponent assign + Pow, /// `a /= b` - Div assign Div, /// `a %= b` - Modulus assign @@ -264,6 +270,7 @@ impl Display for AssignOp { AssignOp::Add => "+=", AssignOp::Sub => "-=", AssignOp::Mul => "*=", + AssignOp::Pow => "**=", AssignOp::Div => "/=", AssignOp::Mod => "%=", AssignOp::And => "&=", diff --git a/src/lib/syntax/parser.rs b/src/lib/syntax/parser.rs index 50d5b11b04..4c94e523c6 100644 --- a/src/lib/syntax/parser.rs +++ b/src/lib/syntax/parser.rs @@ -671,6 +671,9 @@ impl Parser { TokenData::Punctuator(Punctuator::AssignMul) => { result = self.binop(BinOp::Assign(AssignOp::Mul), expr)? } + TokenData::Punctuator(Punctuator::AssignPow) => { + result = self.binop(BinOp::Assign(AssignOp::Pow), expr)? + } TokenData::Punctuator(Punctuator::AssignDiv) => { result = self.binop(BinOp::Assign(AssignOp::Div), expr)? } @@ -711,6 +714,9 @@ impl Parser { TokenData::Punctuator(Punctuator::Mul) => { result = self.binop(BinOp::Num(NumOp::Mul), expr)? } + TokenData::Punctuator(Punctuator::Pow) => { + result = self.binop(BinOp::Num(NumOp::Pow), expr)? + } TokenData::Punctuator(Punctuator::Div) => { result = self.binop(BinOp::Num(NumOp::Div), expr)? } @@ -1148,6 +1154,22 @@ mod tests { Expr::new(ExprDef::Const(Const::Num(2.0))), )], ); + check_parser( + "a ** b", + &[create_bin_op( + BinOp::Num(NumOp::Pow), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a**2", + &[create_bin_op( + BinOp::Num(NumOp::Pow), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Const(Const::Num(2.0))), + )], + ); check_parser( "a % b", &[create_bin_op( @@ -1299,6 +1321,14 @@ mod tests { Expr::new(ExprDef::Local(String::from("b"))), )], ); + check_parser( + "a **= b", + &[create_bin_op( + BinOp::Assign(AssignOp::Pow), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); check_parser( "a /= b", &[create_bin_op(