microsoft/qdk

Public

mirrored fromhttps://github.com/microsoft/qdkAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
v1.0.33

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

compiler/qsc_parse/src/item.rs

423lines · modecode

1// Copyright (c) Microsoft Corporation.
2// Licensed under the MIT License.
3
4#[cfg(test)]
5mod tests;
6
7use super::{
8 expr::expr,
9 keyword::Keyword,
10 prim::{dot_ident, ident, many, opt, pat, seq, token},
11 scan::Scanner,
12 stmt,
13 ty::{self, ty},
14 Error, Result,
15};
16use crate::{
17 lex::{Delim, TokenKind},
18 prim::{barrier, recovering, recovering_token, shorten},
19 stmt::check_semis,
20 ty::array_or_arrow,
21 ErrorKind,
22};
23use qsc_ast::ast::{
24 Attr, Block, CallableBody, CallableDecl, CallableKind, Ident, Item, ItemKind, Namespace,
25 NodeId, Pat, PatKind, Path, Spec, SpecBody, SpecDecl, SpecGen, StmtKind, TopLevelNode, Ty,
26 TyDef, TyDefKind, TyKind, Visibility, VisibilityKind,
27};
28use qsc_data_structures::span::Span;
29
30pub(super) fn parse(s: &mut Scanner) -> Result<Box<Item>> {
31 let lo = s.peek().span.lo;
32 let doc = parse_doc(s);
33 let attrs = many(s, parse_attr)?;
34 let visibility = opt(s, parse_visibility)?;
35 let kind = if let Some(open) = opt(s, parse_open)? {
36 open
37 } else if let Some(ty) = opt(s, parse_newtype)? {
38 ty
39 } else if let Some(callable) = opt(s, parse_callable_decl)? {
40 Box::new(ItemKind::Callable(callable))
41 } else if visibility.is_some() {
42 let err_item = default(s.span(lo));
43 s.push_error(Error(ErrorKind::FloatingVisibility(err_item.span)));
44 return Ok(err_item);
45 } else if !attrs.is_empty() {
46 let err_item = default(s.span(lo));
47 s.push_error(Error(ErrorKind::FloatingAttr(err_item.span)));
48 return Ok(err_item);
49 } else if doc.is_some() {
50 let err_item = default(s.span(lo));
51 s.push_error(Error(ErrorKind::FloatingDocComment(err_item.span)));
52 return Ok(err_item);
53 } else {
54 let p = s.peek();
55 return Err(Error(ErrorKind::Rule("item", p.kind, p.span)));
56 };
57
58 Ok(Box::new(Item {
59 id: NodeId::default(),
60 span: s.span(lo),
61 doc: doc.unwrap_or_default().into(),
62 attrs: attrs.into_boxed_slice(),
63 visibility,
64 kind,
65 }))
66}
67
68#[allow(clippy::vec_box)]
69fn parse_many(s: &mut Scanner) -> Result<Vec<Box<Item>>> {
70 const BARRIER_TOKENS: &[TokenKind] = &[
71 TokenKind::At,
72 TokenKind::Keyword(Keyword::Internal),
73 TokenKind::Keyword(Keyword::Open),
74 TokenKind::Keyword(Keyword::Newtype),
75 TokenKind::Keyword(Keyword::Operation),
76 TokenKind::Keyword(Keyword::Function),
77 ];
78
79 const RECOVERY_TOKENS: &[TokenKind] = &[TokenKind::Semi, TokenKind::Close(Delim::Brace)];
80
81 barrier(s, BARRIER_TOKENS, |s| {
82 many(s, |s| recovering(s, default, RECOVERY_TOKENS, parse))
83 })
84}
85
86fn default(span: Span) -> Box<Item> {
87 Box::new(Item {
88 id: NodeId::default(),
89 span,
90 doc: "".into(),
91 attrs: Vec::new().into_boxed_slice(),
92 visibility: None,
93 kind: Box::new(ItemKind::Err),
94 })
95}
96
97pub(super) fn parse_namespaces(s: &mut Scanner) -> Result<Vec<Namespace>> {
98 let namespaces = many(s, parse_namespace)?;
99 recovering_token(s, TokenKind::Eof)?;
100 Ok(namespaces)
101}
102
103pub(super) fn parse_top_level_nodes(s: &mut Scanner) -> Result<Vec<TopLevelNode>> {
104 let nodes = many(s, parse_top_level_node)?;
105 recovering_token(s, TokenKind::Eof)?;
106 Ok(nodes)
107}
108
109fn parse_top_level_node(s: &mut Scanner) -> Result<TopLevelNode> {
110 // Here we parse any doc comments ahead of calling `parse_namespace` or `stmt::parse` in order
111 // to avoid problems with error reporting. Specifically, if `parse_namespace` consumes the
112 // doc comment and then fails to find a namespace, that becomes an unrecoverable error even with
113 // opt. This pattern can be dropped along with namespaces once we have a module-based design.
114 let doc = parse_doc(s).unwrap_or_default();
115 if let Some(mut namespace) = opt(s, parse_namespace)? {
116 namespace.doc = doc.into();
117 Ok(TopLevelNode::Namespace(namespace))
118 } else {
119 let kind = s.peek().kind;
120 let span = s.peek().span;
121 let mut stmt = stmt::parse(s)?;
122 if let StmtKind::Item(item) = &mut *stmt.kind {
123 item.doc = doc.into();
124 } else if !doc.is_empty() {
125 return Err(Error(ErrorKind::Rule("item", kind, span)));
126 }
127 Ok(TopLevelNode::Stmt(stmt))
128 }
129}
130
131fn parse_namespace(s: &mut Scanner) -> Result<Namespace> {
132 let lo = s.peek().span.lo;
133 let doc = parse_doc(s).unwrap_or_default();
134 token(s, TokenKind::Keyword(Keyword::Namespace))?;
135 let name = dot_ident(s)?;
136 token(s, TokenKind::Open(Delim::Brace))?;
137 let items = barrier(s, &[TokenKind::Close(Delim::Brace)], parse_many)?;
138 recovering_token(s, TokenKind::Close(Delim::Brace))?;
139 Ok(Namespace {
140 id: NodeId::default(),
141 span: s.span(lo),
142 doc: doc.into(),
143 name,
144 items: items.into_boxed_slice(),
145 })
146}
147
148/// See https://github.com/microsoft/qsharp/issues/941 for context.
149/// We want to anticipate docstrings in places people might
150/// put them, but throw them away. This is to maintain
151/// back compatibility.
152/// Eventually, when we support doc comments in more places,
153/// or support warnings, we can use this function to determine
154/// places that need to be updated. This function can then emit
155/// a warning.
156pub(super) fn throw_away_doc(s: &mut Scanner) {
157 let _ = parse_doc(s);
158}
159
160fn parse_doc(s: &mut Scanner) -> Option<String> {
161 let mut content = String::new();
162 while s.peek().kind == TokenKind::DocComment {
163 if !content.is_empty() {
164 content += "\n";
165 }
166
167 let lexeme = s.read();
168 let prefix_len = if lexeme.starts_with("/// ") { 4 } else { 3 };
169 content += shorten(prefix_len, 0, lexeme);
170 s.advance();
171 }
172
173 (!content.is_empty()).then_some(content)
174}
175
176fn parse_attr(s: &mut Scanner) -> Result<Box<Attr>> {
177 let lo = s.peek().span.lo;
178 token(s, TokenKind::At)?;
179 let name = ident(s)?;
180 let arg = expr(s)?;
181 Ok(Box::new(Attr {
182 id: NodeId::default(),
183 span: s.span(lo),
184 name,
185 arg,
186 }))
187}
188
189fn parse_visibility(s: &mut Scanner) -> Result<Visibility> {
190 let lo = s.peek().span.lo;
191 token(s, TokenKind::Keyword(Keyword::Internal))?;
192 Ok(Visibility {
193 id: NodeId::default(),
194 span: s.span(lo),
195 kind: VisibilityKind::Internal,
196 })
197}
198
199fn parse_open(s: &mut Scanner) -> Result<Box<ItemKind>> {
200 token(s, TokenKind::Keyword(Keyword::Open))?;
201 let name = dot_ident(s)?;
202 let alias = if token(s, TokenKind::Keyword(Keyword::As)).is_ok() {
203 Some(dot_ident(s)?)
204 } else {
205 None
206 };
207 token(s, TokenKind::Semi)?;
208 Ok(Box::new(ItemKind::Open(name, alias)))
209}
210
211fn parse_newtype(s: &mut Scanner) -> Result<Box<ItemKind>> {
212 token(s, TokenKind::Keyword(Keyword::Newtype))?;
213 let name = ident(s)?;
214 token(s, TokenKind::Eq)?;
215 let lo = s.peek().span.lo;
216 let mut def = parse_ty_def(s)?;
217 if let Some(ty) = try_tydef_as_ty(def.as_ref()) {
218 let ty = array_or_arrow(s, ty, lo)?;
219 def = Box::new(TyDef {
220 id: def.id,
221 span: ty.span,
222 kind: Box::new(TyDefKind::Field(None, Box::new(ty))),
223 });
224 }
225 token(s, TokenKind::Semi)?;
226 Ok(Box::new(ItemKind::Ty(name, def)))
227}
228
229fn try_tydef_as_ty(tydef: &TyDef) -> Option<Ty> {
230 match tydef.kind.as_ref() {
231 TyDefKind::Field(Some(_), _) => None,
232 TyDefKind::Field(None, ty) => Some(*ty.clone()),
233 TyDefKind::Paren(tydef) => try_tydef_as_ty(tydef.as_ref()),
234 TyDefKind::Tuple(tup) => {
235 let mut ty_tup = Vec::new();
236 for tydef in tup.iter() {
237 ty_tup.push(try_tydef_as_ty(tydef)?)
238 }
239 Some(Ty {
240 id: tydef.id,
241 span: tydef.span,
242 kind: Box::new(TyKind::Tuple(ty_tup.into_boxed_slice())),
243 })
244 }
245 TyDefKind::Err => None,
246 }
247}
248
249fn parse_ty_def(s: &mut Scanner) -> Result<Box<TyDef>> {
250 throw_away_doc(s);
251 let lo = s.peek().span.lo;
252 let kind = if token(s, TokenKind::Open(Delim::Paren)).is_ok() {
253 let (defs, final_sep) = seq(s, parse_ty_def)?;
254 token(s, TokenKind::Close(Delim::Paren))?;
255 final_sep.reify(defs, TyDefKind::Paren, TyDefKind::Tuple)
256 } else {
257 let field_ty = ty(s)?;
258 if token(s, TokenKind::Colon).is_ok() {
259 let name = ty_as_ident(field_ty)?;
260 let field_ty = ty(s)?;
261 TyDefKind::Field(Some(name), Box::new(field_ty))
262 } else {
263 TyDefKind::Field(None, Box::new(field_ty))
264 }
265 };
266
267 Ok(Box::new(TyDef {
268 id: NodeId::default(),
269 span: s.span(lo),
270 kind: Box::new(kind),
271 }))
272}
273
274fn ty_as_ident(ty: Ty) -> Result<Box<Ident>> {
275 let TyKind::Path(path) = *ty.kind else {
276 return Err(Error(ErrorKind::Convert("identifier", "type", ty.span)));
277 };
278 if let Path {
279 namespace: None,
280 name,
281 ..
282 } = *path
283 {
284 Ok(name)
285 } else {
286 Err(Error(ErrorKind::Convert("identifier", "type", ty.span)))
287 }
288}
289
290fn parse_callable_decl(s: &mut Scanner) -> Result<Box<CallableDecl>> {
291 let lo = s.peek().span.lo;
292 let kind = if token(s, TokenKind::Keyword(Keyword::Function)).is_ok() {
293 CallableKind::Function
294 } else if token(s, TokenKind::Keyword(Keyword::Operation)).is_ok() {
295 CallableKind::Operation
296 } else {
297 let token = s.peek();
298 return Err(Error(ErrorKind::Rule(
299 "callable declaration",
300 token.kind,
301 token.span,
302 )));
303 };
304
305 let name = ident(s)?;
306 let generics = if token(s, TokenKind::Lt).is_ok() {
307 throw_away_doc(s);
308 let params = seq(s, ty::param)?.0;
309 token(s, TokenKind::Gt)?;
310 params
311 } else {
312 Vec::new()
313 };
314
315 let input = pat(s)?;
316 check_input_parens(&input)?;
317 token(s, TokenKind::Colon)?;
318 throw_away_doc(s);
319 let output = ty(s)?;
320 let functors = if token(s, TokenKind::Keyword(Keyword::Is)).is_ok() {
321 Some(Box::new(ty::functor_expr(s)?))
322 } else {
323 None
324 };
325 throw_away_doc(s);
326 let body = parse_callable_body(s)?;
327
328 Ok(Box::new(CallableDecl {
329 id: NodeId::default(),
330 span: s.span(lo),
331 kind,
332 name,
333 generics: generics.into_boxed_slice(),
334 input,
335 output: Box::new(output),
336 functors,
337 body: Box::new(body),
338 }))
339}
340
341fn parse_callable_body(s: &mut Scanner) -> Result<CallableBody> {
342 let lo = s.peek().span.lo;
343 token(s, TokenKind::Open(Delim::Brace))?;
344 barrier(s, &[TokenKind::Close(Delim::Brace)], |s| {
345 let specs = many(s, parse_spec_decl)?;
346 if specs.is_empty() {
347 let stmts = stmt::parse_many(s)?;
348 check_semis(s, &stmts);
349 recovering_token(s, TokenKind::Close(Delim::Brace))?;
350 Ok(CallableBody::Block(Box::new(Block {
351 id: NodeId::default(),
352 span: s.span(lo),
353 stmts: stmts.into_boxed_slice(),
354 })))
355 } else {
356 recovering_token(s, TokenKind::Close(Delim::Brace))?;
357 Ok(CallableBody::Specs(specs.into_boxed_slice()))
358 }
359 })
360}
361
362fn parse_spec_decl(s: &mut Scanner) -> Result<Box<SpecDecl>> {
363 let lo = s.peek().span.lo;
364 let spec = if token(s, TokenKind::Keyword(Keyword::Body)).is_ok() {
365 Spec::Body
366 } else if token(s, TokenKind::Keyword(Keyword::Adjoint)).is_ok() {
367 Spec::Adj
368 } else if token(s, TokenKind::Keyword(Keyword::Controlled)).is_ok() {
369 if token(s, TokenKind::Keyword(Keyword::Adjoint)).is_ok() {
370 Spec::CtlAdj
371 } else {
372 Spec::Ctl
373 }
374 } else {
375 return Err(Error(ErrorKind::Rule(
376 "specialization",
377 s.peek().kind,
378 s.peek().span,
379 )));
380 };
381
382 let body = if let Some(gen) = opt(s, parse_spec_gen)? {
383 token(s, TokenKind::Semi)?;
384 SpecBody::Gen(gen)
385 } else {
386 SpecBody::Impl(pat(s)?, stmt::parse_block(s)?)
387 };
388
389 Ok(Box::new(SpecDecl {
390 id: NodeId::default(),
391 span: s.span(lo),
392 spec,
393 body,
394 }))
395}
396
397fn parse_spec_gen(s: &mut Scanner) -> Result<SpecGen> {
398 if token(s, TokenKind::Keyword(Keyword::Auto)).is_ok() {
399 Ok(SpecGen::Auto)
400 } else if token(s, TokenKind::Keyword(Keyword::Distribute)).is_ok() {
401 Ok(SpecGen::Distribute)
402 } else if token(s, TokenKind::Keyword(Keyword::Intrinsic)).is_ok() {
403 Ok(SpecGen::Intrinsic)
404 } else if token(s, TokenKind::Keyword(Keyword::Invert)).is_ok() {
405 Ok(SpecGen::Invert)
406 } else if token(s, TokenKind::Keyword(Keyword::Slf)).is_ok() {
407 Ok(SpecGen::Slf)
408 } else {
409 Err(Error(ErrorKind::Rule(
410 "specialization generator",
411 s.peek().kind,
412 s.peek().span,
413 )))
414 }
415}
416/// Checks that the inputs of the callable are surrounded by parens
417pub(super) fn check_input_parens(inputs: &Pat) -> Result<()> {
418 if !matches!(*inputs.kind, PatKind::Paren(_) | PatKind::Tuple(_)) {
419 Err(Error(ErrorKind::MissingParens(inputs.span)))
420 } else {
421 Ok(())
422 }
423}
424