Améliorations du parser et ajout de nouveaux fichiers de test pour vérifier le comportement des variables sur plusieurs lignes avec Bake et Make, et suppression des fichiers obsolètes.

This commit is contained in:
Moncef STITI 2025-02-22 21:21:51 +01:00
parent a228509e8f
commit 51d6970ef1
13 changed files with 184 additions and 157 deletions

@ -9,41 +9,54 @@ import java.util.regex.Pattern;
import java.util.stream.Collectors;
public class BakefileParser {
/**
/**
* Nom du fichier Bakefile à parser (donc Bakefile...).
*/
private String filename;
/**
/**
* Regex pour détecter les targets et leurs dépendances.
* Format : "nom : dépendance1 dépendance2"
*/
private static final Pattern TARGET_PATTERN = Pattern.compile("^(\\S+)\\s*:\\s*([^#]*?)\\s*(?:#.*)?$");
/**
/**
* Regex pour détecter les lignes de commande associées à une target.
* Format : " gcc -o program program.c" (ligne indentée)
*/
private static final Pattern COMMAND_PATTERN = Pattern.compile("^\\t(.+)$");
/**
/**
* Regex pour détecter les définitions de variables.
* Format : "FLAGS = -ansi -pedantic"
*/
private static final Pattern VARIABLE_PATTERN = Pattern.compile("^(\\w+)\\s*=\\s*([^#]*?)\\s*(?:#.*)?$");
/**
/**
* Regex pour détecter les déclarations .PHONY
* Format : ".PHONY: clean all"
*/
private static final Pattern PHONY_PATTERN = Pattern.compile("^\\.PHONY:\\s*([^#]*?)\\s*(?:#.*)?$");
/**
/**
* Regex pour détecter les lignes de continuation.
* Format : " gcc -o program program.c \"
*/
private static final Pattern CONTINUATION_PATTERN = Pattern.compile("^(.*)\\\\\\s*$");
/**
* Regex pour détecter les références de variables.
* Format : "${VAR}" ou "$(VAR)"
*/
private static final Pattern VARIABLE_REFERENCE = Pattern.compile("\\$\\{(\\w+)\\}|\\$\\((\\w+)\\)");
/**
* Première cible trouvée dans le fichier Bakefile.
*/
private static String firstTarget;
/**
/**
* Stocke les variables définies dans le Bakefile.
*/
private Map<String, String> variables = new HashMap<>();
@ -53,15 +66,77 @@ public class BakefileParser {
firstTarget = null;
}
private String handleContinuationLines(List<String> lines, int startIndex) {
StringBuilder combinedLine = new StringBuilder();
int i = startIndex;
while (i < lines.size()) {
String line = lines.get(i);
Matcher contMatcher = CONTINUATION_PATTERN.matcher(line);
if (contMatcher.matches()) {
// Ajouter la ligne sans le backslash
combinedLine.append(contMatcher.group(1).trim()).append(" ");
i++;
} else {
// Ajouter la dernière ligne et sortir
combinedLine.append(line.trim());
break;
}
}
return combinedLine.toString();
}
private String replaceVariables(String input) {
if (input == null) return null;
String result = input;
Set<String> processedVars = new HashSet<>();
boolean changed;
do {
changed = false;
Matcher matcher = VARIABLE_REFERENCE.matcher(result);
StringBuffer sb = new StringBuffer();
while (matcher.find()) {
String varName = matcher.group(1) != null ? matcher.group(1) : matcher.group(2);
if (!processedVars.contains(varName) && variables.containsKey(varName)) {
String replacement = variables.get(varName);
matcher.appendReplacement(sb, Matcher.quoteReplacement(replacement));
changed = true;
processedVars.add(varName);
}
}
matcher.appendTail(sb);
result = sb.toString();
// Si aucun changement n'a été fait dans ce passage, arrêter
if (!changed) {
break;
}
// Réinitialiser processedVars pour le prochain passage si nécessaire
processedVars.clear();
} while (changed);
return result.trim();
}
private List<String> replaceVariablesInList(List<String> items) {
return items.stream()
.map(this::replaceVariables)
.collect(Collectors.toList());
}
private List<String> splitDependencies(String depStr) {
if (depStr == null || depStr.trim().isEmpty()) {
return new ArrayList<>();
}
// Remplacer les variables avant de split
String resolvedStr = replaceVariables(depStr.trim());
// Split sur un ou plusieurs espaces et filtrer les chaînes vides
return Arrays.stream(resolvedStr.split("\\s+"))
.map(String::trim)
.filter(s -> !s.isEmpty())
@ -82,34 +157,48 @@ public class BakefileParser {
String currentTarget = null;
List<String> dependencies = new ArrayList<>();
List<String> commands = new ArrayList<>();
int lineNumber = 0;
for (String line : lines) {
lineNumber++;
Matcher varMatcher = VARIABLE_PATTERN.matcher(line);
Matcher targetMatcher = TARGET_PATTERN.matcher(line);
Matcher commandMatcher = COMMAND_PATTERN.matcher(line);
Matcher phonyMatcher = PHONY_PATTERN.matcher(line);
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
// Vérifier si la ligne a un caractère de continuation
Matcher contMatcher = CONTINUATION_PATTERN.matcher(line);
if (contMatcher.matches()) {
// Récupérer toute la définition multi-ligne
line = handleContinuationLines(lines, i);
// Ajuster i pour sauter les lignes traitées
while (i + 1 < lines.size() &&
CONTINUATION_PATTERN.matcher(lines.get(i)).matches()) {
i++;
}
}
if (line.trim().isEmpty()) {
continue;
}
if (line.matches("^ +.*$")) {
System.err.println(filename + ":" + lineNumber + ": *** missing separator. Stop.");
System.err.println(filename + ":" + (i+1) + ": *** missing separator. Stop.");
System.exit(1);
}
Matcher varMatcher = VARIABLE_PATTERN.matcher(line);
Matcher targetMatcher = TARGET_PATTERN.matcher(line);
Matcher commandMatcher = COMMAND_PATTERN.matcher(line);
Matcher phonyMatcher = PHONY_PATTERN.matcher(line);
if (phonyMatcher.matches()) {
// Ajouter les cibles .PHONY à l'ensemble des cibles phony
String[] phonies = phonyMatcher.group(1).trim().split("\\s+");
Collections.addAll(phonyTargets, phonies);
continue;
}
if (varMatcher.matches()) {
variables.put(varMatcher.group(1), varMatcher.group(2).trim());
String varName = varMatcher.group(1);
String varValue = varMatcher.group(2).trim();
// Évaluer les variables référencées dans la valeur
varValue = replaceVariables(varValue);
variables.put(varName, varValue);
} else if (targetMatcher.matches()) {
if (currentTarget != null) {
String resolvedTarget = replaceVariables(currentTarget.trim());
@ -121,7 +210,6 @@ public class BakefileParser {
phonyTargets.contains(resolvedTarget)
));
// Ne définir firstTarget que si ce n'est pas une cible .PHONY
if (firstTarget == null && !phonyTargets.contains(resolvedTarget)) {
firstTarget = resolvedTarget;
}
@ -145,7 +233,6 @@ public class BakefileParser {
phonyTargets.contains(resolvedTarget)
));
// Vérifier une dernière fois pour firstTarget
if (firstTarget == null && !phonyTargets.contains(resolvedTarget)) {
firstTarget = resolvedTarget;
}
@ -157,36 +244,10 @@ public class BakefileParser {
return rules;
}
private String replaceVariables(String input) {
if (input == null) return null;
String result = input;
boolean replaced;
do {
replaced = false;
for (Map.Entry<String, String> entry : variables.entrySet()) {
String key = "$(" + entry.getKey() + ")";
if (result.contains(key)) {
// Préserver les espaces dans les valeurs des variables
result = result.replace(key, entry.getValue());
replaced = true;
}
}
} while (replaced);
return result.trim();
}
private List<String> replaceVariablesInList(List<String> items) {
List<String> resolved = new ArrayList<>();
for (String item : items) {
resolved.add(replaceVariables(item));
}
return resolved;
}
/**
* Permet de récupérer la première cible trouvée dans le fichier Bakefile.
* @return La première cible trouvée
*/
/**
* Récupérer la première cible
* @return String la première cible
*/
public static String getFirstTarget() {
return firstTarget;
}

@ -1,40 +1,21 @@
# Ce makefile viens de : https://iut-fbleau.fr/sitebp/doc_make/makefile/
### VARIABLES ###
CC = gcc
COMPILER = gcc
CFLAGS = -Wall \
-ansi \
-pedantic
LIBS = -lm
EXE = exemple
OFILES = extension.o \
main.o
### BUT PAR DEFAUT ###
but : ${EXE}
### REGLES ESSENTIELLES ###
extension.o : extension.h
main.o : extension.h
${EXE} : ${OFILES}
$(CC) $(CFLAGS) -o ${EXE} ${OFILES} ${LIBS}
### REGLES OPTIONNELLES ###
run : but
./${EXE}
clean :
-rm -f ${OFILES} ${EXE}
mrproper : clean but
### BUTS FACTICES ###
.PHONY : but clean mrproper
### FIN ###
OUTPUT = program
OBJS = main.o module.o
# Règle principale
$(OUTPUT): $(OBJS)
$(COMPILER) $(CFLAGS) -o $(OUTPUT) $(OBJS)
main.o: main.c
$(COMPILER) $(CFLAGS) -c main.c -o main.o
module.o: module.c
$(COMPILER) $(CFLAGS) -c module.c -o module.o
clean:
rm -f $(OUTPUT) $(OBJS)

@ -1,6 +0,0 @@
#include <stdio.h>
#include "extension.h"
void afficher_message(void) {
printf("Hello, world! Ceci est un message depuis extension.c\n");
}

@ -1,6 +0,0 @@
#ifndef EXTENSION_H
#define EXTENSION_H
void afficher_message(void);
#endif /* EXTENSION_H */

@ -1,6 +1,8 @@
#include "extension.h"
#include <stdio.h>
#include "module.h"
int main(void) {
afficher_message();
printf("Hello from main!\n");
print_message();
return 0;
}

@ -0,0 +1,6 @@
#include <stdio.h>
#include "module.h"
void print_message(void) {
printf("Hello from module!\n");
}

@ -0,0 +1,6 @@
#ifndef MODULE_H
#define MODULE_H
void print_message(void);
#endif

@ -1,40 +1,21 @@
# Ce makefile viens de : https://iut-fbleau.fr/sitebp/doc_make/makefile/
### VARIABLES ###
CC = gcc
COMPILER = gcc
CFLAGS = -Wall \
-ansi \
-pedantic
LIBS = -lm
EXE = exemple
OFILES = extension.o \
main.o
### BUT PAR DEFAUT ###
but : ${EXE}
### REGLES ESSENTIELLES ###
extension.o : extension.h
main.o : extension.h
${EXE} : ${OFILES}
$(CC) $(CFLAGS) -o ${EXE} ${OFILES} ${LIBS}
### REGLES OPTIONNELLES ###
run : but
./${EXE}
clean :
-rm -f ${OFILES} ${EXE}
mrproper : clean but
### BUTS FACTICES ###
.PHONY : but clean mrproper
### FIN ###
OUTPUT = program
OBJS = main.o module.o
# Règle principale
$(OUTPUT): $(OBJS)
$(COMPILER) $(CFLAGS) -o $(OUTPUT) $(OBJS)
main.o: main.c
$(COMPILER) $(CFLAGS) -c main.c -o main.o
module.o: module.c
$(COMPILER) $(CFLAGS) -c module.c -o module.o
clean:
rm -f $(OUTPUT) $(OBJS)

@ -1,6 +0,0 @@
#include <stdio.h>
#include "extension.h"
void afficher_message(void) {
printf("Hello, world! Ceci est un message depuis extension.c\n");
}

@ -1,6 +0,0 @@
#ifndef EXTENSION_H
#define EXTENSION_H
void afficher_message(void);
#endif /* EXTENSION_H */

@ -1,6 +1,8 @@
#include "extension.h"
#include <stdio.h>
#include "module.h"
int main(void) {
afficher_message();
printf("Hello from main!\n");
print_message();
return 0;
}

@ -0,0 +1,6 @@
#include <stdio.h>
#include "module.h"
void print_message(void) {
printf("Hello from module!\n");
}

@ -0,0 +1,6 @@
#ifndef MODULE_H
#define MODULE_H
void print_message(void);
#endif