oq3_syntax/ast/
node_ext.rs

1// Copyright contributors to the openqasm-parser project
2// SPDX-License-Identifier: Apache-2.0
3
4//! Various extension methods to ast Nodes, which are hard to code-generate.
5//! Extensions for various expressions live in a sibling `expr_extensions` module.
6//!
7//! These methods should only do simple, shallow tasks related to the syntax of the node itself.
8
9use rowan::{GreenNodeData, GreenTokenData};
10use std::borrow::Cow;
11
12use crate::{
13    ast::{self, support, AstNode, SyntaxNode},
14    NodeOrToken, TokenText,
15};
16
17// This function, `text` is borrowed from r-a (may still be commented out below.)
18// There is another method, also called `text` implemented for `SyntaxNode`.
19// The present function differs in that it returns the text of the first token
20// rather than the text of all tokens in the subtree.
21//
22// This trait needs a better name.
23// Several AstNodes have a single token that is a name.
24// This includes, but is not limited to, `Name`.
25pub trait HasTextName: AstNode {
26    fn text(&self) -> TokenText<'_> {
27        text_of_first_token(self.syntax())
28    }
29
30    // FIXME: is this ok? The name of the function does not seem idiomatic
31    // Nor is returning an owned string. but in practice, at the moment, we always,
32    // convert text immediately to a `String`.
33    fn string(&self) -> String {
34        self.text().to_string()
35    }
36}
37
38impl HasTextName for ast::Param {}
39impl HasTextName for ast::Name {}
40impl HasTextName for ast::HardwareQubit {}
41impl HasTextName for ast::Identifier {}
42
43// This was in the r-a code
44// // You didn't really want the name-name, did you?
45// impl ast::Name {
46//     pub fn text(&self) -> TokenText<'_> {
47//         text_of_first_token(self.syntax())
48//     }
49// }
50
51fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
52    fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
53        green_ref
54            .children()
55            .next()
56            .and_then(NodeOrToken::into_token)
57            .unwrap()
58    }
59
60    match node.green() {
61        Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()),
62        Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()),
63    }
64}
65
66// TODO: Implementing something like this would be useful.
67// Determining which kind of for iterable we have is done in semantic
68// analysis by querying via methods on ast::ForIterable
69// We could construct an enum here and expose it to consumers.
70// #[derive(Clone, Debug, PartialEq, Eq, Hash)]
71// pub enum ForIterable {
72//     SetExpression(ast::SetExpression),
73//     RangeExpression(ast::RangeExpr),
74//     Expr(ast::Expr),
75// }
76
77// This was carried over from rust. It seems we do not need this any
78// longer for disambiguation.
79// impl ast::ForStmt {
80// //    pub fn iterable(&self) -> Option<ForIterable> {
81//     pub fn iterable(&self) -> Option<ast::Expr> {
82//         // If the iterable is a BlockExpr, check if the body is missing.
83//         // If it is, assume the iterable is the expression that is missing instead.
84//         // let token = self.token();
85//         // if let Some(t) = ast::SetExpression::cast(token.clone()) {
86//         //     return ForIterable::SetExpression(t);
87//         // }
88//         // if let Some(t) = ast::RangeExpr::cast(token.clone()) {
89//         //     return ForIterable::RangeExpression(t);
90//         // }
91//         // None
92//         let mut exprs = support::children(self.syntax());
93//         let first = exprs.next();
94//         match first {
95//             Some(ast::Expr::BlockExpr(_)) => exprs.next().and(first),
96//             first => first,
97//         }
98//     }
99// }
100
101impl ast::AssignmentStmt {
102    pub fn identifier(&self) -> Option<ast::Identifier> {
103        support::child(&self.syntax)
104    }
105}
106
107impl ast::HasLoopBody for ast::ForStmt {
108    fn loop_body(&self) -> Option<ast::BlockExpr> {
109        let mut exprs = support::children(self.syntax());
110        let first = exprs.next();
111        let second = exprs.next();
112        second.or(first)
113    }
114}
115
116impl ast::WhileStmt {
117    pub fn condition(&self) -> Option<ast::Expr> {
118        // If the condition is a BlockExpr, check if the body is missing.
119        // If it is assume the condition is the expression that is missing instead.
120        let mut exprs = support::children(self.syntax());
121        let first = exprs.next();
122        match first {
123            Some(ast::Expr::BlockExpr(_)) => exprs.next().and(first),
124            first => first,
125        }
126    }
127
128    // FIXME: need to support both single statement and block.
129    // Or, can we / should we collapse the distinction already at this level?
130    pub fn body(&self) -> Option<ast::BlockExpr> {
131        let mut exprs = support::children(self.syntax());
132        exprs.next()
133    }
134}
135
136// FIXME: This is wrong. Use `body` above for now. But must sort this out.
137impl ast::HasLoopBody for ast::WhileStmt {
138    fn loop_body(&self) -> Option<ast::BlockExpr> {
139        let mut exprs = support::children(self.syntax());
140        let first = exprs.next();
141        let second = exprs.next();
142        second.or(first)
143    }
144}
145
146impl ast::PragmaStatement {
147    fn text(&self) -> TokenText<'_> {
148        text_of_first_token(self.syntax())
149    }
150
151    // return the pragma line omitting the word "pragma"
152    pub fn pragma_text(&self) -> String {
153        let text = self.text();
154        if text.starts_with('#') {
155            text[7..].to_string()
156        } else {
157            text[6..].to_string()
158        }
159    }
160}
161
162impl ast::AnnotationStatement {
163    fn text(&self) -> TokenText<'_> {
164        text_of_first_token(self.syntax())
165    }
166
167    pub fn annotation_text(&self) -> String {
168        self.text().to_string()
169    }
170}