Skip to content

Commit 66f9f58

Browse files
committed
feat(make): Refactor macro engine and parser for enhanced capabilities
This commit significantly refactors the macro processing and parsing mechanisms within the utility. The primary goal is to enhance macro functionality and improve the robustness of Makefile parsing. Major changes include: - **Enhanced Macro Preprocessor:** - Macro substitution is now more context-aware (receives target, files, prerequisites). - Added support for built-in macro functions like , , , , and . - Macro table generation is now more sophisticated. - **Centralized Macro Management:** - Macros are now stored and managed within the struct, making them globally accessible. - struct no longer directly holds macro definitions. - Initialized and special macros. - **Improved Parser & Lexer:** - Parser now correctly handles macro definitions (e.g., ). - Added support for pattern rules (e.g., ). - Lexer now skips leading whitespace, simplifying token stream. - More flexible parsing of newlines in recipes and rules. - **Testing Adjustments:** - Updated integration and parser tests to align with the new macro and parsing behaviors. - Introduced a test helper that avoids strict comparison.
1 parent bd6aafa commit 66f9f58

File tree

12 files changed

+404
-182
lines changed

12 files changed

+404
-182
lines changed

make/src/config.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
// SPDX-License-Identifier: MIT
88
//
99

10-
use std::collections::{BTreeMap, BTreeSet};
10+
use std::collections::{BTreeMap, BTreeSet, HashMap};
1111

1212
/// Represents the configuration of the make utility
1313
#[derive(Debug, Clone, PartialEq, Eq)]
@@ -36,6 +36,7 @@ pub struct Config {
3636
pub precious: bool,
3737

3838
pub rules: BTreeMap<String, BTreeSet<String>>,
39+
pub macros: HashMap<String, String>,
3940
}
4041

4142
impl Default for Config {
@@ -113,6 +114,7 @@ impl Default for Config {
113114
.collect::<BTreeSet<String>>(),
114115
)
115116
]),
117+
macros: HashMap::new()
116118
}
117119
}
118120
}

make/src/lib.rs

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use std::{
2020
time::SystemTime,
2121
};
2222

23-
use parser::{Makefile, VariableDefinition};
23+
use parser::Makefile;
2424

