///|
fn Evaluator::eval_list(
self : Evaluator,
items : Array[Expr],
) -> Value raise EvalError {
Value::List(items.map(item => self.eval_expr(item)))
}
///|
fn Evaluator::eval_map(
self : Evaluator,
entries : Array[MapExprEntry],
) -> Value raise EvalError {
let values : Array[ValueEntry] = []
for entry in entries {
let key = self.eval_expr(entry.key)
let value = self.eval_expr(entry.value)
if values.any(existing => existing.key == key) {
raise InvalidArgument(
message="duplicate map key \{key.display()}",
span=entry.span,
)
}
values.push({ key, value })
}
Value::Map(values)
}
///|
fn Evaluator::eval_select(
self : Evaluator,
target : Expr,
field : String,
span : SourceSpan,
) -> Value raise EvalError {
match self.eval_expr(target) {
Value::Map(entries) => {
for entry in entries {
if entry.key == Value::String(field) {
return entry.value
}
}
raise NoSuchField(field~, span~)
}
other => type_mismatch("map", other, span)
}
}
///|
fn Evaluator::eval_index(
self : Evaluator,
target : Expr,
index : Expr,
span : SourceSpan,
) -> Value raise EvalError {
let target_value = self.eval_expr(target)
let index_value = self.eval_expr(index)
match (target_value, index_value) {
(Value::List(values), Value::Int(position)) => {
let position = position.to_int()
if position < 0 || position >= values.length() {
raise IndexOutOfBounds(index=position, length=values.length(), span~)
}
values[position]
}
(Value::Map(entries), key) => {
for entry in entries {
if entry.key == key {
return entry.value
}
}
raise NoSuchKey(key=key.display(), span~)
}
(Value::List(_), other) => type_mismatch("int index", other, span)
(other, _) => type_mismatch("list or map", other, span)
}
}
///|
fn concat_bytes(left : Bytes, right : Bytes) -> Bytes {
let combined = left.to_array()
combined.append(right.to_array())
Bytes::from_array(combined)
}
///|
fn concat_lists(left : Array[Value], right : Array[Value]) -> Array[Value] {
let combined = left.copy()
combined.append(right)
combined
}
///|
fn conversion_error(name : String, span : SourceSpan) -> Value raise EvalError {
raise InvalidArgument(message="invalid argument to \{name}", span~)
}
///|
fn eval_int_conversion(
value : Value,
span : SourceSpan,
) -> Value raise EvalError {
match value {
Value::UInt(value) => {
let result = @string.parse_int64(value.to_string()) catch {
_ => return conversion_error("int", span)
}
Value::Int(result)
}
Value::Double(value) => Value::Int(value.to_int64())
Value::String(value) => {
let result = @string.parse_int64(value) catch {
_ => return conversion_error("int", span)
}
Value::Int(result)
}
other => type_mismatch("uint, double, or string", other, span)
}
}
///|
fn eval_uint_conversion(
value : Value,
span : SourceSpan,
) -> Value raise EvalError {
match value {
Value::Int(value) => {
if value < 0L {
return conversion_error("uint", span)
}
let result = @string.parse_uint64(value.to_string()) catch {
_ => return conversion_error("uint", span)
}
Value::UInt(result)
}
Value::Double(value) => {
if value < 0.0 {
return conversion_error("uint", span)
}
Value::UInt(value.to_uint64())
}
Value::String(value) => {
let result = @string.parse_uint64(value) catch {
_ => return conversion_error("uint", span)
}
Value::UInt(result)
}
other => type_mismatch("int, double, or string", other, span)
}
}
///|
fn eval_double_conversion(
value : Value,
span : SourceSpan,
) -> Value raise EvalError {
match value {
Value::Int(value) => Value::Double(value.to_double())
Value::UInt(value) => Value::Double(value.to_double())
Value::String(value) => {
let result = @string.parse_double(value) catch {
_ => return conversion_error("double", span)
}
Value::Double(result)
}
other => type_mismatch("int, uint, or string", other, span)
}
}
///|
fn eval_string_conversion(
value : Value,
span : SourceSpan,
) -> Value raise EvalError {
match value {
Value::Bool(value) => Value::String(if value { "true" } else { "false" })
Value::Int(value) => Value::String(value.to_string())
Value::UInt(value) => Value::String(value.to_string())
Value::Double(value) => Value::String(value.to_string())
Value::Bytes(value) =>
Value::String(
@utf8.decode(value) catch {
_ => return conversion_error("string", span)
},
)
other => type_mismatch("bool, number, or bytes", other, span)
}
}
///|
fn eval_global_call(
name : String,
arguments : Array[Value],
span : SourceSpan,
) -> Value raise EvalError {
match (name, arguments) {
("size", [value]) =>
match value.size() {
Some(size) => Value::Int(Int64::from_int(size))
None => type_mismatch("string, bytes, list, or map", value, span)
}
("type", [value]) => Value::String(value.kind_name())
("int", [value]) => eval_int_conversion(value, span)
("uint", [value]) => eval_uint_conversion(value, span)
("double", [value]) => eval_double_conversion(value, span)
("string", [value]) => eval_string_conversion(value, span)
("bytes", [Value::String(value)]) => Value::Bytes(@utf8.encode(value))
_ => raise UnknownFunction(name~, span~)
}
}
///|
fn eval_receiver_call(
receiver : Value,
name : String,
arguments : Array[Value],
span : SourceSpan,
) -> Value raise EvalError {
match (receiver, name, arguments) {
(value, "size", []) =>
match value.size() {
Some(size) => Value::Int(Int64::from_int(size))
None => type_mismatch("string, bytes, list, or map", value, span)
}
(Value::String(value), "contains", [Value::String(part)]) =>
Value::Bool(value.contains(part))
(Value::String(value), "startsWith", [Value::String(prefix)]) =>
Value::Bool(value.has_prefix(prefix))
(Value::String(value), "endsWith", [Value::String(suffix)]) =>
Value::Bool(value.has_suffix(suffix))
(Value::String(value), "matches", [Value::String(pattern)]) =>
Value::Bool(bounded_regex_matches(value, pattern, span))
(_, function_name, _) => raise UnknownFunction(name=function_name, span~)
}
}
///|
fn Evaluator::eval_call(
self : Evaluator,
name : String,
arguments : Array[Expr],
span : SourceSpan,
) -> Value raise EvalError {
eval_global_call(name, arguments.map(arg => self.eval_expr(arg)), span)
}
///|
fn Evaluator::eval_receiver(
self : Evaluator,
target : Expr,
name : String,
arguments : Array[Expr],
span : SourceSpan,
) -> Value raise EvalError {
let receiver = self.eval_expr(target)
let values = arguments.map(arg => self.eval_expr(arg))
eval_receiver_call(receiver, name, values, span)
}