use crate::{diag::Diagnostic, token::Span, types::Type};

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum RuntimeType {
    I32,
    I64,
    U32,
    U64,
    F64,
    Bool,
    String,
    ResultI32I32,
    ResultI64I32,
    ResultU32I32,
    ResultU64I32,
    ResultF64I32,
    ResultBoolI32,
    ResultStringI32,
    VecI32,
    VecI64,
    VecF64,
    VecBool,
    VecString,
    Unit,
}

impl RuntimeType {
    pub fn to_type(self) -> Type {
        match self {
            Self::I32 => Type::I32,
            Self::I64 => Type::I64,
            Self::U32 => Type::U32,
            Self::U64 => Type::U64,
            Self::F64 => Type::F64,
            Self::Bool => Type::Bool,
            Self::String => Type::String,
            Self::ResultI32I32 => Type::Result(Box::new(Type::I32), Box::new(Type::I32)),
            Self::ResultI64I32 => Type::Result(Box::new(Type::I64), Box::new(Type::I32)),
            Self::ResultU32I32 => Type::Result(Box::new(Type::U32), Box::new(Type::I32)),
            Self::ResultU64I32 => Type::Result(Box::new(Type::U64), Box::new(Type::I32)),
            Self::ResultF64I32 => Type::Result(Box::new(Type::F64), Box::new(Type::I32)),
            Self::ResultBoolI32 => Type::Result(Box::new(Type::Bool), Box::new(Type::I32)),
            Self::ResultStringI32 => Type::Result(Box::new(Type::String), Box::new(Type::I32)),
            Self::VecI32 => Type::Vec(Box::new(Type::I32)),
            Self::VecI64 => Type::Vec(Box::new(Type::I64)),
            Self::VecF64 => Type::Vec(Box::new(Type::F64)),
            Self::VecBool => Type::Vec(Box::new(Type::Bool)),
            Self::VecString => Type::Vec(Box::new(Type::String)),
            Self::Unit => Type::Unit,
        }
    }
}

#[derive(Debug, Copy, Clone)]
pub struct RuntimeFunction {
    pub source_name: &'static str,
    pub runtime_symbol: &'static str,
    pub params: &'static [RuntimeType],
    pub return_type: RuntimeType,
    pub promoted: bool,
}

const I32_PARAM: &[RuntimeType] = &[RuntimeType::I32];
const I64_PARAM: &[RuntimeType] = &[RuntimeType::I64];
const U32_PARAM: &[RuntimeType] = &[RuntimeType::U32];
const U64_PARAM: &[RuntimeType] = &[RuntimeType::U64];
const F64_PARAM: &[RuntimeType] = &[RuntimeType::F64];
const BOOL_PARAM: &[RuntimeType] = &[RuntimeType::Bool];
const STRING_PARAM: &[RuntimeType] = &[RuntimeType::String];
const STRING_STRING_PARAMS: &[RuntimeType] = &[RuntimeType::String, RuntimeType::String];
const STRING_I32_PARAMS: &[RuntimeType] = &[RuntimeType::String, RuntimeType::I32];
const STRING_I32_I32_PARAMS: &[RuntimeType] =
    &[RuntimeType::String, RuntimeType::I32, RuntimeType::I32];
const I32_STRING_PARAMS: &[RuntimeType] = &[RuntimeType::I32, RuntimeType::String];
const VEC_I32_PARAM: &[RuntimeType] = &[RuntimeType::VecI32];
const VEC_I32_I32_PARAMS: &[RuntimeType] = &[RuntimeType::VecI32, RuntimeType::I32];
const VEC_I64_PARAM: &[RuntimeType] = &[RuntimeType::VecI64];
const VEC_I64_I64_PARAMS: &[RuntimeType] = &[RuntimeType::VecI64, RuntimeType::I64];
const VEC_I64_I32_PARAMS: &[RuntimeType] = &[RuntimeType::VecI64, RuntimeType::I32];
const VEC_F64_PARAM: &[RuntimeType] = &[RuntimeType::VecF64];
const VEC_F64_F64_PARAMS: &[RuntimeType] = &[RuntimeType::VecF64, RuntimeType::F64];
const VEC_F64_I32_PARAMS: &[RuntimeType] = &[RuntimeType::VecF64, RuntimeType::I32];
const VEC_BOOL_PARAM: &[RuntimeType] = &[RuntimeType::VecBool];
const VEC_BOOL_BOOL_PARAMS: &[RuntimeType] = &[RuntimeType::VecBool, RuntimeType::Bool];
const VEC_BOOL_I32_PARAMS: &[RuntimeType] = &[RuntimeType::VecBool, RuntimeType::I32];
const VEC_STRING_PARAM: &[RuntimeType] = &[RuntimeType::VecString];
const VEC_STRING_STRING_PARAMS: &[RuntimeType] = &[RuntimeType::VecString, RuntimeType::String];
const VEC_STRING_I32_PARAMS: &[RuntimeType] = &[RuntimeType::VecString, RuntimeType::I32];

