Skip to main content

wdl_grammar/
tree.rs

1//! Module for the concrete syntax tree (CST) representation.
2
3pub mod dive;
4
5use std::borrow::Cow;
6use std::collections::VecDeque;
7use std::fmt;
8use std::iter;
9
10use rowan::GreenNodeBuilder;
11use rowan::GreenNodeData;
12use strum::VariantArray;
13
14use super::Diagnostic;
15use super::SupportedVersion;
16use super::grammar;
17use super::lexer::Lexer;
18use super::parser::Event;
19use crate::parser::Parser;
20
21/// Represents the kind of syntax element (node or token) in a WDL concrete
22/// syntax tree (CST).
23///
24/// Nodes have at least one token child and represent a syntactic construct.
25///
26/// Tokens are terminal and represent any span of the source.
27///
28/// This enumeration is a union of all supported WDL tokens and nodes.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, VariantArray)]
30#[repr(u16)]
31#[cfg_attr(
32    feature = "unstable-python",
33    pyo3::pyclass(
34        module = "sprocket_bio.grammar",
35        frozen,
36        rename_all = "SCREAMING_SNAKE_CASE",
37        from_py_object,
38        eq,
39        ord,
40        hash
41    )
42)]
43pub enum SyntaxKind {
44    /// The token is unknown to WDL.
45    Unknown,
46    /// The token represents unparsed source.
47    ///
48    /// Unparsed source occurs in WDL source files with unsupported versions.
49    Unparsed,
50    /// A whitespace token.
51    Whitespace,
52    /// A comment token.
53    Comment,
54    /// A WDL version token.
55    Version,
56    /// A literal float token.
57    Float,
58    /// A literal integer token.
59    Integer,
60    /// An identifier token.
61    Ident,
62    /// A single quote token.
63    SingleQuote,
64    /// A double quote token.
65    DoubleQuote,
66    /// An open heredoc token.
67    OpenHeredoc,
68    /// A close heredoc token.
69    CloseHeredoc,
70    /// The `Array` type keyword token.
71    ArrayTypeKeyword,
72    /// The `Boolean` type keyword token.
73    BooleanTypeKeyword,
74    /// The `File` type keyword token.
75    FileTypeKeyword,
76    /// The `Float` type keyword token.
77    FloatTypeKeyword,
78    /// The `Int` type keyword token.
79    IntTypeKeyword,
80    /// The `Map` type keyword token.
81    MapTypeKeyword,
82    /// The `Object` type keyword token.
83    ObjectTypeKeyword,
84    /// The `Pair` type keyword token.
85    PairTypeKeyword,
86    /// The `String` type keyword token.
87    StringTypeKeyword,
88    /// The `after` keyword token.
89    AfterKeyword,
90    /// The `alias` keyword token.
91    AliasKeyword,
92    /// The `as` keyword token.
93    AsKeyword,
94    /// The `call` keyword token.
95    CallKeyword,
96    /// The `command` keyword token.
97    CommandKeyword,
98    /// The `else` keyword token.
99    ElseKeyword,
100    /// The `env` keyword token.
101    EnvKeyword,
102    /// The `false` keyword token.
103    FalseKeyword,
104    /// The `from` keyword token.
105    FromKeyword,
106    /// The `if` keyword token.
107    IfKeyword,
108    /// The `in` keyword token.
109    InKeyword,
110    /// The `import` keyword token.
111    ImportKeyword,
112    /// The `input` keyword token.
113    InputKeyword,
114    /// The `meta` keyword token.
115    MetaKeyword,
116    /// The `None` keyword.
117    NoneKeyword,
118    /// The `null` keyword token.
119    NullKeyword,
120    /// The `object` keyword token.
121    ObjectKeyword,
122    /// The `output` keyword token.
123    OutputKeyword,
124    /// The `parameter_meta` keyword token.
125    ParameterMetaKeyword,
126    /// The `runtime` keyword token.
127    RuntimeKeyword,
128    /// The `scatter` keyword token.
129    ScatterKeyword,
130    /// The `struct` keyword token.
131    StructKeyword,
132    /// The `enum` keyword token.
133    EnumKeyword,
134    /// The `task` keyword token.
135    TaskKeyword,
136    /// The `then` keyword token.
137    ThenKeyword,
138    /// The `true` keyword token.
139    TrueKeyword,
140    /// The `version` keyword token.
141    VersionKeyword,
142    /// The `workflow` keyword token.
143    WorkflowKeyword,
144    /// The 1.2 `Directory` type keyword token.
145    DirectoryTypeKeyword,
146    /// The 1.2 `hints` keyword token.
147    HintsKeyword,
148    /// The 1.2 `requirements` keyword token.
149    RequirementsKeyword,
150    /// The `{` symbol token.
151    OpenBrace,
152    /// The `}` symbol token.
153    CloseBrace,
154    /// The `[` symbol token.
155    OpenBracket,
156    /// The `]` symbol token.
157    CloseBracket,
158    /// The `=` symbol token.
159    Assignment,
160    /// The `:` symbol token.
161    Colon,
162    /// The `,` symbol token.
163    Comma,
164    /// The `(` symbol token.
165    OpenParen,
166    /// The `)` symbol token.
167    CloseParen,
168    /// The `?` symbol token.
169    QuestionMark,
170    /// The `!` symbol token.
171    Exclamation,
172    /// The `+` symbol token.
173    Plus,
174    /// The `-` symbol token.
175    Minus,
176    /// The `||` symbol token.
177    LogicalOr,
178    /// The `&&` symbol token.
179    LogicalAnd,
180    /// The `*` symbol token.
181    Asterisk,
182    /// The `**` symbol token.
183    Exponentiation,
184    /// The `/` symbol token.
185    Slash,
186    /// The `%` symbol token.
187    Percent,
188    /// The `==` symbol token.
189    Equal,
190    /// The `!=` symbol token.
191    NotEqual,
192    /// The `<=` symbol token.
193    LessEqual,
194    /// The `>=` symbol token.
195    GreaterEqual,
196    /// The `<` symbol token.
197    Less,
198    /// The `>` symbol token.
199    Greater,
200    /// The `.` symbol token.
201    Dot,
202    /// A literal text part of a string.
203    LiteralStringText,
204    /// A literal text part of a command.
205    LiteralCommandText,
206    /// A placeholder open token.
207    PlaceholderOpen,
208
209    /// Abandoned nodes are nodes that encountered errors.
210    ///
211    /// Children of abandoned nodes are re-parented to the parent of
212    /// the abandoned node.
213    ///
214    /// As this is an internal implementation of error recovery,
215    /// hide this variant from the documentation.
216    #[doc(hidden)]
217    Abandoned,
218    /// Represents the WDL document root node.
219    RootNode,
220    /// Represents a version statement node.
221    VersionStatementNode,
222    /// Represents an import statement node.
223    ImportStatementNode,
224    /// Represents the braced selected-members clause in an import statement.
225    ImportMembersNode,
226    /// Represents a single selected member in a symbolic import.
227    ImportMemberNode,
228    /// Represents the unquoted module path `Ident ("/" Ident)*`.
229    SymbolicModulePathNode,
230    /// Represents an import alias node.
231    ImportAliasNode,
232    /// Represents a struct definition node.
233    StructDefinitionNode,
234    /// Represents an enum definition node.
235    EnumDefinitionNode,
236    /// Represents an enum type parameter node.
237    EnumTypeParameterNode,
238    /// Represents an enum choice node.
239    EnumChoiceNode,
240    /// Represents a task definition node.
241    TaskDefinitionNode,
242    /// Represents a workflow definition node.
243    WorkflowDefinitionNode,
244    /// Represents an unbound declaration node.
245    UnboundDeclNode,
246    /// Represents a bound declaration node.
247    BoundDeclNode,
248    /// Represents an input section node.
249    InputSectionNode,
250    /// Represents an output section node.
251    OutputSectionNode,
252    /// Represents a command section node.
253    CommandSectionNode,
254    /// Represents a requirements section node.
255    RequirementsSectionNode,
256    /// Represents a requirements item node.
257    RequirementsItemNode,
258    /// Represents a hints section node in a task.
259    TaskHintsSectionNode,
260    /// Represents a hints section node in a workflow.
261    WorkflowHintsSectionNode,
262    /// Represents a hints item node in a task.
263    TaskHintsItemNode,
264    /// Represents a hints item node in a workflow.
265    WorkflowHintsItemNode,
266    /// Represents a literal object in a workflow hints item value.
267    WorkflowHintsObjectNode,
268    /// Represents an item in a workflow hints object.
269    WorkflowHintsObjectItemNode,
270    /// Represents a literal array in a workflow hints item value.
271    WorkflowHintsArrayNode,
272    /// Represents a runtime section node.
273    RuntimeSectionNode,
274    /// Represents a runtime item node.
275    RuntimeItemNode,
276    /// Represents a primitive type node.
277    PrimitiveTypeNode,
278    /// Represents a map type node.
279    MapTypeNode,
280    /// Represents an array type node.
281    ArrayTypeNode,
282    /// Represents a pair type node.
283    PairTypeNode,
284    /// Represents an object type node.
285    ObjectTypeNode,
286    /// Represents a type reference node.
287    TypeRefNode,
288    /// Represents a metadata section node.
289    MetadataSectionNode,
290    /// Represents a parameter metadata section node.
291    ParameterMetadataSectionNode,
292    /// Represents a metadata object item node.
293    MetadataObjectItemNode,
294    /// Represents a metadata object node.
295    MetadataObjectNode,
296    /// Represents a metadata array node.
297    MetadataArrayNode,
298    /// Represents a literal integer node.
299    LiteralIntegerNode,
300    /// Represents a literal float node.
301    LiteralFloatNode,
302    /// Represents a literal boolean node.
303    LiteralBooleanNode,
304    /// Represents a literal `None` node.
305    LiteralNoneNode,
306    /// Represents a literal null node.
307    LiteralNullNode,
308    /// Represents a literal string node.
309    LiteralStringNode,
310    /// Represents a literal pair node.
311    LiteralPairNode,
312    /// Represents a literal array node.
313    LiteralArrayNode,
314    /// Represents a literal map node.
315    LiteralMapNode,
316    /// Represents a literal map item node.
317    LiteralMapItemNode,
318    /// Represents a literal object node.
319    LiteralObjectNode,
320    /// Represents a literal object item node.
321    LiteralObjectItemNode,
322    /// Represents a literal struct node.
323    LiteralStructNode,
324    /// Represents a literal struct item node.
325    LiteralStructItemNode,
326    /// Represents a literal hints node.
327    LiteralHintsNode,
328    /// Represents a literal hints item node.
329    LiteralHintsItemNode,
330    /// Represents a literal input node.
331    LiteralInputNode,
332    /// Represents a literal input item node.
333    LiteralInputItemNode,
334    /// Represents a literal output node.
335    LiteralOutputNode,
336    /// Represents a literal output item node.
337    LiteralOutputItemNode,
338    /// Represents a parenthesized expression node.
339    ParenthesizedExprNode,
340    /// Represents a name reference expression node.
341    NameRefExprNode,
342    /// Represents an `if` expression node.
343    IfExprNode,
344    /// Represents a logical not expression node.
345    LogicalNotExprNode,
346    /// Represents a negation expression node.
347    NegationExprNode,
348    /// Represents a logical `OR` expression node.
349    LogicalOrExprNode,
350    /// Represents a logical `AND` expression node.
351    LogicalAndExprNode,
352    /// Represents an equality expression node.
353    EqualityExprNode,
354    /// Represents an inequality expression node.
355    InequalityExprNode,
356    /// Represents a "less than" expression node.
357    LessExprNode,
358    /// Represents a "less than or equal to" expression node.
359    LessEqualExprNode,
360    /// Represents a "greater than" expression node.
361    GreaterExprNode,
362    /// Represents a "greater than or equal to" expression node.
363    GreaterEqualExprNode,
364    /// Represents an addition expression node.
365    AdditionExprNode,
366    /// Represents a subtraction expression node.
367    SubtractionExprNode,
368    /// Represents a multiplication expression node.
369    MultiplicationExprNode,
370    /// Represents a division expression node.
371    DivisionExprNode,
372    /// Represents a modulo expression node.
373    ModuloExprNode,
374    /// Represents a exponentiation expr node.
375    ExponentiationExprNode,
376    /// Represents a call expression node.'
377    CallExprNode,
378    /// Represents an index expression node.
379    IndexExprNode,
380    /// Represents an an access expression node.
381    AccessExprNode,
382    /// Represents a placeholder node in a string literal.
383    PlaceholderNode,
384    /// Placeholder `sep` option node.
385    PlaceholderSepOptionNode,
386    /// Placeholder `default` option node.
387    PlaceholderDefaultOptionNode,
388    /// Placeholder `true`/`false` option node.
389    PlaceholderTrueFalseOptionNode,
390    /// Represents a conditional statement node.
391    ConditionalStatementNode,
392    /// Represents a clause within a conditional statement.
393    ConditionalStatementClauseNode,
394    /// Represents a scatter statement node.
395    ScatterStatementNode,
396    /// Represents a call statement node.
397    CallStatementNode,
398    /// Represents a call target node in a call statement.
399    CallTargetNode,
400    /// Represents a call alias node in a call statement.
401    CallAliasNode,
402    /// Represents an `after` clause node in a call statement.
403    CallAfterNode,
404    /// Represents a call input item node.
405    CallInputItemNode,
406
407    // WARNING: this must always be the last variant.
408    /// The exclusive maximum syntax kind value.
409    MAX,
410}
411
412impl SyntaxKind {
413    /// Returns whether the token is a symbolic [`SyntaxKind`].
414    ///
415    /// Generally speaking, symbolic [`SyntaxKind`]s have special meanings
416    /// during parsing—they are not real elements of the grammar but rather an
417    /// implementation detail.
418    pub fn is_symbolic(&self) -> bool {
419        matches!(
420            self,
421            Self::Abandoned | Self::Unknown | Self::Unparsed | Self::MAX
422        )
423    }
424
425    /// Describes the syntax kind.
426    pub fn describe(&self) -> &'static str {
427        match self {
428            Self::Unknown => unreachable!(),
429            Self::Unparsed => unreachable!(),
430            Self::Whitespace => "whitespace",
431            Self::Comment => "comment",
432            Self::Version => "version",
433            Self::Float => "float",
434            Self::Integer => "integer",
435            Self::Ident => "identifier",
436            Self::SingleQuote => "single quote",
437            Self::DoubleQuote => "double quote",
438            Self::OpenHeredoc => "open heredoc",
439            Self::CloseHeredoc => "close heredoc",
440            Self::ArrayTypeKeyword => "`Array` type keyword",
441            Self::BooleanTypeKeyword => "`Boolean` type keyword",
442            Self::FileTypeKeyword => "`File` type keyword",
443            Self::FloatTypeKeyword => "`Float` type keyword",
444            Self::IntTypeKeyword => "`Int` type keyword",
445            Self::MapTypeKeyword => "`Map` type keyword",
446            Self::ObjectTypeKeyword => "`Object` type keyword",
447            Self::PairTypeKeyword => "`Pair` type keyword",
448            Self::StringTypeKeyword => "`String` type keyword",
449            Self::AfterKeyword => "`after` keyword",
450            Self::AliasKeyword => "`alias` keyword",
451            Self::AsKeyword => "`as` keyword",
452            Self::CallKeyword => "`call` keyword",
453            Self::CommandKeyword => "`command` keyword",
454            Self::ElseKeyword => "`else` keyword",
455            Self::EnvKeyword => "`env` keyword",
456            Self::FalseKeyword => "`false` keyword",
457            Self::FromKeyword => "`from` keyword",
458            Self::IfKeyword => "`if` keyword",
459            Self::InKeyword => "`in` keyword",
460            Self::ImportKeyword => "`import` keyword",
461            Self::InputKeyword => "`input` keyword",
462            Self::MetaKeyword => "`meta` keyword",
463            Self::NoneKeyword => "`None` keyword",
464            Self::NullKeyword => "`null` keyword",
465            Self::ObjectKeyword => "`object` keyword",
466            Self::OutputKeyword => "`output` keyword",
467            Self::ParameterMetaKeyword => "`parameter_meta` keyword",
468            Self::RuntimeKeyword => "`runtime` keyword",
469            Self::ScatterKeyword => "`scatter` keyword",
470            Self::StructKeyword => "`struct` keyword",
471            Self::EnumKeyword => "`enum` keyword",
472            Self::TaskKeyword => "`task` keyword",
473            Self::ThenKeyword => "`then` keyword",
474            Self::TrueKeyword => "`true` keyword",
475            Self::VersionKeyword => "`version` keyword",
476            Self::WorkflowKeyword => "`workflow` keyword",
477            Self::DirectoryTypeKeyword => "`Directory` type keyword",
478            Self::HintsKeyword => "`hints` keyword",
479            Self::RequirementsKeyword => "`requirements` keyword",
480            Self::OpenBrace => "`{` symbol",
481            Self::CloseBrace => "`}` symbol",
482            Self::OpenBracket => "`[` symbol",
483            Self::CloseBracket => "`]` symbol",
484            Self::Assignment => "`=` symbol",
485            Self::Colon => "`:` symbol",
486            Self::Comma => "`,` symbol",
487            Self::OpenParen => "`(` symbol",
488            Self::CloseParen => "`)` symbol",
489            Self::QuestionMark => "`?` symbol",
490            Self::Exclamation => "`!` symbol",
491            Self::Plus => "`+` symbol",
492            Self::Minus => "`-` symbol",
493            Self::LogicalOr => "`||` symbol",
494            Self::LogicalAnd => "`&&` symbol",
495            Self::Asterisk => "`*` symbol",
496            Self::Exponentiation => "`**` symbol",
497            Self::Slash => "`/` symbol",
498            Self::Percent => "`%` symbol",
499            Self::Equal => "`==` symbol",
500            Self::NotEqual => "`!=` symbol",
501            Self::LessEqual => "`<=` symbol",
502            Self::GreaterEqual => "`>=` symbol",
503            Self::Less => "`<` symbol",
504            Self::Greater => "`>` symbol",
505            Self::Dot => "`.` symbol",
506            Self::LiteralStringText => "literal string text",
507            Self::LiteralCommandText => "literal command text",
508            Self::PlaceholderOpen => "placeholder open",
509            Self::Abandoned => unreachable!(),
510            Self::RootNode => "root node",
511            Self::VersionStatementNode => "version statement",
512            Self::ImportStatementNode => "import statement",
513            Self::ImportMembersNode => "selected members clause",
514            Self::ImportMemberNode => "selected member",
515            Self::SymbolicModulePathNode => "symbolic module path",
516            Self::ImportAliasNode => "import alias",
517            Self::StructDefinitionNode => "struct definition",
518            Self::EnumDefinitionNode => "enum definition",
519            Self::EnumTypeParameterNode => "enum type parameter",
520            Self::EnumChoiceNode => "enum choice",
521            Self::TaskDefinitionNode => "task definition",
522            Self::WorkflowDefinitionNode => "workflow definition",
523            Self::UnboundDeclNode => "declaration without assignment",
524            Self::BoundDeclNode => "declaration with assignment",
525            Self::InputSectionNode => "input section",
526            Self::OutputSectionNode => "output section",
527            Self::CommandSectionNode => "command section",
528            Self::RequirementsSectionNode => "requirements section",
529            Self::RequirementsItemNode => "requirements item",
530            Self::TaskHintsSectionNode | Self::WorkflowHintsSectionNode => "hints section",
531            Self::TaskHintsItemNode | Self::WorkflowHintsItemNode => "hints item",
532            Self::WorkflowHintsObjectNode => "literal object",
533            Self::WorkflowHintsObjectItemNode => "literal object item",
534            Self::WorkflowHintsArrayNode => "literal array",
535            Self::RuntimeSectionNode => "runtime section",
536            Self::RuntimeItemNode => "runtime item",
537            Self::PrimitiveTypeNode => "primitive type",
538            Self::MapTypeNode => "map type",
539            Self::ArrayTypeNode => "array type",
540            Self::PairTypeNode => "pair type",
541            Self::ObjectTypeNode => "object type",
542            Self::TypeRefNode => "type reference",
543            Self::MetadataSectionNode => "metadata section",
544            Self::ParameterMetadataSectionNode => "parameter metadata section",
545            Self::MetadataObjectItemNode => "metadata object item",
546            Self::MetadataObjectNode => "metadata object",
547            Self::MetadataArrayNode => "metadata array",
548            Self::LiteralIntegerNode => "literal integer",
549            Self::LiteralFloatNode => "literal float",
550            Self::LiteralBooleanNode => "literal boolean",
551            Self::LiteralNoneNode => "literal `None`",
552            Self::LiteralNullNode => "literal null",
553            Self::LiteralStringNode => "literal string",
554            Self::LiteralPairNode => "literal pair",
555            Self::LiteralArrayNode => "literal array",
556            Self::LiteralMapNode => "literal map",
557            Self::LiteralMapItemNode => "literal map item",
558            Self::LiteralObjectNode => "literal object",
559            Self::LiteralObjectItemNode => "literal object item",
560            Self::LiteralStructNode => "literal struct",
561            Self::LiteralStructItemNode => "literal struct item",
562            Self::LiteralHintsNode => "literal hints",
563            Self::LiteralHintsItemNode => "literal hints item",
564            Self::LiteralInputNode => "literal input",
565            Self::LiteralInputItemNode => "literal input item",
566            Self::LiteralOutputNode => "literal output",
567            Self::LiteralOutputItemNode => "literal output item",
568            Self::ParenthesizedExprNode => "parenthesized expression",
569            Self::NameRefExprNode => "name reference expression",
570            Self::IfExprNode => "`if` expression",
571            Self::LogicalNotExprNode => "logical not expression",
572            Self::NegationExprNode => "negation expression",
573            Self::LogicalOrExprNode => "logical OR expression",
574            Self::LogicalAndExprNode => "logical AND expression",
575            Self::EqualityExprNode => "equality expression",
576            Self::InequalityExprNode => "inequality expression",
577            Self::LessExprNode => "less than expression",
578            Self::LessEqualExprNode => "less than or equal to expression",
579            Self::GreaterExprNode => "greater than expression",
580            Self::GreaterEqualExprNode => "greater than or equal to expression",
581            Self::AdditionExprNode => "addition expression",
582            Self::SubtractionExprNode => "subtraction expression",
583            Self::MultiplicationExprNode => "multiplication expression",
584            Self::DivisionExprNode => "division expression",
585            Self::ModuloExprNode => "modulo expression",
586            Self::ExponentiationExprNode => "exponentiation expression",
587            Self::CallExprNode => "call expression",
588            Self::IndexExprNode => "index expression",
589            Self::AccessExprNode => "access expression",
590            Self::PlaceholderNode => "placeholder",
591            Self::PlaceholderSepOptionNode => "placeholder `sep` option",
592            Self::PlaceholderDefaultOptionNode => "placeholder `default` option",
593            Self::PlaceholderTrueFalseOptionNode => "placeholder `true`/`false` option",
594            Self::ConditionalStatementNode => "conditional statement",
595            Self::ConditionalStatementClauseNode => "conditional statement clause",
596            Self::ScatterStatementNode => "scatter statement",
597            Self::CallStatementNode => "call statement",
598            Self::CallTargetNode => "call target",
599            Self::CallAliasNode => "call alias",
600            Self::CallAfterNode => "call `after` clause",
601            Self::CallInputItemNode => "call input item",
602            Self::MAX => unreachable!(),
603        }
604    }
605
606    /// Returns whether the [`SyntaxKind`] is trivia.
607    pub fn is_trivia(&self) -> bool {
608        matches!(self, Self::Whitespace | Self::Comment)
609    }
610
611    /// Returns whether the [`SyntaxKind`] is a keyword.
612    ///
613    /// NOTE: This does not include types, see [`Self::is_type()`].
614    pub fn is_keyword(&self) -> bool {
615        matches!(
616            self,
617            SyntaxKind::AfterKeyword
618                | SyntaxKind::AliasKeyword
619                | SyntaxKind::AsKeyword
620                | SyntaxKind::CallKeyword
621                | SyntaxKind::CommandKeyword
622                | SyntaxKind::ElseKeyword
623                | SyntaxKind::EnvKeyword
624                | SyntaxKind::FalseKeyword
625                | SyntaxKind::FromKeyword
626                | SyntaxKind::HintsKeyword
627                | SyntaxKind::IfKeyword
628                | SyntaxKind::ImportKeyword
629                | SyntaxKind::InKeyword
630                | SyntaxKind::InputKeyword
631                | SyntaxKind::MetaKeyword
632                | SyntaxKind::NoneKeyword
633                | SyntaxKind::NullKeyword
634                | SyntaxKind::ObjectKeyword
635                | SyntaxKind::OutputKeyword
636                | SyntaxKind::ParameterMetaKeyword
637                | SyntaxKind::RequirementsKeyword
638                | SyntaxKind::RuntimeKeyword
639                | SyntaxKind::ScatterKeyword
640                | SyntaxKind::StructKeyword
641                | SyntaxKind::EnumKeyword
642                | SyntaxKind::TaskKeyword
643                | SyntaxKind::ThenKeyword
644                | SyntaxKind::TrueKeyword
645                | SyntaxKind::VersionKeyword
646                | SyntaxKind::WorkflowKeyword
647        )
648    }
649
650    /// Returns whether the [`SyntaxKind`] is a predefined type keyword.
651    pub fn is_type(&self) -> bool {
652        matches!(
653            self,
654            SyntaxKind::ArrayTypeKeyword
655                | SyntaxKind::BooleanTypeKeyword
656                | SyntaxKind::DirectoryTypeKeyword
657                | SyntaxKind::FileTypeKeyword
658                | SyntaxKind::FloatTypeKeyword
659                | SyntaxKind::IntTypeKeyword
660                | SyntaxKind::MapTypeKeyword
661                | SyntaxKind::ObjectTypeKeyword
662                | SyntaxKind::PairTypeKeyword
663                | SyntaxKind::StringTypeKeyword
664        )
665    }
666
667    /// Returns whether the [`SyntaxKind`] is an operator.
668    pub fn is_operator(&self) -> bool {
669        matches!(
670            self,
671            SyntaxKind::Plus
672                | SyntaxKind::Minus
673                | SyntaxKind::Slash
674                | SyntaxKind::Percent
675                | SyntaxKind::Asterisk
676                | SyntaxKind::Exponentiation
677                | SyntaxKind::Equal
678                | SyntaxKind::NotEqual
679                | SyntaxKind::Less
680                | SyntaxKind::LessEqual
681                | SyntaxKind::Greater
682                | SyntaxKind::GreaterEqual
683                | SyntaxKind::LogicalAnd
684                | SyntaxKind::LogicalOr
685                | SyntaxKind::Exclamation
686                | SyntaxKind::Assignment
687                | SyntaxKind::QuestionMark
688                | SyntaxKind::Dot
689        )
690    }
691}
692
693/// Every [`SyntaxKind`] variant.
694pub static ALL_SYNTAX_KIND: &[SyntaxKind] = SyntaxKind::VARIANTS;
695
696impl From<SyntaxKind> for rowan::SyntaxKind {
697    fn from(kind: SyntaxKind) -> Self {
698        rowan::SyntaxKind(kind as u16)
699    }
700}
701
702/// Represents the Workflow Definition Language (WDL).
703#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
704pub struct WorkflowDescriptionLanguage;
705
706impl rowan::Language for WorkflowDescriptionLanguage {
707    type Kind = SyntaxKind;
708
709    fn kind_from_raw(raw: rowan::SyntaxKind) -> Self::Kind {
710        assert!(raw.0 <= SyntaxKind::MAX as u16);
711        unsafe { std::mem::transmute::<u16, SyntaxKind>(raw.0) }
712    }
713
714    fn kind_to_raw(kind: Self::Kind) -> rowan::SyntaxKind {
715        kind.into()
716    }
717}
718
719/// Represents a node in the concrete syntax tree.
720pub type SyntaxNode = rowan::SyntaxNode<WorkflowDescriptionLanguage>;
721/// Represents a token in the concrete syntax tree.
722pub type SyntaxToken = rowan::SyntaxToken<WorkflowDescriptionLanguage>;
723/// Represents an element (node or token) in the concrete syntax tree.
724pub type SyntaxElement = rowan::SyntaxElement<WorkflowDescriptionLanguage>;
725/// Represents node children in the concrete syntax tree.
726pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren<WorkflowDescriptionLanguage>;
727
728/// Constructs a concrete syntax tree from a list of parser events.
729pub fn construct_tree(source: &str, mut events: Vec<Event>) -> SyntaxNode {
730    let mut builder = GreenNodeBuilder::default();
731    let mut ancestors = Vec::new();
732
733    for i in 0..events.len() {
734        match std::mem::replace(&mut events[i], Event::abandoned()) {
735            Event::NodeStarted {
736                kind,
737                forward_parent,
738            } => {
739                // Walk the forward parent chain, if there is one, and push
740                // each forward parent to the ancestors list
741                ancestors.push(kind);
742                let mut idx = i;
743                let mut fp: Option<usize> = forward_parent;
744                while let Some(distance) = fp {
745                    idx += distance;
746                    fp = match std::mem::replace(&mut events[idx], Event::abandoned()) {
747                        Event::NodeStarted {
748                            kind,
749                            forward_parent,
750                        } => {
751                            ancestors.push(kind);
752                            forward_parent
753                        }
754                        _ => unreachable!(),
755                    };
756                }
757
758                // As the current node was pushed first and then its ancestors, walk
759                // the list in reverse to start the "oldest" ancestor first
760                for kind in ancestors.drain(..).rev() {
761                    if kind != SyntaxKind::Abandoned {
762                        builder.start_node(kind.into());
763                    }
764                }
765            }
766            Event::NodeFinished => builder.finish_node(),
767            Event::Token { kind, span } => {
768                builder.token(kind.into(), &source[span.start()..span.end()])
769            }
770        }
771    }
772
773    SyntaxNode::new_root(builder.finish())
774}
775
776/// Represents an untyped concrete syntax tree.
777#[derive(Clone, PartialEq, Eq, Hash)]
778pub struct SyntaxTree(SyntaxNode);
779
780impl SyntaxTree {
781    /// Parses WDL source to produce a syntax tree.
782    ///
783    /// This optionally takes a `fallback_version`, which will be used if a
784    /// [`SupportedVersion`] cannot be determined from the document.
785    ///
786    /// A syntax tree is always returned, even for invalid WDL documents.
787    ///
788    /// Additionally, the list of diagnostics encountered during the parse is
789    /// returned; if the list is empty, the tree is syntactically correct.
790    ///
791    /// However, additional validation is required to ensure the source is
792    /// a valid WDL document.
793    ///
794    /// # Example
795    ///
796    /// ```rust
797    /// # use wdl_grammar::SyntaxTree;
798    /// let (tree, diagnostics) = SyntaxTree::parse("version 1.1", None);
799    /// assert!(diagnostics.is_empty());
800    /// println!("{tree:#?}");
801    /// ```
802    pub fn parse(
803        source: &str,
804        fallback_version: Option<SupportedVersion>,
805    ) -> (Self, Vec<Diagnostic>) {
806        let parser = Parser::new(Lexer::new(source));
807        let (events, mut diagnostics) = grammar::document(parser, fallback_version);
808        diagnostics.sort();
809        (Self(construct_tree(source, events)), diagnostics)
810    }
811
812    /// Gets the root syntax node of the tree.
813    pub fn root(&self) -> &SyntaxNode {
814        &self.0
815    }
816
817    /// Gets a copy of the underlying root green node for the tree.
818    pub fn green(&self) -> Cow<'_, GreenNodeData> {
819        self.0.green()
820    }
821
822    /// Converts the tree into a syntax node.
823    pub fn into_syntax(self) -> SyntaxNode {
824        self.0
825    }
826}
827
828impl fmt::Display for SyntaxTree {
829    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
830        self.0.fmt(f)
831    }
832}
833
834impl fmt::Debug for SyntaxTree {
835    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
836        self.0.fmt(f)
837    }
838}
839
840/// An extension trait for [`SyntaxToken`]s.
841pub trait SyntaxTokenExt {
842    /// Gets all of the substantial preceding trivia for an element.
843    fn preceding_trivia(&self) -> impl Iterator<Item = SyntaxToken>;
844
845    /// Get any inline comment directly following an element on the
846    /// same line.
847    fn inline_comment(&self) -> Option<SyntaxToken>;
848}
849
850impl SyntaxTokenExt for SyntaxToken {
851    fn preceding_trivia(&self) -> impl Iterator<Item = SyntaxToken> {
852        let mut tokens = VecDeque::new();
853        let mut cur = self.prev_token();
854        while let Some(token) = cur {
855            cur = token.prev_token();
856            // Stop at first non-trivia
857            if !token.kind().is_trivia() {
858                break;
859            }
860            // Stop if a comment is not on its own line
861            if token.kind() == SyntaxKind::Comment
862                && let Some(prev) = token.prev_token()
863            {
864                if prev.kind() == SyntaxKind::Whitespace {
865                    let has_newlines = prev.text().chars().any(|c| c == '\n');
866                    // If there are newlines in 'prev' then we know
867                    // that the comment is on its own line.
868                    // The comment may still be on its own line if
869                    // 'prev' does not have newlines and nothing comes
870                    // before 'prev'.
871                    if !has_newlines && prev.prev_token().is_some() {
872                        break;
873                    }
874                } else {
875                    // There is something else on this line before the comment.
876                    break;
877                }
878            }
879            // Filter out whitespace that is not substantial
880            match token.kind() {
881                SyntaxKind::Whitespace
882                    if token.text().chars().filter(|c| *c == '\n').count() > 1 =>
883                {
884                    tokens.push_front(token);
885                }
886                SyntaxKind::Comment => {
887                    tokens.push_front(token);
888                }
889                _ => {}
890            }
891        }
892        tokens.into_iter()
893    }
894
895    fn inline_comment(&self) -> Option<SyntaxToken> {
896        let mut next = self.next_token();
897        iter::from_fn(move || {
898            let cur = next.clone()?;
899            next = cur.next_token();
900            Some(cur)
901        })
902        .take_while(|t| {
903            // Stop at non-trivia
904            if !t.kind().is_trivia() {
905                return false;
906            }
907            // Stop on first whitespace containing a newline
908            if t.kind() == SyntaxKind::Whitespace {
909                return !t.text().chars().any(|c| c == '\n');
910            }
911            true
912        })
913        .find(|t| t.kind() == SyntaxKind::Comment)
914    }
915}
916
917/// Python-specific APIs.
918#[cfg(feature = "unstable-python")]
919mod python {
920    use pyo3::exceptions::PyValueError;
921    use pyo3::prelude::*;
922
923    use super::*;
924
925    #[pymethods]
926    impl SyntaxKind {
927        /// Returns whether the token is a symbolic `SyntaxKind`.
928        ///
929        /// Generally speaking, symbolic `SyntaxKind`s have special meanings
930        /// during parsing—they are not real elements of the grammar but rather
931        /// an implementation detail.
932        #[pyo3(name = "is_symbolic")]
933        fn py_is_symbolic(&self) -> bool {
934            self.is_symbolic()
935        }
936
937        /// Describes the syntax kind.
938        ///
939        /// # Errors
940        ///
941        /// This method will throw `ValueError` if the `SyntaxKind` is symbolic
942        /// (when `SyntaxKind.is_symbolic()` returns true).
943        #[pyo3(name = "describe")]
944        fn py_describe(&self) -> PyResult<&'static str> {
945            if self.is_symbolic() {
946                return Err(PyValueError::new_err(format!(
947                    "cannot describe symbolic syntax kind: {}",
948                    self.__pyo3__repr__()
949                )));
950            }
951
952            Ok(self.describe())
953        }
954
955        /// Returns whether the `SyntaxKind` is trivia.
956        #[pyo3(name = "is_trivia")]
957        fn py_is_trivia(&self) -> bool {
958            self.is_trivia()
959        }
960
961        /// Returns whether the `SyntaxKind` is a keyword.
962        ///
963        /// NOTE: This does not include types, see `SyntaxKind.is_type()`.
964        #[pyo3(name = "is_keyword")]
965        fn py_is_keyword(&self) -> bool {
966            self.is_keyword()
967        }
968
969        /// Returns whether the `SyntaxKind` is a predefined type keyword.
970        #[pyo3(name = "is_type")]
971        fn py_is_type(&self) -> bool {
972            self.is_type()
973        }
974
975        /// Returns whether the `SyntaxKind` is an operator.
976        #[pyo3(name = "is_operator")]
977        fn py_is_operator(&self) -> bool {
978            self.is_operator()
979        }
980    }
981}
982
983#[cfg(test)]
984mod tests {
985    use super::*;
986    use crate::SyntaxTree;
987
988    #[test]
989    fn preceding_comments() {
990        let (tree, diagnostics) = SyntaxTree::parse(
991            "version 1.2
992
993# This comment should not be included
994task foo {} # This comment should not be included
995
996# Some
997# comments
998# are
999# long
1000    
1001# Others are short
1002
1003#     and, yet    another
1004workflow foo {} # This should not be collected.
1005
1006# This comment should not be included either.",
1007            None,
1008        );
1009
1010        assert!(diagnostics.is_empty());
1011
1012        let workflow = tree.root().last_child().unwrap();
1013        assert_eq!(workflow.kind(), SyntaxKind::WorkflowDefinitionNode);
1014        let token = workflow.first_token().unwrap();
1015        let mut trivia = token.preceding_trivia();
1016        assert_eq!(trivia.next().unwrap().text(), "\n\n");
1017        assert_eq!(trivia.next().unwrap().text(), "# Some");
1018        assert_eq!(trivia.next().unwrap().text(), "# comments");
1019        assert_eq!(trivia.next().unwrap().text(), "# are");
1020        assert_eq!(trivia.next().unwrap().text(), "# long");
1021        assert_eq!(trivia.next().unwrap().text(), "\n    \n");
1022        assert_eq!(trivia.next().unwrap().text(), "# Others are short");
1023        assert_eq!(trivia.next().unwrap().text(), "\n\n");
1024        assert_eq!(trivia.next().unwrap().text(), "#     and, yet    another");
1025        assert!(trivia.next().is_none());
1026    }
1027
1028    #[test]
1029    fn inline_comment() {
1030        let (tree, diagnostics) = SyntaxTree::parse(
1031            "version 1.2
1032
1033# This comment should not be included
1034task foo {}
1035
1036# This should not be collected.
1037workflow foo {} # Here is a comment that should be collected.
1038
1039# This comment should not be included either.",
1040            None,
1041        );
1042
1043        assert!(diagnostics.is_empty());
1044
1045        let workflow = tree.root().last_child().unwrap();
1046        assert_eq!(workflow.kind(), SyntaxKind::WorkflowDefinitionNode);
1047        let comment = workflow.last_token().unwrap().inline_comment().unwrap();
1048        assert_eq!(
1049            comment.text(),
1050            "# Here is a comment that should be collected."
1051        );
1052    }
1053}