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 incremental;
26mod lex;
27mod lossless;
28mod parse;
29mod pattern;
30mod text;
31
32pub use ast::makefile::MakefileItem;
33pub use ast::rule::RuleItem;
34pub use incremental::{apply_edit_to_text, TextEdit};
35pub use lossless::{
36 ArchiveMember, ArchiveMembers, Conditional, Error, ErrorInfo, Identifier, Include, Lang,
37 Makefile, ParseError, PositionedParseError, Rule, VariableDefinition, VariableReference,
38};
39pub use parse::Parse;
40pub use rowan::TextRange;
41pub use text::{is_in_prerequisites, variable_at_offset, word_at_offset};
42
43#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
44/// The variant of makefile being parsed
45pub enum MakefileVariant {
46 /// GNU Make (most common, supports ifeq/ifneq/ifdef/ifndef conditionals, pattern rules, etc.)
47 GNUMake,
48 /// BSD Make (FreeBSD, NetBSD, OpenBSD - uses .if/.ifdef/.ifndef directives)
49 BSDMake,
50 /// Microsoft nmake (Windows - uses !IF/!IFDEF/!IFNDEF directives)
51 NMake,
52 /// POSIX-compliant make (basic portable subset, no extensions)
53 POSIXMake,
54}
55
56#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
57#[allow(non_camel_case_types)]
58#[repr(u16)]
59#[allow(missing_docs)]
60pub enum SyntaxKind {
61 IDENTIFIER = 0,
62 INDENT,
63 TEXT,
64 WHITESPACE,
65 NEWLINE,
66 DOLLAR,
67 LPAREN,
68 RPAREN,
69 LBRACE,
70 RBRACE,
71 QUOTE,
72 BACKSLASH,
73 COMMA,
74 OPERATOR,
75
76 COMMENT,
77 ERROR,
78
79 // composite nodes
80 ROOT, // The entire file
81 RULE, // A single rule
82 RECIPE, // A command/recipe line
83 VARIABLE, // A variable definition
84 EXPR, // An expression (e.g., targets before colon, or old-style prerequisites)
85 TARGETS, // Container for targets before the colon
86 PREREQUISITES, // Container for prerequisites after the colon
87 PREREQUISITE, // A single prerequisite item
88
89 // Directives
90 CONDITIONAL, // The entire conditional block (ifdef...endif)
91 CONDITIONAL_IF, // The initial conditional (ifdef/ifndef/ifeq/ifneq)
92 CONDITIONAL_ELSE, // An else or else-conditional clause
93 CONDITIONAL_ENDIF, // The endif keyword
94 INCLUDE,
95
96 // Archive members
97 ARCHIVE_MEMBERS, // Container for just the members inside parentheses
98 ARCHIVE_MEMBER, // Individual member like "bar.o" or "baz.o"
99
100 // Blank lines
101 BLANK_LINE, // A blank line between top-level items
102}
103
104/// Convert our `SyntaxKind` into the rowan `SyntaxKind`.
105impl From<SyntaxKind> for rowan::SyntaxKind {
106 fn from(kind: SyntaxKind) -> Self {
107 Self(kind as u16)
108 }
109}