Skip to content

Commit b3871b2

Browse files
committed
Add support for assignment expressions
We now have two kinds of expressions, let and non-let. They mimick def-use chains. Also, I have mandated the usage of `return` keyword.
1 parent f19e953 commit b3871b2

File tree

12 files changed

+138
-25
lines changed

12 files changed

+138
-25
lines changed

examples/math.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@ fn cos(x: f64) : f64 {
88

99
fn exp(x: f64) : f64 {
1010
let e = 2.718;
11-
e * x
11+
return e * x;
1212
}
1313

examples/toss.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import std::Hadamard;
33
fn toss() : qbit {
44
let zero_state = 0q(0, 1); // represent a qubit in zero state simply as 0
55
let superpositioned = Hadamard(zero_state);
6-
superpositioned
6+
return superpositioned;
77
}
88

99
fn main() {
1010
let choice = toss();
1111
if choice == 0 {
12-
print("Heads");
12+
// print("Heads");
1313
} else {
14-
print("Tails");
14+
// print("Tails");
1515
}
1616
}
1717

src/ast.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,7 @@ pub enum Expr {
590590
Tensor(Vec<QccCell<Expr>>),
591591
FnCall(FunctionAST, Vec<QccCell<Expr>>),
592592
Let(VarAST, QccCell<Expr>),
593+
Assign(VarAST, QccCell<Expr>),
593594
Conditional(
594595
QccCell<Expr>,
595596
Vec<QccCell<Expr>>, /* truth_block */
@@ -606,6 +607,7 @@ impl Expr {
606607
Self::Tensor(_) => Default::default(), // TODO: This will require subtracting the dimension of tensor
607608
Self::FnCall(f, _) => f.get_loc().clone(),
608609
Self::Let(var, _) => var.location.clone(),
610+
Self::Assign(v, _) => v.location().clone(),
609611
Self::Conditional(c, _, _) => c.as_ref().borrow().get_location(),
610612
Self::Literal(lit) =>
611613
/*TODO*/
@@ -643,6 +645,7 @@ impl Expr {
643645
}
644646
Self::FnCall(f, args) => *f.get_output_type(),
645647
Self::Let(var, val) => var.get_type(),
648+
Self::Assign(var, val) => var.get_type(),
646649
Self::Conditional(conditional, truth_block, false_block) => {
647650
let last_expr = truth_block.last();
648651
if last_expr.is_none() {
@@ -709,6 +712,7 @@ impl std::fmt::Display for Expr {
709712

710713
Self::Let(var, val) => write!(f, "{} = {}", var, *val.as_ref().borrow()),
711714

715+
Self::Assign(var, val) => write!(f, "{} = {}", var, *val.as_ref().borrow()),
712716
Self::Conditional(cond, _truth, _false) => {
713717
writeln!(f, "{}", *cond.as_ref().borrow())?;
714718

src/inference.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,18 @@ fn check_expr(expr: &QccCell<Expr>) -> Result<Type> {
119119

120120
Ok(val_type)
121121
}
122+
Expr::Assign(ref var, ref val) => {
123+
if !var.is_typed() {
124+
return Err(QccErrorKind::UnknownType)?;
125+
}
126+
let val_type = check_expr(val)?;
127+
128+
if var.get_type() != val_type {
129+
return Err(QccErrorKind::TypeMismatch)?;
130+
}
131+
132+
Ok(val_type)
133+
}
122134
Expr::Conditional(ref conditional, ref truth_block, ref false_block) => {
123135
for expr in truth_block {
124136
check_expr(expr);
@@ -435,6 +447,23 @@ fn infer_expr(expr: &QccCell<Expr>) -> Option<Type> {
435447
}
436448
}
437449

450+
Expr::Assign(ref mut var, ref val) => {
451+
// val is an expression and it must have the same type as var
452+
if var.get_type() == Type::Bottom {
453+
// we need to type check from expression first
454+
let rhs_type = infer_expr(&val)?;
455+
var.set_type(rhs_type);
456+
return Some(rhs_type);
457+
} else {
458+
let lhs_type = var.get_type();
459+
let rhs_type = infer_expr(&val)?;
460+
if lhs_type != rhs_type {
461+
return None;
462+
}
463+
return Some(lhs_type);
464+
}
465+
}
466+
438467
Expr::Conditional(ref conditional, ref truth_block, ref false_block) => {
439468
let mut truth_block_type = Some(Type::Bottom);
440469
for expr in truth_block {
@@ -646,6 +675,55 @@ fn infer_from_table(
646675
.into()))
647676
}
648677
}
678+
Expr::Assign(ref mut var, ref val) => {
679+
let rhs_info = infer_from_table(val, param_st, local_st, function_st);
680+
681+
if rhs_info.is_some() {
682+
return rhs_info;
683+
}
684+
685+
let var_type = var.get_type();
686+
let val_type = val.as_ref().borrow().get_type();
687+
688+
if !var.is_typed() {
689+
var.set_type(val.as_ref().borrow().get_type());
690+
None
691+
} else if (var_type == Type::Qbit || var_type == Type::Bit)
692+
&& (val_type == Type::Qbit || val_type == Type::Bit)
693+
&& (var_type != val_type)
694+
{
695+
// If one of lhs or rhs is qbit while the other is bit, then we
696+
// will put a measure operator before it is assigned during
697+
// codegen.
698+
//
699+
// let x: bit = y; # y := qbit
700+
//
701+
// This holds according to our subtyping rules. Codegen will
702+
// lower this to:
703+
//
704+
// let x0: bit = measure(y);
705+
// let x: bit = x0;
706+
//
707+
// Similarily,
708+
//
709+
// let x: qbit = y; # y := bit
710+
//
711+
// This is also fine. When codegen lowers this code, it
712+
// automatically puts required stub to create a logical qubit.
713+
None
714+
} else if var_type == val_type {
715+
None
716+
} else if var_type != val_type {
717+
// if one is qbit and other is bit, pass
718+
Some(Err(QccErrorKind::TypeMismatch.into()))
719+
} else {
720+
Some(Ok(Expr::Var(VarAST::new(
721+
var.name().clone(),
722+
var.location().clone(),
723+
))
724+
.into()))
725+
}
726+
}
649727
Expr::Conditional(ref mut conditional, ref mut truth_block, ref mut false_block) => {
650728
for expr in truth_block {
651729
let info = infer_from_table(expr, param_st, local_st, function_st);

src/mangle.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ pub(crate) fn mangle_expr(expr: &mut QccCell<Expr>, prefix: Ident) {
3131
Expr::Let(_, ref mut val) => {
3232
mangle_expr(val, prefix);
3333
}
34+
Expr::Assign(_, ref mut val) => {
35+
mangle_expr(val, prefix);
36+
}
3437
Expr::FnCall(ref mut f, ref mut args) => {
3538
for arg in args {
3639
mangle_expr(arg, prefix.clone());
@@ -66,6 +69,9 @@ pub(crate) fn mangle_fns(expr: &mut QccCell<Expr>, module_name: &String, functio
6669
Expr::Let(ref mut var, ref mut val) => {
6770
mangle_fns(val, module_name, functions);
6871
}
72+
Expr::Assign(ref mut var, ref mut val) => {
73+
mangle_fns(val, module_name, functions);
74+
}
6975
Expr::FnCall(ref mut f, ref mut args) => {
7076
for arg in args {
7177
mangle_fns(arg, module_name, functions);
@@ -109,6 +115,9 @@ fn mangle_expr_check(expr: &mut QccCell<Expr>, mod_name: &Ident, fn_name: &Ident
109115
Expr::Let(_, ref mut val) => {
110116
mangle_expr_check(val, mod_name, fn_name);
111117
}
118+
Expr::Assign(_, ref mut val) => {
119+
mangle_expr_check(val, mod_name, fn_name);
120+
}
112121
Expr::FnCall(ref mut f, ref mut args) => {
113122
for arg in args {
114123
mangle_expr_check(arg, mod_name, fn_name);

src/parser.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,22 @@ impl Parser {
194194
} else if self.lexer.is_token(Token::If) {
195195
let expr = self.parse_if_block()?;
196196
body.push(expr);
197+
} else if self.lexer.is_token(Token::Identifier) {
198+
// Normal assignment expressions
199+
let name = self.lexer.identifier();
200+
let location = self.lexer.location.clone();
201+
self.lexer.consume(Token::Identifier)?;
202+
203+
let var = VarAST::new(name.clone(), location.clone());
204+
205+
if !self.lexer.is_token(Token::Assign) {
206+
return Err(QccErrorKind::ExpectedAssign)?;
207+
}
208+
self.lexer.consume(Token::Assign)?;
209+
210+
let val = self.parse_expr()?;
211+
let expr = Expr::Assign(var, val).into();
212+
body.push(expr);
197213
} else {
198214
// function calls with no return or dropped return value.
199215
if self.lexer.token.is_some() {

tests/let_both_typed.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
fn foo() : qbit {
22
let q = 0q(0, 1); // represent a qubit in zero state simply as 0
3-
q
3+
return q;
44
}
55

66
fn main() {
77
let choice : qbit = foo();
88
if choice == 0 {
9-
print("Heads");
9+
let _ = print(0);
1010
} else {
11-
print("Tails");
11+
let _ = print(1);
1212
}
1313
}
1414

tests/package/math.ql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ fn cos(x: f64) : f64 {
1010

1111
fn exp(x: f64) : f64 {
1212
let e = 2.718;
13-
e * x
13+
return e * x;
1414
}
1515

tests/package/toss.ql

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import std::Hadamard;
33
fn toss() : qbit {
44
let zero_state = 0q(0, 1); // represent a qubit in zero state simply as 0
55
let superpositioned = Hadamard(zero_state);
6-
superpositioned
6+
return superpositioned;
77
}
88

99
fn main() {
1010
let choice = toss();
1111
if choice == 0 {
12-
print("Heads");
12+
// print("Heads");
1313
} else {
14-
print("Tails");
14+
// print("Tails");
1515
}
1616
}
1717

tests/qbit_float.ql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
fn foo(q0: qbit) : qbit {
22
let q1 = 2 * q0;
3-
q1
3+
return q1;
44
}
55

66
fn bar(q0: qbit) : qbit {
77
let q1 = q0 * 2;
8-
q1
8+
return q1;
99
}
1010

1111
fn main() {

0 commit comments

Comments
 (0)