Skip to main content

cmakefmt/parser/
ast.rs

1// SPDX-FileCopyrightText: Copyright 2026 Puneet Matharu
2//
3// SPDX-License-Identifier: MIT OR Apache-2.0
4
5//! AST types returned by [`crate::parser::parse`].
6//!
7//! A CMake file parses into a [`File`] containing an ordered list of
8//! top-level [`Statement`]s. Commands carry their argument list
9//! ([`Argument`]), recognised comment forms ([`Comment`]), and the
10//! source byte span. The AST preserves blank-line and comment
11//! positions so the formatter can round-trip files with stable
12//! semantics.
13
14/// A parsed CMake source file.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct File {
17    /// Top-level statements in source order.
18    pub statements: Vec<Statement>,
19}
20
21/// A top-level statement in a CMake file.
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum Statement {
24    /// A command invocation, e.g. `target_link_libraries(foo PUBLIC bar)`.
25    Command(CommandInvocation),
26    /// A top-level configure-file placeholder line such as `@PACKAGE_INIT@`.
27    ///
28    /// These occur in `.cmake.in` templates and must be preserved verbatim.
29    TemplatePlaceholder(String),
30    /// A standalone comment (on its own line).
31    Comment(Comment),
32    /// One or more consecutive blank lines between statements.
33    /// The value is the number of blank lines (>= 1).
34    ///
35    /// Blank lines at the start of the file and at the end of the
36    /// file are also preserved as `BlankLines` statements, so a
37    /// round-tripped AST matches the source's whitespace envelope.
38    BlankLines(usize),
39}
40
41/// A CMake command invocation.
42#[derive(Debug, Clone, PartialEq, Eq)]
43pub struct CommandInvocation {
44    /// The command name, e.g. "target_link_libraries". Case as written in source.
45    pub name: String,
46    /// The argument list, in source order.
47    pub arguments: Vec<Argument>,
48    /// A comment that appears after the closing paren on the same line.
49    pub trailing_comment: Option<Comment>,
50    /// Half-open byte range `[start, end)` into the original source.
51    /// `start` is inclusive, `end` is exclusive.
52    pub span: (usize, usize),
53}
54
55/// A single argument (or inline comment) in an argument list.
56#[derive(Debug, Clone, PartialEq, Eq)]
57pub enum Argument {
58    /// `[[...]]`, `[=[...]=]`, etc. Content is verbatim.
59    Bracket(BracketArgument),
60    /// `"..."` — includes the surrounding quotes verbatim.
61    Quoted(String),
62    /// Any other token — unquoted argument, variable reference
63    /// (`${VAR}`), environment reference (`$ENV{X}`), cache reference
64    /// (`$CACHE{X}`), generator expression (`$<...>`), legacy
65    /// unquoted arguments containing embedded `"..."` segments, or a
66    /// parenthesised group inside a condition (e.g. `(A OR B)`
67    /// inside `if(...)`).
68    Unquoted(String),
69    /// A comment that appears inline between arguments.
70    InlineComment(Comment),
71}
72
73impl Argument {
74    /// The source text of this argument. For
75    /// [`Argument::InlineComment`] the returned slice includes the
76    /// leading `#` (and, for bracket comments, the enclosing
77    /// `#[[...]]` delimiters).
78    pub fn as_str(&self) -> &str {
79        match self {
80            Argument::Bracket(b) => &b.raw,
81            Argument::Quoted(s) | Argument::Unquoted(s) => s,
82            Argument::InlineComment(c) => c.as_str(),
83        }
84    }
85
86    /// Returns `true` when the argument is an inline comment placeholder rather
87    /// than a normal CMake argument token.
88    pub fn is_comment(&self) -> bool {
89        matches!(self, Argument::InlineComment(_))
90    }
91}
92
93/// A bracket argument with its "=" nesting level.
94#[derive(Debug, Clone, PartialEq, Eq)]
95pub struct BracketArgument {
96    /// Number of `=` characters between the outer brackets. 0 = `[[...]]`.
97    pub level: usize,
98    /// The raw source text, e.g. `[==[content]==]`.
99    pub raw: String,
100}
101
102/// A CMake comment.
103#[derive(Debug, Clone, PartialEq, Eq)]
104pub enum Comment {
105    /// `# text to end of line` (stored with the leading `#`).
106    Line(String),
107    /// `#[[...]]` or `#[=[...]=]` (stored as the full raw text including `#`).
108    Bracket(String),
109}
110
111impl Comment {
112    /// Return the raw source text of the comment, including the leading `#`.
113    pub fn as_str(&self) -> &str {
114        match self {
115            Comment::Line(s) | Comment::Bracket(s) => s,
116        }
117    }
118}