pub const FUNCTIONS: &[RuntimeFunction] = &[
    RuntimeFunction {
        source_name: "print_i32",
        runtime_symbol: "print_i32",
        params: I32_PARAM,
        return_type: RuntimeType::Unit,
        promoted: false,
    },
    RuntimeFunction {
        source_name: "print_string",
        runtime_symbol: "print_string",
        params: STRING_PARAM,
        return_type: RuntimeType::Unit,
        promoted: false,
    },
    RuntimeFunction {
        source_name: "print_bool",
        runtime_symbol: "print_bool",
        params: BOOL_PARAM,
        return_type: RuntimeType::Unit,
        promoted: false,
    },
    RuntimeFunction {
        source_name: "string_len",
        runtime_symbol: "string_len",
        params: STRING_PARAM,
        return_type: RuntimeType::I32,
        promoted: false,
    },
    RuntimeFunction {
        source_name: "std.io.print_i32",
        runtime_symbol: "print_i32",
        params: I32_PARAM,
        return_type: RuntimeType::Unit,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.io.print_f64",
        runtime_symbol: "print_f64",
        params: F64_PARAM,
        return_type: RuntimeType::Unit,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.io.print_i64",
        runtime_symbol: "print_i64",
        params: I64_PARAM,
        return_type: RuntimeType::Unit,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.io.print_u32",
        runtime_symbol: "print_u32",
        params: U32_PARAM,
        return_type: RuntimeType::Unit,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.io.print_u64",
        runtime_symbol: "print_u64",
        params: U64_PARAM,
        return_type: RuntimeType::Unit,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.io.print_string",
        runtime_symbol: "print_string",
        params: STRING_PARAM,
        return_type: RuntimeType::Unit,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.io.print_bool",
        runtime_symbol: "print_bool",
        params: BOOL_PARAM,
        return_type: RuntimeType::Unit,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.len",
        runtime_symbol: "string_len",
        params: STRING_PARAM,
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.concat",
        runtime_symbol: "__glagol_string_concat",
        params: STRING_STRING_PARAMS,
        return_type: RuntimeType::String,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.byte_at_result",
        runtime_symbol: "__glagol_string_byte_at_result",
        params: STRING_I32_PARAMS,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.slice_result",
        runtime_symbol: "__glagol_string_slice_result",
        params: STRING_I32_I32_PARAMS,
        return_type: RuntimeType::ResultStringI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.starts_with",
        runtime_symbol: "__glagol_string_starts_with",
        params: STRING_STRING_PARAMS,
        return_type: RuntimeType::Bool,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.ends_with",
        runtime_symbol: "__glagol_string_ends_with",
        params: STRING_STRING_PARAMS,
        return_type: RuntimeType::Bool,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.parse_i32_result",
        runtime_symbol: "__glagol_string_parse_i32_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.parse_i64_result",
        runtime_symbol: "__glagol_string_parse_i64_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultI64I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.parse_u32_result",
        runtime_symbol: "__glagol_string_parse_u32_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultU32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.parse_u64_result",
        runtime_symbol: "__glagol_string_parse_u64_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultU64I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.parse_f64_result",
        runtime_symbol: "__glagol_string_parse_f64_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultF64I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.string.parse_bool_result",
        runtime_symbol: "__glagol_string_parse_bool_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultBoolI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.json.quote_string",
        runtime_symbol: "__glagol_json_quote_string",
        params: STRING_PARAM,
        return_type: RuntimeType::String,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.json.parse_string_value_result",
        runtime_symbol: "__glagol_json_parse_string_value_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultStringI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.json.parse_bool_value_result",
        runtime_symbol: "__glagol_json_parse_bool_value_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultBoolI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.json.parse_i32_value_result",
        runtime_symbol: "__glagol_json_parse_i32_value_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.json.parse_u32_value_result",
        runtime_symbol: "__glagol_json_parse_u32_value_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultU32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.json.parse_i64_value_result",
        runtime_symbol: "__glagol_json_parse_i64_value_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultI64I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.json.parse_u64_value_result",
        runtime_symbol: "__glagol_json_parse_u64_value_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultU64I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.json.parse_f64_value_result",
        runtime_symbol: "__glagol_json_parse_f64_value_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultF64I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.io.eprint",
        runtime_symbol: "__glagol_io_eprint",
        params: STRING_PARAM,
        return_type: RuntimeType::Unit,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.io.read_stdin_result",
        runtime_symbol: "__glagol_io_read_stdin_result",
        params: &[],
        return_type: RuntimeType::ResultStringI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.process.argc",
        runtime_symbol: "__glagol_process_argc",
        params: &[],
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.process.arg",
        runtime_symbol: "__glagol_process_arg",
        params: I32_PARAM,
        return_type: RuntimeType::String,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.process.arg_result",
        runtime_symbol: "__glagol_process_arg_result",
        params: I32_PARAM,
        return_type: RuntimeType::ResultStringI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.env.get",
        runtime_symbol: "__glagol_env_get",
        params: STRING_PARAM,
        return_type: RuntimeType::String,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.env.get_result",
        runtime_symbol: "__glagol_env_get_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultStringI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.read_text",
        runtime_symbol: "__glagol_fs_read_text",
        params: STRING_PARAM,
        return_type: RuntimeType::String,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.read_text_result",
        runtime_symbol: "__glagol_fs_read_text_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultStringI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.write_text",
        runtime_symbol: "__glagol_fs_write_text",
        params: STRING_STRING_PARAMS,
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.write_text_result",
        runtime_symbol: "__glagol_fs_write_text_result",
        params: STRING_STRING_PARAMS,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.exists",
        runtime_symbol: "__glagol_fs_exists",
        params: STRING_PARAM,
        return_type: RuntimeType::Bool,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.is_file",
        runtime_symbol: "__glagol_fs_is_file",
        params: STRING_PARAM,
        return_type: RuntimeType::Bool,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.is_dir",
        runtime_symbol: "__glagol_fs_is_dir",
        params: STRING_PARAM,
        return_type: RuntimeType::Bool,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.remove_file_result",
        runtime_symbol: "__glagol_fs_remove_file_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.create_dir_result",
        runtime_symbol: "__glagol_fs_create_dir_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.open_text_read_result",
        runtime_symbol: "__glagol_fs_open_text_read_result",
        params: STRING_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.read_open_text_result",
        runtime_symbol: "__glagol_fs_read_open_text_result",
        params: I32_PARAM,
        return_type: RuntimeType::ResultStringI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.fs.close_result",
        runtime_symbol: "__glagol_fs_close_result",
        params: I32_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.net.tcp_connect_loopback_result",
        runtime_symbol: "__glagol_net_tcp_connect_loopback_result",
        params: I32_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.net.tcp_listen_loopback_result",
        runtime_symbol: "__glagol_net_tcp_listen_loopback_result",
        params: I32_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.net.tcp_bound_port_result",
        runtime_symbol: "__glagol_net_tcp_bound_port_result",
        params: I32_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.net.tcp_accept_result",
        runtime_symbol: "__glagol_net_tcp_accept_result",
        params: I32_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.net.tcp_read_all_result",
        runtime_symbol: "__glagol_net_tcp_read_all_result",
        params: I32_PARAM,
        return_type: RuntimeType::ResultStringI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.net.tcp_write_text_result",
        runtime_symbol: "__glagol_net_tcp_write_text_result",
        params: I32_STRING_PARAMS,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.net.tcp_close_result",
        runtime_symbol: "__glagol_net_tcp_close_result",
        params: I32_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.i32.empty",
        runtime_symbol: "__glagol_vec_i32_empty",
        params: &[],
        return_type: RuntimeType::VecI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.i32.append",
        runtime_symbol: "__glagol_vec_i32_append",
        params: VEC_I32_I32_PARAMS,
        return_type: RuntimeType::VecI32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.i32.len",
        runtime_symbol: "__glagol_vec_i32_len",
        params: VEC_I32_PARAM,
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.i32.index",
        runtime_symbol: "__glagol_vec_i32_index",
        params: VEC_I32_I32_PARAMS,
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.i64.empty",
        runtime_symbol: "__glagol_vec_i64_empty",
        params: &[],
        return_type: RuntimeType::VecI64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.i64.append",
        runtime_symbol: "__glagol_vec_i64_append",
        params: VEC_I64_I64_PARAMS,
        return_type: RuntimeType::VecI64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.i64.len",
        runtime_symbol: "__glagol_vec_i64_len",
        params: VEC_I64_PARAM,
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.i64.index",
        runtime_symbol: "__glagol_vec_i64_index",
        params: VEC_I64_I32_PARAMS,
        return_type: RuntimeType::I64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.f64.empty",
        runtime_symbol: "__glagol_vec_f64_empty",
        params: &[],
        return_type: RuntimeType::VecF64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.f64.append",
        runtime_symbol: "__glagol_vec_f64_append",
        params: VEC_F64_F64_PARAMS,
        return_type: RuntimeType::VecF64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.f64.len",
        runtime_symbol: "__glagol_vec_f64_len",
        params: VEC_F64_PARAM,
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.f64.index",
        runtime_symbol: "__glagol_vec_f64_index",
        params: VEC_F64_I32_PARAMS,
        return_type: RuntimeType::F64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.bool.empty",
        runtime_symbol: "__glagol_vec_bool_empty",
        params: &[],
        return_type: RuntimeType::VecBool,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.bool.append",
        runtime_symbol: "__glagol_vec_bool_append",
        params: VEC_BOOL_BOOL_PARAMS,
        return_type: RuntimeType::VecBool,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.bool.len",
        runtime_symbol: "__glagol_vec_bool_len",
        params: VEC_BOOL_PARAM,
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.bool.index",
        runtime_symbol: "__glagol_vec_bool_index",
        params: VEC_BOOL_I32_PARAMS,
        return_type: RuntimeType::Bool,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.string.empty",
        runtime_symbol: "__glagol_vec_string_empty",
        params: &[],
        return_type: RuntimeType::VecString,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.string.append",
        runtime_symbol: "__glagol_vec_string_append",
        params: VEC_STRING_STRING_PARAMS,
        return_type: RuntimeType::VecString,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.string.len",
        runtime_symbol: "__glagol_vec_string_len",
        params: VEC_STRING_PARAM,
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.vec.string.index",
        runtime_symbol: "__glagol_vec_string_index",
        params: VEC_STRING_I32_PARAMS,
        return_type: RuntimeType::String,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.time.monotonic_ms",
        runtime_symbol: "__glagol_time_monotonic_ms",
        params: &[],
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.time.sleep_ms",
        runtime_symbol: "__glagol_time_sleep_ms",
        params: I32_PARAM,
        return_type: RuntimeType::Unit,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.random.i32",
        runtime_symbol: "__glagol_random_i32",
        params: &[],
        return_type: RuntimeType::I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.i32_to_i64",
        runtime_symbol: "std.num.i32_to_i64",
        params: I32_PARAM,
        return_type: RuntimeType::I64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.i32_to_f64",
        runtime_symbol: "std.num.i32_to_f64",
        params: I32_PARAM,
        return_type: RuntimeType::F64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.i64_to_f64",
        runtime_symbol: "std.num.i64_to_f64",
        params: I64_PARAM,
        return_type: RuntimeType::F64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.u32_to_u64",
        runtime_symbol: "std.num.u32_to_u64",
        params: U32_PARAM,
        return_type: RuntimeType::U64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.u32_to_i64",
        runtime_symbol: "std.num.u32_to_i64",
        params: U32_PARAM,
        return_type: RuntimeType::I64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.u32_to_f64",
        runtime_symbol: "std.num.u32_to_f64",
        params: U32_PARAM,
        return_type: RuntimeType::F64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.u64_to_f64",
        runtime_symbol: "std.num.u64_to_f64",
        params: U64_PARAM,
        return_type: RuntimeType::F64,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.i64_to_i32_result",
        runtime_symbol: "std.num.i64_to_i32_result",
        params: I64_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.i32_to_u32_result",
        runtime_symbol: "std.num.i32_to_u32_result",
        params: I32_PARAM,
        return_type: RuntimeType::ResultU32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.i64_to_u64_result",
        runtime_symbol: "std.num.i64_to_u64_result",
        params: I64_PARAM,
        return_type: RuntimeType::ResultU64I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.u64_to_i64_result",
        runtime_symbol: "std.num.u64_to_i64_result",
        params: U64_PARAM,
        return_type: RuntimeType::ResultI64I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.f64_to_i32_result",
        runtime_symbol: "std.num.f64_to_i32_result",
        params: F64_PARAM,
        return_type: RuntimeType::ResultI32I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.f64_to_i64_result",
        runtime_symbol: "std.num.f64_to_i64_result",
        params: F64_PARAM,
        return_type: RuntimeType::ResultI64I32,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.i32_to_string",
        runtime_symbol: "__glagol_num_i32_to_string",
        params: I32_PARAM,
        return_type: RuntimeType::String,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.i64_to_string",
        runtime_symbol: "__glagol_num_i64_to_string",
        params: I64_PARAM,
        return_type: RuntimeType::String,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.u32_to_string",
        runtime_symbol: "__glagol_num_u32_to_string",
        params: U32_PARAM,
        return_type: RuntimeType::String,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.u64_to_string",
        runtime_symbol: "__glagol_num_u64_to_string",
        params: U64_PARAM,
        return_type: RuntimeType::String,
        promoted: true,
    },
    RuntimeFunction {
        source_name: "std.num.f64_to_string",
        runtime_symbol: "__glagol_num_f64_to_string",
        params: F64_PARAM,
        return_type: RuntimeType::String,
        promoted: true,
    },
];

