use super::chartable::make_char_table_value;
use super::error::{EvalResult, Flow, signal};
use super::value::*;
use crate::emacs_core::value::ValueKind;
fn expect_range_args(name: &str, args: &[Value], min: usize, max: usize) -> Result<(), Flow> {
if args.len() < min || args.len() > max {
Err(signal(
"wrong-number-of-arguments",
vec![Value::symbol(name), Value::fixnum(args.len() as i64)],
))
} else {
Ok(())
}
}
fn expect_args(name: &str, args: &[Value], n: usize) -> Result<(), Flow> {
if args.len() != n {
Err(signal(
"wrong-number-of-arguments",
vec![Value::symbol(name), Value::fixnum(args.len() as i64)],
))
} else {
Ok(())
}
}
fn expect_max_args(name: &str, args: &[Value], max: usize) -> Result<(), Flow> {
if args.len() > max {
Err(signal(
"wrong-number-of-arguments",
vec![Value::symbol(name), Value::fixnum(args.len() as i64)],
))
} else {
Ok(())
}
}
fn expect_integerp(arg: &Value) -> Result<(), Flow> {
match arg.kind() {
ValueKind::Fixnum(_) => Ok(()),
other => Err(signal(
"wrong-type-argument",
vec![Value::symbol("integerp"), *arg],
)),
}
}
fn expect_integer_or_marker_p(arg: &Value) -> Result<(), Flow> {
match arg.kind() {
ValueKind::Fixnum(_) => Ok(()),
other => Err(signal(
"wrong-type-argument",
vec![Value::symbol("integer-or-marker-p"), *arg],
)),
}
}
fn integer_value(arg: &Value) -> i64 {
match arg.kind() {
ValueKind::Fixnum(n) => n,
_ => 0,
}
}
fn expect_string_value(arg: &Value) -> Result<&crate::heap_types::LispString, Flow> {
arg.as_lisp_string()
.ok_or_else(|| signal("wrong-type-argument", vec![Value::symbol("stringp"), *arg]))
}
pub(crate) fn builtin_compose_region_internal(
ctx: &mut super::eval::Context,
args: Vec<Value>,
) -> EvalResult {
expect_range_args("compose-region-internal", &args, 2, 4)?;
expect_integer_or_marker_p(&args[0])?;
expect_integer_or_marker_p(&args[1])?;
let start = integer_value(&args[0]);
let end = integer_value(&args[1]);
let (buffer_handle, point_max) = if let Some(buf) = ctx.buffers.current_buffer() {
(Value::make_buffer(buf.id), buf.point_max_char() as i64 + 1)
} else {
(Value::NIL, 1)
};
if start < 1 || end < 1 || start > end || start > point_max || end > point_max {
return Err(signal(
"args-out-of-range",
vec![buffer_handle, Value::fixnum(start), Value::fixnum(end)],
));
}
Ok(Value::NIL)
}
pub(crate) fn builtin_compose_string_internal(args: Vec<Value>) -> EvalResult {
expect_range_args("compose-string-internal", &args, 3, 5)?;
if !args[0].is_string() {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("stringp"), args[0]],
));
}
expect_integerp(&args[1])?;
expect_integerp(&args[2])?;
let start = integer_value(&args[1]);
let end = integer_value(&args[2]);
let len = expect_string_value(&args[0])?.schars() as i64;
if start < 0 || end < 0 || start > end || end > len {
return Err(signal(
"args-out-of-range",
vec![args[0], Value::fixnum(start), Value::fixnum(end)],
));
}
Ok(args[0])
}
pub(crate) fn builtin_find_composition_internal(args: Vec<Value>) -> EvalResult {
expect_args("find-composition-internal", &args, 4)?;
expect_integer_or_marker_p(&args[0])?;
if !args[1].is_nil() {
expect_integer_or_marker_p(&args[1])?;
}
if !args[2].is_nil() && !args[2].is_string() {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("stringp"), args[2]],
));
}
let pos = integer_value(&args[0]);
if let Some(text) = args[2].as_lisp_string() {
let len = text.schars() as i64;
if pos < 0 || pos > len {
return Err(signal(
"args-out-of-range",
vec![args[2], Value::fixnum(pos)],
));
}
} else if pos <= 0 {
return Err(signal(
"args-out-of-range",
vec![Value::NIL, Value::fixnum(pos)],
));
}
Ok(Value::NIL)
}
pub(crate) fn builtin_composition_get_gstring(args: Vec<Value>) -> EvalResult {
expect_args("composition-get-gstring", &args, 4)?;
expect_integerp(&args[0])?;
expect_integerp(&args[1])?;
if !args[3].is_string() {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("stringp"), args[3]],
));
}
let from = match args[0].kind() {
ValueKind::Fixnum(n) => n,
_ => unreachable!("validated by expect_integerp"),
};
let to = match args[1].kind() {
ValueKind::Fixnum(n) => n,
_ => unreachable!("validated by expect_integerp"),
};
let text = expect_string_value(&args[3])?;
let codes = crate::emacs_core::builtins::lisp_string_char_codes(text);
let len = codes.len() as i64;
if from > to || from > len || to > len {
return Err(signal(
"args-out-of-range",
vec![args[3], Value::fixnum(from), Value::fixnum(to)],
));
}
if from < 0 || from == to {
return Err(signal(
"error",
vec![Value::string("Attempt to shape zero-length text")],
));
}
let from_usize = from as usize;
let to_usize = to as usize;
if from_usize >= codes.len() || to_usize > codes.len() || from_usize >= to_usize {
return Err(signal(
"args-out-of-range",
vec![args[3], Value::fixnum(from), Value::fixnum(to)],
));
}
let segment = &codes[from_usize..to_usize];
let mut encoded = vec![Value::symbol("utf-8-unix")];
encoded.extend(segment.iter().map(|code| Value::fixnum(*code as i64)));
let mut gstring = vec![Value::vector(encoded), Value::NIL];
for code in segment {
let code = *code as i64;
gstring.push(Value::vector(vec![
Value::fixnum(0),
Value::fixnum(0),
Value::fixnum(code),
Value::fixnum(code),
Value::fixnum(1),
Value::fixnum(0),
Value::fixnum(1),
Value::fixnum(1),
Value::fixnum(0),
Value::NIL,
]));
}
while gstring.len() < 10 {
gstring.push(Value::NIL);
}
Ok(Value::vector(gstring))
}
pub(crate) fn builtin_clear_composition_cache(args: Vec<Value>) -> EvalResult {
expect_max_args("clear-composition-cache", &args, 0)?;
Ok(Value::NIL)
}
pub(crate) fn builtin_composition_sort_rules(args: Vec<Value>) -> EvalResult {
expect_args("composition-sort-rules", &args, 1)?;
if args[0].is_nil() {
return Ok(Value::NIL);
}
let items = list_to_vec(&args[0])
.ok_or_else(|| signal("wrong-type-argument", vec![Value::symbol("listp"), args[0]]))?;
for item in items {
if !item.is_cons() {
return Err(signal(
"error",
vec![Value::string("Invalid composition rule in RULES argument")],
));
}
}
Ok(args[0])
}
pub fn register_bootstrap_vars(obarray: &mut crate::emacs_core::symbol::Obarray) {
obarray.set_symbol_value("unicode-category-table", Value::NIL);
obarray.set_symbol_value(
"composition-function-table",
make_char_table_value(Value::NIL, Value::NIL),
);
obarray.set_symbol_value("auto-composition-mode", Value::T);
}
#[cfg(test)]
#[path = "composite_test.rs"]
mod tests;