makefile_lossless/
lib.rs

1#![allow(clippy::tabs_in_doc_comments)] // Makefile uses tabs
2#![deny(missing_docs)]
3
4//! A lossless parser for Makefiles
5//!
6//! Example:
7//!
8//! ```rust
9//! use std::io::Read;
10//! let contents = r#"PYTHON = python3
11//!
12//! .PHONY: all
13//!
14//! all: build
15//!
16//! build:
17//! 	$(PYTHON) setup.py build
18//! "#;
19//! let makefile: makefile_lossless::Makefile = contents.parse().unwrap();
20//!
21//! assert_eq!(makefile.rules().count(), 3);
22//! ```
23
24mod ast;
25mod lex;
26mod lossless;
27mod parse;
28mod pattern;
29
30pub use ast::makefile::MakefileItem;
31pub use ast::rule::RuleItem;
32pub use lossless::{
33    ArchiveMember, ArchiveMembers, Conditional, Error, Identifier, Include, Lang, Makefile,
34    ParseError, Rule, VariableDefinition,
35};
36pub use parse::Parse;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
39/// The variant of makefile being parsed
40pub enum MakefileVariant {
41    /// GNU Make (most common, supports ifeq/ifneq/ifdef/ifndef conditionals, pattern rules, etc.)
42    GNUMake,
43    /// BSD Make (FreeBSD, NetBSD, OpenBSD - uses .if/.ifdef/.ifndef directives)
44    BSDMake,
45    /// Microsoft nmake (Windows - uses !IF/!IFDEF/!IFNDEF directives)
46    NMake,
47    /// POSIX-compliant make (basic portable subset, no extensions)
48    POSIXMake,
49}
50
51#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
52#[allow(non_camel_case_types)]
53#[repr(u16)]
54#[allow(missing_docs)]
55pub enum SyntaxKind {
56    IDENTIFIER = 0,
57    INDENT,
58    TEXT,
59    WHITESPACE,
60    NEWLINE,
61    DOLLAR,
62    LPAREN,
63    RPAREN,
64    QUOTE,
65    BACKSLASH,
66    COMMA,
67    OPERATOR,
68
69    COMMENT,
70    ERROR,
71
72    // composite nodes
73    ROOT,          // The entire file
74    RULE,          // A single rule
75    RECIPE,        // A command/recipe line
76    VARIABLE,      // A variable definition
77    EXPR,          // An expression (e.g., targets before colon, or old-style prerequisites)
78    TARGETS,       // Container for targets before the colon
79    PREREQUISITES, // Container for prerequisites after the colon
80    PREREQUISITE,  // A single prerequisite item
81
82    // Directives
83    CONDITIONAL,       // The entire conditional block (ifdef...endif)
84    CONDITIONAL_IF,    // The initial conditional (ifdef/ifndef/ifeq/ifneq)
85    CONDITIONAL_ELSE,  // An else or else-conditional clause
86    CONDITIONAL_ENDIF, // The endif keyword
87    INCLUDE,
88
89    // Archive members
90    ARCHIVE_MEMBERS, // Container for just the members inside parentheses
91    ARCHIVE_MEMBER,  // Individual member like "bar.o" or "baz.o"
92
93    // Blank lines
94    BLANK_LINE, // A blank line between top-level items
95}
96
97/// Convert our `SyntaxKind` into the rowan `SyntaxKind`.
98impl From<SyntaxKind> for rowan::SyntaxKind {
99    fn from(kind: SyntaxKind) -> Self {
100        Self(kind as u16)
101    }
102}