const RESERVED_HELPER_SYMBOLS: &[&str] = &[
    "std.result.is_ok",
    "std.result.is_err",
    "std.result.unwrap_ok",
    "std.result.unwrap_err",
    "__glagol_string_eq",
    "__glagol_array_bounds_trap",
    "__glagol_unwrap_some_trap",
    "__glagol_unwrap_ok_trap",
    "__glagol_unwrap_err_trap",
    "__glagol_process_init",
    "__glagol_io_read_stdin_result",
    "__glagol_process_arg_trap",
    "__glagol_process_arg_result",
    "__glagol_env_get_result",
    "__glagol_fs_read_trap",
    "__glagol_fs_read_text_result",
    "__glagol_fs_write_text_result",
    "__glagol_fs_exists",
    "__glagol_fs_is_file",
    "__glagol_fs_is_dir",
    "__glagol_fs_remove_file_result",
    "__glagol_fs_create_dir_result",
    "__glagol_fs_open_text_read_result",
    "__glagol_fs_read_open_text_result",
    "__glagol_fs_close_result",
    "__glagol_net_tcp_connect_loopback_result",
    "__glagol_net_tcp_listen_loopback_result",
    "__glagol_net_tcp_bound_port_result",
    "__glagol_net_tcp_accept_result",
    "__glagol_net_tcp_read_all_result",
    "__glagol_net_tcp_write_text_result",
    "__glagol_net_tcp_close_result",
    "__glagol_vec_i32_eq",
    "__glagol_vec_i32_allocation_trap",
    "__glagol_vec_i32_index_trap",
    "__glagol_vec_i64_eq",
    "__glagol_vec_i64_allocation_trap",
    "__glagol_vec_i64_index_trap",
    "__glagol_vec_string_eq",
    "__glagol_vec_string_allocation_trap",
    "__glagol_vec_string_index_trap",
    "__glagol_time_monotonic_unavailable_trap",
    "__glagol_time_sleep_negative_trap",
    "__glagol_time_sleep_failed_trap",
    "__glagol_string_parse_i32_result",
    "__glagol_string_parse_i64_result",
    "__glagol_string_parse_u32_result",
    "__glagol_string_parse_u64_result",
    "__glagol_string_parse_f64_result",
    "__glagol_string_parse_bool_result",
    "__glagol_string_byte_at_result",
    "__glagol_string_slice_result",
    "__glagol_string_starts_with",
    "__glagol_string_ends_with",
    "__glagol_json_quote_string",
    "__glagol_json_parse_string_value_result",
    "__glagol_json_parse_bool_value_result",
    "__glagol_json_parse_i32_value_result",
    "__glagol_json_parse_u32_value_result",
    "__glagol_json_parse_i64_value_result",
    "__glagol_json_parse_u64_value_result",
    "__glagol_json_parse_f64_value_result",
    "__glagol_num_u32_to_string",
    "__glagol_num_u64_to_string",
    "__glagol_num_f64_to_string",
    "print_f64",
    "print_u32",
    "print_u64",
    "print_i64",
];

