-
Notifications
You must be signed in to change notification settings - Fork 15
Expand file tree
/
Copy pathFileParser.java
More file actions
144 lines (128 loc) · 6.2 KB
/
FileParser.java
File metadata and controls
144 lines (128 loc) · 6.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
package io.github.syst3ms.skriptparser.file;
import io.github.syst3ms.skriptparser.log.ErrorType;
import io.github.syst3ms.skriptparser.log.SkriptLogger;
import io.github.syst3ms.skriptparser.util.FileUtils;
import io.github.syst3ms.skriptparser.util.color.Color;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* A class for parsing a plaintext file into a list of {@link FileElement}s representing every line.
*/
public class FileParser {
public static final Pattern LINE_PATTERN = Pattern.compile("^((?:[^#]|##)*)(\\s*#(?!#).*)$"); // Might as well take that from Skript
// 0 = not in a comment, 1 = in a one-line comment, 2 = in a block comment
private static int COMMENT_STATUS = 0;
/**
* Parses a {@linkplain List} of strings into a list of {@link FileElement}s. This creates {@link FileElement} and
* {@link FileSection} objects from the lines, effectively structuring the lines into a tree.
* This removes comments from each line, and discards any blank lines afterwards.
* @param fileName the name of the file the lines belong to
* @param lines the list of lines to parse
* @param expectedIndentation the indentation level the first line is expected to be at
* @param lastLine a parameter that keeps track of the line count throughout recursive calls of this method when
* parsing sections
* @param logger the logger
* @return a list of {@link FileElement}s
*/
public static List<FileElement> parseFileLines(String fileName, List<String> lines, int expectedIndentation, int lastLine, SkriptLogger logger) {
List<FileElement> elements = new ArrayList<>();
for (var i = 0; i < lines.size(); i++) {
var line = lines.get(i);
String content = removeComments(line);
//System.out.println(content);
if (content.isEmpty()) {
elements.add(new VoidElement(fileName, lastLine + i, expectedIndentation));
continue;
}
var lineIndentation = FileUtils.getIndentationLevel(line, false);
if (lineIndentation > expectedIndentation) { // The line is indented too much
logger.error(
"The line is indented too much (line " + (lastLine + i) + ": \"" + content + "\")",
ErrorType.STRUCTURE_ERROR,
"You only need to indent once (using tabs) after each section (a line that ends with a ':'). Try to omit some tabs so the line suffices this rule"
);
continue;
} else if (lineIndentation < expectedIndentation) { // One indentation behind marks the end of a section
return elements;
}
if (content.endsWith(":")) {
if (i + 1 == lines.size()) {
elements.add(new FileSection(fileName, lastLine + i, content.substring(0, content.length() - 1),
new ArrayList<>(), expectedIndentation
));
} else {
var sectionElements = parseFileLines(fileName, lines.subList(i + 1, lines.size()),
expectedIndentation + 1, lastLine + i + 1,
logger);
elements.add(new FileSection(fileName, lastLine + i, content.substring(0, content.length() - 1),
sectionElements, expectedIndentation
));
i += count(sectionElements);
}
} else {
elements.add(new FileElement(fileName, lastLine + i, content, expectedIndentation));
}
}
return elements;
}
private static int count(List<FileElement> elements) {
var count = 0;
for (var element : elements) {
if (element instanceof FileSection) {
count += count(((FileSection) element).getElements()) + 1;
} else {
count++;
}
}
return count;
}
/**
* Removes all comments from a given String
* @param string the String
* @return the String with the comments removed; {@literal null} if no comments were found; an empty string if the whole String was a comment
*/
@Nullable
private static String removeComments(String string) {
StringBuilder stringBuilder = new StringBuilder();
for (int i = 0; i < string.length(); i++) {
char c = string.charAt(i);
if (c == '#') {
switch (COMMENT_STATUS) {
case 0:
//System.out.println("debug color match: " + Color.COLOR_PATTERN.matcher("ffff0000").matches());
for (int j : new int[]{3, 6, 8}) {
if (i + j <= string.length() - 1) {
//System.out.println("debug string: " + string.substring(i + 1, i + j + 1));
if (!Color.COLOR_PATTERN.matcher(string.substring(i + 1, i + j + 1)).matches()) {
//System.out.println("yolo debug 1");
COMMENT_STATUS = 1;
}
} else {
COMMENT_STATUS = 1;
break;
}
}
break;
case 1:
if (string.length() <= i + 1 && string.charAt(i + 1) == '#')
COMMENT_STATUS = 2;
else if(string.charAt(i - 1) == '#')
COMMENT_STATUS = 0;
}
}
/*
System.out.println(
"Caractere : " + c + '\n' + "Caractere numero : " + i
+ '\n' + "Status commentaire: " + COMMENT_STATUS + '\n'
);
*/
if (COMMENT_STATUS == 0)
stringBuilder.append(c);
}
if (COMMENT_STATUS != 2) COMMENT_STATUS = 0;
return stringBuilder.toString().strip();
}
}