You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by gn...@apache.org on 2018/04/19 18:07:32 UTC
svn commit: r1829590 - in /felix/trunk/gogo/jline/src:
main/java/org/apache/felix/gogo/jline/ParsedLineImpl.java
main/java/org/apache/felix/gogo/jline/Parser.java
test/java/org/apache/felix/gogo/jline/ParserTest.java
Author: gnodet
Date: Thu Apr 19 18:07:32 2018
New Revision: 1829590
URL: http://svn.apache.org/viewvc?rev=1829590&view=rev
Log:
[FELIX-5833][gogo][jline] Fix parser to support quotes and escape characters
Added:
felix/trunk/gogo/jline/src/test/java/org/apache/felix/gogo/jline/ParserTest.java
Modified:
felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ParsedLineImpl.java
felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/Parser.java
Modified: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ParsedLineImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ParsedLineImpl.java?rev=1829590&r1=1829589&r2=1829590&view=diff
==============================================================================
--- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ParsedLineImpl.java (original)
+++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/ParsedLineImpl.java Thu Apr 19 18:07:32 2018
@@ -18,14 +18,15 @@
*/
package org.apache.felix.gogo.jline;
-import java.util.ArrayList;
-import java.util.List;
-
import org.apache.felix.gogo.runtime.Parser.Program;
import org.apache.felix.gogo.runtime.Token;
-import org.jline.reader.ParsedLine;
+import org.jline.reader.CompletingParsedLine;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.function.Predicate;
-public class ParsedLineImpl implements ParsedLine {
+public class ParsedLineImpl implements CompletingParsedLine {
private final Program program;
private final String source;
@@ -33,6 +34,8 @@ public class ParsedLineImpl implements P
private final List<String> tokens;
private final int wordIndex;
private final int wordCursor;
+ private final CharSequence rawWord;
+ private final int rawWordCursor;
public ParsedLineImpl(Program program, Token line, int cursor, List<Token> tokens) {
this.program = program;
@@ -40,7 +43,7 @@ public class ParsedLineImpl implements P
this.cursor = cursor - line.start();
this.tokens = new ArrayList<>();
for (Token token : tokens) {
- this.tokens.add(token.toString());
+ this.tokens.add(unquote(token, null).toString());
}
int wi = tokens.size();
int wc = 0;
@@ -62,9 +65,16 @@ public class ParsedLineImpl implements P
}
if (wi == tokens.size()) {
this.tokens.add("");
+ rawWord = "";
+ wordCursor = 0;
+ } else {
+ rawWord = tokens.get(wi);
+ int[] c = new int[] { wc };
+ unquote(rawWord, c);
+ wordCursor = c[0];
}
wordIndex = wi;
- wordCursor = wc;
+ rawWordCursor = wc;
}
public String word() {
@@ -94,4 +104,99 @@ public class ParsedLineImpl implements P
public Program program() {
return program;
}
+
+ public int rawWordCursor() {
+ return rawWordCursor;
+ }
+
+ public int rawWordLength() {
+ return rawWord.length();
+ }
+
+ public CharSequence escape(CharSequence str, boolean complete) {
+ StringBuilder sb = new StringBuilder(str);
+ Predicate<Character> needToBeEscaped;
+ char quote = 0;
+ char first = rawWord.length() > 0 ? rawWord.charAt(0) : 0;
+ if (first == '\'') {
+ quote = '\'';
+ needToBeEscaped = i -> i == '\'';
+ } else if (first == '"') {
+ quote = '"';
+ needToBeEscaped = i -> i == '"';
+ } else {
+ needToBeEscaped = i -> i == ' ' || i == '\t';
+ }
+ for (int i = 0; i < sb.length(); i++) {
+ if (needToBeEscaped.test(str.charAt(i))) {
+ sb.insert(i++, '\\');
+ }
+ }
+ if (quote != 0) {
+ sb.insert(0, quote);
+ if (complete) {
+ sb.append(quote);
+ }
+ }
+ return sb;
+ }
+
+ private CharSequence unquote(CharSequence arg, int[] cursor) {
+ boolean hasEscape = false;
+ for (int i = 0; i < arg.length(); i++) {
+ int c = arg.charAt(i);
+ if (c == '\\' || c == '"' || c == '\'') {
+ hasEscape = true;
+ break;
+ }
+ }
+ if (!hasEscape) {
+ return arg;
+ }
+ boolean singleQuoted = false;
+ boolean doubleQuoted = false;
+ boolean escaped = false;
+ StringBuilder buf = new StringBuilder(arg.length());
+ for (int i = 0; i < arg.length(); i++) {
+ if (cursor != null && cursor[0] == i) {
+ cursor[0] = buf.length();
+ cursor = null;
+ }
+ char c = arg.charAt(i);
+ if (doubleQuoted && escaped) {
+ if (c != '"' && c != '\\' && c != '$' && c != '%') {
+ buf.append('\\');
+ }
+ buf.append(c);
+ escaped = false;
+ } else if (escaped) {
+ buf.append(c);
+ escaped = false;
+ } else if (singleQuoted) {
+ if (c == '\'') {
+ singleQuoted = false;
+ } else {
+ buf.append(c);
+ }
+ } else if (doubleQuoted) {
+ if (c == '\\') {
+ escaped = true;
+ } else if (c == '\"') {
+ doubleQuoted = false;
+ } else {
+ buf.append(c);
+ }
+ } else if (c == '\\') {
+ escaped = true;
+ } else if (c == '\'') {
+ singleQuoted = true;
+ } else if (c == '"') {
+ doubleQuoted = true;
+ } else {
+ buf.append(c);
+ }
+ }
+ return buf.toString();
+ }
+
}
Modified: felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/Parser.java
URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/Parser.java?rev=1829590&r1=1829589&r2=1829590&view=diff
==============================================================================
--- felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/Parser.java (original)
+++ felix/trunk/gogo/jline/src/main/java/org/apache/felix/gogo/jline/Parser.java Thu Apr 19 18:07:32 2018
@@ -18,6 +18,7 @@
*/
package org.apache.felix.gogo.jline;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -32,7 +33,7 @@ public class Parser implements org.jline
public ParsedLine parse(String line, int cursor, ParseContext context) throws org.jline.reader.SyntaxError {
try {
- return doParse(line, cursor);
+ return doParse(line, cursor, context);
} catch (EOFError e) {
throw new org.jline.reader.EOFError(e.line(), e.column(), e.getMessage(), e.missing());
} catch (SyntaxError e) {
@@ -40,10 +41,24 @@ public class Parser implements org.jline
}
}
- private ParsedLine doParse(CharSequence line, int cursor) throws SyntaxError {
- org.apache.felix.gogo.runtime.Parser parser = new org.apache.felix.gogo.runtime.Parser(line);
- Program program = parser.program();
- List<Statement> statements = parser.statements();
+ private ParsedLine doParse(String line, int cursor, ParseContext parseContext) throws SyntaxError {
+ Program program = null;
+ List<Statement> statements = null;
+ String repaired = line;
+ while (program == null) {
+ try {
+ org.apache.felix.gogo.runtime.Parser parser = new org.apache.felix.gogo.runtime.Parser(repaired);
+ program = parser.program();
+ statements = parser.statements();
+ } catch (EOFError e) {
+ // Make sure we don't loop forever
+ if (parseContext == ParseContext.COMPLETE && repaired.length() < line.length() + 1024) {
+ repaired = repaired + " " + e.repair();
+ } else {
+ throw e;
+ }
+ }
+ }
// Find corresponding statement
Statement statement = null;
for (int i = statements.size() - 1; i >= 0; i--) {
@@ -60,7 +75,14 @@ public class Parser implements org.jline
break;
}
}
- if (statement != null) {
+ if (statement != null && statement.tokens() != null && !statement.tokens().isEmpty()) {
+ if (repaired != line) {
+ Token stmt = statement.subSequence(0, line.length() - statement.start());
+ List<Token> tokens = new ArrayList<>(statement.tokens());
+ Token last = tokens.get(tokens.size() - 1);
+ tokens.set(tokens.size() - 1, last.subSequence(0, line.length() - last.start()));
+ return new ParsedLineImpl(program, stmt, cursor, tokens);
+ }
return new ParsedLineImpl(program, statement, cursor, statement.tokens());
} else {
// TODO:
Added: felix/trunk/gogo/jline/src/test/java/org/apache/felix/gogo/jline/ParserTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/gogo/jline/src/test/java/org/apache/felix/gogo/jline/ParserTest.java?rev=1829590&view=auto
==============================================================================
--- felix/trunk/gogo/jline/src/test/java/org/apache/felix/gogo/jline/ParserTest.java (added)
+++ felix/trunk/gogo/jline/src/test/java/org/apache/felix/gogo/jline/ParserTest.java Thu Apr 19 18:07:32 2018
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.gogo.jline;
+
+import org.jline.reader.CompletingParsedLine;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+public class ParserTest {
+
+ @Test
+ public void testEscapedWord() {
+ Parser parser = new Parser();
+ CompletingParsedLine line = (CompletingParsedLine) parser.parse("foo second\\ param \"quoted param\"", 15);
+ assertNotNull(line);
+ assertNotNull(line.words());
+ assertEquals("foo second\\ param \"quoted param\"", line.line());
+ assertEquals(15, line.cursor());
+ assertEquals(3, line.words().size());
+ assertEquals("second param", line.word());
+ assertEquals(10, line.wordCursor());
+ assertEquals(11, line.rawWordCursor());
+ assertEquals(13, line.rawWordLength());
+ }
+
+ @Test
+ public void testQuotedWord() {
+ Parser parser = new Parser();
+ CompletingParsedLine line = (CompletingParsedLine) parser.parse("foo second\\ param \"quoted param\"", 20);
+ assertNotNull(line);
+ assertNotNull(line.words());
+ assertEquals("foo second\\ param \"quoted param\"", line.line());
+ assertEquals(20, line.cursor());
+ assertEquals(3, line.words().size());
+ assertEquals("quoted param", line.word());
+ assertEquals(1, line.wordCursor());
+ assertEquals(2, line.rawWordCursor());
+ assertEquals(14, line.rawWordLength());
+ }
+
+}