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}