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();
+ }
+}