From c8b24673cc3105a8d16e37a915d5b04074b3a3a1 Mon Sep 17 00:00:00 2001 From: Peizhao Ou <peizhaoo@uci.edu> Date: Fri, 14 Mar 2014 16:46:21 -0700 Subject: [PATCH] temporal --- grammer/spec_compiler.jj | 90 +++- grammer/util.jj | 436 ++++++++++++++++++ run-javacc.sh | 6 + .../codeGenerator/CodeVariables.java | 4 +- .../SequentialDefineSubConstruct.java | 2 +- .../specExtraction/SpecExtractor.java | 3 + .../specExtraction/SpecInfoScanner.java | 67 +++ 7 files changed, 585 insertions(+), 23 deletions(-) create mode 100644 grammer/util.jj create mode 100644 src/edu/uci/eecs/specCompiler/specExtraction/SpecInfoScanner.java diff --git a/grammer/spec_compiler.jj b/grammer/spec_compiler.jj index a58db93..9950ad8 100644 --- a/grammer/spec_compiler.jj +++ b/grammer/spec_compiler.jj @@ -114,6 +114,7 @@ import java.io.File; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; +import java.util.Arrays; import edu.uci.eecs.specCompiler.specExtraction.Construct; import edu.uci.eecs.specCompiler.specExtraction.GlobalConstruct; @@ -141,10 +142,10 @@ import edu.uci.eecs.specCompiler.specExtraction.VariableDeclaration; public static void main(String[] argvs) throws ParseException, TokenMgrError { try { - File f = new File("./grammer/spec.txt"); + File f = new File("./grammer/spec1.txt"); FileInputStream fis = new FileInputStream(f); SpecParser parser = new SpecParser(fis); - /* + ArrayList<String> content = new ArrayList<String>(); ArrayList<Construct> constructs = new ArrayList<Construct>(); ArrayList<String> headers = new ArrayList<String>(); @@ -156,9 +157,9 @@ import edu.uci.eecs.specCompiler.specExtraction.VariableDeclaration; for (int i = 0; i < constructs.size(); i++) { System.out.println(constructs.get(i)); } - */ - parser.Test(); + + //parser.Test(); System.out.println("Parsing finished!"); } catch (FileNotFoundException e) { e.printStackTrace(); @@ -181,6 +182,13 @@ import edu.uci.eecs.specCompiler.specExtraction.VariableDeclaration; return null; } + + private static ArrayList<String> breakLines(String all) { + String lines[] = all.split("[\\r\\n]+"); + return new ArrayList<String>(Arrays.asList(lines)); + } + + public static ArrayList<VariableDeclaration> getTemplateArg(String line) throws ParseException { InputStream input = new ByteArrayInputStream(line.getBytes()); @@ -260,7 +268,7 @@ PARSER_END(SpecParser) -<*> SKIP : +<IN_POTENTIAL_SPEC, IN_SPEC> SKIP : { " " | @@ -289,6 +297,10 @@ SKIP : { "/*": IN_COMMENT } +<DEFAULT> TOKEN: { + <ANY: ~[]> +} + <*> SKIP : { // "//" comment for the specification <"//" (~["\n", "\r"])* (["\n", "\r"])> @@ -366,7 +378,7 @@ SKIP : { } -<IN_SPEC, DEFAULT> TOKEN : +<IN_SPEC> TOKEN : { /* Specification & C/C++ shared tokens */ // Reserved keywords @@ -562,7 +574,7 @@ String Type() : } { { type = ""; } - ("const" + (<CONST> { type = "const"; } )? (((str = <STRUCT>.image | str = <CLASS>.image | str = <UNSIGNED>.image) { type = type + " " + str; })? @@ -574,7 +586,7 @@ String Type() : type = name.fullName; }) ) - ((str = "const".image { + ((str = <CONST>.image { if (!type.equals("")) type = type + " " + str; else @@ -624,8 +636,8 @@ String ParameterizedName() : } { (str = <IDENTIFIER>.image {res = str;}) - ("<" str = Type() { res = res + "<" + str; } - ("," str = Type() { res = res + ", " + str; })* ">" + (<OPEN_BRACKET> str = Type() { res = res + "<" + str; } + (<COMMA> str = Type() { res = res + ", " + str; })* <CLOSE_BRACKET> { res = res + ">"; } )? { @@ -678,7 +690,7 @@ ArrayList<VariableDeclaration> TemplateParamList() : params = new ArrayList<VariableDeclaration>(); } <TEMPLATE> - "<" + <OPEN_BRACKET> (type = <IDENTIFIER>.image name = <IDENTIFIER>.image { @@ -686,13 +698,13 @@ ArrayList<VariableDeclaration> TemplateParamList() : } ) - ("," type = <IDENTIFIER>.image + (<COMMA> type = <IDENTIFIER>.image name = <IDENTIFIER>.image { params.add(new VariableDeclaration(type, name)); } )* - ">" + <CLOSE_BRACKET> { //System.out.println(params); return params; @@ -708,10 +720,10 @@ ArrayList<VariableDeclaration > FormalParamList() : { typeParams = new ArrayList<VariableDeclaration >(); } - "(" + <OPEN_PAREN> ((varDecl = TypeParam() {typeParams.add(varDecl);}) ((<COMMA> varDecl = TypeParam() {typeParams.add(varDecl);}))*)? - ")" + <CLOSE_PAREN> { return typeParams; } @@ -829,17 +841,55 @@ ArrayList<String> C_CPP_CODE(ArrayList<String> headers) : void Parse(File f, ArrayList<String> content, ArrayList<Construct> constructs, ArrayList<String> headers) : { Construct inst; - ArrayList<String> code; + StringBuilder sb; + boolean flushSB; } { { _file = f; _content = content; _constructs = constructs; + sb = new StringBuilder(); + } + ( + (inst = ParseSpec() + { + _constructs.add(inst); + } + ) | + //((code = C_CPP_CODE(headers)) { _content.addAll(code); }) + ( + flushSB = OriginalCode(sb) + { + if (flushSB) { + sb = new StringBuilder(); + } + } + ) + )* + // For the last piece of code + { + _content.add(sb.toString()); + } + <EOF> +} + +// If true, there's a new line and sb should be flushed +boolean OriginalCode(StringBuilder sb) : +{ + String str; +} +{ + str = <ANY>.image + { + if (!str.equals("\n")) { + sb.append(str); + return false; + } else { + _content.add(sb.toString()); + return true; + } } - ((inst = ParseSpec() { _constructs.add(inst); }) | - ((code = C_CPP_CODE(headers)) { _content.addAll(code); }) - )* <EOF> } Construct ParseSpec() : @@ -920,7 +970,7 @@ SequentialDefineSubConstruct Global_define() : <GLOBAL_DEFINE> (<DECLARE_STRUCT> (declareStruct = C_CPP_CODE(null) { declareStructs.add(declareStruct); }))* - (<DECLARE_VAR> ((declareVar = TypeParam() ";" { + (<DECLARE_VAR> ((declareVar = TypeParam() <SEMI_COLON> { declareVars.add(declareVar); } )*))? (<INIT_VAR> (code = C_CPP_CODE(null) { initVar = code; } ))? (<DEFINE_FUNC> (defineFunc = C_CPP_CODE(null) { defineFuncs.add(defineFunc); }))* diff --git a/grammer/util.jj b/grammer/util.jj new file mode 100644 index 0000000..2be56d6 --- /dev/null +++ b/grammer/util.jj @@ -0,0 +1,436 @@ +/* util.jj Grammer definition for utility functions */ + +options { + STATIC = false; + JAVA_UNICODE_ESCAPE = true; +} + +PARSER_BEGIN(UtilParser) +package edu.uci.eecs.specCompiler.grammerParser.utilParser; +import edu.uci.eecs.specCompiler.specExtraction.FunctionHeader; +import edu.uci.eecs.specCompiler.specExtraction.QualifiedName; +import edu.uci.eecs.specCompiler.specExtraction.VariableDeclaration; + +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.util.ArrayList; + + public class UtilParser { + public static void main(String[] argvs) + throws ParseException, TokenMgrError { + try { + File f = new File("./grammer/spec1.txt"); + FileInputStream fis = new FileInputStream(f); + UtilParser parser = new UtilParser(fis); + + //parser.Test(); + System.out.println("Parsing finished!"); + } catch (FileNotFoundException e) { + e.printStackTrace(); + } + } + + public static ArrayList<VariableDeclaration> getTemplateArg(String line) + throws ParseException { + InputStream input = new ByteArrayInputStream(line.getBytes()); + UtilParser parser = new UtilParser(input); + return parser.TemplateParamList(); + } + + public static FunctionHeader parseFuncHeader(String line) + throws ParseException { + InputStream input = new ByteArrayInputStream(line.getBytes()); + UtilParser parser = new UtilParser(input); + return parser.FuncDecl(); + } + + + public static String stringArray2String(ArrayList<String> content) { + StringBuilder sb = new StringBuilder(); + if (content.size() == 1) + return content.get(0); + for (int i = 0; i < content.size(); i++) { + sb.append(content.get(i) + "\n"); + } + return sb.toString(); + } + } +PARSER_END(UtilParser) + +SKIP : +{ + " " +| + "\n" +| + "\r" +| + "\r\n" +| + "\t" +} + +TOKEN : +{ +/* Specification & C/C++ shared tokens */ +// Reserved keywords + <CONST: "const"> +| + <STRUCT: "struct"> +| + <CLASS: "class"> +| + <UNSIGNED: "unsigned"> +| + <TEMPLATE: "template"> +| + <INLINE: "inline"> +| + <STATIC: "static"> +| + <FOR: "for"> +| + <#DIGIT: ["0"-"9"]> +| + <#LETTER: ["a"-"z", "A"-"Z"]> +| + <IDENTIFIER: (<LETTER> | "_") (<LETTER> | <DIGIT> | "_")*> +| + <POUND: "#"> +| + <OPEN_BRACKET: "["> +| + <CLOSE_BRACKET: "]"> +| + <EQUALS: "="> +| + <OPEN_PAREN: "("> +| + <CLOSE_PAREN: ")"> +| + <OPEN_BRACE: "{"> +| + <CLOSE_BRACE: "}"> +| + <HB_SYMBOL: "->"> +| + <COMMA: ","> +| +/* C/C++ only token*/ + <DOT: "."> +| + <DOLLAR: "$"> +| + <STAR: "*"> +| + <NEGATE: "~"> +| + <EXCLAMATION: "!"> +| + <AND: "&"> +| + <OR: "|"> +| + <MOD: "%"> +| + <PLUS: "+"> +| + <PLUSPLUS: "++"> +| + <MINUS: "-"> +| + <MINUSMINUS: "--"> +| + <DIVIDE: "/"> +| + <BACKSLASH: "\\"> +| + <LESS_THAN: "<"> +| + <GREATER_THAN: ">"> +| + <GREATER_EQUALS: ">="> +| + <LESS_EQUALS: "<="> +| + <LOGICAL_EQUALS: "=="> +| + <NOT_EQUALS: "!="> +| + <LOGICAL_AND: "&&"> +| + <LOGICAL_OR: "||"> +| + <XOR: "^"> +| + <QUESTION_MARK: "?"> +| + <COLON: ":"> +| + <DOUBLECOLON: "::"> +| + <DOUBLELESSTHAN: "<<"> +| + <DOUBLEGREATERTHAN: ">>"> +| + <TRIPLEGREATERTHAN: ">>>"> +| + <PLUS_EQUALS: "+="> +| + <MINUS_EQUALS: "-="> +| + <TIMES_EQUALS: "*="> +| + <DIVIDE_EQUALS: "/="> +| + <MOD_EQUALS: "%="> +| + <XOR_EQUALS: "^="> +| + <OR_EQUALS: "|="> +| + <AND_EQUALS: "&="> +| + <SEMI_COLON: ";"> +| + <STRING_LITERAL: + "\"" + ((~["\"","\\","\n","\r"]) + | ("\\" + ( ["n","t","b","r","f","\\","'","\""] + | ["0"-"7"] ( ["0"-"7"] )? + | ["0"-"3"] ["0"-"7"] + ["0"-"7"] + ) + ) + )* + "\""> +| + <CHARACTER_LITERAL: + "'" + ((~["'","\\","\n","\r"]) + | ("\\" + (["n","t","b","r","f","\\","'","\""] + | ["0"-"7"] ( ["0"-"7"] )? + | ["0"-"3"] ["0"-"7"] + ["0"-"7"] + ) + ) + ) + "'"> +| + < INTEGER_LITERAL: + <DECIMAL_LITERAL> (["l","L"])? + | <HEX_LITERAL> (["l","L"])? + | <OCTAL_LITERAL> (["l","L"])?> +| + < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* > +| + < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ > +| + < #OCTAL_LITERAL: "0" (["0"-"7"])* > +| + < FLOATING_POINT_LITERAL: + <DECIMAL_FLOATING_POINT_LITERAL> + | <HEXADECIMAL_FLOATING_POINT_LITERAL> > +| + < #DECIMAL_FLOATING_POINT_LITERAL: + (["0"-"9"])+ "." (["0"-"9"])* (<DECIMAL_EXPONENT>)? (["f","F","d","D"])? + | "." (["0"-"9"])+ (<DECIMAL_EXPONENT>)? (["f","F","d","D"])? + | (["0"-"9"])+ <DECIMAL_EXPONENT> (["f","F","d","D"])? + | (["0"-"9"])+ (<DECIMAL_EXPONENT>)? ["f","F","d","D"]> +| + < #DECIMAL_EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ > +| + < #HEXADECIMAL_FLOATING_POINT_LITERAL: + "0" ["x", "X"] (["0"-"9","a"-"f","A"-"F"])+ (".")? <HEXADECIMAL_EXPONENT> (["f","F","d","D"])? + | "0" ["x", "X"] (["0"-"9","a"-"f","A"-"F"])* "." (["0"-"9","a"-"f","A"-"F"])+ <HEXADECIMAL_EXPONENT> (["f","F","d","D"])?> +| + < #HEXADECIMAL_EXPONENT: ["p","P"] (["+","-"])? (["0"-"9"])+ > +| + < #SPACE: (" " | "\t")+> +| + < #TO_END_OF_LINE: (~["\n"])+> +| + /* Macro token */ + <INCLUDE: "#" (<SPACE>)? "include" <SPACE> (<STRING_LITERAL> | "<" (<LETTER> | <DOT>)+ ">")> +| + <DEFINE: "#" (<SPACE>)? <TO_END_OF_LINE>> +} + +String Type() : +{ + String type; + String str; + QualifiedName name; +} +{ + { type = ""; } + (<CONST> + { type = "const"; } + )? + (((str = <STRUCT>.image | str = <CLASS>.image | str = <UNSIGNED>.image) { type = type + " " + str; })? + ( + name = ParseQualifiedName() { + if (!type.equals("")) + type = type + " " + name.fullName; + else + type = name.fullName; + }) + ) + ((str = <CONST>.image { + if (!type.equals("")) + type = type + " " + str; + else + type = str; + }) | + (str = <STAR>.image { + if (!type.equals("")) + type = type + " " + str; + else + type = str; + }) | + (str = <AND>.image { + if (!type.equals("")) + type = type + " " + str; + else + type = str; + }) + )* + { + return type; + } +} + +void Test() : +{ + String str; + FunctionHeader func; +} +{ + /* + str = Type() + { + System.out.println(str); + } + */ + func = FuncDecl() + { + System.out.println(func); + } + +} + +String ParameterizedName() : +{ + String res = ""; + String str; +} +{ + (str = <IDENTIFIER>.image {res = str;}) + (<OPEN_BRACKET> str = Type() { res = res + "<" + str; } + (<COMMA> str = Type() { res = res + ", " + str; })* <CLOSE_BRACKET> + { res = res + ">"; } + )? + { + return res; + } +} + +FunctionHeader FuncDecl() : +{ + String ret; + QualifiedName funcName; + ArrayList<VariableDeclaration> args; +} +{ + (<STATIC> | <INLINE>)* + ret = Type() + funcName = ParseQualifiedName() + args = FormalParamList() + { + FunctionHeader res = new FunctionHeader(ret, funcName, args); + //System.out.println(res); + return res; + } +} + +QualifiedName ParseQualifiedName() : +{ + String qualifiedName, str; +} +{ + { qualifiedName = ""; } + (str = ParameterizedName() { qualifiedName = qualifiedName + str; } ) + ( <DOUBLECOLON> (str = ParameterizedName() { qualifiedName = qualifiedName + + "::" + str; } ))* + { + QualifiedName res = new QualifiedName(qualifiedName); + //System.out.println(res); + return res; + } +} + +ArrayList<VariableDeclaration> TemplateParamList() : +{ + ArrayList<VariableDeclaration> params; + String type; + String name; +} +{ + { + params = new ArrayList<VariableDeclaration>(); + } + <TEMPLATE> + <OPEN_BRACKET> + (type = <IDENTIFIER>.image + name = <IDENTIFIER>.image + { + params.add(new VariableDeclaration(type, name)); + } + ) + + (<COMMA> type = <IDENTIFIER>.image + name = <IDENTIFIER>.image + { + params.add(new VariableDeclaration(type, name)); + } + )* + <CLOSE_BRACKET> + { + //System.out.println(params); + return params; + } +} + +ArrayList<VariableDeclaration > FormalParamList() : +{ + ArrayList<VariableDeclaration > typeParams; + VariableDeclaration varDecl; +} +{ + { + typeParams = new ArrayList<VariableDeclaration >(); + } + <OPEN_PAREN> + ((varDecl = TypeParam() {typeParams.add(varDecl);}) + ((<COMMA> varDecl = TypeParam() {typeParams.add(varDecl);}))*)? + <CLOSE_PAREN> + { + return typeParams; + } +} + +VariableDeclaration TypeParam() : +{ + String type, param; +} +{ + (type = Type()) (param = <IDENTIFIER>.image) + { + return new VariableDeclaration(type, param); + } +} diff --git a/run-javacc.sh b/run-javacc.sh index ab350be..fa9bdad 100755 --- a/run-javacc.sh +++ b/run-javacc.sh @@ -8,15 +8,21 @@ JAVACC_PATH=$SPEC_COMPILER_HOME/lib PRE_SCANNER_FILE=$SPEC_COMPILER_HOME/grammer/pre_scanner.jj GRAMMER_FILE=$SPEC_COMPILER_HOME/grammer/spec_compiler.jj +UTIL_FILE=$SPEC_COMPILER_HOME/grammer/util.jj PRE_SCANNER_OUTPUT_PATH=$SPEC_COMPILER_HOME/src/edu/uci/eecs/specCompiler/grammerParser/preScanner GRAMMER_OUTPUT_PATH=$SPEC_COMPILER_HOME/src/edu/uci/eecs/specCompiler/grammerParser +UTIL_OUTPUT_PATH=$SPEC_COMPILER_HOME/src/edu/uci/eecs/specCompiler/grammerParser/utilParser echo "Deleting the old generated java files." +rm -r $PRE_SCANNER_OUTPUT_PATH/* rm -r $GRAMMER_OUTPUT_PATH/* +rm -r $UTIL_OUTPUT_PATH/* mkdir -p $PRE_SCANNER_OUTPUT_PATH mkdir -p $GRAMMER_OUTPUT_PATH +mkdir -p $UTIL_OUTPUT_PATH java -cp $JAVACC_PATH/javacc.jar javacc -OUTPUT_DIRECTORY=$PRE_SCANNER_OUTPUT_PATH $PRE_SCANNER_FILE java -cp $JAVACC_PATH/javacc.jar javacc -OUTPUT_DIRECTORY=$GRAMMER_OUTPUT_PATH $GRAMMER_FILE +java -cp $JAVACC_PATH/javacc.jar javacc -OUTPUT_DIRECTORY=$UTIL_OUTPUT_PATH $UTIL_FILE diff --git a/src/edu/uci/eecs/specCompiler/codeGenerator/CodeVariables.java b/src/edu/uci/eecs/specCompiler/codeGenerator/CodeVariables.java index 5f2ef35..3a253c1 100644 --- a/src/edu/uci/eecs/specCompiler/codeGenerator/CodeVariables.java +++ b/src/edu/uci/eecs/specCompiler/codeGenerator/CodeVariables.java @@ -300,10 +300,10 @@ public class CodeVariables { private static FunctionHeader getFunctionHeader(SemanticsChecker semantics, Construct construct) { ArrayList<String> content = semantics.srcFilesInfo.get(construct.file).content; - String headerLine = content.get(construct.beginLineNum), templateLine = null; + String headerLine = content.get(construct.beginLineNum + 1), templateLine = null; if (headerLine.startsWith("template")) { templateLine = headerLine; - headerLine = content.get(construct.beginLineNum + 1); + headerLine = content.get(construct.beginLineNum + 2); } headerLine = headerLine.substring(0, headerLine.indexOf(')') + 1); try { diff --git a/src/edu/uci/eecs/specCompiler/specExtraction/SequentialDefineSubConstruct.java b/src/edu/uci/eecs/specCompiler/specExtraction/SequentialDefineSubConstruct.java index bb6b8e5..9d3bff6 100644 --- a/src/edu/uci/eecs/specCompiler/specExtraction/SequentialDefineSubConstruct.java +++ b/src/edu/uci/eecs/specCompiler/specExtraction/SequentialDefineSubConstruct.java @@ -22,7 +22,7 @@ public class SequentialDefineSubConstruct { StringBuffer res = new StringBuffer(); res.append("@Sequential_define:\n"); res.append("@DeclareStruct:\n"); - for (int i = 0; i < defineFuncs.size(); i++) { + for (int i = 0; i < declareStructs.size(); i++) { res.append(ParserUtils.array2Str(declareStructs.get(i)) + "\n"); } res.append("@DeclareVar:\n"); diff --git a/src/edu/uci/eecs/specCompiler/specExtraction/SpecExtractor.java b/src/edu/uci/eecs/specCompiler/specExtraction/SpecExtractor.java index 52862f4..9d1fa91 100644 --- a/src/edu/uci/eecs/specCompiler/specExtraction/SpecExtractor.java +++ b/src/edu/uci/eecs/specCompiler/specExtraction/SpecExtractor.java @@ -60,6 +60,9 @@ public class SpecExtractor { SourceFileInfo srcFileInfo; try { srcFileInfo = SpecParser.ParseFile(file); + for (int i = 0; i < srcFileInfo.content.size(); i++) { + System.out.println(srcFileInfo.content.get(i)); + } ParserUtils.write2File(srcFileInfo.file, srcFileInfo.content); srcFilesInfo.put(file, srcFileInfo); } catch (ParseException e) { diff --git a/src/edu/uci/eecs/specCompiler/specExtraction/SpecInfoScanner.java b/src/edu/uci/eecs/specCompiler/specExtraction/SpecInfoScanner.java new file mode 100644 index 0000000..093ab44 --- /dev/null +++ b/src/edu/uci/eecs/specCompiler/specExtraction/SpecInfoScanner.java @@ -0,0 +1,67 @@ +package edu.uci.eecs.specCompiler.specExtraction; + +import java.util.HashMap; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileReader; +import java.io.IOException; +import java.io.LineNumberReader; + +import java.util.regex.Pattern; +import java.util.regex.Matcher; + +/** + * <p> + * This class will scan all the input files, extract all the "special comments" + * (specifications). It should include information: 1. Beginning and end line of + * the specs; 2. The next two lines of code if it is interface constrcut. + * </p> + * + * @author peizhaoo + * + */ +public class SpecInfoScanner { + public final HashMap<File, Construct> constructs; + + public SpecInfoScanner() { + constructs = new HashMap<File, Construct>(); + } + + /** + * <p> + * Scan + * </p> + * + * @param file + */ + private void scanFile(File file) { + try { + FileReader fr = new FileReader(file); + LineNumberReader lnr = new LineNumberReader(fr); + String line = null; + // Info to keep when parsing the Spec + // 0 for default, 1 for potential sepc, 2 for in spec + int state = 0; + Pattern pBegin = Pattern.compile("^[\\s|\\t]*/**"), pEnd = Pattern + .compile("*/$"); + while ((line = lnr.readLine()) != null) { + Matcher m1 = pBegin.matcher(line); + if (m1.matches()) + state = 1; // Go to 'potential spec' state + if (state == 1) { + + } + } + } catch (FileNotFoundException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void scanFiles(File[] files) { + for (int i = 0; i < files.length; i++) { + scanFile(files[i]); + } + } +} -- 2.34.1