diff --git a/src/fr/monlouyan/bakefile/BakefileParser.java b/src/fr/monlouyan/bakefile/BakefileParser.java index 3177d7a..3393208 100644 --- a/src/fr/monlouyan/bakefile/BakefileParser.java +++ b/src/fr/monlouyan/bakefile/BakefileParser.java @@ -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; } diff --git a/tests/C/test-16-strange-variables/bake/Bakefile b/tests/C/test-16-strange-variables/bake/Bakefile index d26125b..943bec4 100644 --- a/tests/C/test-16-strange-variables/bake/Bakefile +++ b/tests/C/test-16-strange-variables/bake/Bakefile @@ -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 ### \ No newline at end of file +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) + diff --git a/tests/C/test-16-strange-variables/bake/extension.c b/tests/C/test-16-strange-variables/bake/extension.c deleted file mode 100644 index 2351a03..0000000 --- a/tests/C/test-16-strange-variables/bake/extension.c +++ /dev/null @@ -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"); -} diff --git a/tests/C/test-16-strange-variables/bake/extension.h b/tests/C/test-16-strange-variables/bake/extension.h deleted file mode 100644 index 1897c4b..0000000 --- a/tests/C/test-16-strange-variables/bake/extension.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef EXTENSION_H -#define EXTENSION_H - -void afficher_message(void); - -#endif /* EXTENSION_H */ diff --git a/tests/C/test-16-strange-variables/bake/main.c b/tests/C/test-16-strange-variables/bake/main.c index 2c3554b..506aec3 100644 --- a/tests/C/test-16-strange-variables/bake/main.c +++ b/tests/C/test-16-strange-variables/bake/main.c @@ -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; } diff --git a/tests/C/test-16-strange-variables/bake/module.c b/tests/C/test-16-strange-variables/bake/module.c new file mode 100644 index 0000000..e4ba9e9 --- /dev/null +++ b/tests/C/test-16-strange-variables/bake/module.c @@ -0,0 +1,6 @@ +#include <stdio.h> +#include "module.h" + +void print_message(void) { + printf("Hello from module!\n"); +} diff --git a/tests/C/test-16-strange-variables/bake/module.h b/tests/C/test-16-strange-variables/bake/module.h new file mode 100644 index 0000000..5c126b5 --- /dev/null +++ b/tests/C/test-16-strange-variables/bake/module.h @@ -0,0 +1,6 @@ +#ifndef MODULE_H +#define MODULE_H + +void print_message(void); + +#endif diff --git a/tests/C/test-16-strange-variables/make/Makefile b/tests/C/test-16-strange-variables/make/Makefile index d26125b..943bec4 100644 --- a/tests/C/test-16-strange-variables/make/Makefile +++ b/tests/C/test-16-strange-variables/make/Makefile @@ -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 ### \ No newline at end of file +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) + diff --git a/tests/C/test-16-strange-variables/make/extension.c b/tests/C/test-16-strange-variables/make/extension.c deleted file mode 100644 index 2351a03..0000000 --- a/tests/C/test-16-strange-variables/make/extension.c +++ /dev/null @@ -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"); -} diff --git a/tests/C/test-16-strange-variables/make/extension.h b/tests/C/test-16-strange-variables/make/extension.h deleted file mode 100644 index 1897c4b..0000000 --- a/tests/C/test-16-strange-variables/make/extension.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef EXTENSION_H -#define EXTENSION_H - -void afficher_message(void); - -#endif /* EXTENSION_H */ diff --git a/tests/C/test-16-strange-variables/make/main.c b/tests/C/test-16-strange-variables/make/main.c index 2c3554b..506aec3 100644 --- a/tests/C/test-16-strange-variables/make/main.c +++ b/tests/C/test-16-strange-variables/make/main.c @@ -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; } diff --git a/tests/C/test-16-strange-variables/make/module.c b/tests/C/test-16-strange-variables/make/module.c new file mode 100644 index 0000000..e4ba9e9 --- /dev/null +++ b/tests/C/test-16-strange-variables/make/module.c @@ -0,0 +1,6 @@ +#include <stdio.h> +#include "module.h" + +void print_message(void) { + printf("Hello from module!\n"); +} diff --git a/tests/C/test-16-strange-variables/make/module.h b/tests/C/test-16-strange-variables/make/module.h new file mode 100644 index 0000000..5c126b5 --- /dev/null +++ b/tests/C/test-16-strange-variables/make/module.h @@ -0,0 +1,6 @@ +#ifndef MODULE_H +#define MODULE_H + +void print_message(void); + +#endif