Skip to content

Commit 688935a

Browse files
committed
Add compound arithmetic assignment operators
Close #5.
1 parent 7699f05 commit 688935a

File tree

8 files changed

+119
-0
lines changed

8 files changed

+119
-0
lines changed

src/ast.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ pub struct IfThenStmt {
146146
#[derive(Debug, Clone)]
147147
pub enum Stmt {
148148
Assign(LhsExprNode, ExprNode),
149+
AssignOp(LhsExprNode, BinOp, ExprNode),
149150
VarDecl(Variable, ExprNode),
150151
Expr(ExprNode),
151152
Block(Vec<StmtNode>),

src/ast_walk_interpreter.rs

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,9 @@ impl AstWalkInterpreter {
6868
match s.data {
6969
Stmt::VarDecl(ref variable, ref expr) => self.eval_stmt_var_decl(variable, expr),
7070
Stmt::Assign(ref lhs_expr, ref expr) => self.eval_stmt_assign(lhs_expr, expr),
71+
Stmt::AssignOp(ref lhs_expr, ref op, ref expr) => {
72+
self.eval_stmt_assign_with_op(lhs_expr, op, expr, s)
73+
}
7174
Stmt::Block(ref statements) => self.eval_stmt_block(statements),
7275
Stmt::Expr(ref expr) => {
7376
let val = self.eval_expr(expr)?;
@@ -149,6 +152,42 @@ impl AstWalkInterpreter {
149152
Ok(StmtResult::None)
150153
}
151154

155+
fn eval_stmt_assign_with_op(&mut self,
156+
lhs_expr: &LhsExprNode,
157+
op: &BinOp,
158+
expr: &ExprNode,
159+
stmt: &StmtNode)
160+
-> Result<StmtResult, RuntimeErrorWithPosition> {
161+
let val = self.eval_expr_as_value(expr)?;
162+
match lhs_expr.data {
163+
LhsExpr::Identifier(ref id) => {
164+
let prev_expr_val = match self.env.borrow_mut().get_value(id) {
165+
Some(v) => v,
166+
None => {
167+
return Err((RuntimeError::ReferenceError(id.to_owned()), lhs_expr.pos));
168+
}
169+
};
170+
let retval = match *op {
171+
BinOp::Add => operations::add(prev_expr_val, val),
172+
BinOp::Sub => operations::subtract(prev_expr_val, val),
173+
BinOp::Mul => operations::multiply(prev_expr_val, val),
174+
BinOp::Div => operations::divide(prev_expr_val, val),
175+
BinOp::Mod => operations::modulo(prev_expr_val, val),
176+
BinOp::Lt | BinOp::Lte | BinOp::Gt | BinOp::Gte | BinOp::Eq => unreachable!(),
177+
};
178+
let new_val = match retval {
179+
Ok(val) => val,
180+
Err(e) => {
181+
return Err((e, stmt.pos));
182+
}
183+
};
184+
// id must exist, because it was checked above
185+
self.env.borrow_mut().set(id, new_val);
186+
}
187+
};
188+
Ok(StmtResult::None)
189+
}
190+
152191
fn eval_stmt_block(&mut self,
153192
statements: &[StmtNode])
154193
-> Result<StmtResult, RuntimeErrorWithPosition> {

src/grammar.rustpeg

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,16 @@ assignment_statement -> Stmt
2626
= lpos:#position i:identifier rpos:#position __ EQUALS __ e:expr_node __ TERMINATOR {
2727
Stmt::Assign(LhsExprNode { pos: (lpos, rpos), data: LhsExpr::Identifier(i) }, e)
2828
}
29+
/ lpos:#position i:identifier rpos:#position __ op:assign_with_op __ e:expr_node __ TERMINATOR {
30+
Stmt::AssignOp(LhsExprNode { pos: (lpos, rpos), data: LhsExpr::Identifier(i) }, op, e)
31+
}
32+
33+
assign_with_op -> BinOp
34+
= OP_PLUS __ EQUALS { BinOp::Add }
35+
/ OP_MINUS __ EQUALS { BinOp::Sub }
36+
/ OP_ASTERISK __ EQUALS { BinOp::Mul }
37+
/ OP_SLASH __ EQUALS { BinOp::Div }
38+
/ OP_MOD __ EQUALS { BinOp::Mod }
2939

3040
variable_declaration -> Stmt
3141
= b:binding_type __ i:identifier __ EQUALS __ e:expr_node __ TERMINATOR {

src/typechecker.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,10 @@ impl TypeChecker {
289289
self.check_statement_assignment(lhs_expr, expr);
290290
StmtEffect::None
291291
}
292+
Stmt::AssignOp(ref lhs_expr, ref op, ref expr) => {
293+
self.check_statement_assignment_with_op(lhs_expr, op, expr, s);
294+
StmtEffect::None
295+
}
292296
Stmt::Block(ref statements) => {
293297
let current_env = self.env.clone();
294298
self.env = TypeEnvironment::create_child(current_env.clone());
@@ -392,6 +396,47 @@ impl TypeChecker {
392396
};
393397
}
394398

399+
fn check_statement_assignment_with_op(&mut self,
400+
lhs_expr: &LhsExprNode,
401+
op: &BinOp,
402+
expr: &ExprNode,
403+
stmt: &StmtNode) {
404+
let checked_type = self.check_expr_as_value(expr);
405+
match lhs_expr.data {
406+
LhsExpr::Identifier(ref id) => {
407+
let prev_type = match self.env.borrow_mut().get_type(id) {
408+
Some(t) => t,
409+
None => {
410+
self.issues
411+
.push((RuntimeError::ReferenceError(id.to_owned()).into(),
412+
lhs_expr.pos));
413+
Type::Any
414+
}
415+
};
416+
let retval = match *op {
417+
BinOp::Add => check_add_for_types(&prev_type, &checked_type),
418+
ref op @ BinOp::Sub |
419+
ref op @ BinOp::Mul |
420+
ref op @ BinOp::Div |
421+
ref op @ BinOp::Mod => {
422+
check_binary_arithmetic_for_types(op.clone(), &prev_type, &checked_type)
423+
}
424+
_ => unreachable!(),
425+
};
426+
let new_type = match retval {
427+
Ok(t) => t,
428+
Err(issue) => {
429+
self.issues.push((issue, stmt.pos));
430+
Type::Any
431+
}
432+
};
433+
434+
// if id does not exist, then error was reported above
435+
self.env.borrow_mut().set(id, new_type);
436+
}
437+
};
438+
}
439+
395440
fn check_statement_if_then_else(&mut self,
396441
statement: &StmtNode,
397442
if_then_stmt: &IfThenStmt)

tests/run-pass/compound-assign.bl

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
var s = "Hello";
2+
s += ", world!";
3+
assert_eq(s, "Hello, world!");
4+
5+
var x = 5;
6+
x += 5;
7+
assert_eq(x, 10);
8+
x -= 5;
9+
assert_eq(x, 5);
10+
x *= 2;
11+
assert_eq(x, 10);
12+
x /= 2;
13+
assert_eq(x, 5);
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
var x = true;
2+
x += 1;
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[(RuntimeError(BinaryTypeError(Add, Bool, Number)), (14, 21))]
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
var s = "Hello";
2+
s += ", world!";
3+
4+
var x = 5;
5+
x += 5;
6+
x -= 5;
7+
x *= 2;
8+
x /= 2;

0 commit comments

Comments
 (0)