1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Copyright contributors to the openqasm-parser project
// SPDX-License-Identifier: Apache-2.0

//! Various extension methods to ast Nodes, which are hard to code-generate.
//! Extensions for various expressions live in a sibling `expr_extensions` module.
//!
//! These methods should only do simple, shallow tasks related to the syntax of the node itself.

use rowan::{GreenNodeData, GreenTokenData};
use std::borrow::Cow;

use crate::{
    ast::{self, support, AstNode, SyntaxNode},
    NodeOrToken, TokenText,
};

// This function, `text` is borrowed from r-a (may still be commented out below.)
// There is another method, also called `text` implemented for `SyntaxNode`.
// The present function differs in that it returns the text of the first token
// rather than the text of all tokens in the subtree.
//
// This trait needs a better name.
// Several AstNodes have a single token that is a name.
// This includes, but is not limited to, `Name`.
pub trait HasTextName: AstNode {
    fn text(&self) -> TokenText<'_> {
        text_of_first_token(self.syntax())
    }

    // FIXME: is this ok? The name of the function does not seem idiomatic
    // Nor is returning an owned string. but in practice, at the moment, we always,
    // convert text immediately to a `String`.
    fn string(&self) -> String {
        self.text().to_string()
    }
}

impl HasTextName for ast::Param {}
impl HasTextName for ast::Name {}
impl HasTextName for ast::HardwareQubit {}
impl HasTextName for ast::Identifier {}

// This was in the r-a code
// // You didn't really want the name-name, did you?
// impl ast::Name {
//     pub fn text(&self) -> TokenText<'_> {
//         text_of_first_token(self.syntax())
//     }
// }

fn text_of_first_token(node: &SyntaxNode) -> TokenText<'_> {
    fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
        green_ref
            .children()
            .next()
            .and_then(NodeOrToken::into_token)
            .unwrap()
    }

    match node.green() {
        Cow::Borrowed(green_ref) => TokenText::borrowed(first_token(green_ref).text()),
        Cow::Owned(green) => TokenText::owned(first_token(&green).to_owned()),
    }
}

// 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<ForIterable> {
//     pub fn iterable(&self) -> Option<ast::Expr> {
//         // 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::AssignmentStmt {
    pub fn identifier(&self) -> Option<ast::Identifier> {
        support::child(&self.syntax)
    }
}

impl ast::HasLoopBody for ast::ForStmt {
    fn loop_body(&self) -> Option<ast::BlockExpr> {
        let mut exprs = support::children(self.syntax());
        let first = exprs.next();
        let second = exprs.next();
        second.or(first)
    }
}

impl ast::WhileStmt {
    pub fn condition(&self) -> Option<ast::Expr> {
        // If the condition is a BlockExpr, check if the body is missing.
        // If it is assume the condition 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,
        }
    }

    // FIXME: need to support both single statement and block.
    // Or, can we / should we collapse the distinction already at this level?
    pub fn body(&self) -> Option<ast::BlockExpr> {
        let mut exprs = support::children(self.syntax());
        exprs.next()
    }
}

// FIXME: This is wrong. Use `body` above for now. But must sort this out.
impl ast::HasLoopBody for ast::WhileStmt {
    fn loop_body(&self) -> Option<ast::BlockExpr> {
        let mut exprs = support::children(self.syntax());
        let first = exprs.next();
        let second = exprs.next();
        second.or(first)
    }
}

impl ast::PragmaStatement {
    fn text(&self) -> TokenText<'_> {
        text_of_first_token(self.syntax())
    }

    // return the pragma line omitting the word "pragma"
    pub fn pragma_text(&self) -> String {
        let text = self.text();
        if text.starts_with('#') {
            text[7..].to_string()
        } else {
            text[6..].to_string()
        }
    }
}

impl ast::AnnotationStatement {
    fn text(&self) -> TokenText<'_> {
        text_of_first_token(self.syntax())
    }

    pub fn annotation_text(&self) -> String {
        self.text().to_string()
    }
}