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. ------------------------------------------------------------------------------ _______________________________________________ Jnode-svn-commits mailing list Jnode-svn-commits@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jnode-svn-commits