Documented gel.* packages.

git-svn-id: http://google-refine.googlecode.com/svn/trunk@333 7d457c2a-affb-35e4-300a-418c747d4874
This commit is contained in:
David Huynh 2010-03-22 05:44:35 +00:00
parent 60dd7eab82
commit ec0110d65b
16 changed files with 135 additions and 48 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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>();

View File

@ -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))) {

View File

@ -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;

View File

@ -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) {

View File

@ -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;
}

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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();

View File

@ -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 {