|
From: <cr...@us...> - 2009-07-06 14:33:05
|
Revision: 5593
http://jnode.svn.sourceforge.net/jnode/?rev=5593&view=rev
Author: crawley
Date: 2009-07-06 14:33:04 +0000 (Mon, 06 Jul 2009)
Log Message:
-----------
Change the shell / interpreter interactions to handle multi-line input
more cleanly. This will allow some bjorne features to be implemented.
Modified Paths:
--------------
trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java
trunk/shell/src/shell/org/jnode/shell/CommandShell.java
trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java
trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java
trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java
trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java
trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java
trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java
trunk/shell/src/shell/org/jnode/shell/bjorne/SetBuiltin.java
trunk/shell/src/shell/org/jnode/shell/bjorne/SourceBuiltin.java
trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTest.java
trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneTokenizerTest.java
Modified: trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/shell/org/jnode/shell/CommandInterpreter.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -41,24 +41,14 @@
}
/**
- * Parse and execute a command line, and return the resulting return code.
+ * Parse and execute commands read from a reader, returning the resulting return code.
*
* @param shell the CommandShell that provides low-level command invocation,
* command history and so on.
- * @param line the line of input to be interpreted.
- * @return the return code.
- * @throws ShellException
- */
- int interpret(CommandShell shell, String line) throws ShellException;
-
- /**
- * Parse and execute a command file, returning the resulting return code.
- *
- * @param shell the CommandShell that provides low-level command invocation,
- * command history and so on.
* @param reader the reader to be interpreted. <b>The implementation must close it.</b>
- * @param alias this will supply the script's notional command name to the interpreter.
- * @param args command line arguments to be passed to the script. If this parameter
+ * @param alias this will supply a script's notional command name to the interpreter. If
+ * this parameter is {@code null}, no command name passed.
+ * @param args optional command line arguments to be passed to the script. If this parameter
* is {@code null}, no arguments are passed.
* @return the return code.
* @throws ShellException
Modified: trunk/shell/src/shell/org/jnode/shell/CommandShell.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/shell/org/jnode/shell/CommandShell.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -379,44 +379,10 @@
readingCommand = true;
input = readInputLine();
if (input.length() > 0) {
- // This hairy bit of code deals with shell commands that span multiple
- // input lines. If an interpreter encounters the end of the line that
- // we have given it and it requires more input to get a complete command,
- // it may throw IncompleteCommandException. The shell responds by
- // outputting a different prompt (supplied in the exception), and then
- // attempting to get the next line. If that succeeds, the line is
- // appended to the input we gave the interpreter last time, and the
- // interpreter is called again. This continues until either the
- // interpreter manages to run the command, or we get some other
- // shell syntax exception.
- boolean done = false;
- do {
- try {
- runCommand(input, true, this.interpreter);
- done = true;
- } catch (IncompleteCommandException ex) {
- String continuation = null;
- // (Tell completer to use command history not app. history)
- readingCommand = true;
- if (this.interpreter.supportsMultilineCommands()) {
- String prompt = ex.getPrompt();
- if (prompt != null) {
- outPW.print(prompt);
- }
- continuation = readInputLine();
- }
- if (continuation == null) {
- diagnose(ex, null);
- break;
- } else {
- input = input + "\n" + continuation;
- }
- } catch (ShellException ex) {
- diagnose(ex, null);
- done = true;
- }
- } while (!done);
+ runCommand(input, true, this.interpreter);
}
+ } catch (ShellException ex) {
+ diagnose(ex, null);
} catch (VmExit ex) {
// This should only happen if the interpreter wants the shell to
// exit. The interpreter will typically intercept any VmExits
@@ -427,6 +393,7 @@
+ ex.getMessage());
stackTrace(ex);
} finally {
+ // FIXME ...
if (input != null && input.trim().length() > 0) {
String lines[] = input.split("\\n");
for (String line : lines) {
@@ -564,8 +531,8 @@
}
}
- private int runCommand(String cmdLineStr, boolean interactive,
- CommandInterpreter interpreter) throws ShellException {
+ private int runCommand(String command, boolean interactive, CommandInterpreter interpreter)
+ throws ShellException {
try {
if (interactive) {
clearEof();
@@ -574,7 +541,7 @@
// for input completion
applicationHistory.set(new InputHistory());
}
- return interpreter.interpret(this, cmdLineStr);
+ return interpreter.interpret(this, new StringReader(command), null, null);
} finally {
if (interactive) {
applicationHistory.set(null);
Modified: trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/shell/org/jnode/shell/DefaultInterpreter.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -89,20 +89,11 @@
public String getName() {
return "default";
}
-
- @Override
- public int interpret(CommandShell shell, String line) throws ShellException {
- CommandLine cmd = doParseCommandLine(line);
- if (cmd == null) {
- return 0;
- }
- return shell.invoke(cmd, null, null);
- }
-
+
/**
* {@inheritDoc}
*
- * The default interpreter and its subtypes treat a command script as a sequence of commands.
+ * The default interpreter treats a command script as a sequence of commands.
* Commands are expected to consist of exactly one line. Any line whose first non-whitespace
* character is '#' will be ignored. Command line arguments from the script are not supported,
* and will result in a {@link ShellException} being thrown.
@@ -167,6 +158,15 @@
return false;
}
+ protected int interpret(CommandShell shell, String line)
+ throws ShellException {
+ CommandLine cmd = doParseCommandLine(line);
+ if (cmd == null) {
+ return 0;
+ }
+ return shell.invoke(cmd, null, null);
+ }
+
private CommandLine doParseCommandLine(String line) throws ShellException {
Tokenizer tokenizer = new Tokenizer(line);
if (!tokenizer.hasNext()) {
Modified: trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/shell/org/jnode/shell/RedirectingInterpreter.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -68,7 +68,7 @@
}
@Override
- public int interpret(CommandShell shell, String line) throws ShellException {
+ protected int interpret(CommandShell shell, String line) throws ShellException {
Tokenizer tokenizer = new Tokenizer(line, REDIRECTS_FLAG);
List<CommandDescriptor> commands = new LinkedList<CommandDescriptor>();
parse(tokenizer, commands, false);
Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneContext.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -53,7 +53,6 @@
import org.jnode.shell.Command;
import org.jnode.shell.CommandLine;
import org.jnode.shell.CommandShell;
-import org.jnode.shell.IncompleteCommandException;
import org.jnode.shell.PathnamePattern;
import org.jnode.shell.ShellException;
import org.jnode.shell.ShellFailureException;
@@ -595,7 +594,7 @@
private StringBuffer runBacktickCommand(String commandLine) throws ShellException {
StringWriter capture = new StringWriter();
- interpreter.interpret(interpreter.getShell(), commandLine, capture, false);
+ interpreter.interpret(interpreter.getShell(), new StringReader(commandLine), capture, false);
StringBuffer output = capture.getBuffer();
while (output.length() > 0 && output.charAt(output.length() - 1) == '\n') {
output.setLength(output.length() - 1);
@@ -1300,8 +1299,7 @@
return interpreter.getUniqueName();
}
- public BjorneToken[] substituteAliases(BjorneToken[] words)
- throws IncompleteCommandException {
+ public BjorneToken[] substituteAliases(BjorneToken[] words) throws ShellSyntaxException {
String alias = aliases.get(words[0].getText());
if (alias == null) {
return words;
@@ -1311,8 +1309,7 @@
return list.toArray(new BjorneToken[list.size()]);
}
- private void substituteAliases(List<BjorneToken> list, int pos, int depth)
- throws IncompleteCommandException {
+ private void substituteAliases(List<BjorneToken> list, int pos, int depth) throws ShellSyntaxException {
if (depth > 10) {
throw new ShellFailureException("probable cycle detected in alias expansion");
}
Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneInterpreter.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -30,7 +30,6 @@
import static org.jnode.shell.bjorne.BjorneToken.TOK_LESSAND;
import static org.jnode.shell.bjorne.BjorneToken.TOK_LESSGREAT;
-import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
@@ -45,10 +44,8 @@
import org.jnode.shell.CommandLine;
import org.jnode.shell.CommandShell;
import org.jnode.shell.Completable;
-import org.jnode.shell.IncompleteCommandException;
import org.jnode.shell.ShellException;
import org.jnode.shell.ShellFailureException;
-import org.jnode.shell.ShellInvocationException;
import org.jnode.shell.ShellSyntaxException;
import org.jnode.shell.io.CommandIO;
import org.jnode.shell.io.CommandOutput;
@@ -164,10 +161,10 @@
return "bjorne";
}
- @Override
- public int interpret(CommandShell shell, String command) throws ShellException {
+ public int interpret(CommandShell shell, Reader reader)
+ throws ShellException {
try {
- return interpret(shell, command, null, false);
+ return interpret(shell, reader, null, false);
} catch (BjorneControlException ex) {
switch (ex.getControl()) {
case BjorneInterpreter.BRANCH_EXIT:
@@ -194,7 +191,7 @@
BjorneTokenizer tokens = new BjorneTokenizer(partial);
BjorneCompleter completions = new BjorneCompleter(context);
try {
- new BjorneParser(tokens, "> ").parse(completions);
+ new BjorneParser(tokens).parse(completions);
} catch (ShellSyntaxException ex) {
// squelch both syntax and incomplete command exceptions.
}
@@ -217,7 +214,7 @@
}
}
- int interpret(CommandShell shell, String command, StringWriter capture, boolean source)
+ int interpret(CommandShell shell, Reader reader, StringWriter capture, boolean source)
throws ShellException {
BjorneContext myContext;
// FIXME ... I think there is something wrong / incomplete with the way I'm handling
@@ -229,8 +226,8 @@
myContext = new BjorneContext(this);
myContext.setIO(1, new CommandOutput(capture), true);
}
- BjorneTokenizer tokens = new BjorneTokenizer(command);
- CommandNode tree = new BjorneParser(tokens, "> ").parse();
+ BjorneTokenizer tokens = new BjorneTokenizer(reader);
+ CommandNode tree = new BjorneParser(tokens).parse();
if (tree == null) {
// An empty command line
return myContext.getLastReturnCode();
@@ -252,44 +249,27 @@
context.setCommand(alias == null ? "" : alias);
context.setArgs(args == null ? new String[0] : args);
try {
- BufferedReader br = new BufferedReader(reader);
- String line;
- int rc = 0;
- while ((line = br.readLine()) != null) {
- boolean done = false;
- do {
- try {
- rc = interpret(shell, line, null, false);
- done = true;
- } catch (BjorneControlException ex) {
- switch (ex.getControl()) {
- case BjorneInterpreter.BRANCH_EXIT:
- // The script will exit immediately
- return ex.getCount();
- case BjorneInterpreter.BRANCH_BREAK:
- throw new ShellSyntaxException(
- "'break' has been executed in an inappropriate context");
- case BjorneInterpreter.BRANCH_CONTINUE:
- throw new ShellSyntaxException(
- "'continue' has been executed in an inappropriate context");
- case BjorneInterpreter.BRANCH_RETURN:
- throw new ShellSyntaxException(
- "'return' has been executed in an inappropriate context");
- }
- } catch (IncompleteCommandException ex) {
- String continuation = br.readLine();
- if (continuation == null) {
- throw ex;
- }
- line = line + "\n" + continuation;
- } catch (VmExit ex) {
- return ex.getStatus();
- }
- } while (!done);
+ return interpret(shell, reader, null, false);
+ } catch (BjorneControlException ex) {
+ switch (ex.getControl()) {
+ case BjorneInterpreter.BRANCH_EXIT:
+ // The script will exit immediately
+ return ex.getCount();
+ case BjorneInterpreter.BRANCH_BREAK:
+ throw new ShellSyntaxException(
+ "'break' has been executed in an inappropriate context");
+ case BjorneInterpreter.BRANCH_CONTINUE:
+ throw new ShellSyntaxException(
+ "'continue' has been executed in an inappropriate context");
+ case BjorneInterpreter.BRANCH_RETURN:
+ throw new ShellSyntaxException(
+ "'return' has been executed in an inappropriate context");
+ default:
+ throw new ShellFailureException(
+ "unknown 'control' in BjorneControlException");
}
- return rc;
- } catch (IOException ex) {
- throw new ShellInvocationException("Problem reading command file: " + ex.getMessage(), ex);
+ } catch (VmExit ex) {
+ return ex.getStatus();
} finally {
if (reader != null) {
try {
@@ -318,7 +298,8 @@
Properties sysProps, Map<String, String> env, boolean isBuiltin)
throws ShellException {
if (isBuiltin) {
- BjorneBuiltinCommandInfo builtin = BUILTINS.get(cmdLine.getCommandName()).buildCommandInfo(context);
+ BjorneBuiltinCommandInfo builtin =
+ BUILTINS.get(cmdLine.getCommandName()).buildCommandInfo(context);
cmdLine.setCommandInfo(builtin);
}
cmdLine.setStreams(streams);
Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneParser.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -107,7 +107,6 @@
import java.util.LinkedList;
import java.util.List;
-import org.jnode.shell.IncompleteCommandException;
import org.jnode.shell.ShellSyntaxException;
/**
@@ -122,14 +121,11 @@
private final BjorneTokenizer tokens;
private BjorneCompleter completer;
- private final String continuationPrompt;
-
private final List<RedirectionNode> hereRedirections = new ArrayList<RedirectionNode>();
private boolean allowLineBreaks;
- public BjorneParser(BjorneTokenizer tokens, String continuationPrompt) {
+ public BjorneParser(BjorneTokenizer tokens) {
this.tokens = tokens;
- this.continuationPrompt = continuationPrompt;
}
/**
@@ -143,6 +139,9 @@
public CommandNode parse() throws ShellSyntaxException {
hereRedirections.clear();
List<CommandNode> commands = new LinkedList<CommandNode>();
+ // (The POSIX syntax doesn't seem to allow line breaks at the start, but I
+ // don't think that can be right ...)
+ skipLineBreaks();
while (peek().getTokenType() != TOK_END_OF_STREAM) {
CommandNode command = parseList();
commands.add(command);
@@ -344,12 +343,6 @@
// FIXME ... built-in commands should use the Syntax mechanisms so
// that completion, help, etc will work as expected.
}
- } catch (IncompleteCommandException ex) {
- if (completer != null) {
- completer.setCommand(new SimpleCommandNode(CMD_COMMAND,
- words.toArray(new BjorneToken[words.size()]), builtin));
- }
- throw ex;
} catch (ShellSyntaxException ex) {
if (completer != null) {
completer.setCommand(words.size() == 0 ? null :
@@ -693,21 +686,21 @@
return token;
}
- private BjorneToken next() throws IncompleteCommandException {
+ private BjorneToken next() throws ShellSyntaxException {
if (allowLineBreaks) {
doLineBreaks(0L, false);
}
return tokens.next();
}
- private BjorneToken peek() throws IncompleteCommandException {
+ private BjorneToken peek() throws ShellSyntaxException {
if (allowLineBreaks) {
doLineBreaks(0L, false);
}
return tokens.peek();
}
- private BjorneToken peekEager() throws IncompleteCommandException {
+ private BjorneToken peekEager() throws ShellSyntaxException {
if (allowLineBreaks) {
doLineBreaks(0L, true);
}
@@ -721,9 +714,8 @@
if (((1L << tt) & expectedSet) == 0L) {
if (mandatory) {
if (tt == TOK_END_OF_STREAM) {
- throw new IncompleteCommandException(
- "EOF reached while looking for " + BjorneToken.formatExpectedSet(expectedSet),
- continuationPrompt);
+ throw new ShellSyntaxException(
+ "EOF reached while looking for " + BjorneToken.formatExpectedSet(expectedSet));
} else {
throw new ShellSyntaxException(
"expected " + BjorneToken.formatExpectedSet(expectedSet) + " but got " + token);
@@ -765,16 +757,16 @@
}
}
- private void skipLineBreaks() throws IncompleteCommandException {
+ private void skipLineBreaks() throws ShellSyntaxException {
this.allowLineBreaks = true;
doLineBreaks(0L, false);
}
- private void allowLineBreaks() throws IncompleteCommandException {
+ private void allowLineBreaks() throws ShellSyntaxException {
this.allowLineBreaks = true;
}
- private void doLineBreaks(long expectedSet, boolean needMore) throws IncompleteCommandException {
+ private void doLineBreaks(long expectedSet, boolean needMore) throws ShellSyntaxException {
// NB: use tokens.peek() / next() rather than the wrappers here!!
this.allowLineBreaks = false;
BjorneToken token = tokens.peek();
@@ -782,9 +774,8 @@
if (tt == TOK_END_OF_STREAM) {
captureCompletions(token, expectedSet);
if (needMore) {
- throw new IncompleteCommandException(
- "EOF reached while looking for optional linebreak(s)",
- continuationPrompt);
+ throw new ShellSyntaxException(
+ "EOF reached while looking for optional linebreak(s)");
}
} else if (tt == TOK_END_OF_LINE) {
tokens.next();
@@ -797,9 +788,8 @@
} else if (tt == TOK_END_OF_STREAM) {
captureCompletions(token, expectedSet);
if (needMore) {
- throw new IncompleteCommandException(
- "EOF reached while dealing with optional linebreak(s)",
- continuationPrompt);
+ throw new ShellSyntaxException(
+ "EOF reached while dealing with optional linebreak(s)");
} else {
break;
}
@@ -810,7 +800,7 @@
}
}
- private void captureHereDocuments() throws IncompleteCommandException {
+ private void captureHereDocuments() throws ShellSyntaxException {
for (RedirectionNode redirection : hereRedirections) {
StringBuilder sb = new StringBuilder();
String marker = redirection.getArg().getText();
@@ -818,8 +808,8 @@
while (true) {
String line = tokens.readHereLine(trimTabs);
if (line == null) {
- throw new IncompleteCommandException("EOF reached while looking for '" +
- marker + "' to end a HERE document", continuationPrompt);
+ throw new ShellSyntaxException("EOF reached while looking for '" +
+ marker + "' to end a HERE document");
}
if (line.equals(marker)) {
break;
Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/shell/org/jnode/shell/bjorne/BjorneTokenizer.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -66,92 +66,58 @@
import static org.jnode.shell.bjorne.BjorneToken.TOK_WHILE;
import static org.jnode.shell.bjorne.BjorneToken.TOK_WORD;
-import org.jnode.shell.IncompleteCommandException;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+
import org.jnode.shell.ShellFailureException;
+import org.jnode.shell.ShellSyntaxException;
public class BjorneTokenizer {
- private final char[] chars;
+ private final Reader reader;
- private final int len;
-
- private int pos;
-
private BjorneToken prev, current, next;
-
private static final int EOS = -1;
+ private static final int INVALID = -2;
+
+ private int pos = 0;
+ private int lastCh = INVALID;
+ private int nextCh = INVALID;
private final boolean debug;
/**
* 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
+ * @throws ShellSyntaxException
*/
- public BjorneTokenizer(String text) throws IncompleteCommandException {
- this(text, false);
+ public BjorneTokenizer(String text)
+ throws ShellSyntaxException {
+ this(new StringReader(text), false);
}
/**
- * 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
+ * Create a tokenizer for the supplied shell input reader.
+ * @param reader the reader to be tokenized.
+ * @throws ShellSyntaxException
*/
- public BjorneTokenizer(String text, boolean debug) throws IncompleteCommandException {
- chars = foldContinuations(text);
- len = chars.length;
- this.debug = debug;
+ public BjorneTokenizer(Reader reader)
+ throws ShellSyntaxException {
+ this(reader, false);
}
/**
- * 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
+ * Create a tokenizer for the supplied shell input text.
+ * @param reader the reader to be tokenized.
+ * @param debug if {@code true}, produce debug output
+ * @throws ShellSyntaxException
*/
- private char[] foldContinuations(String text) throws IncompleteCommandException {
- // FIXME this is wrong ... if we are going to imitate the documented behaviour
- // of bash. (In bash, if the the MARKER is quoted, '\<newline>' is apparently
- // not interpreted as a continuation.)
- 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();
+ public BjorneTokenizer(Reader reader, boolean debug)
+ throws ShellSyntaxException {
+ this.reader = reader;
+ this.debug = debug;
}
/**
@@ -180,7 +146,7 @@
public BjorneToken peek(int context) {
BjorneToken res = reinterpret(peek(), context);
if (debug) {
- System.err.println("--> " + res);
+ System.err.println("peek(" + context + ") --> " + res);
}
return res;
}
@@ -240,7 +206,7 @@
public BjorneToken next(int context) {
BjorneToken res = reinterpret(next(), context);
if (debug) {
- System.err.println("--> " + res);
+ System.err.println("next(" + context + ") --> " + res);
}
return res;
}
@@ -290,15 +256,17 @@
System.err.print("advance() ... {" + prev + "," + current + ","
+ next + "} ...");
}
- int ch = nextCh();
+ int ch = peekCh();
while (ch == '\t' || ch == ' ') {
- ch = nextCh();
+ nextCh();
+ ch = peekCh();
}
- int start = pos - 1;
+ int start = getPos() - 1;
switch (ch) {
case EOS:
- return makeToken(TOK_END_OF_STREAM, len);
+ return makeToken(TOK_END_OF_STREAM, getPos());
case '\n':
+ nextCh();
return makeToken(TOK_END_OF_LINE, start);
case '#':
while ((ch = nextCh()) != EOS) {
@@ -308,8 +276,10 @@
}
return makeToken(TOK_END_OF_STREAM, start);
case '(':
+ nextCh();
return makeToken(TOK_LPAREN, start);
case ')':
+ nextCh();
return makeToken(TOK_RPAREN, start);
case '<':
case '>':
@@ -323,18 +293,18 @@
}
private BjorneToken makeToken(int tokenType, int start) {
- return new BjorneToken(tokenType, "", start, pos);
+ return new BjorneToken(tokenType, "", start, getPos());
}
private BjorneToken makeToken(int tokenType, String value, int start) {
- return new BjorneToken(tokenType, value, start, pos);
+ return new BjorneToken(tokenType, value, start, getPos());
}
private BjorneToken parseWord() {
int quoteChar = 0;
StringBuffer sb = new StringBuffer();
- int ch = prevCh();
- int start = pos - 1;
+ int ch = peekCh();
+ int start = getPos() - 1;
LOOP:
while (true) {
switch (ch) {
@@ -364,31 +334,36 @@
}
break;
case '\\':
- ch = nextCh();
+ nextCh();
+ ch = peekCh();
if (ch == '\n') {
- ch = nextCh();
+ // A '\\' followed by a newline is a line continuation:
+ // the two characters are skipped.
+ nextCh();
+ ch = peekCh();
continue;
+ } else if (ch == EOS) {
+ // Silently eat a '\\' at the end of stream position.
+ nextCh();
+ break LOOP;
} else {
+ // The '\\' is included in the (raw) word.
sb.append('\\');
- if (ch == EOS) {
- break LOOP;
- }
}
break;
default:
- /* empty */
+ // include anything else in the word.
break;
}
sb.append((char) ch);
- ch = nextCh();
+ nextCh();
+ ch = peekCh();
}
- if (ch != EOS) {
- backupCh();
- }
if (ch == '<' || ch == '>') {
boolean allDigits = true;
for (int i = 0; i < sb.length(); i++) {
ch = sb.charAt(i);
+ // FIXME ... I should deal with "\\\n" here I think.
if (ch < '0' || ch > '9') {
allDigits = false;
break;
@@ -402,8 +377,8 @@
}
private BjorneToken parseOperator() {
- int start = pos - 1;
- switch (prevCh()) {
+ int start = getPos() - 1;
+ switch (nextCh()) {
case '<':
switch (peekCh()) {
case '<':
@@ -458,26 +433,37 @@
throw new ShellFailureException("bad lexer state");
}
- private int nextCh() {
- return (pos >= len) ? EOS : chars[pos++];
+ private int nextCh() throws ShellFailureException {
+ try {
+ if (nextCh == INVALID) {
+ if (lastCh != EOS) {
+ lastCh = reader.read();
+ pos++;
+ }
+ } else {
+ lastCh = nextCh;
+ nextCh = INVALID;
+ pos++;
+ }
+ return lastCh;
+ } catch (IOException ex) {
+ throw new ShellFailureException("Unexpected exception", ex);
+ }
}
private int peekCh() {
- return (pos >= len) ? EOS : chars[pos];
- }
-
- private int prevCh() {
- if (pos <= 0) {
- throw new ShellFailureException("nextCh not called yet");
+ try {
+ if (nextCh == INVALID) {
+ nextCh = reader.read();
+ }
+ return nextCh;
+ } catch (IOException ex) {
+ throw new ShellFailureException("Unexpected exception", ex);
}
- return chars[pos - 1];
}
-
- private void backupCh() {
- if (pos == 0) {
- throw new ShellFailureException("cannot backup");
- }
- pos--;
+
+ private int getPos() {
+ return pos;
}
/**
Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/SetBuiltin.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/bjorne/SetBuiltin.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/shell/org/jnode/shell/bjorne/SetBuiltin.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -26,8 +26,8 @@
import org.jnode.shell.syntax.OptionalSyntax;
import org.jnode.shell.syntax.PowersetSyntax;
import org.jnode.shell.syntax.RepeatSyntax;
+import org.jnode.shell.syntax.SequenceSyntax;
import org.jnode.shell.syntax.StringArgument;
-import org.jnode.shell.syntax.SequenceSyntax;
import org.jnode.shell.syntax.SyntaxBundle;
import org.jnode.shell.syntax.VerbSyntax;
Modified: trunk/shell/src/shell/org/jnode/shell/bjorne/SourceBuiltin.java
===================================================================
--- trunk/shell/src/shell/org/jnode/shell/bjorne/SourceBuiltin.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/shell/org/jnode/shell/bjorne/SourceBuiltin.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -59,37 +59,27 @@
@Override
public void execute() throws Exception {
File file = argScript.getValue();
- long size = file.length();
- String commandStr = null;
- FileReader fin = null;
+ FileReader reader = null;
try {
- fin = new FileReader(file);
- if (size > 1000000) {
- // Since we are going to read the whole script into memory, we
- // need to set some limit on the script's file size ...
- getError().getPrintWriter().println("source: " + file + ": file too big");
- exit(1);
+ reader = new FileReader(file);
+
+ // TODO ... implement args.
+ BjorneContext pc = getParentContext();
+ int rc = pc.getInterpreter().interpret(pc.getShell(), reader, "", new String[0]);
+ if (rc != 0) {
+ exit(rc);
}
- char[] buffer = new char[(int) size];
- int nosRead = fin.read(buffer);
- commandStr = new String(buffer, 0, nosRead);
} catch (IOException ex) {
getError().getPrintWriter().println("source: " + file + ": " + ex.getMessage());
exit(1);
} finally {
- if (fin != null) {
+ if (reader != null) {
try {
- fin.close();
+ reader.close();
} catch (IOException ex) {
/* blah */
}
}
}
- // TODO ... implement args.
- BjorneContext pc = getParentContext();
- int rc = pc.getInterpreter().interpret(pc.getShell(), commandStr, null, true);
- if (rc != 0) {
- exit(rc);
- }
}
}
Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTest.java
===================================================================
--- trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTest.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneParserTest.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -20,6 +20,8 @@
package org.jnode.test.shell.bjorne;
+import java.io.StringReader;
+
import junit.framework.TestCase;
import org.jnode.shell.ShellException;
@@ -31,7 +33,7 @@
private static final boolean DEBUG = false;
public void testParser() throws ShellException {
- new BjorneParser(new BjorneTokenizer(""), null);
+ new BjorneParser(new BjorneTokenizer(""));
}
public void test1() throws ShellException {
@@ -147,7 +149,7 @@
}
private String doTest(String input) throws ShellException {
- BjorneParser p = new BjorneParser(new BjorneTokenizer(input, DEBUG), null);
+ BjorneParser p = new BjorneParser(new BjorneTokenizer(new StringReader(input), DEBUG));
String res = p.parse().toString();
if (DEBUG) {
System.err.println(res);
Modified: trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneTokenizerTest.java
===================================================================
--- trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneTokenizerTest.java 2009-07-05 06:40:00 UTC (rev 5592)
+++ trunk/shell/src/test/org/jnode/test/shell/bjorne/BjorneTokenizerTest.java 2009-07-06 14:33:04 UTC (rev 5593)
@@ -67,26 +67,17 @@
import static org.jnode.shell.bjorne.BjorneToken.TOK_WORD;
import junit.framework.TestCase;
-import org.jnode.shell.IncompleteCommandException;
+import org.jnode.shell.ShellSyntaxException;
import org.jnode.shell.bjorne.BjorneToken;
import org.jnode.shell.bjorne.BjorneTokenizer;
public class BjorneTokenizerTest extends TestCase {
- public void testBjorneTokenizer() throws IncompleteCommandException {
+ public void testBjorneTokenizer() throws ShellSyntaxException {
new BjorneTokenizer("hello");
}
- public void testBjorneTokenizer2() {
- try {
- new BjorneTokenizer("hello\\");
- fail("no exception");
- } catch (IncompleteCommandException ex) {
- // expected
- }
- }
-
- public void testEmpty() throws IncompleteCommandException {
+ public void testEmpty() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer("");
BjorneToken token = tokenizer.peek();
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
@@ -98,7 +89,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testNewline() throws IncompleteCommandException {
+ public void testNewline() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer("\n");
BjorneToken token = tokenizer.next();
assertEquals(TOK_END_OF_LINE, token.getTokenType());
@@ -106,7 +97,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testBlanksAndNewlines() throws IncompleteCommandException {
+ public void testBlanksAndNewlines() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer(" \n\t\n ");
BjorneToken token = tokenizer.next();
assertEquals(TOK_END_OF_LINE, token.getTokenType());
@@ -116,7 +107,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testComments() throws IncompleteCommandException {
+ public void testComments() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
"# comment\n #comment 2\n # comment # 3");
BjorneToken token = tokenizer.next();
@@ -127,7 +118,25 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testSymbols() throws IncompleteCommandException {
+ public void testContinuation() throws ShellSyntaxException {
+ BjorneTokenizer tokenizer = new BjorneTokenizer("hello\\\nthere");
+ BjorneToken token = tokenizer.next();
+ assertEquals(TOK_WORD, token.getTokenType());
+ assertEquals("hellothere", token.getText());
+ token = tokenizer.next();
+ assertEquals(TOK_END_OF_STREAM, token.getTokenType());
+ }
+
+ public void testBackslashAtEnd() throws ShellSyntaxException {
+ BjorneTokenizer tokenizer = new BjorneTokenizer("hello\\");
+ BjorneToken token = tokenizer.next();
+ assertEquals(TOK_WORD, token.getTokenType());
+ assertEquals("hello", token.getText());
+ token = tokenizer.next();
+ assertEquals(TOK_END_OF_STREAM, token.getTokenType());
+ }
+
+ public void testSymbols() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer("; | & < > ( )");
BjorneToken token = tokenizer.next();
assertEquals(TOK_SEMI, token.getTokenType());
@@ -147,9 +156,8 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testSymbols2() throws IncompleteCommandException {
- BjorneTokenizer tokenizer = new BjorneTokenizer(
- "; ;; | || & && < << > >>");
+ public void testSymbols2() throws ShellSyntaxException {
+ BjorneTokenizer tokenizer = new BjorneTokenizer("; ;; | || & && < << > >>");
BjorneToken token = tokenizer.next();
assertEquals(TOK_SEMI, token.getTokenType());
token = tokenizer.next();
@@ -174,7 +182,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testSymbols3() throws IncompleteCommandException {
+ public void testSymbols3() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer(";;;|||&&&<<<>>>");
BjorneToken token = tokenizer.next();
assertEquals(TOK_DSEMI, token.getTokenType());
@@ -198,9 +206,8 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testSymbols4() throws IncompleteCommandException {
- BjorneTokenizer tokenizer = new BjorneTokenizer(
- "< << <<- <& <> > >> >| >&");
+ public void testSymbols4() throws ShellSyntaxException {
+ BjorneTokenizer tokenizer = new BjorneTokenizer("< << <<- <& <> > >> >| >&");
BjorneToken token = tokenizer.next();
assertEquals(TOK_LESS, token.getTokenType());
token = tokenizer.next();
@@ -223,7 +230,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testWords() throws IncompleteCommandException {
+ public void testWords() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer("hello there");
BjorneToken token = tokenizer.next();
assertEquals(TOK_WORD, token.getTokenType());
@@ -235,9 +242,8 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testWords2() throws IncompleteCommandException {
- BjorneTokenizer tokenizer = new BjorneTokenizer(
- "hello\\ there\\\n friend");
+ public void testWords2() throws ShellSyntaxException {
+ BjorneTokenizer tokenizer = new BjorneTokenizer("hello\\ there\\\n friend");
BjorneToken token = tokenizer.next();
assertEquals(TOK_WORD, token.getTokenType());
assertEquals("hello\\ there", token.getText());
@@ -266,7 +272,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testWords3() throws IncompleteCommandException {
+ public void testWords3() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer("'1 2' \"3 4\" `5 6`");
BjorneToken token = tokenizer.next();
assertEquals(TOK_WORD, token.getTokenType());
@@ -281,7 +287,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testWords4() throws IncompleteCommandException {
+ public void testWords4() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer("'1 \"2\"' \"3\\\"4\"");
BjorneToken token = tokenizer.next();
assertEquals(TOK_WORD, token.getTokenType());
@@ -293,7 +299,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testWords5() throws IncompleteCommandException {
+ public void testWords5() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer("1<2>3&4;5|6)7");
BjorneToken token = tokenizer.next();
assertEquals(TOK_IO_NUMBER, token.getTokenType());
@@ -332,10 +338,9 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule1() throws IncompleteCommandException {
+ public void testRule1() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
- "if then else elif fi for done while until "
- + "case { } ! do in esac");
+ "if then else elif fi for done while until case { } ! do in esac");
BjorneToken token = tokenizer.next(RULE_1_CONTEXT);
assertEquals(TOK_IF, token.getTokenType());
token = tokenizer.next(RULE_1_CONTEXT);
@@ -372,9 +377,8 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule5() throws IncompleteCommandException {
- BjorneTokenizer tokenizer = new BjorneTokenizer(
- "if a a1 9a a_b a,b AB A=b");
+ public void testRule5() throws ShellSyntaxException {
+ BjorneTokenizer tokenizer = new BjorneTokenizer("if a a1 9a a_b a,b AB A=b");
BjorneToken token = tokenizer.next(RULE_5_CONTEXT);
assertEquals(TOK_NAME, token.getTokenType());
token = tokenizer.next(RULE_5_CONTEXT);
@@ -395,7 +399,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule6() throws IncompleteCommandException {
+ public void testRule6() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer("if in do");
BjorneToken token = tokenizer.next(RULE_6_CONTEXT);
assertEquals(TOK_WORD, token.getTokenType());
@@ -407,10 +411,9 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule7a() throws IncompleteCommandException {
+ public void testRule7a() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
- "if then else elif fi for done while until "
- + "case { } ! do in esac a= a=b 1a=b =c");
+ "if then else elif fi for done while until case { } ! do in esac a= a=b 1a=b =c");
BjorneToken token = tokenizer.next(RULE_7a_CONTEXT);
assertEquals(TOK_IF, token.getTokenType());
token = tokenizer.next(RULE_7a_CONTEXT);
@@ -455,10 +458,9 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule7b() throws IncompleteCommandException {
+ public void testRule7b() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer(
- "if then else elif fi for done while until "
- + "case { } ! do in esac a= a=b 1a=b =c");
+ "if then else elif fi for done while until case { } ! do in esac a= a=b 1a=b =c");
BjorneToken token = tokenizer.next(RULE_7b_CONTEXT);
assertEquals(TOK_WORD, token.getTokenType());
token = tokenizer.next(RULE_7b_CONTEXT);
@@ -503,10 +505,9 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRule8() throws IncompleteCommandException {
+ public void testRule8() throws ShellSyntaxException {
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");
+ "if then else elif fi for done while until case { } ! do in esac a a_b a= a=b 1a=b =c");
BjorneToken token = tokenizer.next(RULE_8_CONTEXT);
assertEquals(TOK_IF, token.getTokenType());
token = tokenizer.next(RULE_8_CONTEXT);
@@ -555,7 +556,7 @@
assertEquals(TOK_END_OF_STREAM, token.getTokenType());
}
- public void testRegress() throws IncompleteCommandException {
+ public void testRegress() throws ShellSyntaxException {
BjorneTokenizer tokenizer = new BjorneTokenizer("ls -l");
BjorneToken token = tokenizer.peek(RULE_7a_CONTEXT);
assertEquals(TOK_WORD, token.getTokenType());
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
|