mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-10-02 11:10:50 +03:00
Logic parser improvements for quote/escape handling + unit tests
This commit is contained in:
@@ -56,22 +56,19 @@ public class LAssembler{
|
||||
return new LParser(text, privileged).parse();
|
||||
}
|
||||
|
||||
/** @return a variable by name.
|
||||
* This may be a constant variable referring to a number or object. */
|
||||
/**
|
||||
* @return a variable by name. This may be a constant variable referring to a number or object.
|
||||
* @param symbol the string literal, numeric literal, or variable name. Leading or trailing spaces are not allowed.
|
||||
* */
|
||||
public LVar var(String symbol){
|
||||
LVar constVar = Vars.logicVars.get(symbol, privileged);
|
||||
if(constVar != null) return constVar;
|
||||
|
||||
symbol = symbol.trim();
|
||||
|
||||
//string case
|
||||
if(!symbol.isEmpty() && symbol.charAt(0) == '\"' && symbol.charAt(symbol.length() - 1) == '\"'){
|
||||
return putConst("___" + symbol, symbol.substring(1, symbol.length() - 1).replace("\\n", "\n"));
|
||||
if(symbol.length() > 1 && symbol.charAt(0) == '\"' && symbol.charAt(symbol.length() - 1) == '\"'){
|
||||
return putConst("___" + symbol, symbol.substring(1, symbol.length() - 1));
|
||||
}
|
||||
|
||||
//remove spaces for non-strings
|
||||
symbol = symbol.replace(' ', '_');
|
||||
|
||||
//use a positive invalid number if number might be negative, else use a negative invalid number
|
||||
double value = parseDouble(symbol);
|
||||
|
||||
|
||||
@@ -25,6 +25,11 @@ public class LParser{
|
||||
LParser(String text, boolean privileged){
|
||||
this.privileged = privileged;
|
||||
this.chars = text.toCharArray();
|
||||
|
||||
//normalize CRLF and lone-CR line endings to LF in place; avoids extra allocations, and an extra \n is harmless
|
||||
for(int i = 0; i < chars.length; i++){
|
||||
if(chars[i] == '\r') chars[i] = '\n';
|
||||
}
|
||||
}
|
||||
|
||||
void comment(){
|
||||
@@ -39,15 +44,44 @@ public class LParser{
|
||||
String string(){
|
||||
int from = pos;
|
||||
int utflen = 0;
|
||||
//only allocated if an escape sequence is found
|
||||
StringBuilder escaped = null;
|
||||
|
||||
while(++pos < chars.length){
|
||||
char c = chars[pos];
|
||||
|
||||
//handle \n and \" escape sequences (also \\ so a literal backslash can be written)
|
||||
if(c == '\\' && pos + 1 < chars.length){
|
||||
char translated = switch(chars[pos + 1]){
|
||||
case 'n' -> '\n';
|
||||
case '"' -> '"';
|
||||
case '\\' -> '\\';
|
||||
default -> 0;
|
||||
};
|
||||
|
||||
if(translated != 0){
|
||||
if(escaped == null){
|
||||
//copy everything read so far (without the surrounding quote)
|
||||
escaped = new StringBuilder();
|
||||
escaped.append(chars, from + 1, pos - from - 1);
|
||||
}
|
||||
|
||||
escaped.append(translated);
|
||||
utflen += translated <= 0x7F ? 1 : translated <= 0x7FF ? 2 : 3;
|
||||
pos ++; //consume the escaped character too
|
||||
continue;
|
||||
}
|
||||
//not a recognized escape sequence; fall through and treat the backslash as a literal character (TODO: isn't this an invalid escape?)
|
||||
}
|
||||
|
||||
if(c == '\n'){
|
||||
error("Missing closing quote \" before end of line.");
|
||||
}else if(c == '"'){
|
||||
break;
|
||||
}
|
||||
|
||||
if(escaped != null) escaped.append(c);
|
||||
|
||||
// See ByteBufferOutput.writeUTF()
|
||||
utflen += c != 0 && c <= 0x7F ? 1 : c <= 0x7FF ? 2 : 3;
|
||||
}
|
||||
@@ -55,7 +89,13 @@ public class LParser{
|
||||
if(pos >= chars.length || chars[pos] != '"') error("Missing closing quote \" before end of file.");
|
||||
if(utflen > 65535) error("String value too long.");
|
||||
|
||||
return new String(chars, from, ++pos - from);
|
||||
pos ++; //move past the closing quote
|
||||
|
||||
if(escaped != null){
|
||||
return "\"" + escaped + "\"";
|
||||
}
|
||||
|
||||
return new String(chars, from, pos - from);
|
||||
}
|
||||
|
||||
String token(){
|
||||
@@ -63,7 +103,7 @@ public class LParser{
|
||||
|
||||
while(pos < chars.length){
|
||||
char c = chars[pos];
|
||||
if(c == '\n' || c == ' ' || c == '#' || c == '\t' || c == ';') break;
|
||||
if(c == '\n' || c == ' ' || c == '#' || c == '\t' || c == ';' || c == '"') break;
|
||||
pos ++;
|
||||
}
|
||||
|
||||
@@ -191,7 +231,6 @@ public class LParser{
|
||||
while(pos < chars.length && line < LExecutor.maxInstructions){
|
||||
switch(chars[pos]){
|
||||
case '\n', ';', ' ' -> pos ++; //skip newlines and spaces
|
||||
case '\r' -> pos += 2; //skip the newline after the \r
|
||||
default -> statement();
|
||||
}
|
||||
}
|
||||
@@ -217,4 +256,4 @@ public class LParser{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -83,7 +83,8 @@ public abstract class LStatement{
|
||||
tooltip(label, text);
|
||||
}
|
||||
|
||||
protected String sanitize(String value){
|
||||
/** Sanitizes variable input strings from text fields into values that will not break logic parsing. */
|
||||
public static String sanitize(String value){
|
||||
if(value.length() == 0){
|
||||
return "";
|
||||
}else if(value.length() == 1){
|
||||
@@ -94,12 +95,14 @@ public abstract class LStatement{
|
||||
StringBuilder res = new StringBuilder(value.length());
|
||||
if(value.charAt(0) == '"' && value.charAt(value.length() - 1) == '"'){
|
||||
res.append('\"');
|
||||
//strip out extra quotes
|
||||
//escape characters that would otherwise break out of the string or corrupt it
|
||||
for(int i = 1; i < value.length() - 1; i++){
|
||||
if(value.charAt(i) == '"'){
|
||||
res.append('\'');
|
||||
}else{
|
||||
res.append(value.charAt(i));
|
||||
char c = value.charAt(i);
|
||||
switch(c){
|
||||
case '"' -> res.append("\\\"");
|
||||
case '\\' -> res.append("\\\\");
|
||||
case '\n' -> res.append("\\n");
|
||||
default -> res.append(c);
|
||||
}
|
||||
}
|
||||
res.append('\"');
|
||||
|
||||
@@ -138,7 +138,7 @@ public class LogicDialog extends BaseDialog{
|
||||
t.button("@load.clipboard", Icon.download, style, () -> {
|
||||
dialog.hide();
|
||||
try{
|
||||
canvas.load(Core.app.getClipboardText().replace("\r\n", "\n"));
|
||||
canvas.load(Core.app.getClipboardText());
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user