Skip to content

Commit

Permalink
Implement return (#135)
Browse files Browse the repository at this point in the history
* Implement `return`

* Set return type in ReturnExpression from returned value

* Record error for `return` in global scope
  • Loading branch information
jlapeyre authored Feb 16, 2024
1 parent b174b2b commit e7d1156
Show file tree
Hide file tree
Showing 3 changed files with 58 additions and 32 deletions.
25 changes: 24 additions & 1 deletion crates/oq3_semantics/src/asg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ pub enum Expr {
// But we need to handle these in a consistent way. Is there any situation where the "type" of Range is meaningful or useful?
// For example, in syntax_to_semantics, have a routine that handles out-of-tree expressions.
Range(Range),
Return(Box<ReturnExpression>),
Call, // stub function (def) call
Set, // stub
MeasureExpression(MeasureExpression),
Expand Down Expand Up @@ -197,7 +198,6 @@ pub enum Stmt {
OldStyleDeclaration, // stub
Pragma(Pragma),
Reset(Reset),
Return, // stub
SwitchCaseStmt(SwitchCaseStmt),
While(While),
}
Expand Down Expand Up @@ -294,6 +294,29 @@ impl IndexExpression {
}
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ReturnExpression {
value: Option<TExpr>,
}

impl ReturnExpression {
pub fn new(value: Option<TExpr>) -> ReturnExpression {
ReturnExpression { value }
}

pub fn to_texpr(self) -> TExpr {
let return_type = match self.value() {
Some(expr) => expr.get_type().clone(),
_ => Type::Void,
};
TExpr::new(Expr::Return(Box::new(self)), return_type)
}

pub fn value(&self) -> Option<&TExpr> {
self.value.as_ref()
}
}

#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct IndexedIdentifier {
identifier: SymbolIdResult,
Expand Down
1 change: 1 addition & 0 deletions crates/oq3_semantics/src/semantic_error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub enum SemanticErrorKind {
IncompatibleTypesError,
MutateConstError,
IncludeNotInGlobalScopeError,
ReturnInGlobalScopeError,
NumGateParamsError,
NumGateQubitsError,
}
Expand Down
64 changes: 33 additions & 31 deletions crates/oq3_semantics/src/syntax_to_semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ pub fn syntax_to_semantic<T: SourceTrait>(
fn from_stmt(stmt: synast::Stmt, context: &mut Context) -> Option<asg::Stmt> {
match stmt {
synast::Stmt::IfStmt(if_stmt) => {
let condition = from_expr(if_stmt.condition().unwrap(), context);
let condition = from_expr(if_stmt.condition(), context);
with_scope!(context, ScopeType::Local,
let then_branch = from_block_expr(if_stmt.then_branch().unwrap(), context);
);
Expand All @@ -206,7 +206,7 @@ fn from_stmt(stmt: synast::Stmt, context: &mut Context) -> Option<asg::Stmt> {
}

synast::Stmt::WhileStmt(while_stmt) => {
let condition = from_expr(while_stmt.condition().unwrap(), context);
let condition = from_expr(while_stmt.condition(), context);
with_scope!(context, ScopeType::Local,
let loop_body = from_block_expr(while_stmt.body().unwrap(), context);
);
Expand All @@ -216,7 +216,7 @@ fn from_stmt(stmt: synast::Stmt, context: &mut Context) -> Option<asg::Stmt> {
// Note: The outer curlies do not entail a new scope. But the inner curlies do entail a
// new scope, one for each case.
synast::Stmt::SwitchCaseStmt(switch_case_stmt) => {
let control = from_expr(switch_case_stmt.control().unwrap(), context);
let control = from_expr(switch_case_stmt.control(), context);
let case_exprs = switch_case_stmt.case_exprs().map(|case_expr| {
let int_exprs = inner_expression_list(case_expr.expression_list().unwrap(), context);
with_scope!(context, ScopeType::Local,
Expand Down Expand Up @@ -362,17 +362,15 @@ fn from_stmt(stmt: synast::Stmt, context: &mut Context) -> Option<asg::Stmt> {

fn from_expr_stmt(expr_stmt: synast::ExprStmt, context: &mut Context) -> Option<asg::Stmt> {
use synast::Expr::{GPhaseCallExpr, GateCallExpr, ModifiedGateCallExpr};
let syn_expr = expr_stmt.expr().unwrap();

// At present, three expressions, those for gate calls, are handled specially. In oq3_syntax, gate calls
// are expressions wrapped in `ExprStmt`. But in the ASG, gate calls are are variant of `Stmt`. All
// other `synast::ExprStmt` are translated to `asg::ExprStmt`.
match syn_expr {
GateCallExpr(gate_call) => {
match expr_stmt.expr() {
Some(GateCallExpr(gate_call)) => {
from_gate_call_expr(gate_call, Vec::<asg::GateModifier>::new(), context)
}

ModifiedGateCallExpr(mod_gate_call) => {
Some(ModifiedGateCallExpr(mod_gate_call)) => {
let modifiers = mod_gate_call
.modifiers()
.map(|modifier| match modifier {
Expand Down Expand Up @@ -406,21 +404,19 @@ fn from_expr_stmt(expr_stmt: synast::ExprStmt, context: &mut Context) -> Option<
from_gate_call_expr(gate_call, modifiers, context)
} else {
let gphase = mod_gate_call.g_phase_call_expr().unwrap();
let synarg = gphase.arg().unwrap();
let arg = from_expr(synarg, context).unwrap();
let arg = from_expr(gphase.arg(), context).unwrap();
Some(asg::Stmt::ModifiedGPhaseCall(asg::ModifiedGPhaseCall::new(
arg, modifiers,
)))
}
}

GPhaseCallExpr(gphase) => {
let synarg = gphase.arg().unwrap();
let arg = from_expr(synarg, context).unwrap();
Some(GPhaseCallExpr(gphase)) => {
let arg = from_expr(gphase.arg(), context).unwrap();
Some(asg::Stmt::GPhaseCall(asg::GPhaseCall::new(arg)))
}

_ => {
syn_expr => {
let expr = from_expr(syn_expr, context);
expr.map_or_else(
|| panic!("expr::ExprStmt is None. Expression not implemented in the ASG."),
Expand All @@ -431,10 +427,11 @@ fn from_expr_stmt(expr_stmt: synast::ExprStmt, context: &mut Context) -> Option<
}

fn from_paren_expr(paren_expr: synast::ParenExpr, context: &mut Context) -> Option<asg::TExpr> {
from_expr(paren_expr.expr().unwrap(), context)
from_expr(paren_expr.expr(), context)
}

fn from_expr(expr: synast::Expr, context: &mut Context) -> Option<asg::TExpr> {
fn from_expr(expr_maybe: Option<synast::Expr>, context: &mut Context) -> Option<asg::TExpr> {
let expr = expr_maybe?;
match expr {
// FIXME: Ugh. could clean up logic here
synast::Expr::PrefixExpr(prefix_expr) => {
Expand All @@ -460,7 +457,7 @@ fn from_expr(expr: synast::Expr, context: &mut Context) -> Option<asg::TExpr> {
Some(synexpr) => Some(
asg::UnaryExpr::new(
asg::UnaryOp::Minus,
from_expr(synexpr, context).unwrap(),
from_expr(Some(synexpr), context).unwrap(),
)
.to_texpr(),
),
Expand All @@ -476,8 +473,8 @@ fn from_expr(expr: synast::Expr, context: &mut Context) -> Option<asg::TExpr> {

synast::Expr::BinExpr(bin_expr) => {
let synast_op = bin_expr.op_kind().unwrap();
let left_syn = bin_expr.lhs().unwrap();
let right_syn = bin_expr.rhs().unwrap();
let left_syn = bin_expr.lhs();
let right_syn = bin_expr.rhs();

let op = from_binary_op(synast_op);
let left = from_expr(left_syn, context).unwrap();
Expand All @@ -497,15 +494,15 @@ fn from_expr(expr: synast::Expr, context: &mut Context) -> Option<asg::TExpr> {

synast::Expr::RangeExpr(range_expr) => {
let (start, step, stop) = range_expr.start_step_stop();
let start = from_expr(start.unwrap(), context).unwrap();
let stop = from_expr(stop.unwrap(), context).unwrap();
let step = step.and_then(|step| from_expr(step, context));
let start = from_expr(start, context).unwrap();
let stop = from_expr(stop, context).unwrap();
let step = from_expr(step, context);
let range = asg::Range::new(start, step, stop);
Some(range.to_texpr(Type::Range))
}

synast::Expr::IndexExpr(index_expr) => {
let expr = from_expr(index_expr.expr().unwrap(), context);
let expr = from_expr(index_expr.expr(), context);
let index = from_index_operator(index_expr.index_operator().unwrap(), context);
Some(asg::IndexExpression::new(expr.unwrap(), index).to_texpr())
}
Expand All @@ -521,6 +518,14 @@ fn from_expr(expr: synast::Expr, context: &mut Context) -> Option<asg::TExpr> {
Some(asg::MeasureExpression::new(gate_operand_asg).to_texpr())
}

synast::Expr::ReturnExpr(ref return_expr) => {
let expr_asg = from_expr(return_expr.expr(), context);
if context.symbol_table().current_scope_type() == ScopeType::Global {
context.insert_error(ReturnInGlobalScopeError, &expr);
}
Some(asg::ReturnExpression::new(expr_asg).to_texpr())
}

// Everything else is not yet implemented
_ => {
println!("Expression not supported {:?}", expr);
Expand Down Expand Up @@ -652,7 +657,7 @@ fn inner_expression_list(
) -> Vec<asg::TExpr> {
expression_list
.exprs()
.filter_map(|x| from_expr(x, context))
.filter_map(|x| from_expr(Some(x), context))
.collect()
}

Expand Down Expand Up @@ -762,10 +767,7 @@ fn from_classical_declaration_statement(
};

let name_str = type_decl.name().unwrap().string();
let initializer = type_decl
.expr()
.and_then(|initializer| from_expr(initializer, context));

let initializer = from_expr(type_decl.expr(), context);
// FIXME: This error and several others can and should be moved to a subsequent pass.
// However, we would lose the information in `text_range` unless we do something to preserve it.
let symbol_id = context.new_binding(name_str.as_ref(), &typ, type_decl);
Expand All @@ -786,7 +788,7 @@ fn from_assignment_stmt(
if nameb.is_some() {
let name = nameb.as_ref().unwrap();
let name_str = name.string();
let expr = from_expr(assignment_stmt.rhs().unwrap(), context); // rhs of `=` operator
let expr = from_expr(assignment_stmt.rhs(), context); // rhs of `=` operator

let (symbol_id, typ) = context.lookup_symbol(name_str.as_str(), name).as_tuple();
let is_mutating_const = symbol_id.is_ok() && typ.is_const();
Expand All @@ -800,8 +802,8 @@ fn from_assignment_stmt(
let indexed_identifier_ast = assignment_stmt.indexed_identifier();
let (indexed_identifier, _typ) =
ast_indexed_identifier(&indexed_identifier_ast.unwrap(), context);
let expr = from_expr(assignment_stmt.rhs().unwrap(), context); // rhs of `=` operator
// let is_mutating_const = symbol_id.is_ok() && typ.is_const();
let expr = from_expr(assignment_stmt.rhs(), context); // rhs of `=` operator
// let is_mutating_const = symbol_id.is_ok() && typ.is_const();
let lvalue = asg::LValue::IndexedIdentifier(indexed_identifier);
Some(asg::Assignment::new(lvalue, expr.unwrap()).to_stmt())
}
Expand Down

0 comments on commit e7d1156

Please sign in to comment.