2525
use crate::special_target::InferenceTarget;
2626
use config::Config;
@@ -38,7 +38,6 @@ const DEFAULT_SHELL: &str = "/bin/sh";
3838
///
3939
/// The only way to create a Make is from a Makefile and a Config.
4040
pub struct Make {
41-
macros: Vec<VariableDefinition>,
4241
rules: Vec<Rule>,
4342
default_rule: Option<Rule>, // .DEFAULT
4443
pub config: Config,
@@ -111,7 +110,7 @@ impl Make {
111110
for prerequisite in &newer_prerequisites {
112111
self.build_target(prerequisite)?;
113112
}
114-
rule.run(&self.config, &self.macros, target, up_to_date)?;
113+
rule.run(&self.config, target, up_to_date)?;
115114

116115
Ok(true)
117116
}
@@ -183,11 +182,17 @@ impl Make {
183182
impl TryFrom<(Makefile, Config)> for Make {
184183
type Error = ErrorCode;
185184

186-
fn try_from((makefile, config): (Makefile, Config)) -> Result<Self, Self::Error> {
185+
fn try_from((makefile, mut config): (Makefile, Config)) -> Result<Self, Self::Error> {
187186
let mut rules = vec![];
188187
let mut special_rules = vec![];
189188
let mut inference_rules = vec![];
190189

190+
for macr in makefile.macros() {
191+
config
192+
.macros
193+
.insert(macr.name().unwrap(), macr.raw_value().unwrap());
194+
}
195+
191196
for rule in makefile.rules() {
192197
let rule = Rule::from(rule);
193198
let Some(target) = rule.targets().next() else {
@@ -205,7 +210,6 @@ impl TryFrom<(Makefile, Config)> for Make {
205210

206211
let mut make = Self {
207212
rules,
208-
macros: makefile.variable_definitions().collect(),
209213
default_rule: None,
210214
config,
211215
};

make/src/main.rs

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
//
99

1010
use core::str::FromStr;
11-
use std::collections::{BTreeMap, BTreeSet};
11+
use std::collections::{BTreeMap, BTreeSet, HashMap};
1212
use std::ffi::OsString;
1313
use std::io::Read;
1414
use std::path::{Path, PathBuf};
@@ -179,6 +179,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
179179
keep_going,
180180
mut targets,
181181
} = Args::parse();
182+
let target_list = targets
183+
.iter()
184+
.filter_map(|x| x.clone().into_string().ok())
185+
.fold(String::new(), |acc, x| acc + " " + &x);
182186

183187
let mut status_code = 0;
184188

@@ -199,6 +203,10 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
199203
print,
200204
precious: false,
201205
terminate,
206+
macros: HashMap::from([
207+
(String::from("MAKECMDGOALS"), target_list),
208+
(String::from("MAKE"), env::args().next().unwrap()),
209+
]),
202210
..Default::default()
203211
};
204212

make/src/parser/lex.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,6 @@ impl<'a> Lexer<'a> {
2727
}
2828
}
2929

30-
fn is_whitespace(c: char) -> bool {
31-
c == ' ' || c == '\t'
32-
}
33-
3430
fn is_newline(c: char) -> bool {
3531
c == '\n' || c == '\r'
3632
}
@@ -64,6 +60,9 @@ impl<'a> Lexer<'a> {
6460
/// - `None` if the input is exhausted.
6561
///
6662
fn next_token(&mut self) -> Option<(SyntaxKind, String)> {
63+
while matches!(self.input.peek(), Some(' ')) {
64+
self.input.next();
65+
}
6766
if let Some(&c) = self.input.peek() {
6867
match (c, self.line_type) {
6968
('\t', None) => {
@@ -96,9 +95,6 @@ impl<'a> Lexer<'a> {
9695
Some((SyntaxKind::TEXT, self.read_while(|c| !Self::is_newline(c))))
9796
}
9897
LineType::Other => match c {
99-
c if Self::is_whitespace(c) => {
100-
Some((SyntaxKind::WHITESPACE, self.read_while(Self::is_whitespace)))
101-
}
10298
c if Self::is_valid_identifier_char(c) => {
10399
let ident = self.read_while(Self::is_valid_identifier_char);
104100

make/src/parser/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ pub mod lex;
1010
pub mod parse;
1111
pub mod preprocessor;
1212

13-
pub use parse::{Identifier, Makefile, Rule, VariableDefinition};
13+
pub use parse::{Identifier, MacroDef, Makefile, Rule};
1414

1515
/// Let's start with defining all kinds of tokens and
1616
/// composite nodes.
@@ -71,6 +71,7 @@ pub enum SyntaxKind {
7171
RECIPE,
7272
VARIABLE,
7373
EXPR,
74+
MACRODEF,
7475
MACRO,
7576
}
7677

make/src/parser/parse.rs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ impl rowan::Language for Lang {
7575
use rowan::GreenNode;
7676

7777
use super::SyntaxKind;
78-
use crate::parser::preprocessor::preprocess;
7978
/// You can construct GreenNodes by hand, but a builder
8079
/// is helpful for top-down parsers: it maintains a stack
8180
/// of currently in-progress nodes
@@ -144,7 +143,7 @@ pub fn parse(text: &str) -> Result<Parsed, ParseError> {
144143
self.builder.start_node(RECIPE.into());
145144
self.expect(INDENT);
146145
self.expect(TEXT);
147-
self.expect(NEWLINE);
146+
self.try_expect(NEWLINE);
148147
self.builder.finish_node();
149148
}
150149

@@ -156,18 +155,30 @@ pub fn parse(text: &str) -> Result<Parsed, ParseError> {
156155
self.expect(IDENTIFIER);
157156
self.skip_ws();
158157
}
159-
self.expect(NEWLINE);
158+
self.try_expect(NEWLINE);
160159
self.builder.token(IDENTIFIER.into(), "variables.mk");
161160
dbg!(&self.builder);
162161
self.builder.finish_node();
163162
}
164163

164+
fn parse_macro_defintion(&mut self) {
165+
self.builder.start_node(MACRODEF.into());
166+
self.try_expect(EXPORT);
167+
self.expect(IDENTIFIER);
168+
self.expect(EQUALS);
169+
self.parse_expr();
170+
self.builder.finish_node();
171+
}
172+
165173
fn parse_rule(&mut self) {
166174
self.builder.start_node(RULE.into());
167175
self.skip_ws();
168176
self.try_expect(EXPORT);
169177
self.skip_ws();
170-
self.expect(IDENTIFIER);
178+
let is_pattern = self.try_expect(PERCENT);
179+
if !is_pattern {
180+
self.expect(IDENTIFIER);
181+
}
171182
self.skip_ws();
172183
if self.tokens.pop() == Some((COLON, ":".to_string())) {
173184
self.builder.token(COLON.into(), ":");
@@ -176,7 +187,7 @@ pub fn parse(text: &str) -> Result<Parsed, ParseError> {
176187
}
177188
self.skip_ws();
178189
self.parse_expr();
179-
self.expect(NEWLINE);
190+
self.try_expect(NEWLINE);
180191
loop {
181192
match self.current() {
182193
Some(INDENT) => {
@@ -197,7 +208,10 @@ pub fn parse(text: &str) -> Result<Parsed, ParseError> {
197208
fn parse(mut self) -> Parse {
198209
self.builder.start_node(ROOT.into());
199210
loop {
200-
match self.find(|&&(k, _)| k == COLON || k == NEWLINE || k == INCLUDE) {
211+
match self
212+
.find(|&&(k, _)| k == COLON || k == NEWLINE || k == INCLUDE || k == EQUALS)
213+
{
214+
Some((EQUALS, "=")) => self.parse_macro_defintion(),
201215
Some((COLON, ":")) => {
202216
self.parse_rule();
203217
}
@@ -347,12 +361,16 @@ macro_rules! ast_node {
347361
};
348362
}
349363

364+
ast_node!(Macro, MACRO);
365+
ast_node!(MacroDef, MACRODEF);
350366
ast_node!(Makefile, ROOT);
351367
ast_node!(Rule, RULE);
352368
ast_node!(Identifier, IDENTIFIER);
353-
ast_node!(VariableDefinition, VARIABLE);
354369

355-
impl VariableDefinition {
370+
impl Macro {}
371+
impl MacroDef {}
372+
373+
impl MacroDef {
356374
pub fn name(&self) -> Option<String> {
357375
self.syntax().children_with_tokens().find_map(|it| {
358376
it.as_token().and_then(|it| {
@@ -403,15 +421,17 @@ impl Makefile {
403421
self.syntax().children().filter_map(Rule::cast)
404422
}
405423

424+
pub fn macros(&self) -> impl Iterator<Item = MacroDef> {
425+
self.syntax().children().filter_map(MacroDef::cast)
426+
}
427+
406428
pub fn rules_by_target<'a>(&'a self, target: &'a str) -> impl Iterator<Item = Rule> + 'a {
407429
self.rules()
408430
.filter(move |rule| rule.targets().any(|t| t == target))
409431
}
410432

411-
pub fn variable_definitions(&self) -> impl Iterator<Item = VariableDefinition> {
412-
self.syntax()
413-
.children()
414-
.filter_map(VariableDefinition::cast)
433+
pub fn variable_definitions(&self) -> impl Iterator<Item = MacroDef> {
434+
self.syntax().children().filter_map(MacroDef::cast)
415435
}
416436

417437
pub fn add_rule(&mut self, target: &str) -> Rule {
@@ -530,7 +550,6 @@ impl FromStr for Makefile {
530550
type Err = ParseError;
531551

532552
fn from_str(s: &str) -> Result<Self, Self::Err> {
533-
let processed = preprocess(s).map_err(|e| ParseError(vec![e.to_string()]))?;
534-
parse(&processed).map(|node| node.root())
553+
parse(s).map(|node| node.root())
535554
}
536555
}

0 commit comments

Comments
 (0)