// Execution of the `transform` array of a specification.
///|
fn json_literal(v : @value.Value) -> String {
match v {
Str(s) => s.escape()
Num(x) => @util.js_number_to_string(x)
Bool(true) => "true"
Bool(false) => "false"
Null | Undef => "null"
Arr(a) => "[" + a.map(json_literal).join(",") + "]"
Obj(_) => "null"
}
}
///|
fn datum_ref(field : String) -> String {
"datum[" + field.escape() + "]"
}
///|
/// The vega expression equivalent to a field predicate.
fn predicate_expr(p : @spec.Predicate) -> String {
match p {
Equal(f, v, _) => datum_ref(f) + "===" + json_literal(v)
Lt(f, v, _) => datum_ref(f) + "<" + json_literal(v)
Lte(f, v, _) => datum_ref(f) + "<=" + json_literal(v)
Gt(f, v, _) => datum_ref(f) + ">" + json_literal(v)
Gte(f, v, _) => datum_ref(f) + ">=" + json_literal(v)
Range(f, a, b, _) =>
"inrange(" +
datum_ref(f) +
", [" +
json_literal(a) +
", " +
json_literal(b) +
"])"
OneOf(f, vs, _) =>
"indexof([" +
vs.map(json_literal).join(",") +
"], " +
datum_ref(f) +
") !== -1"
Valid(f) =>
"isValid(" + datum_ref(f) + ") && isFinite(+" + datum_ref(f) + ")"
And(ps) => ps.map(fn(q) { "(" + predicate_expr(q) + ")" }).join(" && ")
Or(ps) => ps.map(fn(q) { "(" + predicate_expr(q) + ")" }).join(" || ")
Not(q) => "!(" + predicate_expr(q) + ")"
Expr(src) => src
}
}
///|
fn num_prop(m : Map[String, @value.Value], key : String) -> Double? {
match m.get(key) {
Some(Num(x)) => Some(x)
_ => None
}
}
///|
fn bin_params(
rows : Array[Map[String, @value.Value]],
field : String,
m : Map[String, @value.Value],
default_maxbins : Double,
) -> @transform.BinParams {
let extent = match m.get("extent") {
Some(Arr([Num(a), Num(b)])) => (a, b)
_ =>
match @transform.extent(rows, field) {
Some(e) => e
None => (0.0, 0.0)
}
}
let maxbins = match num_prop(m, "maxbins") {
Some(v) => v
None => default_maxbins
}
let nice = match m.get("nice") {
Some(Bool(false)) => false
_ => true
}
@transform.BinParams::new(
extent,
maxbins~,
base=match num_prop(m, "base") {
Some(v) => v
None => 10.0
},
step?=num_prop(m, "step"),
minstep=match num_prop(m, "minstep") {
Some(v) => v
None => 0.0
},
nice~,
anchor?=num_prop(m, "anchor"),
)
}
///|
fn transform_err(e : @transform.TransformError) -> CompileError {
match e {
TransformError(m) => CompileError(m)
}
}
///|
fn apply_transforms(
rows0 : Array[Map[String, @value.Value]],
ts : Array[@spec.Transform],
) -> Array[Map[String, @value.Value]] raise CompileError {
let mut rows = rows0
for t in ts {
match t {
Filter(Expr(src)) =>
rows = @transform.filter(rows, src) catch {
e => raise transform_err(e)
}
Filter(Predicate(p)) =>
rows = @transform.filter(rows, predicate_expr(p)) catch {
e => raise transform_err(e)
}
Calculate(src, as_name) =>
@transform.formula(rows, src, as_name) catch {
e => raise transform_err(e)
}
Aggregate(ops, groupby) =>
rows = @transform.aggregate(
rows,
groupby,
ops.map(fn(o) { { op: o.op, field: o.field, out: o.out, } }),
) catch {
e => raise transform_err(e)
}
Bin(field, props, as_names) => {
let p = bin_params(rows, field, props, 10.0)
let a0 = as_names[0]
let a1 = if as_names.length() > 1 { as_names[1] } else { a0 + "_end" }
@transform.bin_rows(rows, field, p, a0, a1)
}
TimeUnit(unit, field, as_name) =>
@transform.timeunit(
rows,
field,
[unit],
as0=as_name,
as1=as_name + "_end",
) catch {
e => raise transform_err(e)
}
Fold(fields, as_names) =>
rows = @transform.fold(rows, fields, as_names[0], as_names[1])
}
}
rows
}