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}