From 75013a5ca6b610a0ffb135abdc4e2f70bf39e6f6 Mon Sep 17 00:00:00 2001 From: John Lapeyre Date: Mon, 19 Feb 2024 22:13:34 -0500 Subject: [PATCH] Implement for loop --- crates/oq3_parser/src/grammar/expressions.rs | 37 +++++++- .../src/grammar/expressions/atom.rs | 39 +------- crates/oq3_parser/src/grammar/items.rs | 28 ++++++ crates/oq3_parser/src/grammar/params.rs | 14 +-- .../src/syntax_kind/syntax_kind_enum.rs | 1 + crates/oq3_semantics/src/asg.rs | 69 ++++++++++---- .../oq3_semantics/src/syntax_to_semantics.rs | 85 ++++++++++++----- crates/oq3_syntax/openqasm3.ungram | 13 ++- crates/oq3_syntax/src/ast/generated/nodes.rs | 94 ++++++++++++++----- crates/oq3_syntax/src/ast/node_ext.rs | 46 ++++++--- crates/oq3_syntax/src/tests/ast_src.rs | 1 + 11 files changed, 293 insertions(+), 134 deletions(-) diff --git a/crates/oq3_parser/src/grammar/expressions.rs b/crates/oq3_parser/src/grammar/expressions.rs index 9be39ce..ba8d623 100644 --- a/crates/oq3_parser/src/grammar/expressions.rs +++ b/crates/oq3_parser/src/grammar/expressions.rs @@ -5,6 +5,7 @@ pub mod atom; use super::*; +pub(crate) use atom::block_expr; pub(crate) use atom::try_block_expr; pub(super) use atom::LITERAL_FIRST; // Pretty sure semicolon is always required in OQ3 @@ -22,6 +23,27 @@ pub(super) fn expr(p: &mut Parser<'_>) -> Option { expr_bp(p, None, r, 1).map(|(m, _)| m) } +// This inlcudes square brackets +pub(crate) fn range_expr(p: &mut Parser<'_>) -> Option { + let r = Restrictions { prefer_stmt: false }; + let m = p.start(); + assert!(p.at(T!['['])); + p.bump(T!['[']); + expr_bp(p, None, r, 1).map(|(m, _)| m); + if p.at(COLON) { + p.bump(COLON); + expr_bp(p, None, r, 1).map(|(m, _)| m); + if p.at(COLON) { + p.bump(COLON); + expr_bp(p, None, r, 1).map(|(m, _)| m); + } + } else { + p.error("Expecting colon in range expression."); + } + p.expect(T![']']); + Some(m.complete(p, RANGE_EXPR)) +} + pub(super) fn expr_or_range_expr(p: &mut Parser<'_>) -> Option { let r = Restrictions { prefer_stmt: false }; let m = p.start(); @@ -460,16 +482,21 @@ pub(crate) fn indexed_identifer(p: &mut Parser<'_>, lhs: CompletedMarker) -> Com m.complete(p, INDEXED_IDENTIFIER) } +pub(crate) fn set_expression(p: &mut Parser<'_>) { + assert!(p.at(T!['{'])); + let m = p.start(); + p.bump(T!['{']); + params::expression_list(p); + p.bump(T!['}']); + m.complete(p, SET_EXPRESSION); +} + pub(crate) fn index_operator(p: &mut Parser<'_>) { assert!(p.at(T!['['])); let m = p.start(); p.expect(T!['[']); if p.at(T!['{']) { - let m1 = p.start(); - p.bump(T!['{']); - params::expression_list(p); - p.bump(T!['}']); - m1.complete(p, SET_EXPRESSION); + set_expression(p); } else { params::expression_list(p); } diff --git a/crates/oq3_parser/src/grammar/expressions/atom.rs b/crates/oq3_parser/src/grammar/expressions/atom.rs index 1a8252a..e1c732e 100644 --- a/crates/oq3_parser/src/grammar/expressions/atom.rs +++ b/crates/oq3_parser/src/grammar/expressions/atom.rs @@ -84,7 +84,6 @@ pub(super) fn atom_expr( T![measure] => measure_expression(p), T![return] => return_expr(p), T!['{'] => block_expr(p), - T![for] => for_expr(p, None), T![inv] | T![pow] | T![ctrl] | T![negctrl] => modified_gate_call_expr(p), T![gphase] => gphase_call_expr(p), IDENT if (la == IDENT || la == HARDWAREIDENT) => gate_call_expr(p), @@ -94,7 +93,7 @@ pub(super) fn atom_expr( // Also `NAME` is probably not correct. IDENT => identifier(p), _ => { - p.err_and_bump("expected expression"); + p.err_and_bump("atom_expr: expected expression"); return None; } }; @@ -235,7 +234,7 @@ fn tuple_expr(p: &mut Parser<'_>) -> CompletedMarker { let mut saw_expr = false; if p.eat(T![,]) { - p.error("expected expression"); + p.error("expected expression, found comma instead"); saw_comma = true; } @@ -292,40 +291,6 @@ fn array_expr(p: &mut Parser<'_>) -> CompletedMarker { m.complete(p, ARRAY_EXPR) } -// FIXME: finish this. need to disambiguate from block of statments -// fn set_expr(p: &mut Parser<'_>) -> CompletedMarker { -// assert!(p.at(T!['{'])); -// if p.at(INT_NUMBER) { -// p.bump(INT_NUMBER) -// } else if p.at(T!['}']) { -// p.bump(T!['}']); -// m.complete(p, SET_EXPR); -// return (); -// } -// // FIXME find , -// p.error("expecting an integer or '}'"); -// // todo eat until ';' maybe, for recovery -// m.abandon(p); -// return (); -// p.expect(T!['}']); -// m.complete(p, SET_EXPR) -// } - -// test for_expr -// fn foo() { -// for x in [] {}; -// } -fn for_expr(p: &mut Parser<'_>, m: Option) -> CompletedMarker { - assert!(p.at(T![for])); - let m = m.unwrap_or_else(|| p.start()); - p.bump(T![for]); - p.expect(IDENT); - p.expect(T![in]); - expr_no_struct(p); - block_expr(p); - m.complete(p, FOR_STMT) -} - pub(crate) fn try_block_expr(p: &mut Parser<'_>) { if !p.at(T!['{']) { p.error("expected a block"); diff --git a/crates/oq3_parser/src/grammar/items.rs b/crates/oq3_parser/src/grammar/items.rs index cfc6327..dfc74d8 100644 --- a/crates/oq3_parser/src/grammar/items.rs +++ b/crates/oq3_parser/src/grammar/items.rs @@ -85,6 +85,7 @@ pub(super) fn opt_item(p: &mut Parser<'_>, m: Marker) -> Result<(), Marker> { T![end] => end_(p, m), T![if] => if_stmt(p, m), T![while] => while_stmt(p, m), + T![for] => for_stmt(p, m), T![def] => def_(p, m), T![defcal] => defcal_(p, m), T![cal] => cal_(p, m), @@ -152,6 +153,33 @@ fn while_stmt(p: &mut Parser<'_>, m: Marker) { m.complete(p, WHILE_STMT); } +fn for_stmt(p: &mut Parser<'_>, m: Marker) { + assert!(p.at(T![for])); + p.bump(T![for]); + // Type specifier of the loop variable. + expressions::type_spec(p); + // The loop variable. + name(p); + p.expect(T![in]); + // The "iterator" + let m1 = p.start(); + if p.at(T!['{']) { + expressions::set_expression(p); + } else if p.at(T!['[']) { + expressions::range_expr(p); + } else { + expressions::expr(p); + } + m1.complete(p, FOR_ITERABLE); + // The body of the for loop + if p.at(T!['{']) { + expressions::block_expr(p); + } else { + expressions::stmt(p, expressions::Semicolon::Required); + } + m.complete(p, FOR_STMT); +} + // Called from atom::atom_expr // FIXME: return CompletedMarker because this is called from `atom_expr` // Where functions are called and what they return should be made more uniform. diff --git a/crates/oq3_parser/src/grammar/params.rs b/crates/oq3_parser/src/grammar/params.rs index 015fcad..8bd79d9 100644 --- a/crates/oq3_parser/src/grammar/params.rs +++ b/crates/oq3_parser/src/grammar/params.rs @@ -48,10 +48,6 @@ pub(super) fn param_list_defcal_qubits(p: &mut Parser<'_>) { _param_list_openqasm(p, DefFlavor::DefCalQubits, None); } -// pub(super) fn set_expression(p: &mut Parser<'_>) { -// _param_list_openqasm(p, DefFlavor::SetExpression, None); -// } - pub(super) fn expression_list(p: &mut Parser<'_>) { _param_list_openqasm(p, DefFlavor::ExpressionList, None); } @@ -69,7 +65,6 @@ enum DefFlavor { DefParams, // parens, type DefCalParams, // parens, opt type DefCalQubits, // no parens, no type, '{' or '->' terminates - // SetExpression, ExpressionList, } @@ -81,7 +76,6 @@ fn _param_list_openqasm(p: &mut Parser<'_>, flavor: DefFlavor, m: Option let want_parens = matches!(flavor, GateParams | DefParams | DefCalParams); match flavor { GateParams | DefParams | DefCalParams => p.bump(T!['(']), - // SetExpression => p.bump(T!['{']), _ => (), } // FIXME: find implementation that does not require [T![')'], T![')']] @@ -97,7 +91,6 @@ fn _param_list_openqasm(p: &mut Parser<'_>, flavor: DefFlavor, m: Option GateQubits => [T!['{'], T!['{']], GateCallQubits => [SEMICOLON, SEMICOLON], DefCalQubits => [T!['{'], T![->]], - // SetExpression => [T!['}'], T!['}']], }; let mut param_marker = m; // let mut param_marker = None; @@ -119,6 +112,7 @@ fn _param_list_openqasm(p: &mut Parser<'_>, flavor: DefFlavor, m: Option } GateCallQubits => arg_gate_call_qubit(p, m), DefParams | DefCalParams => param_typed(p, m), + // The following is pretty ugly. Probably inefficient as well _ => param_untyped(p, m), }; if !found_param { @@ -158,11 +152,7 @@ fn _param_list_openqasm(p: &mut Parser<'_>, flavor: DefFlavor, m: Option if want_parens { p.expect(T![')']); } - // if matches!(flavor, SetExpression) { - // p.expect(T!['}']); - // } let kind = match flavor { - // SetExpression => SET_EXPRESSION, GateQubits => PARAM_LIST, DefCalQubits => QUBIT_LIST, GateCallQubits => QUBIT_LIST, @@ -241,7 +231,7 @@ pub(crate) fn arg_gate_call_qubit(p: &mut Parser<'_>, m: Marker) -> bool { } if !p.at(IDENT) { - p.error("Expected parameter name"); + p.error("Expected name in qubit argument"); m.abandon(p); return false; } diff --git a/crates/oq3_parser/src/syntax_kind/syntax_kind_enum.rs b/crates/oq3_parser/src/syntax_kind/syntax_kind_enum.rs index 9cf6d9e..abe2e7c 100644 --- a/crates/oq3_parser/src/syntax_kind/syntax_kind_enum.rs +++ b/crates/oq3_parser/src/syntax_kind/syntax_kind_enum.rs @@ -151,6 +151,7 @@ pub enum SyntaxKind { IF_STMT, WHILE_STMT, FOR_STMT, + FOR_ITERABLE, END_STMT, CONTINUE_STMT, BREAK_STMT, diff --git a/crates/oq3_semantics/src/asg.rs b/crates/oq3_semantics/src/asg.rs index b1ca7d1..b794269 100644 --- a/crates/oq3_semantics/src/asg.rs +++ b/crates/oq3_semantics/src/asg.rs @@ -126,10 +126,6 @@ pub enum Expr { IndexExpression(IndexExpression), IndexedIdentifier(IndexedIdentifier), GateOperand(GateOperand), - // FIXME: for Range and similiar, it is easiest to shove everything in expression tree and give it a useless type. - // 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), Call, // stub function (def) call Set, // stub @@ -185,8 +181,8 @@ pub enum Stmt { Delay, // stub End, ExprStmt(TExpr), - Extern, // stub - For, // stub + Extern, // stub + ForStmt(ForStmt), // stub GPhaseCall(GPhaseCall), GateCall(GateCall), // A statement because a gate call does not return anything GateDeclaration(GateDeclaration), @@ -400,7 +396,7 @@ pub enum LValue { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum ArraySliceIndex { Expr(TExpr), - Range(TExpr), + RangeExpression(RangeExpression), } // FIXME: Is this the slice, or just the index ??? @@ -408,7 +404,7 @@ pub enum ArraySliceIndex { #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub enum RegisterSlice { Expr(TExpr), - Range(Range), + RangeExpression(RangeExpression), } // example: v[3:4]. Includes multidimensional index @@ -1087,16 +1083,21 @@ impl Identifier { } } +// RangeExpression has been moved in and out of the expression tree. It is now out. +// Advantages: 1) I think it's maybe just as easy or easier to move RangeExpression out of the tree. 2) Increases correctness. +// Disadvantage: No type. Having a type associated with RangeExpression will probably be useful even if it doesn't mesh perfectly +// with the larger type system. We can probably do type checking compating to the declared iterator. +// So implement this out of the tree and implement type information later if needed. #[derive(Clone, Debug, PartialEq, Eq, Hash)] -pub struct Range { +pub struct RangeExpression { start: Box, step: Box>, stop: Box, } -impl Range { - pub fn new(start: TExpr, step: Option, stop: TExpr) -> Range { - Range { +impl RangeExpression { + pub fn new(start: TExpr, step: Option, stop: TExpr) -> RangeExpression { + RangeExpression { start: Box::new(start), step: Box::new(step), stop: Box::new(stop), @@ -1114,10 +1115,6 @@ impl Range { pub fn stop(&self) -> &TExpr { &self.stop } - - pub fn to_texpr(self, typ: Type) -> TExpr { - TExpr::new(Expr::Range(self), typ) - } } #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -1180,6 +1177,46 @@ impl While { } } +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub enum ForIterable { + SetExpression(SetExpression), + RangeExpression(RangeExpression), + Expr(TExpr), +} + +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct ForStmt { + loop_var: SymbolIdResult, + iterable: ForIterable, + loop_body: Block, +} + +impl ForStmt { + pub fn new(loop_var: SymbolIdResult, iterable: ForIterable, loop_body: Block) -> ForStmt { + ForStmt { + loop_var, + iterable, + loop_body, + } + } + + pub fn loop_var(&self) -> &SymbolIdResult { + &self.loop_var + } + + pub fn iterable(&self) -> &ForIterable { + &self.iterable + } + + pub fn loop_body(&self) -> &Block { + &self.loop_body + } + + pub fn to_stmt(self) -> Stmt { + Stmt::ForStmt(self) + } +} + #[derive(Clone, Debug, PartialEq, Eq, Hash)] pub struct SwitchCaseStmt { control: TExpr, diff --git a/crates/oq3_semantics/src/syntax_to_semantics.rs b/crates/oq3_semantics/src/syntax_to_semantics.rs index 63f19ab..bb060cc 100644 --- a/crates/oq3_semantics/src/syntax_to_semantics.rs +++ b/crates/oq3_semantics/src/syntax_to_semantics.rs @@ -213,6 +213,28 @@ fn from_stmt(stmt: synast::Stmt, context: &mut Context) -> Option { Some(asg::While::new(condition.unwrap(), loop_body).to_stmt()) } + synast::Stmt::ForStmt(for_stmt) => { + let loop_var = for_stmt.loop_var().unwrap(); + let ty = from_scalar_type(&for_stmt.scalar_type().unwrap(), false, context); + let iterable_ast = for_stmt.for_iterable().unwrap(); + let iterable = if let Some(set_expression) = iterable_ast.set_expression() { + asg::ForIterable::SetExpression(from_set_expression(set_expression, context)) + } else if let Some(range_expression) = iterable_ast.range_expr() { + asg::ForIterable::RangeExpression(from_range_expression(range_expression, context)) + } else if let Some(expression) = iterable_ast.for_iterable_expr() { + asg::ForIterable::Expr(from_expr(Some(expression), context).unwrap()) + } else { + // It would be nice to use an enum on the other side. + // This error should be caught before semantic analysis. Eg in validation of the AST + unreachable!() // probably is reachable + }; + with_scope!(context, ScopeType::Local, + let loop_var_symbol_id = context.new_binding(loop_var.string().as_ref(), &ty, &loop_var); + let loop_body = from_block_expr(for_stmt.body().unwrap(), context); + ); + Some(asg::ForStmt::new(loop_var_symbol_id, iterable, loop_body).to_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) => { @@ -493,12 +515,8 @@ fn from_expr(expr_maybe: Option, context: &mut Context) -> Option< synast::Expr::HardwareQubit(hwq) => Some(ast_hardware_qubit(&hwq).to_texpr()), synast::Expr::RangeExpr(range_expr) => { - let (start, step, stop) = range_expr.start_step_stop(); - 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)) + context.insert_error(IncompatibleTypesError, &range_expr); + None } synast::Expr::IndexExpr(index_expr) => { @@ -534,6 +552,27 @@ fn from_expr(expr_maybe: Option, context: &mut Context) -> Option< } } +fn from_set_expression( + set_expression: synast::SetExpression, + context: &mut Context, +) -> asg::SetExpression { + asg::SetExpression::new(inner_expression_list( + set_expression.expression_list().unwrap(), + context, + )) +} + +fn from_range_expression( + range_expr: synast::RangeExpr, + context: &mut Context, +) -> asg::RangeExpression { + let (start, step, stop) = range_expr.start_step_stop(); + let start = from_expr(start, context).unwrap(); + let stop = from_expr(stop, context).unwrap(); + let step = from_expr(step, context); + asg::RangeExpression::new(start, step, stop) +} + fn from_gate_call_expr( gate_call_expr: synast::GateCallExpr, modifiers: Vec, @@ -634,16 +673,6 @@ fn from_index_operator( } } -fn from_set_expression( - set_expression: synast::SetExpression, - context: &mut Context, -) -> asg::SetExpression { - asg::SetExpression::new(inner_expression_list( - set_expression.expression_list().unwrap(), - context, - )) -} - fn from_expression_list( expression_list: synast::ExpressionList, context: &mut Context, @@ -730,13 +759,12 @@ fn from_block_expr(block_synast: synast::BlockExpr, context: &mut Context) -> as asg::Block::new(statement_list_from_block(block_synast, context)) } -fn from_classical_declaration_statement( - type_decl: &synast::ClassicalDeclarationStatement, +// Convert AST scalar type to a `types::Type` +fn from_scalar_type( + scalar_type: &synast::ScalarType, + isconst: bool, context: &mut Context, -) -> asg::Stmt { - let scalar_type = type_decl.scalar_type().unwrap(); - let isconst = type_decl.const_token().is_some(); - +) -> Type { // We only support literal integer designators at the moment. let width = match scalar_type.designator().and_then(|desg| desg.expr()) { Some(synast::Expr::Literal(ref literal)) => { @@ -753,8 +781,7 @@ fn from_classical_declaration_statement( Some(expr) => panic!("Unsupported designator type: {:?}", type_name_of(expr)), None => None, }; - - let typ = match scalar_type.kind() { + match scalar_type.kind() { synast::ScalarTypeKind::Int => Type::Int(width, isconst.into()), synast::ScalarTypeKind::Float => Type::Float(width, isconst.into()), synast::ScalarTypeKind::Angle => Type::Angle(width, isconst.into()), @@ -764,7 +791,15 @@ fn from_classical_declaration_statement( }, synast::ScalarTypeKind::Bool => Type::Bool(isconst.into()), _ => todo!(), - }; + } +} + +fn from_classical_declaration_statement( + type_decl: &synast::ClassicalDeclarationStatement, + context: &mut Context, +) -> asg::Stmt { + let scalar_type = type_decl.scalar_type().unwrap(); + let typ = from_scalar_type(&scalar_type, type_decl.const_token().is_some(), context); let name_str = type_decl.name().unwrap().string(); let initializer = from_expr(type_decl.expr(), context); diff --git a/crates/oq3_syntax/openqasm3.ungram b/crates/oq3_syntax/openqasm3.ungram index baa008d..5462af6 100644 --- a/crates/oq3_syntax/openqasm3.ungram +++ b/crates/oq3_syntax/openqasm3.ungram @@ -306,9 +306,18 @@ IfStmt = ('else' else_branch:Expr)? // label 'iterable' is handled manually in node_ext.rs +// ForStmt = +// 'for' loop_var:Name 'in' iterable:Expr +// loop_body:Expr + ForStmt = - 'for' loop_var:Name 'in' iterable:Expr - loop_body:Expr + 'for' ScalarType loop_var:Name 'in' ForIterable + body:BlockExpr | stmt:Stmt + +// Recall, the label prevents code generation from implementing this alternation +// as an `enum`. Leaving out the label would cause an error during codegen. +ForIterable = + SetExpression | RangeExpr | for_iterable_expr:Expr WhileStmt = 'while' '(' condition:Expr ')' diff --git a/crates/oq3_syntax/src/ast/generated/nodes.rs b/crates/oq3_syntax/src/ast/generated/nodes.rs index ea3ad5b..f9ad22a 100644 --- a/crates/oq3_syntax/src/ast/generated/nodes.rs +++ b/crates/oq3_syntax/src/ast/generated/nodes.rs @@ -232,13 +232,22 @@ impl ForStmt { pub fn for_token(&self) -> Option { support::token(&self.syntax, T![for]) } + pub fn scalar_type(&self) -> Option { + support::child(&self.syntax) + } pub fn loop_var(&self) -> Option { support::child(&self.syntax) } pub fn in_token(&self) -> Option { support::token(&self.syntax, T![in]) } - pub fn loop_body(&self) -> Option { + pub fn for_iterable(&self) -> Option { + support::child(&self.syntax) + } + pub fn body(&self) -> Option { + support::child(&self.syntax) + } + pub fn stmt(&self) -> Option { support::child(&self.syntax) } } @@ -935,6 +944,36 @@ impl NegCtrlModifier { } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct ForIterable { + pub(crate) syntax: SyntaxNode, +} +impl ForIterable { + pub fn set_expression(&self) -> Option { + support::child(&self.syntax) + } + pub fn range_expr(&self) -> Option { + support::child(&self.syntax) + } + pub fn for_iterable_expr(&self) -> Option { + support::child(&self.syntax) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct SetExpression { + pub(crate) syntax: SyntaxNode, +} +impl SetExpression { + pub fn l_curly_token(&self) -> Option { + support::token(&self.syntax, T!['{']) + } + pub fn expression_list(&self) -> Option { + support::child(&self.syntax) + } + pub fn r_curly_token(&self) -> Option { + support::token(&self.syntax, T!['}']) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Designator { pub(crate) syntax: SyntaxNode, } @@ -962,21 +1001,6 @@ impl QubitType { } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct SetExpression { - pub(crate) syntax: SyntaxNode, -} -impl SetExpression { - pub fn l_curly_token(&self) -> Option { - support::token(&self.syntax, T!['{']) - } - pub fn expression_list(&self) -> Option { - support::child(&self.syntax) - } - pub fn r_curly_token(&self) -> Option { - support::token(&self.syntax, T!['}']) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct IODeclarationStatement { pub(crate) syntax: SyntaxNode, } @@ -2040,9 +2064,9 @@ impl AstNode for NegCtrlModifier { &self.syntax } } -impl AstNode for Designator { +impl AstNode for ForIterable { fn can_cast(kind: SyntaxKind) -> bool { - kind == DESIGNATOR + kind == FOR_ITERABLE } fn cast(syntax: SyntaxNode) -> Option { if Self::can_cast(syntax.kind()) { @@ -2055,9 +2079,9 @@ impl AstNode for Designator { &self.syntax } } -impl AstNode for QubitType { +impl AstNode for SetExpression { fn can_cast(kind: SyntaxKind) -> bool { - kind == QUBIT_TYPE + kind == SET_EXPRESSION } fn cast(syntax: SyntaxNode) -> Option { if Self::can_cast(syntax.kind()) { @@ -2070,9 +2094,24 @@ impl AstNode for QubitType { &self.syntax } } -impl AstNode for SetExpression { +impl AstNode for Designator { fn can_cast(kind: SyntaxKind) -> bool { - kind == SET_EXPRESSION + kind == DESIGNATOR + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } +} +impl AstNode for QubitType { + fn can_cast(kind: SyntaxKind) -> bool { + kind == QUBIT_TYPE } fn cast(syntax: SyntaxNode) -> Option { if Self::can_cast(syntax.kind()) { @@ -3014,17 +3053,22 @@ impl std::fmt::Display for NegCtrlModifier { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for Designator { +impl std::fmt::Display for ForIterable { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for QubitType { +impl std::fmt::Display for SetExpression { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } } -impl std::fmt::Display for SetExpression { +impl std::fmt::Display for Designator { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} +impl std::fmt::Display for QubitType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) } diff --git a/crates/oq3_syntax/src/ast/node_ext.rs b/crates/oq3_syntax/src/ast/node_ext.rs index c5c4431..1ed7e0d 100644 --- a/crates/oq3_syntax/src/ast/node_ext.rs +++ b/crates/oq3_syntax/src/ast/node_ext.rs @@ -63,18 +63,40 @@ fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> { } } -impl ast::ForStmt { - pub fn iterable(&self) -> Option { - // If the iterable is a BlockExpr, check if the body is missing. - // If it is assume the iterable is the expression that is missing instead. - let mut exprs = support::children(self.syntax()); - let first = exprs.next(); - match first { - Some(ast::Expr::BlockExpr(_)) => exprs.next().and(first), - first => first, - } - } -} +// TODO: Implementing something like this would be useful. +// Determining which kind of for iterable we have is done in semantic +// analysis by querying via methods on ast::ForIterable +// We could construct an enum here and expose it to consumers. +// #[derive(Clone, Debug, PartialEq, Eq, Hash)] +// pub enum ForIterable { +// SetExpression(ast::SetExpression), +// RangeExpression(ast::RangeExpr), +// Expr(ast::Expr), +// } + +// This was carried over from rust. It seems we do not need this any +// longer for disambiguation. +// impl ast::ForStmt { +// // pub fn iterable(&self) -> Option { +// pub fn iterable(&self) -> Option { +// // If the iterable is a BlockExpr, check if the body is missing. +// // If it is, assume the iterable is the expression that is missing instead. +// // let token = self.token(); +// // if let Some(t) = ast::SetExpression::cast(token.clone()) { +// // return ForIterable::SetExpression(t); +// // } +// // if let Some(t) = ast::RangeExpr::cast(token.clone()) { +// // return ForIterable::RangeExpression(t); +// // } +// // None +// let mut exprs = support::children(self.syntax()); +// let first = exprs.next(); +// match first { +// Some(ast::Expr::BlockExpr(_)) => exprs.next().and(first), +// first => first, +// } +// } +// } impl ast::HasLoopBody for ast::ForStmt { fn loop_body(&self) -> Option { diff --git a/crates/oq3_syntax/src/tests/ast_src.rs b/crates/oq3_syntax/src/tests/ast_src.rs index 6244003..9ec44da 100644 --- a/crates/oq3_syntax/src/tests/ast_src.rs +++ b/crates/oq3_syntax/src/tests/ast_src.rs @@ -154,6 +154,7 @@ pub(crate) const KINDS_SRC: KindsSrc<'_> = KindsSrc { "IF_STMT", "WHILE_STMT", "FOR_STMT", + "FOR_ITERABLE", "END_STMT", "CONTINUE_STMT", "BREAK_STMT",