///|
fn Vm::binary(
self : Vm,
op : String,
a : Value,
b : Value,
loc : Location,
) -> Value raise {
match op {
"+" =>
match (a, b) {
(Num(x), Num(y)) => finite(x + y, loc)
(ArrayValue(x), ArrayValue(y)) => {
self.tick(loc)
if x.length() > self.limits.steps - y.length() ||
x.length() > self.limits.output_bytes - y.length() {
raise problem(Limit, loc, "array size limit exceeded")
}
ArrayValue(x + y)
}
(ObjectValue(x, xt), ObjectValue(y, yt)) => {
if xt > self.limits.steps - yt {
raise problem(Limit, loc, "object layer limit exceeded")
}
let layers = self.objects[x].layers[:xt].to_owned() +
self.objects[y].layers[:yt].to_owned()
self.object_value(layers)
}
(Str(x), _) => self.join_string(x, self.coerce_string(b, loc), loc)
(_, Str(y)) => self.join_string(self.coerce_string(a, loc), y, loc)
_ => {
let message = match a {
Num(_) => "Unexpected type " + value_type(b) + ", expected number"
ArrayValue(_) =>
"Unexpected type " + value_type(b) + ", expected array"
ObjectValue(_, _) =>
"Unexpected type " + value_type(b) + ", expected object"
_ => "Unexpected type " + value_type(a)
}
raise problem(Type, loc, message)
}
}
"-" => finite(number_value(a, loc) - number_value(b, loc), loc)
"*" => finite(number_value(a, loc) * number_value(b, loc), loc)
"/" | "%" => {
if op == "%" && a is Str(format) {
return Str(self.format_string(format, b, loc))
}
let x = number_value(a, loc)
let y = number_value(b, loc)
if y == 0 {
raise problem(Runtime, loc, "Division by zero.")
}
finite(if op == "/" { x / y } else { x % y }, loc)
}
"==" => Bool(self.equal(a, b, loc))
"!=" => Bool(!self.equal(a, b, loc))
"<" | "<=" | ">" | ">=" => {
let c = self.compare_values(a, b, loc)
Bool(
match op {
"<" => c < 0
"<=" => c <= 0
">" => c > 0
_ => c >= 0
},
)
}
"in" => {
let k = text_value(a, loc)
guard b is ObjectValue(id, top) else {
raise problem(Type, loc, "right operand of in must be object")
}
Bool(self.has_field(id, top, k))
}
"&" | "|" | "^" | "<<" | ">>" => {
let x = bitwise_integer(a, loc)
let y = bitwise_integer(b, loc)
let n = match op {
"&" => x.land(y)
"|" => x.lor(y)
"^" => x.lxor(y)
_ => {
if number_value(b, loc) < 0 {
raise problem(Runtime, loc, "negative shift")
}
let shift = (y % 64L).to_int()
if op == "<<" {
x << shift
} else {
x >> shift
}
}
}
Num(n.to_double())
}
_ => raise problem(Runtime, loc, "invalid operator '\{op}'")
}
}
///|
fn bitwise_integer(v : Value, loc : Location) -> Int64 raise {
let n = number_value(v, loc)
// Go's floating-point MaxInt64 bound rounds to 2^63. Its conversion at that
// bound depends on the native architecture and is defined by the FFI helper.
if n < -9223372036854775808.0 || n > 9223372036854775808.0 {
raise problem(
Runtime,
loc,
"Bitwise operator argument " +
diagnostic_number(n) +
" outside of range [-9223372036854775808, 9223372036854775807]",
)
}
reference_int64(n)
}
///|
fn reference_int64(n : Double) -> Int64 {
@number.reference_int64(n)
}
///|
fn Vm::compare_arrays(
self : Vm,
a : Array[Int],
b : Array[Int],
loc : Location,
) -> Int raise {
if self.depth >= self.limits.depth {
raise problem(Limit, loc, "comparison depth limit exceeded")
}
self.depth += 1
defer {
self.depth -= 1
}
for i in 0.. Int raise {
match a {
Num(x) => compare_numbers(x, number_value(b, loc))
Str(x) => compare_unicode(x, text_value(b, loc))
ArrayValue(x) => self.compare_arrays(x, array_value(b, loc), loc)
_ => raise problem(Type, loc, "Unexpected type " + value_type(a))
}
}
///|
fn compare_numbers(a : Double, b : Double) -> Int {
if a < b {
-1
} else if a > b {
1
} else {
0
}
}
///|
fn compare_unicode(a : String, b : String) -> Int {
let x = a.to_array()
let y = b.to_array()
for i in 0.. Value raise {
if a.length() > self.limits.output_bytes - b.length() {
raise problem(Limit, loc, "string size limit exceeded")
}
Str(a + b)
}
///|
fn Vm::coerce_string(self : Vm, v : Value, loc : Location) -> String raise {
match v {
Str(s) => s
_ =>
render_json(
self.manifest(v, loc, 0),
false,
0,
self.limits.output_bytes,
loc,
)
}
}
///|
fn Vm::equal(self : Vm, a : Value, b : Value, loc : Location) -> Bool raise {
if self.depth >= self.limits.depth {
raise problem(Limit, loc, "comparison depth limit exceeded")
}
self.depth += 1
defer {
self.depth -= 1
}
self.equal_inner(a, b, loc)
}
///|
fn Vm::equal_inner(
self : Vm,
a : Value,
b : Value,
loc : Location,
) -> Bool raise {
self.tick(loc)
if value_type(a) != value_type(b) {
return false
}
match (a, b) {
(Closure(_, _, _), _)
| (_, Closure(_, _, _))
| (Builtin(_), _)
| (_, Builtin(_))
| (Native(_), _)
| (_, Native(_)) =>
raise problem(Type, loc, "Cannot test equality of functions")
(Nil, Nil) => true
(Bool(x), Bool(y)) => x == y
(Num(x), Num(y)) => x == y
(Str(x), Str(y)) => x == y
(ArrayValue(x), ArrayValue(y)) => {
if x.length() != y.length() {
return false
}
for i in 0.. {
let keys = self.keys(x, xt, false)
if keys != self.keys(y, yt, false) {
return false
}
for k in keys {
if !self.equal(
self.field(x, xt, k, loc),
self.field(y, yt, k, loc),
loc,
) {
return false
}
}
true
}
_ => false
}
}