|
1 | 1 | package org.skriptlang.skript.common.function; |
2 | 2 |
|
| 3 | +import ch.njol.skript.lang.ParseContext; |
| 4 | +import ch.njol.skript.lang.SkriptParser; |
3 | 5 | import org.skriptlang.skript.common.function.FunctionReference.Argument; |
4 | 6 | import org.skriptlang.skript.common.function.FunctionReference.ArgumentType; |
5 | 7 |
|
6 | 8 | import java.util.ArrayList; |
7 | 9 | import java.util.List; |
| 10 | +import java.util.regex.Matcher; |
| 11 | +import java.util.regex.Pattern; |
8 | 12 |
|
9 | 13 | /** |
10 | 14 | * Parses the arguments of a function reference. |
11 | 15 | */ |
12 | 16 | final class FunctionArgumentParser { |
13 | 17 |
|
| 18 | + private static final Pattern PART_PATTERN = Pattern.compile("(?:\\s*(?<name>[_a-zA-Z0-9]+):)?(?<value>.+)"); |
| 19 | + |
14 | 20 | /** |
15 | 21 | * The input string. |
16 | 22 | */ |
@@ -38,192 +44,50 @@ public FunctionArgumentParser(String args) { |
38 | 44 | */ |
39 | 45 | private int index = 0; |
40 | 46 |
|
41 | | - /** |
42 | | - * The current character. |
43 | | - */ |
44 | | - private char c; |
45 | | - |
46 | | - /** |
47 | | - * Whether the current argument being parsed starts with a name declaration. |
48 | | - */ |
49 | | - private boolean nameFound = false; |
50 | | - |
51 | | - /** |
52 | | - * A builder which keeps track of the name part of an argument. |
53 | | - * <p> |
54 | | - * This builder may contain a part of the expression at the start of parsing an argument, |
55 | | - * when it is unclear whether we are currently parsing a name or not. On realization that |
56 | | - * this argument does not have a name, its contents are cleared. |
57 | | - * </p> |
58 | | - */ |
59 | | - private final StringBuilder namePart = new StringBuilder(); |
60 | | - |
61 | | - /** |
62 | | - * A builder which keeps track of the expression part of an argument. |
63 | | - * <p> |
64 | | - * This builder may contain a part of the name at the start of parsing an argument, |
65 | | - * when it is unclear whether we are currently parsing a name or not. On realization that |
66 | | - * this argument has a name, its contents are cleared. |
67 | | - * </p> |
68 | | - */ |
69 | | - private final StringBuilder exprPart = new StringBuilder(); |
70 | | - |
71 | | - /** |
72 | | - * Whether we are currently in a string or not. |
73 | | - * <p> |
74 | | - * To avoid parsing a comma in a string as the start of a new argument, we keep track of whether we're |
75 | | - * in a string or not to ignore commas found in strings. |
76 | | - * A new argument can only start when {@code nesting == 0 && !inString}. |
77 | | - * </p> |
78 | | - */ |
79 | | - private boolean inString = false; |
80 | | - |
81 | | - /** |
82 | | - * The level of nesting we are currently in. |
83 | | - * <p> |
84 | | - * The nesting level is increased when entering special expressions which may contain commas, |
85 | | - * thereby avoiding incorrectly parsing a comma in variables or parentheses as the start of a new argument. |
86 | | - * A new argument can only start when {@code nesting == 0 && !inString}. |
87 | | - * </p> |
88 | | - */ |
89 | | - private int nesting = 0; |
90 | | - |
91 | 47 | /** |
92 | 48 | * Parses the input string into arguments. |
93 | | - * <p> |
94 | | - * For every argument, during the parsing of the first few characters, one of the following things occurs. |
95 | | - * <ul> |
96 | | - * <li>A legal parameter name character is encountered. The character is added to {@link #namePart} and |
97 | | - * {@link #exprPart}.</li> |
98 | | - * <li>An illegal parameter name character is encountered. This means that the previous data added to {@link #namePart} |
99 | | - * cannot be a name. {@link #namePart} is cleared and the rest of the argument is parsed as the expression.</li> |
100 | | - * <li>A colon {@code :} is encountered. When all previous characters for this argument match the requirements |
101 | | - * for a parameter name, the name is stored in {@link #namePart} and the rest of the argument is parsed as the expression.</li> |
102 | | - * <li>A comma {@code ,} is encountered. This means that the end of the argument has been reached. If no name was found, |
103 | | - * the entire argument is parsed as {@link #exprPart}. If a name was found, {@link #exprPart} gets stored alongside {@link #namePart}.</li> |
104 | | - * </ul> |
105 | | - * </p> |
106 | 49 | */ |
107 | 50 | private void parse() { |
108 | 51 | // if we have no args to parse, give up instantly |
109 | 52 | if (args.isEmpty()) { |
110 | 53 | return; |
111 | 54 | } |
112 | 55 |
|
113 | | - while (index < args.length()) { |
114 | | - c = args.charAt(index); |
115 | | - |
116 | | - // first try to compile the name |
117 | | - if (!nameFound) { |
118 | | - // if a name matches the legal characters, update name part |
119 | | - if (c == '_' || Character.isLetterOrDigit(c)) { |
120 | | - namePart.append(c); |
121 | | - exprPart.append(c); |
122 | | - index++; |
123 | | - continue; |
124 | | - } |
125 | | - |
126 | | - // then if we have a name, start parsing the second part |
127 | | - if (nesting == 0 && c == ':' && !namePart.isEmpty()) { |
128 | | - exprPart.setLength(0); |
129 | | - index++; |
130 | | - nameFound = true; |
131 | | - continue; |
132 | | - } |
133 | | - |
134 | | - if (isSpecialCharacter(ArgumentType.UNNAMED)) { |
135 | | - continue; |
136 | | - } |
137 | | - |
138 | | - // given that the character did not match the legal name chars, reset name |
139 | | - namePart.setLength(0); |
140 | | - nextExpr(); |
141 | | - continue; |
| 56 | + int next = 0; |
| 57 | + while (next < args.length()) { |
| 58 | + next = SkriptParser.next(args, next, ParseContext.DEFAULT); |
| 59 | + if (next == -1) { |
| 60 | + // if no end is found, just parse the whole passed string as an argument and pray it works |
| 61 | + index = 0; |
| 62 | + next = args.length(); |
142 | 63 | } |
143 | 64 |
|
144 | | - if (isSpecialCharacter(ArgumentType.NAMED)) { |
| 65 | + if (next < args.length() && args.charAt(next) != ',') { |
145 | 66 | continue; |
146 | 67 | } |
147 | 68 |
|
148 | | - nextExpr(); // add to expression |
149 | | - } |
150 | | - |
151 | | - // make sure to save the last argument |
152 | | - if (nameFound) { |
153 | | - save(ArgumentType.NAMED); |
154 | | - } else { |
155 | | - save(ArgumentType.UNNAMED); |
156 | | - } |
157 | | - } |
| 69 | + String part = args.substring(index, next); |
| 70 | + index = next + 1; |
158 | 71 |
|
159 | | - /** |
160 | | - * Manages special character handling by updating the {@link #nesting} and {@link #inString} variables. |
161 | | - * |
162 | | - * @param type The type of argument that is currently being parsed. |
163 | | - * @return True when {@link #c} is a special character, false if not. |
164 | | - */ |
165 | | - private boolean isSpecialCharacter(ArgumentType type) { |
166 | | - // for strings |
167 | | - if (!inString && c == '"') { |
168 | | - nesting++; |
169 | | - inString = true; |
170 | | - nextExpr(); |
171 | | - return true; |
172 | | - } |
173 | | - |
174 | | - if (inString && c == '"' |
175 | | - && index < args.length() - 1 && args.charAt(index + 1) != '"') { // allow double string char in strings |
176 | | - nesting--; |
177 | | - inString = false; |
178 | | - nextExpr(); |
179 | | - return true; |
180 | | - } |
181 | | - |
182 | | - if (c == '(' || c == '{') { |
183 | | - nesting++; |
184 | | - nextExpr(); |
185 | | - return true; |
186 | | - } |
187 | | - |
188 | | - if (c == ')' || c == '}') { |
189 | | - nesting--; |
190 | | - nextExpr(); |
191 | | - return true; |
192 | | - } |
193 | | - |
194 | | - if (nesting == 0 && c == ',') { |
195 | | - save(type); |
196 | | - return true; |
197 | | - } |
198 | | - |
199 | | - return false; |
200 | | - } |
201 | | - |
202 | | - /** |
203 | | - * Moves the parser to the next part of the expression that is being parsed. |
204 | | - */ |
205 | | - private void nextExpr() { |
206 | | - exprPart.append(c); |
207 | | - index++; |
208 | | - } |
| 72 | + Matcher matcher = PART_PATTERN.matcher(part); |
| 73 | + if (!matcher.matches()) { |
| 74 | + continue; |
| 75 | + } |
209 | 76 |
|
210 | | - /** |
211 | | - * Saves the string parts stored in {@link #exprPart} (and optionally {@link #namePart}) as a new argument in |
212 | | - * {@link #arguments}. Then, all data for the current argument is cleared. |
213 | | - * |
214 | | - * @param type The type of argument to save as. |
215 | | - */ |
216 | | - private void save(ArgumentType type) { |
217 | | - if (type == ArgumentType.UNNAMED) { |
218 | | - arguments.add(new Argument<>(ArgumentType.UNNAMED, null, exprPart.toString().trim())); |
219 | | - } else { |
220 | | - arguments.add(new Argument<>(ArgumentType.NAMED, namePart.toString().trim(), exprPart.toString().trim())); |
| 77 | + String name = matcher.group("name"); |
| 78 | + String value = matcher.group("value"); |
| 79 | + if (name == null) { |
| 80 | + arguments.add(new Argument<>(ArgumentType.UNNAMED, |
| 81 | + null, |
| 82 | + value.trim(), |
| 83 | + value)); |
| 84 | + } else { |
| 85 | + arguments.add(new Argument<>(ArgumentType.NAMED, |
| 86 | + name.trim(), |
| 87 | + value.trim(), |
| 88 | + name + ":" + value)); |
| 89 | + } |
221 | 90 | } |
222 | | - |
223 | | - namePart.setLength(0); |
224 | | - exprPart.setLength(0); |
225 | | - index++; |
226 | | - nameFound = false; |
227 | 91 | } |
228 | 92 |
|
229 | 93 | /** |
|
0 commit comments