Documented gel.* packages.
git-svn-id: http://google-refine.googlecode.com/svn/trunk@333 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
parent
60dd7eab82
commit
ec0110d65b
@ -91,6 +91,18 @@ public class ExpressionUtils {
|
||||
new EvalError(v.getClass().getSimpleName() + " value not storable");
|
||||
}
|
||||
|
||||
static public boolean isArray(Object v) {
|
||||
return v != null && v.getClass().isArray();
|
||||
}
|
||||
|
||||
static public boolean isArrayOrCollection(Object v) {
|
||||
return v != null && (v.getClass().isArray() || v instanceof Collection<?>);
|
||||
}
|
||||
|
||||
static public boolean isArrayOrList(Object v) {
|
||||
return v != null && (v.getClass().isArray() || v instanceof List<?>);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static public List<Object> toObjectList(Object v) {
|
||||
return (List<Object>) v;
|
||||
|
@ -5,6 +5,11 @@ import java.util.Properties;
|
||||
import com.metaweb.gridworks.Jsonizable;
|
||||
import com.metaweb.gridworks.expr.Evaluable;
|
||||
|
||||
/**
|
||||
* Interface of GEL controls such as if, forEach, forNonBlank, with. A control can
|
||||
* decide which part of the code to execute and can affect the environment bindings.
|
||||
* Functions, on the other hand, can't do either.
|
||||
*/
|
||||
public interface Control extends Jsonizable {
|
||||
public Object call(Properties bindings, Evaluable[] args);
|
||||
|
||||
|
@ -4,6 +4,10 @@ import java.util.Properties;
|
||||
|
||||
import com.metaweb.gridworks.Jsonizable;
|
||||
|
||||
/**
|
||||
* Interface for functions. When a function is called, its arguments have already
|
||||
* been evaluated down into non-error values.
|
||||
*/
|
||||
public interface Function extends Jsonizable {
|
||||
public Object call(Properties bindings, Object[] args);
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ import com.metaweb.gridworks.gel.ast.OperatorCallExpr;
|
||||
import com.metaweb.gridworks.gel.ast.VariableExpr;
|
||||
|
||||
public class Parser {
|
||||
protected Scanner _scanner;
|
||||
protected Scanner _scanner;
|
||||
protected Token _token;
|
||||
protected Evaluable _root;
|
||||
|
||||
@ -47,6 +47,10 @@ public class Parser {
|
||||
return new ParsingException("Parsing error at offset " + index + ": " + desc);
|
||||
}
|
||||
|
||||
/**
|
||||
* <expression> := <sub-expression>
|
||||
* | <expression> [ "<" "<=" ">" ">=" "==" "!=" ] <sub-expression>
|
||||
*/
|
||||
protected Evaluable parseExpression() throws ParsingException {
|
||||
Evaluable sub = parseSubExpression();
|
||||
|
||||
@ -66,6 +70,10 @@ public class Parser {
|
||||
return sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* <sub-expression> := <term>
|
||||
* | <sub-expression> [ "+" "-" ] <term>
|
||||
*/
|
||||
protected Evaluable parseSubExpression() throws ParsingException {
|
||||
Evaluable sub = parseTerm();
|
||||
|
||||
@ -85,6 +93,10 @@ public class Parser {
|
||||
return sub;
|
||||
}
|
||||
|
||||
/**
|
||||
* <term> := <factor>
|
||||
* | <term> [ "*" "/" ] <factor>
|
||||
*/
|
||||
protected Evaluable parseTerm() throws ParsingException {
|
||||
Evaluable factor = parseFactor();
|
||||
|
||||
@ -104,6 +116,17 @@ public class Parser {
|
||||
return factor;
|
||||
}
|
||||
|
||||
/**
|
||||
* <term> := <term-start> ( <path-segment> )*
|
||||
* <term-start> :=
|
||||
* <string> | <number> | - <number> | <regex> | <identifier> |
|
||||
* <identifier> ( <expression-list> )
|
||||
*
|
||||
* <path-segment> := "[" <expression-list> "]"
|
||||
* | "." <identifier>
|
||||
* | "." <identifier> "(" <expression-list> ")"
|
||||
*
|
||||
*/
|
||||
protected Evaluable parseFactor() throws ParsingException {
|
||||
if (_token == null) {
|
||||
throw makeException("Expecting something more at end of expression");
|
||||
@ -214,6 +237,11 @@ public class Parser {
|
||||
return eval;
|
||||
}
|
||||
|
||||
/**
|
||||
* <expression-list> := <empty>
|
||||
* | <expression> ( "," <expression> )*
|
||||
*
|
||||
*/
|
||||
protected List<Evaluable> parseExpressionList(String closingDelimiter) throws ParsingException {
|
||||
List<Evaluable> l = new LinkedList<Evaluable>();
|
||||
|
||||
|
@ -14,8 +14,8 @@ public class Scanner {
|
||||
static public class Token {
|
||||
final public int start;
|
||||
final public int end;
|
||||
final public TokenType type;
|
||||
final public String text;
|
||||
final public TokenType type;
|
||||
final public String text;
|
||||
|
||||
Token(int start, int end, TokenType type, String text) {
|
||||
this.start = start;
|
||||
@ -26,7 +26,7 @@ public class Scanner {
|
||||
}
|
||||
|
||||
static public class ErrorToken extends Token {
|
||||
final public String detail; // error detail
|
||||
final public String detail; // error detail
|
||||
|
||||
public ErrorToken(int start, int end, String text, String detail) {
|
||||
super(start, end, TokenType.Error, text);
|
||||
@ -52,9 +52,9 @@ public class Scanner {
|
||||
}
|
||||
}
|
||||
|
||||
protected String _text;
|
||||
protected int _index;
|
||||
protected int _limit;
|
||||
protected String _text; // input text to tokenize
|
||||
protected int _index; // index of the next character to process
|
||||
protected int _limit; // process up to this index
|
||||
|
||||
public Scanner(String s) {
|
||||
this(s, 0, s.length());
|
||||
@ -70,6 +70,16 @@ public class Scanner {
|
||||
return _index;
|
||||
}
|
||||
|
||||
/**
|
||||
* The regexPossible flag is used by the parser to hint the scanner what to do
|
||||
* when it encounters a slash. Since the divide operator / and the opening
|
||||
* delimiter of a regex literal are the same, but divide operators and regex
|
||||
* literals can't occur at the same place in an expression, this flag is a cheap
|
||||
* way to distinguish the two without having to look ahead.
|
||||
*
|
||||
* @param regexPossible
|
||||
* @return
|
||||
*/
|
||||
public Token next(boolean regexPossible) {
|
||||
// skip whitespace
|
||||
while (_index < _limit && Character.isWhitespace(_text.charAt(_index))) {
|
||||
|
@ -5,6 +5,9 @@ import java.util.Properties;
|
||||
import com.metaweb.gridworks.expr.Evaluable;
|
||||
import com.metaweb.gridworks.gel.Control;
|
||||
|
||||
/**
|
||||
* An abstract syntax tree node encapsulating a control call, such as "if".
|
||||
*/
|
||||
public class ControlCallExpr implements Evaluable {
|
||||
final protected Evaluable[] _args;
|
||||
final protected Control _control;
|
||||
|
@ -7,6 +7,11 @@ import com.metaweb.gridworks.expr.Evaluable;
|
||||
import com.metaweb.gridworks.expr.ExpressionUtils;
|
||||
import com.metaweb.gridworks.expr.HasFields;
|
||||
|
||||
/**
|
||||
* An abstract syntax tree node encapsulating a field accessor,
|
||||
* e.g., "cell.value" is accessing the field named "value" on the
|
||||
* variable called "cell".
|
||||
*/
|
||||
public class FieldAccessorExpr implements Evaluable {
|
||||
final protected Evaluable _inner;
|
||||
final protected String _fieldName;
|
||||
@ -19,7 +24,7 @@ public class FieldAccessorExpr implements Evaluable {
|
||||
public Object evaluate(Properties bindings) {
|
||||
Object o = _inner.evaluate(bindings);
|
||||
if (ExpressionUtils.isError(o)) {
|
||||
return o;
|
||||
return o; // bubble the error up
|
||||
} else if (o == null) {
|
||||
return new EvalError("Cannot retrieve field from null");
|
||||
} else if (o instanceof HasFields) {
|
||||
|
@ -6,6 +6,12 @@ import com.metaweb.gridworks.expr.Evaluable;
|
||||
import com.metaweb.gridworks.expr.ExpressionUtils;
|
||||
import com.metaweb.gridworks.gel.Function;
|
||||
|
||||
/**
|
||||
* An abstract syntax tree node encapsulating a function call. The function's
|
||||
* arguments are all evaluated down to values before the function is applied.
|
||||
* If any argument is an error, the function is not applied, and the error is
|
||||
* the result of the expression.
|
||||
*/
|
||||
public class FunctionCallExpr implements Evaluable {
|
||||
final protected Evaluable[] _args;
|
||||
final protected Function _function;
|
||||
@ -20,7 +26,7 @@ public class FunctionCallExpr implements Evaluable {
|
||||
for (int i = 0; i < _args.length; i++) {
|
||||
Object v = _args[i].evaluate(bindings);
|
||||
if (ExpressionUtils.isError(v)) {
|
||||
return v;
|
||||
return v; // bubble up the error
|
||||
}
|
||||
args[i] = v;
|
||||
}
|
||||
|
@ -6,6 +6,9 @@ import org.json.JSONObject;
|
||||
|
||||
import com.metaweb.gridworks.expr.Evaluable;
|
||||
|
||||
/**
|
||||
* An abstract syntax tree node encapsulating a literal value.
|
||||
*/
|
||||
public class LiteralExpr implements Evaluable {
|
||||
final protected Object _value;
|
||||
|
||||
|
@ -5,6 +5,9 @@ import java.util.Properties;
|
||||
import com.metaweb.gridworks.expr.Evaluable;
|
||||
import com.metaweb.gridworks.expr.ExpressionUtils;
|
||||
|
||||
/**
|
||||
* An abstract syntax tree node encapsulating an operator call, such as "+".
|
||||
*/
|
||||
public class OperatorCallExpr implements Evaluable {
|
||||
final protected Evaluable[] _args;
|
||||
final protected String _op;
|
||||
|
@ -4,6 +4,9 @@ import java.util.Properties;
|
||||
|
||||
import com.metaweb.gridworks.expr.Evaluable;
|
||||
|
||||
/**
|
||||
* An abstract syntax tree node encapsulating the retrieval of a variable's content.
|
||||
*/
|
||||
public class VariableExpr implements Evaluable {
|
||||
final protected String _name;
|
||||
|
||||
|
@ -2,7 +2,6 @@ package com.metaweb.gridworks.gel.controls;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
@ -21,7 +20,8 @@ public class ForEach implements Control {
|
||||
if (args.length != 3) {
|
||||
return ControlFunctionRegistry.getControlName(this) + " expects 3 arguments";
|
||||
} else if (!(args[1] instanceof VariableExpr)) {
|
||||
return ControlFunctionRegistry.getControlName(this) + " expects second argument to be a variable name";
|
||||
return ControlFunctionRegistry.getControlName(this) +
|
||||
" expects second argument to be a variable name";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -30,7 +30,7 @@ public class ForEach implements Control {
|
||||
Object o = args[0].evaluate(bindings);
|
||||
if (ExpressionUtils.isError(o)) {
|
||||
return o;
|
||||
} else if (o == null || (!o.getClass().isArray() && !(o instanceof Iterable<?>))) {
|
||||
} else if (!ExpressionUtils.isArrayOrCollection(o)) {
|
||||
return new EvalError("First argument to forEach is not an array");
|
||||
}
|
||||
|
||||
@ -40,36 +40,36 @@ public class ForEach implements Control {
|
||||
try {
|
||||
List<Object> results = null;
|
||||
|
||||
if (o.getClass().isArray()) {
|
||||
Object[] values = (Object[]) o;
|
||||
|
||||
results = new ArrayList<Object>(values.length);
|
||||
for (Object v : values) {
|
||||
bindings.put(name, v);
|
||||
|
||||
Object r = args[2].evaluate(bindings);
|
||||
|
||||
results.add(r);
|
||||
}
|
||||
} else {
|
||||
results = o instanceof Collection<?> ?
|
||||
new ArrayList<Object>(ExpressionUtils.toObjectCollection(o).size()) :
|
||||
new ArrayList<Object>();
|
||||
|
||||
Iterator<Object> i = ExpressionUtils.toObjectCollection(o).iterator();
|
||||
while (i.hasNext()) {
|
||||
Object v = i.next();
|
||||
|
||||
bindings.put(name, v);
|
||||
|
||||
Object r = args[2].evaluate(bindings);
|
||||
|
||||
results.add(r);
|
||||
}
|
||||
}
|
||||
|
||||
if (o.getClass().isArray()) {
|
||||
Object[] values = (Object[]) o;
|
||||
|
||||
results = new ArrayList<Object>(values.length);
|
||||
for (Object v : values) {
|
||||
bindings.put(name, v);
|
||||
|
||||
Object r = args[2].evaluate(bindings);
|
||||
|
||||
results.add(r);
|
||||
}
|
||||
} else {
|
||||
Collection<Object> collection = ExpressionUtils.toObjectCollection(o);
|
||||
|
||||
results = new ArrayList<Object>(collection.size());
|
||||
|
||||
for (Object v : collection) {
|
||||
bindings.put(name, v);
|
||||
|
||||
Object r = args[2].evaluate(bindings);
|
||||
|
||||
results.add(r);
|
||||
}
|
||||
}
|
||||
|
||||
return results.toArray();
|
||||
} finally {
|
||||
/*
|
||||
* Restore the old value bound to the variable, if any.
|
||||
*/
|
||||
if (oldValue != null) {
|
||||
bindings.put(name, oldValue);
|
||||
} else {
|
||||
|
@ -16,7 +16,8 @@ public class ForNonBlank implements Control {
|
||||
if (args.length != 4) {
|
||||
return ControlFunctionRegistry.getControlName(this) + " expects 4 arguments";
|
||||
} else if (!(args[1] instanceof VariableExpr)) {
|
||||
return ControlFunctionRegistry.getControlName(this) + " expects second argument to be a variable name";
|
||||
return ControlFunctionRegistry.getControlName(this) +
|
||||
" expects second argument to be a variable name";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -28,12 +29,15 @@ public class ForNonBlank implements Control {
|
||||
String name = ((VariableExpr) var).getName();
|
||||
|
||||
if (ExpressionUtils.isNonBlankData(o)) {
|
||||
Object oldValue = bindings.containsKey(name) ? bindings.get(name) : null;
|
||||
Object oldValue = bindings.get(name);
|
||||
bindings.put(name, o);
|
||||
|
||||
try {
|
||||
return args[2].evaluate(bindings);
|
||||
} finally {
|
||||
/*
|
||||
* Restore the old value bound to the variable, if any.
|
||||
*/
|
||||
if (oldValue != null) {
|
||||
bindings.put(name, oldValue);
|
||||
} else {
|
||||
|
@ -21,7 +21,7 @@ public class If implements Control {
|
||||
public Object call(Properties bindings, Evaluable[] args) {
|
||||
Object o = args[0].evaluate(bindings);
|
||||
if (ExpressionUtils.isError(o)) {
|
||||
return o;
|
||||
return o; // bubble the error up
|
||||
} else if (ExpressionUtils.isTrue(o)) {
|
||||
return args[1].evaluate(bindings);
|
||||
} else {
|
||||
|
@ -27,10 +27,7 @@ abstract class IsTest implements Control {
|
||||
throws JSONException {
|
||||
|
||||
writer.object();
|
||||
writer.key("description"); writer.value(
|
||||
"Evaluates expression o. If it is true, evaluates expression eTrue and returns the result. " +
|
||||
"Otherwise, evaluates expression eFalse and returns that result instead."
|
||||
);
|
||||
writer.key("description"); writer.value(getDescription());
|
||||
writer.key("params"); writer.value("expression o");
|
||||
writer.key("returns"); writer.value("boolean");
|
||||
writer.endObject();
|
||||
|
@ -15,7 +15,8 @@ public class With implements Control {
|
||||
if (args.length != 3) {
|
||||
return ControlFunctionRegistry.getControlName(this) + " expects 3 arguments";
|
||||
} else if (!(args[1] instanceof VariableExpr)) {
|
||||
return ControlFunctionRegistry.getControlName(this) + " expects second argument to be a variable name";
|
||||
return ControlFunctionRegistry.getControlName(this) +
|
||||
" expects second argument to be a variable name";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -34,6 +35,9 @@ public class With implements Control {
|
||||
|
||||
return args[2].evaluate(bindings);
|
||||
} finally {
|
||||
/*
|
||||
* Restore the old value bound to the variable, if any.
|
||||
*/
|
||||
if (oldValue != null) {
|
||||
bindings.put(name, oldValue);
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user