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");
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
static public List<Object> toObjectList(Object v) {
|
static public List<Object> toObjectList(Object v) {
|
||||||
return (List<Object>) v;
|
return (List<Object>) v;
|
||||||
|
@ -5,6 +5,11 @@ import java.util.Properties;
|
|||||||
import com.metaweb.gridworks.Jsonizable;
|
import com.metaweb.gridworks.Jsonizable;
|
||||||
import com.metaweb.gridworks.expr.Evaluable;
|
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 interface Control extends Jsonizable {
|
||||||
public Object call(Properties bindings, Evaluable[] args);
|
public Object call(Properties bindings, Evaluable[] args);
|
||||||
|
|
||||||
|
@ -4,6 +4,10 @@ import java.util.Properties;
|
|||||||
|
|
||||||
import com.metaweb.gridworks.Jsonizable;
|
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 interface Function extends Jsonizable {
|
||||||
public Object call(Properties bindings, Object[] args);
|
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;
|
import com.metaweb.gridworks.gel.ast.VariableExpr;
|
||||||
|
|
||||||
public class Parser {
|
public class Parser {
|
||||||
protected Scanner _scanner;
|
protected Scanner _scanner;
|
||||||
protected Token _token;
|
protected Token _token;
|
||||||
protected Evaluable _root;
|
protected Evaluable _root;
|
||||||
|
|
||||||
@ -47,6 +47,10 @@ public class Parser {
|
|||||||
return new ParsingException("Parsing error at offset " + index + ": " + desc);
|
return new ParsingException("Parsing error at offset " + index + ": " + desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <expression> := <sub-expression>
|
||||||
|
* | <expression> [ "<" "<=" ">" ">=" "==" "!=" ] <sub-expression>
|
||||||
|
*/
|
||||||
protected Evaluable parseExpression() throws ParsingException {
|
protected Evaluable parseExpression() throws ParsingException {
|
||||||
Evaluable sub = parseSubExpression();
|
Evaluable sub = parseSubExpression();
|
||||||
|
|
||||||
@ -66,6 +70,10 @@ public class Parser {
|
|||||||
return sub;
|
return sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <sub-expression> := <term>
|
||||||
|
* | <sub-expression> [ "+" "-" ] <term>
|
||||||
|
*/
|
||||||
protected Evaluable parseSubExpression() throws ParsingException {
|
protected Evaluable parseSubExpression() throws ParsingException {
|
||||||
Evaluable sub = parseTerm();
|
Evaluable sub = parseTerm();
|
||||||
|
|
||||||
@ -85,6 +93,10 @@ public class Parser {
|
|||||||
return sub;
|
return sub;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <term> := <factor>
|
||||||
|
* | <term> [ "*" "/" ] <factor>
|
||||||
|
*/
|
||||||
protected Evaluable parseTerm() throws ParsingException {
|
protected Evaluable parseTerm() throws ParsingException {
|
||||||
Evaluable factor = parseFactor();
|
Evaluable factor = parseFactor();
|
||||||
|
|
||||||
@ -104,6 +116,17 @@ public class Parser {
|
|||||||
return factor;
|
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 {
|
protected Evaluable parseFactor() throws ParsingException {
|
||||||
if (_token == null) {
|
if (_token == null) {
|
||||||
throw makeException("Expecting something more at end of expression");
|
throw makeException("Expecting something more at end of expression");
|
||||||
@ -214,6 +237,11 @@ public class Parser {
|
|||||||
return eval;
|
return eval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* <expression-list> := <empty>
|
||||||
|
* | <expression> ( "," <expression> )*
|
||||||
|
*
|
||||||
|
*/
|
||||||
protected List<Evaluable> parseExpressionList(String closingDelimiter) throws ParsingException {
|
protected List<Evaluable> parseExpressionList(String closingDelimiter) throws ParsingException {
|
||||||
List<Evaluable> l = new LinkedList<Evaluable>();
|
List<Evaluable> l = new LinkedList<Evaluable>();
|
||||||
|
|
||||||
|
@ -14,8 +14,8 @@ public class Scanner {
|
|||||||
static public class Token {
|
static public class Token {
|
||||||
final public int start;
|
final public int start;
|
||||||
final public int end;
|
final public int end;
|
||||||
final public TokenType type;
|
final public TokenType type;
|
||||||
final public String text;
|
final public String text;
|
||||||
|
|
||||||
Token(int start, int end, TokenType type, String text) {
|
Token(int start, int end, TokenType type, String text) {
|
||||||
this.start = start;
|
this.start = start;
|
||||||
@ -26,7 +26,7 @@ public class Scanner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static public class ErrorToken extends Token {
|
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) {
|
public ErrorToken(int start, int end, String text, String detail) {
|
||||||
super(start, end, TokenType.Error, text);
|
super(start, end, TokenType.Error, text);
|
||||||
@ -52,9 +52,9 @@ public class Scanner {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String _text;
|
protected String _text; // input text to tokenize
|
||||||
protected int _index;
|
protected int _index; // index of the next character to process
|
||||||
protected int _limit;
|
protected int _limit; // process up to this index
|
||||||
|
|
||||||
public Scanner(String s) {
|
public Scanner(String s) {
|
||||||
this(s, 0, s.length());
|
this(s, 0, s.length());
|
||||||
@ -70,6 +70,16 @@ public class Scanner {
|
|||||||
return _index;
|
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) {
|
public Token next(boolean regexPossible) {
|
||||||
// skip whitespace
|
// skip whitespace
|
||||||
while (_index < _limit && Character.isWhitespace(_text.charAt(_index))) {
|
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.expr.Evaluable;
|
||||||
import com.metaweb.gridworks.gel.Control;
|
import com.metaweb.gridworks.gel.Control;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract syntax tree node encapsulating a control call, such as "if".
|
||||||
|
*/
|
||||||
public class ControlCallExpr implements Evaluable {
|
public class ControlCallExpr implements Evaluable {
|
||||||
final protected Evaluable[] _args;
|
final protected Evaluable[] _args;
|
||||||
final protected Control _control;
|
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.ExpressionUtils;
|
||||||
import com.metaweb.gridworks.expr.HasFields;
|
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 {
|
public class FieldAccessorExpr implements Evaluable {
|
||||||
final protected Evaluable _inner;
|
final protected Evaluable _inner;
|
||||||
final protected String _fieldName;
|
final protected String _fieldName;
|
||||||
@ -19,7 +24,7 @@ public class FieldAccessorExpr implements Evaluable {
|
|||||||
public Object evaluate(Properties bindings) {
|
public Object evaluate(Properties bindings) {
|
||||||
Object o = _inner.evaluate(bindings);
|
Object o = _inner.evaluate(bindings);
|
||||||
if (ExpressionUtils.isError(o)) {
|
if (ExpressionUtils.isError(o)) {
|
||||||
return o;
|
return o; // bubble the error up
|
||||||
} else if (o == null) {
|
} else if (o == null) {
|
||||||
return new EvalError("Cannot retrieve field from null");
|
return new EvalError("Cannot retrieve field from null");
|
||||||
} else if (o instanceof HasFields) {
|
} 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.expr.ExpressionUtils;
|
||||||
import com.metaweb.gridworks.gel.Function;
|
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 {
|
public class FunctionCallExpr implements Evaluable {
|
||||||
final protected Evaluable[] _args;
|
final protected Evaluable[] _args;
|
||||||
final protected Function _function;
|
final protected Function _function;
|
||||||
@ -20,7 +26,7 @@ public class FunctionCallExpr implements Evaluable {
|
|||||||
for (int i = 0; i < _args.length; i++) {
|
for (int i = 0; i < _args.length; i++) {
|
||||||
Object v = _args[i].evaluate(bindings);
|
Object v = _args[i].evaluate(bindings);
|
||||||
if (ExpressionUtils.isError(v)) {
|
if (ExpressionUtils.isError(v)) {
|
||||||
return v;
|
return v; // bubble up the error
|
||||||
}
|
}
|
||||||
args[i] = v;
|
args[i] = v;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,9 @@ import org.json.JSONObject;
|
|||||||
|
|
||||||
import com.metaweb.gridworks.expr.Evaluable;
|
import com.metaweb.gridworks.expr.Evaluable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract syntax tree node encapsulating a literal value.
|
||||||
|
*/
|
||||||
public class LiteralExpr implements Evaluable {
|
public class LiteralExpr implements Evaluable {
|
||||||
final protected Object _value;
|
final protected Object _value;
|
||||||
|
|
||||||
|
@ -5,6 +5,9 @@ import java.util.Properties;
|
|||||||
import com.metaweb.gridworks.expr.Evaluable;
|
import com.metaweb.gridworks.expr.Evaluable;
|
||||||
import com.metaweb.gridworks.expr.ExpressionUtils;
|
import com.metaweb.gridworks.expr.ExpressionUtils;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract syntax tree node encapsulating an operator call, such as "+".
|
||||||
|
*/
|
||||||
public class OperatorCallExpr implements Evaluable {
|
public class OperatorCallExpr implements Evaluable {
|
||||||
final protected Evaluable[] _args;
|
final protected Evaluable[] _args;
|
||||||
final protected String _op;
|
final protected String _op;
|
||||||
|
@ -4,6 +4,9 @@ import java.util.Properties;
|
|||||||
|
|
||||||
import com.metaweb.gridworks.expr.Evaluable;
|
import com.metaweb.gridworks.expr.Evaluable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstract syntax tree node encapsulating the retrieval of a variable's content.
|
||||||
|
*/
|
||||||
public class VariableExpr implements Evaluable {
|
public class VariableExpr implements Evaluable {
|
||||||
final protected String _name;
|
final protected String _name;
|
||||||
|
|
||||||
|
@ -2,7 +2,6 @@ package com.metaweb.gridworks.gel.controls;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
|
|
||||||
@ -21,7 +20,8 @@ public class ForEach implements Control {
|
|||||||
if (args.length != 3) {
|
if (args.length != 3) {
|
||||||
return ControlFunctionRegistry.getControlName(this) + " expects 3 arguments";
|
return ControlFunctionRegistry.getControlName(this) + " expects 3 arguments";
|
||||||
} else if (!(args[1] instanceof VariableExpr)) {
|
} 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;
|
return null;
|
||||||
}
|
}
|
||||||
@ -30,7 +30,7 @@ public class ForEach implements Control {
|
|||||||
Object o = args[0].evaluate(bindings);
|
Object o = args[0].evaluate(bindings);
|
||||||
if (ExpressionUtils.isError(o)) {
|
if (ExpressionUtils.isError(o)) {
|
||||||
return 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");
|
return new EvalError("First argument to forEach is not an array");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,36 +40,36 @@ public class ForEach implements Control {
|
|||||||
try {
|
try {
|
||||||
List<Object> results = null;
|
List<Object> results = null;
|
||||||
|
|
||||||
if (o.getClass().isArray()) {
|
if (o.getClass().isArray()) {
|
||||||
Object[] values = (Object[]) o;
|
Object[] values = (Object[]) o;
|
||||||
|
|
||||||
results = new ArrayList<Object>(values.length);
|
results = new ArrayList<Object>(values.length);
|
||||||
for (Object v : values) {
|
for (Object v : values) {
|
||||||
bindings.put(name, v);
|
bindings.put(name, v);
|
||||||
|
|
||||||
Object r = args[2].evaluate(bindings);
|
Object r = args[2].evaluate(bindings);
|
||||||
|
|
||||||
results.add(r);
|
results.add(r);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
results = o instanceof Collection<?> ?
|
Collection<Object> collection = ExpressionUtils.toObjectCollection(o);
|
||||||
new ArrayList<Object>(ExpressionUtils.toObjectCollection(o).size()) :
|
|
||||||
new ArrayList<Object>();
|
|
||||||
|
|
||||||
Iterator<Object> i = ExpressionUtils.toObjectCollection(o).iterator();
|
results = new ArrayList<Object>(collection.size());
|
||||||
while (i.hasNext()) {
|
|
||||||
Object v = i.next();
|
|
||||||
|
|
||||||
bindings.put(name, v);
|
for (Object v : collection) {
|
||||||
|
bindings.put(name, v);
|
||||||
|
|
||||||
Object r = args[2].evaluate(bindings);
|
Object r = args[2].evaluate(bindings);
|
||||||
|
|
||||||
results.add(r);
|
results.add(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return results.toArray();
|
return results.toArray();
|
||||||
} finally {
|
} finally {
|
||||||
|
/*
|
||||||
|
* Restore the old value bound to the variable, if any.
|
||||||
|
*/
|
||||||
if (oldValue != null) {
|
if (oldValue != null) {
|
||||||
bindings.put(name, oldValue);
|
bindings.put(name, oldValue);
|
||||||
} else {
|
} else {
|
||||||
|
@ -16,7 +16,8 @@ public class ForNonBlank implements Control {
|
|||||||
if (args.length != 4) {
|
if (args.length != 4) {
|
||||||
return ControlFunctionRegistry.getControlName(this) + " expects 4 arguments";
|
return ControlFunctionRegistry.getControlName(this) + " expects 4 arguments";
|
||||||
} else if (!(args[1] instanceof VariableExpr)) {
|
} 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;
|
return null;
|
||||||
}
|
}
|
||||||
@ -28,12 +29,15 @@ public class ForNonBlank implements Control {
|
|||||||
String name = ((VariableExpr) var).getName();
|
String name = ((VariableExpr) var).getName();
|
||||||
|
|
||||||
if (ExpressionUtils.isNonBlankData(o)) {
|
if (ExpressionUtils.isNonBlankData(o)) {
|
||||||
Object oldValue = bindings.containsKey(name) ? bindings.get(name) : null;
|
Object oldValue = bindings.get(name);
|
||||||
bindings.put(name, o);
|
bindings.put(name, o);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return args[2].evaluate(bindings);
|
return args[2].evaluate(bindings);
|
||||||
} finally {
|
} finally {
|
||||||
|
/*
|
||||||
|
* Restore the old value bound to the variable, if any.
|
||||||
|
*/
|
||||||
if (oldValue != null) {
|
if (oldValue != null) {
|
||||||
bindings.put(name, oldValue);
|
bindings.put(name, oldValue);
|
||||||
} else {
|
} else {
|
||||||
|
@ -21,7 +21,7 @@ public class If implements Control {
|
|||||||
public Object call(Properties bindings, Evaluable[] args) {
|
public Object call(Properties bindings, Evaluable[] args) {
|
||||||
Object o = args[0].evaluate(bindings);
|
Object o = args[0].evaluate(bindings);
|
||||||
if (ExpressionUtils.isError(o)) {
|
if (ExpressionUtils.isError(o)) {
|
||||||
return o;
|
return o; // bubble the error up
|
||||||
} else if (ExpressionUtils.isTrue(o)) {
|
} else if (ExpressionUtils.isTrue(o)) {
|
||||||
return args[1].evaluate(bindings);
|
return args[1].evaluate(bindings);
|
||||||
} else {
|
} else {
|
||||||
|
@ -27,10 +27,7 @@ abstract class IsTest implements Control {
|
|||||||
throws JSONException {
|
throws JSONException {
|
||||||
|
|
||||||
writer.object();
|
writer.object();
|
||||||
writer.key("description"); writer.value(
|
writer.key("description"); writer.value(getDescription());
|
||||||
"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("params"); writer.value("expression o");
|
writer.key("params"); writer.value("expression o");
|
||||||
writer.key("returns"); writer.value("boolean");
|
writer.key("returns"); writer.value("boolean");
|
||||||
writer.endObject();
|
writer.endObject();
|
||||||
|
@ -15,7 +15,8 @@ public class With implements Control {
|
|||||||
if (args.length != 3) {
|
if (args.length != 3) {
|
||||||
return ControlFunctionRegistry.getControlName(this) + " expects 3 arguments";
|
return ControlFunctionRegistry.getControlName(this) + " expects 3 arguments";
|
||||||
} else if (!(args[1] instanceof VariableExpr)) {
|
} 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;
|
return null;
|
||||||
}
|
}
|
||||||
@ -34,6 +35,9 @@ public class With implements Control {
|
|||||||
|
|
||||||
return args[2].evaluate(bindings);
|
return args[2].evaluate(bindings);
|
||||||
} finally {
|
} finally {
|
||||||
|
/*
|
||||||
|
* Restore the old value bound to the variable, if any.
|
||||||
|
*/
|
||||||
if (oldValue != null) {
|
if (oldValue != null) {
|
||||||
bindings.put(name, oldValue);
|
bindings.put(name, oldValue);
|
||||||
} else {
|
} else {
|
||||||
|
Loading…
Reference in New Issue
Block a user