pub fn function(source_name: &str) -> Option<&'static RuntimeFunction> {
    FUNCTIONS
        .iter()
        .find(|function| function.source_name == source_name)
}

pub fn is_promoted_source_name(source_name: &str) -> bool {
    function(source_name).is_some_and(|function| function.promoted)
}

pub fn is_reserved_name(name: &str) -> bool {
    FUNCTIONS
        .iter()
        .any(|function| function.source_name == name || function.runtime_symbol == name)
        || RESERVED_HELPER_SYMBOLS.contains(&name)
}

pub fn is_standard_path(source_name: &str) -> bool {
    source_name.starts_with("std.")
}

pub fn runtime_symbol(source_name: &str) -> Option<&'static str> {
    function(source_name).map(|function| function.runtime_symbol)
}

pub fn unsupported_standard_library_call(file: &str, span: Span, source_name: &str) -> Diagnostic {
    Diagnostic::new(
        file,
        "UnsupportedStandardLibraryCall",
        format!("standard library call `{}` is not supported", source_name),
    )
    .with_span(span)
    .expected("std.io.print_i32, std.io.print_u32, std.io.print_i64, std.io.print_u64, std.io.print_f64, std.io.print_string, std.io.print_bool, std.io.eprint, std.io.read_stdin_result, std.string.len, std.string.concat, std.string.byte_at_result, std.string.slice_result, std.string.starts_with, std.string.ends_with, std.string.parse_i32_result, std.string.parse_u32_result, std.string.parse_i64_result, std.string.parse_u64_result, std.string.parse_f64_result, std.string.parse_bool_result, std.json.quote_string, std.json.parse_string_value_result, std.json.parse_bool_value_result, std.json.parse_i32_value_result, std.json.parse_u32_value_result, std.json.parse_i64_value_result, std.json.parse_u64_value_result, std.json.parse_f64_value_result, std.process.argc, std.process.arg, std.process.arg_result, std.env.get, std.env.get_result, std.fs.read_text, std.fs.read_text_result, std.fs.write_text, std.fs.write_text_result, std.fs.exists, std.fs.is_file, std.fs.is_dir, std.fs.remove_file_result, std.fs.create_dir_result, std.fs.open_text_read_result, std.fs.read_open_text_result, std.fs.close_result, std.vec.i32.empty, std.vec.i32.append, std.vec.i32.len, std.vec.i32.index, std.vec.i64.empty, std.vec.i64.append, std.vec.i64.len, std.vec.i64.index, std.vec.f64.empty, std.vec.f64.append, std.vec.f64.len, std.vec.f64.index, std.vec.bool.empty, std.vec.bool.append, std.vec.bool.len, std.vec.bool.index, std.vec.string.empty, std.vec.string.append, std.vec.string.len, std.vec.string.index, std.time.monotonic_ms, std.time.sleep_ms, std.random.i32, std.num.i32_to_i64, std.num.i32_to_f64, std.num.i64_to_f64, std.num.i64_to_i32_result, std.num.f64_to_i32_result, std.num.f64_to_i64_result, std.num.i32_to_string, std.num.u32_to_string, std.num.i64_to_string, std.num.u64_to_string, std.num.f64_to_string, std.result.is_ok, std.result.is_err, std.result.unwrap_ok, or std.result.unwrap_err")
    .found(source_name)
    .hint("use a promoted standard-runtime name or a legacy intrinsic alias")
}
