|
From: <cr...@us...> - 2009-02-10 14:24:06
|
Revision: 5016
http://jnode.svn.sourceforge.net/jnode/?rev=5016&view=rev
Author: crawley
Date: 2009-02-10 14:23:53 +0000 (Tue, 10 Feb 2009)
Log Message:
-----------
Implement \<newline> continuations.
Modified Paths:
--------------
trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java
trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTests.java
trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneTokenizerTests.java
trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml
Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java 2009-02-09 16:04:03 UTC (rev 5015)
+++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java 2009-02-10 14:23:53 UTC (rev 5016)
@@ -66,6 +66,7 @@
import static org.jnode.shell.bjorne.BjorneToken.TOK_WHILE;
import static org.jnode.shell.bjorne.BjorneToken.TOK_WORD;
+import org.jnode.shell.IncompleteCommandException;
import org.jnode.shell.ShellFailureException;
public class BjorneTokenizer {
@@ -83,16 +84,79 @@
private final boolean debug;
- public BjorneTokenizer(String text) {
+ /**
+ * Create a tokenizer for the supplied shell input text.
+ * @param text the text to be tokenized
+ * @throws IncompleteCommandException if the text ends with a line continuation
+ */
+ public BjorneTokenizer(String text) throws IncompleteCommandException {
this(text, false);
}
- public BjorneTokenizer(String text, boolean debug) {
- chars = text.toCharArray();
+ /**
+ * Create a tokenizer for the supplied shell input text.
+ * @param text the text to be tokenized
+ * @param debug if {@code true}, produce debug output
+ * @throws IncompleteCommandException if the text ends with a line continuation
+ */
+ public BjorneTokenizer(String text, boolean debug) throws IncompleteCommandException {
+ chars = foldContinuations(text);
len = chars.length;
this.debug = debug;
}
+ /**
+ * Rewrite the supplied text to fold any line continuations.
+ *
+ * @param text the text to be processed
+ * @return the characters of text with any line continuations removed.
+ * @throws IncompleteCommandException
+ */
+ private char[] foldContinuations(String text) throws IncompleteCommandException {
+ if (text.indexOf('\\') == -1) {
+ return text.toCharArray();
+ }
+ int len = text.length();
+ StringBuilder sb = new StringBuilder(len);
+ boolean escape = false;
+ for (int i = 0; i < len; i++) {
+ char ch = text.charAt(i);
+ switch (ch) {
+ case '\\':
+ if (escape) {
+ sb.append('\\');
+ } else if (i == len - 1) {
+ // If we get a continuation sequence at the end of the
+ // text, the simplest thing is to ask for more input.
+ throw new IncompleteCommandException(
+ "More input required after '\\<newline>'", " > ");
+ }
+ escape = !escape;
+ break;
+ case '\n':
+ if (!escape) {
+ sb.append('\n');
+ } else {
+ escape = false;
+ }
+ break;
+ default:
+ if (escape) {
+ sb.append('\\');
+ escape = false;
+ }
+ sb.append(ch);
+ }
+ }
+ return sb.toString().toCharArray();
+ }
+
+ /**
+ * Get the next token without advancing. The default tokenization
+ * rules are used.
+ *
+ * @return the next token
+ */
public BjorneToken peek() {
if (current == null) {
current = advance();
@@ -103,6 +167,13 @@
return current;
}
+ /**
+ * Get the next token without advancing, using the tokenization
+ * rules corresponding to the supplied 'context'.
+ *
+ * @param context gives the tokenization rules
+ * @return the next token
+ */
public BjorneToken peek(int context) {
BjorneToken res = reinterpret(peek(), context);
if (debug) {
@@ -111,10 +182,20 @@
return res;
}
+ /**
+ * Test if {@link #next()} will return something other that EOS.
+ * @return <code>true</code> if there are more tokens to be delivered.
+ */
public boolean hasNext() {
return peek().getTokenType() != TOK_END_OF_STREAM;
}
+ /**
+ * Get the next token and advance. The default tokenization
+ * rules are used.
+ *
+ * @return the next token
+ */
public BjorneToken next() {
if (current == null) {
prev = advance();
@@ -129,6 +210,10 @@
return prev;
}
+ /**
+ * Backup one token in the token sequence. Calling this method twice without
+ * an intervening {@link #next()} call is invalid.
+ */
public void backup() {
if (prev == null) {
throw new ShellFailureException("incorrect backup");
@@ -142,6 +227,13 @@
prev = null;
}
+ /**
+ * Get the next token and advance, using the tokenization
+ * rules corresponding to the supplied 'context'.
+ *
+ * @param context gives the tokenization rules
+ * @return the next token
+ */
public BjorneToken next(int context) {
BjorneToken res = reinterpret(next(), context);
if (debug) {
@@ -150,6 +242,9 @@
return res;
}
+ /**
+ * This operation is not supported.
+ */
public void remove() {
throw new UnsupportedOperationException("remove not supported");
}
Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTests.java
===================================================================
--- trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTests.java 2009-02-09 16:04:03 UTC (rev 5015)
+++ trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTests.java 2009-02-10 14:23:53 UTC (rev 5016)
@@ -30,7 +30,7 @@
private static final boolean DEBUG = true;
- public void testParser() {
+ public void testParser() throws ShellException {
new BjorneParser(new BjorneTokenizer(""), null);
}
Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneTokenizerTests.java
===================================================================
--- trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneTokenizerTests.java 2009-02-09 16:04:03 UTC (rev 5015)
+++ trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneTokenizerTests.java 2009-02-10 14:23:53 UTC (rev 5016)
@@ -67,16 +67,26 @@
import static org.jnode.shell.bjorne.BjorneToken.TOK_WORD;
import junit.framework.TestCase;
+import org.jnode.shell.IncompleteCommandException;
import org.jnode.shell.bjorne.BjorneToken;
import org.jnode.shell.bjorne.BjorneTokenizer;
public class BjorneTokenizerTests extends TestCase {
- public void testBjorneTokenizer() {
+ public void testBjorneTokenizer() throws IncompleteCommandException {
new BjorneTokenizer("hello");
}
- public void testEmpty() {
+ public void testBjorneTokenizer2() {
+ try {
+ new BjorneTokenizer("hello\\");
+ fail("no exception");
+ } catch (IncompleteCommandException ex) {
+ // expected
+ }
+ }
+
+ public void testEmpty() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer("");
BjorneToken token = tokenizer.peek();
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
@@ -88,7 +98,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testNewline() {
+ public void testNewline() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer("\n");
BjorneToken token = tokenizer.next();
assertEquals(TOK_END_OF_LINE, token.getTokenType());
@@ -96,7 +106,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testBlanksAndNewlines() {
+ public void testBlanksAndNewlines() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(" \n\t\n ");
BjorneToken token = tokenizer.next();
assertEquals(TOK_END_OF_LINE, token.getTokenType());
@@ -106,7 +116,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testComments() {
+ public void testComments() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
"# comment\n #comment 2\n # comment # 3");
BjorneToken token = tokenizer.next();
@@ -117,7 +127,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testSymbols() {
+ public void testSymbols() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer("; | & < > ( )");
BjorneToken token = tokenizer.next();
assertEquals(TOK_SEMI, token.getTokenType());
@@ -137,7 +147,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testSymbols2() {
+ public void testSymbols2() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
"; ;; | || & && < << > >>");
BjorneToken token = tokenizer.next();
@@ -164,7 +174,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testSymbols3() {
+ public void testSymbols3() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(";;;|||&&&<<<>>>");
BjorneToken token = tokenizer.next();
assertEquals(TOK_DSEMI, token.getTokenType());
@@ -188,7 +198,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testSymbols4() {
+ public void testSymbols4() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
"< << <<- <& <> > >> >| >&");
BjorneToken token = tokenizer.next();
@@ -213,7 +223,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testWords() {
+ public void testWords() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer("hello there");
BjorneToken token = tokenizer.next();
assertEquals(TOK_WORD, token.getTokenType());
@@ -225,7 +235,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testWords2() {
+ public void testWords2() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
"hello\\ there\\\n friend");
BjorneToken token = tokenizer.next();
@@ -243,9 +253,20 @@
assertEquals("hello\\ there\\ friend", token.getText());
token = tokenizer.next();
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
+
+ tokenizer = new BjorneTokenizer(
+ "hello\\\nthere\\\n friend");
+ token = tokenizer.next();
+ assertEquals(TOK_WORD, token.getTokenType());
+ assertEquals("hellothere", token.getText());
+ token = tokenizer.next();
+ assertEquals(TOK_WORD, token.getTokenType());
+ assertEquals("friend", token.getText());
+ token = tokenizer.next();
+ assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testWords3() {
+ public void testWords3() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer("'1 2' \"3 4\" `5 6`");
BjorneToken token = tokenizer.next();
assertEquals(TOK_WORD, token.getTokenType());
@@ -260,7 +281,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testWords4() {
+ public void testWords4() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer("'1 \"2\"' \"3\\\"4\"");
BjorneToken token = tokenizer.next();
assertEquals(TOK_WORD, token.getTokenType());
@@ -272,7 +293,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testWords5() {
+ public void testWords5() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer("1<2>3&4;5|6)7");
BjorneToken token = tokenizer.next();
assertEquals(TOK_IO_NUMBER, token.getTokenType());
@@ -311,7 +332,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule1() {
+ public void testRule1() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
"if then else elif fi for done while until "
+ "case { } ! do in esac");
@@ -351,7 +372,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule5() {
+ public void testRule5() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
"if a a1 9a a_b a,b AB A=b");
BjorneToken token = tokenizer.next(RULE_5_CONTEXT);
@@ -374,7 +395,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule6() {
+ public void testRule6() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer("if in do");
BjorneToken token = tokenizer.next(RULE_6_CONTEXT);
assertEquals(TOK_WORD, token.getTokenType());
@@ -386,7 +407,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule7a() {
+ public void testRule7a() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
"if then else elif fi for done while until "
+ "case { } ! do in esac a= a=b 1a=b =c");
@@ -434,7 +455,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule7b() {
+ public void testRule7b() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
"if then else elif fi for done while until "
+ "case { } ! do in esac a= a=b 1a=b =c");
@@ -482,7 +503,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule8() {
+ public void testRule8() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
"if then else elif fi for done while until "
+ "case { } ! do in esac a a_b a= a=b 1a=b =c");
@@ -534,7 +555,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRegress() {
+ public void testRegress() throws IncompleteCommandException {
BjorneTokenizer tokenizer = new BjorneTokenizer("ls -l");
BjorneToken token = tokenizer.peek(RULE_7a_CONTEXT);
assertEquals(TOK_WORD, token.getTokenType());
Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml
===================================================================
--- trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-02-09 16:04:03 UTC (rev 5015)
+++ trunk/shell/src/test/org/jnode/test/shell/bjorne/bjorne-shell-tests.xml 2009-02-10 14:23:53 UTC (rev 5016)
@@ -8,6 +8,14 @@
<output>HI
</output>
</testSpec>
+ <testSpec title="continuation" command="test" runMode="AS_SCRIPT" rc="0">
+ <script>#!bjorne
+ echo hello \
+mother
+ </script>
+ <output>hello mother
+</output>
+ </testSpec>
<testSpec title="$?" command="test" runMode="AS_SCRIPT" rc="0">
<script>#!bjorne
true
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|