Initial commit of OSGi Shell contribution. (FELIX-946)


git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@783826 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/gogo/src/aQute/shell/runtime/Parser.java b/gogo/src/aQute/shell/runtime/Parser.java
new file mode 100644
index 0000000..8585847
--- /dev/null
+++ b/gogo/src/aQute/shell/runtime/Parser.java
@@ -0,0 +1,280 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package aQute.shell.runtime;
+
+import java.util.*;
+
+public class Parser {
+	int					current	= 0;
+	CharSequence		text;
+	boolean				escaped;
+	static final String	SPECIAL	= "<;|{[\"'$'`(=";
+
+	public Parser(CharSequence program) {
+		text = program;
+	}
+
+	void ws() {
+		while (!eof() && Character.isWhitespace(peek())) {
+			current++;
+			if (peek() == '/' && current < text.length()-2 && text.charAt(current + 1) == '/') {
+				comment();
+			}
+		}
+	}
+
+	private void comment() {
+		while (!eof() && peek() != '\n' && peek() != '\r')
+			next();
+	}
+
+	boolean eof() {
+		return current >= text.length();
+	}
+
+	char peek() {
+		escaped = false;
+		if (eof())
+			return 0;
+
+		char c = text.charAt(current);
+
+		if (c == '\\') {
+			escaped = true;
+			c = text.charAt(++current);
+
+			switch (c) {
+			case 't':
+				c = '\t';
+				break;
+			case '\r':
+			case '\n':
+				c = ' ';
+				break;
+			case 'b':
+				c = '\b';
+				break;
+			case 'f':
+				c = '\f';
+				break;
+			case 'n':
+				c = '\n';
+				break;
+			case 'r':
+				c = '\r';
+				break;
+			case 'u':
+				c = unicode();
+				break;
+			default:
+				// We just take the next character literally
+				// but have the escaped flag set, important for {},[] etc
+			}
+		}
+		return c;
+	}
+
+	public List<List<List<CharSequence>>> program() {
+		List<List<List<CharSequence>>> program = new ArrayList<List<List<CharSequence>>>();
+		ws();
+		if (!eof()) {
+			program.add(statements());
+			while (peek() == '|') {
+				current++;
+				program.add(statements());
+			}
+		}
+		if (!eof())
+			throw new RuntimeException("Program has trailing text: "
+					+ context(current));
+
+		return program;
+	}
+
+	CharSequence context(int around) {
+		return text.subSequence(Math.max(0, current - 20), Math.min(text
+				.length(), current + 4));
+	}
+
+	public List<List<CharSequence>> statements() {
+		List<List<CharSequence>> statements = new ArrayList<List<CharSequence>>();
+		statements.add(statement());
+		while (peek() == ';') {
+			current++;
+			statements.add(statement());
+		}
+		return statements;
+	}
+
+	public List<CharSequence> statement() {
+		List<CharSequence> statement = new ArrayList<CharSequence>();
+		statement.add(value());
+		while (!eof()) {
+			ws();
+			if (peek() == '|' || peek() == ';')
+				break;
+
+			if (!eof())
+				statement.add(messy());
+		}
+		return statement;
+	}
+
+	public CharSequence messy() {
+		char c = peek();
+		if (c > 0 && SPECIAL.indexOf(c)< 0) {
+			int start = current++;
+			while (!eof()) {
+				c = peek();
+				if (c == ';' || c == '|' || Character.isWhitespace(c))
+					break;
+				next();
+			}
+
+			return text.subSequence(start, current);
+		} else
+			return value();
+	}
+
+	CharSequence value() {
+		ws();
+
+		int start = current;
+		char c = next();
+		switch (c) {
+		case '{':
+			return text.subSequence(start, find('}', '{'));
+		case '(':
+			return text.subSequence(start, find(')', '('));
+		case '[':
+			return text.subSequence(start, find(']', '['));
+		case '"':
+			return text.subSequence(start + 1, quote('"'));
+		case '\'':
+			return text.subSequence(start + 1, quote('\''));
+		case '<':
+			return text.subSequence(start, find('>', '<'));
+		case '$':
+			value();
+			return text.subSequence(start, current);
+		}
+
+		if (Character.isJavaIdentifierPart(c)) {
+			// Some identifier or number
+			while (!eof()) {
+				c = peek();
+				if (c!=':' && !Character.isJavaIdentifierPart(c) && c != '.')
+					break;
+				next();
+			}
+		} else {
+			// Operator, repeat while in operator class
+			while (!eof()) {
+				c = peek();
+				if (Character.isWhitespace(c)
+						|| Character.isJavaIdentifierPart(c))
+					break;
+			}
+		}
+		return text.subSequence(start, current);
+	}
+
+	char next() {
+		char c = peek();
+		current++;
+		return c;
+	}
+
+	char unicode() {
+		if (current + 4 > text.length())
+			throw new IllegalArgumentException(
+					"Unicode \\u escape at eof at pos ..." + context(current)
+							+ "...");
+
+		String s = text.subSequence(current, current + 4).toString();
+		int n = Integer.parseInt(s, 16);
+		return (char) n;
+	}
+
+	private int find(char target, char deeper) {
+		int start = current;
+		int level = 1;
+
+		while (level != 0) {
+			if (eof())
+				throw new RuntimeException(
+						"Eof found in the middle of a compound for '" + target
+								+ deeper + "', begins at " + context(start));
+
+			char c = next();
+			if (!escaped) {
+				if (c == target)
+					level--;
+				else if (c == deeper)
+					level++;
+				else if (c == '"')
+					quote('"');
+				else if (c == '\'')
+					quote('\'');
+				else if (c == '`')
+					quote('`');
+			}
+		}
+		return current;
+	}
+
+	int quote(char which) {
+		while (!eof() && (peek() != which || escaped))
+			next();
+
+		return current++;
+	}
+
+	CharSequence findVar() {
+		int start = current - 1;
+		char c = peek();
+
+		if (c == '{') {
+			next();
+			int end = find('}', '{');
+			return text.subSequence(start, end);
+		}
+
+		if (Character.isJavaIdentifierStart(c)) {
+			while (!eof() && Character.isJavaIdentifierPart(c) || c == '.') {
+				next();
+			}
+			return text.subSequence(start, current);
+		}
+		throw new IllegalArgumentException(
+				"Reference to variable does not match syntax of a variable: "
+						+ context(start));
+	}
+
+	public String toString() {
+		return "..." + context(current) + "...";
+	}
+
+	public String unescape() {
+		StringBuilder sb = new StringBuilder();
+		while (!eof())
+			sb.append(next());
+		return sb.toString();
+	}
+}