From d98b42c8bba291d9a4a4a7ad510d2bdbb1c7a2f2 Mon Sep 17 00:00:00 2001 From: Olle Sandberg Date: Fri, 20 Sep 2019 00:08:33 +0200 Subject: [PATCH] Implement assign operators (#95) (#97) * Operators implemented: +=, -=, /=, *=, %=, &=, |=, ^=, <<=, >>= * Add parser tests for new operators --- src/lib/exec.rs | 36 ++++++++++- src/lib/syntax/ast/op.rs | 50 ++++++++++++++++ src/lib/syntax/parser.rs | 126 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 210 insertions(+), 2 deletions(-) diff --git a/src/lib/exec.rs b/src/lib/exec.rs index 4b42a2e196..5e4881d681 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -11,7 +11,7 @@ use crate::{ syntax::ast::{ constant::Const, expr::{Expr, ExprDef}, - op::{BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp}, + op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp}, }, }; use gc::{Gc, GcCell}; @@ -42,6 +42,21 @@ pub struct InterpreterBuilder { global: Value, } +fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value { + Gc::new(match *op { + AssignOp::Add => v_a + v_b, + AssignOp::Sub => v_a - v_b, + AssignOp::Mul => v_a * v_b, + AssignOp::Div => v_a / v_b, + AssignOp::Mod => v_a % v_b, + AssignOp::And => v_a & v_b, + AssignOp::Or => v_a | v_b, + AssignOp::Xor => v_a ^ v_b, + AssignOp::Shl => v_a << v_b, + AssignOp::Shr => v_a << v_b, + }) +} + impl Executor for Interpreter { fn new() -> Self { InterpreterBuilder::new().build() @@ -272,6 +287,25 @@ impl Executor for Interpreter { LogOp::Or => to_value(v_a || v_b), }) } + ExprDef::BinOp(BinOp::Assign(ref op), ref a, ref b) => match a.def { + ExprDef::Local(ref name) => { + let v_a = (*self.environment.get_binding_value(&name)).clone(); + let v_b = (*self.run(b)?).clone(); + let value = exec_assign_op(op, v_a, v_b); + self.environment + .set_mutable_binding(&name, value.clone(), true); + Ok(value) + } + ExprDef::GetConstField(ref obj, ref field) => { + let v_r_a = self.run(obj)?; + let v_a = (*v_r_a.borrow().get_field(field)).clone(); + let v_b = (*self.run(b)?).clone(); + let value = exec_assign_op(op, v_a, v_b.clone()); + v_r_a.borrow().set_field(field.clone(), value.clone()); + Ok(value) + } + _ => Ok(Gc::new(ValueData::Undefined)), + }, ExprDef::Construct(ref callee, ref args) => { let func_object = self.run(callee)?; let mut v_args = Vec::with_capacity(args.len()); diff --git a/src/lib/syntax/ast/op.rs b/src/lib/syntax/ast/op.rs index 4d6d0ca80a..e71ec44917 100644 --- a/src/lib/syntax/ast/op.rs +++ b/src/lib/syntax/ast/op.rs @@ -183,6 +183,8 @@ pub enum BinOp { Comp(CompOp), /// Logical operation Log(LogOp), + /// Assign operation + Assign(AssignOp), } impl Operator for BinOp { @@ -207,6 +209,7 @@ impl Operator for BinOp { BinOp::Bit(BitOp::Or) => 12, BinOp::Log(LogOp::And) => 13, BinOp::Log(LogOp::Or) => 14, + BinOp::Assign(_) => 15, } } } @@ -221,6 +224,53 @@ impl Display for BinOp { BinOp::Bit(ref op) => op.to_string(), BinOp::Comp(ref op) => op.to_string(), BinOp::Log(ref op) => op.to_string(), + BinOp::Assign(ref op) => op.to_string(), + } + ) + } +} + +#[derive(Clone, Debug, Trace, Finalize, PartialEq)] +/// A binary operation between 2 values +pub enum AssignOp { + /// `a += b` - Add assign + Add, + /// `a -= b` - Sub assign + Sub, + /// `a *= b` - Mul assign + Mul, + /// `a /= b` - Div assign + Div, + /// `a %= b` - Modulus assign + Mod, + /// `a &= b` - Bitwise and assign + And, + /// `a |= b` - Bitwise or assign + Or, + /// `a ^= b` - Bitwise xor assign + Xor, + /// `a <<= b` - Left shift assign + Shl, + /// `a >>= b` - Right shift assign + Shr, +} + +impl Display for AssignOp { + fn fmt(&self, f: &mut Formatter) -> Result { + write!( + f, + "{}", + match *self { + AssignOp::Add => "+=", + AssignOp::Sub => "-=", + AssignOp::Mul => "*=", + AssignOp::Div => "/=", + AssignOp::Mod => "%=", + AssignOp::And => "&=", + AssignOp::Or => "|=", + AssignOp::Xor => "^=", + AssignOp::Shl => "<<=", + AssignOp::Shr => ">>=", } ) } diff --git a/src/lib/syntax/parser.rs b/src/lib/syntax/parser.rs index fed369d528..50d5b11b04 100644 --- a/src/lib/syntax/parser.rs +++ b/src/lib/syntax/parser.rs @@ -1,7 +1,7 @@ use crate::syntax::ast::constant::Const; use crate::syntax::ast::expr::{Expr, ExprDef}; use crate::syntax::ast::keyword::Keyword; -use crate::syntax::ast::op::{BinOp, BitOp, CompOp, LogOp, NumOp, Operator, UnaryOp}; +use crate::syntax::ast::op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, Operator, UnaryOp}; use crate::syntax::ast::punc::Punctuator; use crate::syntax::ast::token::{Token, TokenData}; use std::collections::btree_map::BTreeMap; @@ -662,6 +662,36 @@ impl Parser { let next = self.parse()?; result = mk!(self, ExprDef::Assign(Box::new(expr), Box::new(next))); } + TokenData::Punctuator(Punctuator::AssignAdd) => { + result = self.binop(BinOp::Assign(AssignOp::Add), expr)? + } + TokenData::Punctuator(Punctuator::AssignSub) => { + result = self.binop(BinOp::Assign(AssignOp::Sub), expr)? + } + TokenData::Punctuator(Punctuator::AssignMul) => { + result = self.binop(BinOp::Assign(AssignOp::Mul), expr)? + } + TokenData::Punctuator(Punctuator::AssignDiv) => { + result = self.binop(BinOp::Assign(AssignOp::Div), expr)? + } + TokenData::Punctuator(Punctuator::AssignAnd) => { + result = self.binop(BinOp::Assign(AssignOp::And), expr)? + } + TokenData::Punctuator(Punctuator::AssignOr) => { + result = self.binop(BinOp::Assign(AssignOp::Or), expr)? + } + TokenData::Punctuator(Punctuator::AssignXor) => { + result = self.binop(BinOp::Assign(AssignOp::Xor), expr)? + } + TokenData::Punctuator(Punctuator::AssignRightSh) => { + result = self.binop(BinOp::Assign(AssignOp::Shr), expr)? + } + TokenData::Punctuator(Punctuator::AssignLeftSh) => { + result = self.binop(BinOp::Assign(AssignOp::Shl), expr)? + } + TokenData::Punctuator(Punctuator::AssignMod) => { + result = self.binop(BinOp::Assign(AssignOp::Mod), expr)? + } TokenData::Punctuator(Punctuator::Arrow) => { self.pos += 1; let mut args = Vec::with_capacity(1); @@ -1243,5 +1273,99 @@ mod tests { Expr::new(ExprDef::Local(String::from("b"))), )], ); + + // Check assign ops + check_parser( + "a += b", + &[create_bin_op( + BinOp::Assign(AssignOp::Add), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a -= b", + &[create_bin_op( + BinOp::Assign(AssignOp::Sub), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a *= b", + &[create_bin_op( + BinOp::Assign(AssignOp::Mul), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a /= b", + &[create_bin_op( + BinOp::Assign(AssignOp::Div), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a %= b", + &[create_bin_op( + BinOp::Assign(AssignOp::Mod), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a &= b", + &[create_bin_op( + BinOp::Assign(AssignOp::And), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a |= b", + &[create_bin_op( + BinOp::Assign(AssignOp::Or), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a ^= b", + &[create_bin_op( + BinOp::Assign(AssignOp::Xor), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a <<= b", + &[create_bin_op( + BinOp::Assign(AssignOp::Shl), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a >>= b", + &[create_bin_op( + BinOp::Assign(AssignOp::Shr), + Expr::new(ExprDef::Local(String::from("a"))), + Expr::new(ExprDef::Local(String::from("b"))), + )], + ); + check_parser( + "a %= 10 / 2", + &[create_bin_op( + BinOp::Assign(AssignOp::Mod), + Expr::new(ExprDef::Local(String::from("a"))), + create_bin_op( + BinOp::Num(NumOp::Div), + Expr::new(ExprDef::Const(Const::Num(10.0))), + Expr::new(ExprDef::Const(Const::Num(2.0))), + ), + )], + ); } }