///|
/// Evaluate binary operation
fn eval_binary_op(
op : @ast.BinaryOp,
left : Json,
right : Json,
) -> Json raise PrimitiveError {
match op {
Add => json_add(left, right)
Subtract => json_subtract(left, right)
Multiply => json_multiply(left, right)
Divide => json_divide(left, right)
Modulo => json_modulo(left, right)
Equal => Json::boolean(compare_json(left, right) == 0)
NotEqual => Json::boolean(compare_json(left, right) != 0)
LessThan => Json::boolean(compare_json(left, right) < 0)
LessEq => Json::boolean(compare_json(left, right) <= 0)
GreaterThan => Json::boolean(compare_json(left, right) > 0)
GreaterEq => Json::boolean(compare_json(left, right) >= 0)
And =>
Json::boolean(
@ast_internal.is_truthy(left) && @ast_internal.is_truthy(right),
)
Or =>
Json::boolean(
@ast_internal.is_truthy(left) || @ast_internal.is_truthy(right),
)
}
}
///|
/// Arithmetic operations
fn json_add(left : Json, right : Json) -> Json raise PrimitiveError {
match (left, right) {
// null is the identity for `+` in jq.
(Null, other) | (other, Null) => other
(Number(a, ..), Number(b, ..)) => Json::number(a + b)
(String(a), String(b)) => Json::string(a + b)
(Array(a), Array(b)) => Json::array(a + b)
(Object(a), Object(b)) => Json::object(a.merge(b))
_ =>
raise InvalidOperation(
binary_error_message(left, right, "cannot be added"),
)
}
}
///|
fn json_subtract(left : Json, right : Json) -> Json raise PrimitiveError {
match (left, right) {
(Number(a, ..), Number(b, ..)) => Json::number(a - b)
(Array(a), Array(b)) => Json::array(array_without(a, b))
_ =>
raise InvalidOperation(
binary_error_message(left, right, "cannot be subtracted"),
)
}
}
///|
fn json_multiply(left : Json, right : Json) -> Json raise PrimitiveError {
match (left, right) {
(Number(a, ..), Number(b, ..)) => Json::number(a * b)
(String(s), Number(n, ..)) | (Number(n, ..), String(s)) => {
let count = n.to_int()
Json::string(repeat_string(s, count))
}
_ =>
raise InvalidOperation(
binary_error_message(left, right, "cannot be multiplied"),
)
}
}
///|
fn repeat_string(s : String, count : Int32) -> String {
if count <= 0 {
""
} else {
let builder = StringBuilder()
for _ in 0.. Bool {
haystack.iter().any(fn(value) { compare_json(value, needle) == 0 })
}
///|
fn array_without(
source : ArrayView[Json],
remove : ArrayView[Json],
) -> Array[Json] {
source
.iter()
.filter(fn(value) { !array_contains_compare(remove, value) })
.collect()
}
///|
fn json_divide(left : Json, right : Json) -> Json raise PrimitiveError {
match (left, right) {
(Number(a, ..), Number(b, ..)) => {
if b == 0.0 {
raise DivisionByZero
}
Json::number(a / b)
}
_ =>
raise InvalidOperation(
binary_error_message(left, right, "cannot be divided"),
)
}
}
///|
fn json_modulo(left : Json, right : Json) -> Json raise PrimitiveError {
match (left, right) {
(Number(a, ..), Number(b, ..)) => {
if b == 0.0 {
raise DivisionByZero
}
Json::number(a % b)
}
_ =>
raise InvalidOperation(
binary_error_message(left, right, "cannot be divided (remainder)"),
)
}
}
///|
fn binary_error_message(
left : Json,
right : Json,
operation : String,
) -> String {
"\{@ast_internal.json_type_name(left)} (\{left.stringify()}) and \{@ast_internal.json_type_name(right)} (\{right.stringify()}) \{operation}"
}