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:
parent
a228509e8f
commit
51d6970ef1
src/fr/monlouyan/bakefile
tests/C/test-16-strange-variables
@ -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;
|
||||
}
|
||||
|
6
tests/C/test-16-strange-variables/bake/module.c
Normal file
6
tests/C/test-16-strange-variables/bake/module.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
#include "module.h"
|
||||
|
||||
void print_message(void) {
|
||||
printf("Hello from module!\n");
|
||||
}
|
6
tests/C/test-16-strange-variables/bake/module.h
Normal file
6
tests/C/test-16-strange-variables/bake/module.h
Normal file
@ -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;
|
||||
}
|
||||
|
6
tests/C/test-16-strange-variables/make/module.c
Normal file
6
tests/C/test-16-strange-variables/make/module.c
Normal file
@ -0,0 +1,6 @@
|
||||
#include <stdio.h>
|
||||
#include "module.h"
|
||||
|
||||
void print_message(void) {
|
||||
printf("Hello from module!\n");
|
||||
}
|
6
tests/C/test-16-strange-variables/make/module.h
Normal file
6
tests/C/test-16-strange-variables/make/module.h
Normal file
@ -0,0 +1,6 @@
|
||||
#ifndef MODULE_H
|
||||
#define MODULE_H
|
||||
|
||||
void print_message(void);
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user