/*
* $Id$
*
* Copyright (C) 2003-2015 JNode.org
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
* License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; If not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
package org.jnode.command.common;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.jnode.shell.AbstractCommand;
/**
* JNode implementation of the UNIX 'expr' command
*
* @author crawley@jnode.org
*/
public class ExprCommand extends AbstractCommand {
private static class ExprException extends Exception {
private static final long serialVersionUID = 1L;
private ExprException(String msg) {
super(msg);
}
}
private static class ExprSyntaxException extends ExprException {
private static final long serialVersionUID = 1L;
private ExprSyntaxException(String msg) {
super(msg);
}
private ExprSyntaxException() {
super("syntax error");
}
}
private static final Set<String> RESERVED_WORDS = new HashSet<String>(
Arrays.asList(new String[] {
"|", "&", "+", "-", "*", "/", "%", "(", ")", "<", "<=", "=", "!=",
">", ">=", ":", "match", "index", "substr", "find"}));
// FIXME convert to use the new commandline syntax mechanism so that
// we get command completion.
String[] args;
int pos;
public void execute() throws Exception {
PrintWriter out = getOutput().getPrintWriter(true);
PrintWriter err = getError().getPrintWriter(true);
try {
this.args = getCommandLine().getArguments();
if (args.length == 0) {
throw new ExprSyntaxException("missing operand");
}
this.pos = 0;
Object res = parseExpr(true);
if (this.pos < this.args.length) {
throw new ExprSyntaxException();
}
out.println(res);
out.flush();
exit(isTrue(res) ? 0 : 1);
} catch (ExprSyntaxException ex) {
err.println(ex.getMessage());
exit(2);
} catch (ExprException ex) {
err.println(ex.getMessage());
exit(3);
}
}
/**
* Parse an expression. If 'evaluate' is true, the parsed expression
* is evaluate and the resulting value is returned. Otherwise, the
* result is a non-null dummy value.
*
* @param evaluate
* @return
* @throws ExprException
*/
private Object parseExpr(boolean evaluate) throws ExprException {
if (pos >= args.length) {
throw new ExprSyntaxException();
}
String op = args[pos];
if (op.equals("match")) {
return parseMatch(evaluate);
} else if (op.equals("index")) {
return parseIndex(evaluate);
} else if (op.equals("substr")) {
return parseSubstr(evaluate);
} else if (op.equals("length")) {
return parseLength(evaluate);
} else {
return parseOrExpr(evaluate);
}
}
private Object parseOrExpr(final boolean evaluate) throws ExprException {
Object res = parseAndExpr(evaluate);
boolean eval = evaluate;
while (pos < args.length && args[pos].equals("|")) {
pos++;
eval = eval && !isTrue(res);
Object tmp = parseAndExpr(eval);
if (eval) {
res = tmp;
}
}
return evaluate ? res : 0;
}
private Object parseAndExpr(final boolean evaluate) throws ExprException {
Object res = parseRelExpr(evaluate);
boolean eval = evaluate;
while (pos < args.length && args[pos].equals("&")) {
pos++;
if (isTrue(res)) {
parseRelExpr(eval);
res = 0;
} else {
parseRelExpr(false);
}
}
return res;
}
private Object parseRelExpr(boolean evaluate) throws ExprException {
Object res = parseAddExpr(evaluate);
String op;
while (pos < args.length && (
(op = args[pos]).equals("=") || op.equals("!=") ||
op.equals("<") || op.equals("<=") ||
op.equals(">") || op.equals(">="))) {
pos++;
Object res2 = parseAddExpr(evaluate);
if (evaluate) {
if (isInteger(res) && isInteger(res2)) {
if (op.equals("=")) {
res = asInteger(res) == asInteger(res2) ? 1 : 0;
} else if (op.equals("!=")) {
res = asInteger(res) != asInteger(res2) ? 1 : 0;
} else if (op.equals("<")) {
res = asInteger(res) < asInteger(res2) ? 1 : 0;
} else if (op.equals("<=")) {
res = asInteger(res) <= asInteger(res2) ? 1 : 0;
} else if (op.equals(">")) {
res = asInteger(res) > asInteger(res2) ? 1 : 0;
} else if (op.equals(">=")) {
res = asInteger(res) >= asInteger(res2) ? 1 : 0;
}
} else {
if (op.equals("=")) {
res = asString(res).equals(asString(res2)) ? 1 : 0;
} else if (op.equals("!=")) {
res = !asString(res).equals(asString(res2)) ? 1 : 0;
} else if (op.equals("<")) {
res = asString(res).compareTo(asString(res2)) < 0 ? 1 : 0;
} else if (op.equals("<=")) {
res = asString(res).compareTo(asString(res2)) <= 0 ? 1 : 0;
} else if (op.equals(">")) {
res = asString(res).compareTo(asString(res2)) > 0 ? 1 : 0;
} else if (op.equals(">=")) {
res = asString(res).compareTo(asString(res2)) >= 0 ? 1 : 0;
}
}
}
}
return res;
}
private Object parseAddExpr(boolean evaluate) throws ExprException {
Object res = parseMulExpr(evaluate);
String op;
while (pos < args.length && (
(op = args[pos]).equals("+") || op.equals("-"))) {
pos++;
Object res2 = parseMulExpr(evaluate);
if (evaluate) {
if (op.equals("+")) {
res = asInteger(res) + asInteger(res2);
} else if (op.equals("-")) {
res = asInteger(res) - asInteger(res2);
}
}
}
return res;
}
private Object parseMulExpr(boolean evaluate) throws ExprException {
Object res = parsePrimary(evaluate);
String op;
while (pos < args.length && (
(op = args[pos]).equals("*") || op.equals("/") || op.equals("%"))) {
pos++;
Object res2 = parseMulExpr(evaluate);
if (evaluate) {
try {
if (op.equals("*")) {
return asInteger(res) * asInteger(res2);
} else if (op.equals("/")) {
return asInteger(res) / asInteger(res2);
} else if (op.equals("%")) {
return asInteger(res) % asInteger(res2);
}
} catch (ArithmeticException ex) {
throw new ExprException("divide by zero");
}
}
}
return res;
}
private Object parsePrimary(boolean evaluate) throws ExprException {
if (pos == args.length) {
throw new ExprSyntaxException();
}
String tok = args[pos];
if (tok.equals("(")) {
pos++;
Object expr = parseExpr(evaluate);
if (pos >= args.length || !args[pos].equals(")")) {
throw new ExprSyntaxException();
}
pos++;
return expr;
} else if (tok.equals("+")) {
pos++;
if (pos == args.length) {
throw new ExprSyntaxException();
}
return args[pos++];
} else if (RESERVED_WORDS.contains(tok)) {
throw new ExprSyntaxException();
} else {
pos++;
return tok;
}
}
private Object parseLength(boolean evaluate) throws ExprException {
pos++;
Object str = parseExpr(evaluate);
if (str == null) {
throw new ExprSyntaxException();
}
return str.toString().length();
}
private Object parseSubstr(boolean evaluate) throws ExprException {
pos++;
String str = asString(parseExpr(evaluate));
int pos = asInteger(parseExpr(evaluate));
int length = asInteger(parseExpr(evaluate));
try {
return str.substring(pos + 1, pos + length + 1);
} catch (StringIndexOutOfBoundsException ex) {
return "";
}
}
private Object parseMatch(boolean evaluate) {
// TODO Auto-generated method stub
return null;
}
private Object parseIndex(boolean evaluate) throws ExprException {
pos++;
String str = asString(parseExpr(evaluate));
String chars = asString(parseExpr(evaluate));
int pos = -1;
for (char ch : chars.toCharArray()) {
int tmp = str.indexOf(ch);
if (tmp >= 0) {
pos = (pos == -1) ? tmp : Math.min(pos, tmp);
}
}
return pos + 1;
}
private boolean isTrue(Object obj) throws ExprSyntaxException {
return !obj.equals("") && !obj.toString().equals("0");
}
private boolean isInteger(Object obj) {
if (obj instanceof Integer) {
return true;
} else {
try {
Integer.parseInt(obj.toString());
return true;
} catch (NumberFormatException ex) {
return false;
}
}
}
private int asInteger(Object obj) throws ExprException {
if (obj instanceof Integer) {
return (Integer) obj;
} else {
try {
return Integer.parseInt(obj.toString());
} catch (NumberFormatException ex) {
throw new ExprException("non-numeric argument");
}
}
}
private String asString(Object obj) throws ExprException {
return obj.toString();
}
public static void main(String[] args) throws Exception {
new ExprCommand().execute(args);
}
}