use super::error::{EvalResult, Flow, signal};
use super::eval::Context;
use super::intern::resolve_sym;
use super::value::*;
const CHAR_TABLE_TAG: &str = "--char-table--";
const BOOL_VECTOR_TAG: &str = "--bool-vector--";
const CT_DEFAULT: usize = 1; const CT_PARENT: usize = 2; const CT_SUBTYPE: usize = 3; const CT_EXTRA_COUNT: usize = 4; const CT_EXTRA_START: usize = 5; const CT_LOGICAL_LENGTH: i64 = 0x3F_FFFF;
const MAX_CHAR: i64 = 0x3F_FFFF;
const BV_SIZE: usize = 1;
pub fn is_char_table(v: &Value) -> bool {
if v.is_vector() {
let vec = v.as_vector_data().unwrap();
vec.len() >= CT_EXTRA_START
&& vec[0]
.as_symbol_id()
.map_or(false, |id| resolve_sym(id) == CHAR_TABLE_TAG)
} else {
false
}
}
pub fn is_bool_vector(v: &Value) -> bool {
if v.is_vector() {
let vec = v.as_vector_data().unwrap();
vec.len() >= 2
&& vec[0]
.as_symbol_id()
.map_or(false, |id| resolve_sym(id) == BOOL_VECTOR_TAG)
} else {
false
}
}
pub(crate) fn bool_vector_length(v: &Value) -> Option<i64> {
if !v.is_vector() {
return None;
};
let vec = v.as_vector_data().unwrap();
if vec.len() < 2
|| !vec[0]
.as_symbol_id()
.map_or(false, |id| resolve_sym(id) == BOOL_VECTOR_TAG)
{
return None;
}
Some(match vec[BV_SIZE].kind() {
ValueKind::Fixnum(n) => n,
_ => 0,
})
}
pub(crate) fn char_table_length(v: &Value) -> Option<i64> {
if !v.is_vector() {
return None;
};
let vec = v.as_vector_data().unwrap();
if vec.len() >= CT_EXTRA_START
&& vec[0]
.as_symbol_id()
.map_or(false, |id| resolve_sym(id) == CHAR_TABLE_TAG)
{
Some(CT_LOGICAL_LENGTH)
} else {
None
}
}
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_min_args(name: &str, args: &[Value], min: usize) -> Result<(), Flow> {
if args.len() < min {
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 wrong_type(pred: &str, got: &Value) -> Flow {
signal("wrong-type-argument", vec![Value::symbol(pred), *got])
}
fn expect_int(value: &Value) -> Result<i64, Flow> {
match value.kind() {
ValueKind::Fixnum(n) => Ok(n),
_other => Err(wrong_type("integerp", value)),
}
}
fn expect_wholenump(value: &Value) -> Result<i64, Flow> {
let n = match value.kind() {
ValueKind::Fixnum(n) => n,
_ => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("wholenump"), *value],
));
}
};
if n < 0 {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("wholenump"), *value],
));
}
Ok(n)
}
fn ct_data_start(vec: &[Value]) -> usize {
let extra_count = match vec[CT_EXTRA_COUNT].kind() {
ValueKind::Fixnum(n) => n as usize,
_ => 0,
};
CT_EXTRA_START + extra_count
}
pub fn make_char_table_value(sub_type: Value, default: Value) -> Value {
make_char_table_with_extra_slots(sub_type, default, 0)
}
pub fn make_char_table_with_extra_slots(sub_type: Value, default: Value, n_extras: i64) -> Value {
let mut vec = vec![
Value::symbol(CHAR_TABLE_TAG),
default, Value::NIL, sub_type, Value::fixnum(n_extras), ];
for _ in 0..n_extras {
vec.push(Value::NIL);
}
Value::vector(vec)
}
pub fn ct_set_single(table: &Value, ch: i64, value: Value) {
if table.is_vector() {
let mut vec = table.as_vector_data().cloned().unwrap_or_default();
ct_set_char(&mut vec, ch, value);
let _ = table.replace_vector_data(vec);
} else {
panic!("ct_set_single: expected char-table Vector");
}
}
pub(crate) fn builtin_make_char_table(eval: &mut Context, args: Vec<Value>) -> EvalResult {
expect_min_args("make-char-table", &args, 1)?;
expect_max_args("make-char-table", &args, 2)?;
let sub_type = args[0];
let default = if args.len() > 1 { args[1] } else { Value::NIL };
let n_extras = if let Some(name) = sub_type.as_symbol_name() {
eval.obarray
.get_property(name, "char-table-extra-slots")
.and_then(|v| v.as_int())
.unwrap_or(0)
} else {
0
};
Ok(make_char_table_with_extra_slots(
sub_type, default, n_extras,
))
}
pub(crate) fn builtin_char_table_p(args: Vec<Value>) -> EvalResult {
expect_args("char-table-p", &args, 1)?;
Ok(Value::bool_val(is_char_table(&args[0])))
}
pub(crate) fn builtin_set_char_table_range(args: Vec<Value>) -> EvalResult {
expect_args("set-char-table-range", &args, 3)?;
let table = &args[0];
let range = &args[1];
let value = &args[2];
if !is_char_table(table) {
return Err(wrong_type("char-table-p", table));
}
let mut vec = table.as_vector_data().unwrap().clone();
match range.kind() {
ValueKind::Nil => {
vec[CT_DEFAULT] = *value;
}
ValueKind::T => {
ct_set_range(&mut vec, 0, MAX_CHAR, *value);
}
ValueKind::Fixnum(_) => {
let ch = expect_int(range)?;
ct_set_char(&mut vec, ch, *value);
}
ValueKind::Cons => {
let pair_car = range.cons_car();
let pair_cdr = range.cons_cdr();
let min = expect_int(&pair_car)?;
let max = expect_int(&pair_cdr)?;
if min > max {
return Err(signal(
"args-out-of-range",
vec![Value::fixnum(min), Value::fixnum(max)],
));
}
ct_set_range(&mut vec, min, max, *value);
}
_ => {
return Err(signal(
"wrong-type-argument",
vec![Value::symbol("char-table-range"), *range],
));
}
}
let _ = table.replace_vector_data(vec);
Ok(*value)
}
fn ct_set_char(vec: &mut Vec<Value>, ch: i64, value: Value) {
vec.push(Value::fixnum(ch));
vec.push(value);
}
fn ct_set_range(vec: &mut Vec<Value>, min: i64, max: i64, value: Value) {
vec.push(Value::cons(Value::fixnum(min), Value::fixnum(max)));
vec.push(value);
}
fn ct_get_char(vec: &[Value], ch: i64) -> Option<Value> {
let start = ct_data_start(vec);
let mut i = start;
let mut match_value: Option<Value> = None;
while i + 1 < vec.len() {
match vec[i].kind() {
ValueKind::Fixnum(existing) => {
if existing == ch {
match_value = Some(vec[i + 1]);
}
}
ValueKind::Cons => {
let pair_car = vec[i].cons_car();
let pair_cdr = vec[i].cons_cdr();
if let (Some(min), Some(max)) = (pair_car.as_fixnum(), pair_cdr.as_fixnum()) {
if ch >= min && ch <= max {
match_value = Some(vec[i + 1]);
}
}
}
_ => {}
}
i += 2;
}
match_value
}
pub(crate) fn builtin_char_table_range(args: Vec<Value>) -> EvalResult {
expect_args("char-table-range", &args, 2)?;
let table = &args[0];
let range = &args[1];
if !is_char_table(table) {
return Err(wrong_type("char-table-p", table));
}
match range.kind() {
ValueKind::Nil => {
let vec = table.as_vector_data().unwrap();
Ok(vec[CT_DEFAULT])
}
ValueKind::Fixnum(_) => {
let ch = expect_int(range)?;
ct_lookup(table, ch)
}
ValueKind::Cons => {
let pair_car = range.cons_car();
let pair_cdr = range.cons_cdr();
let from = expect_int(&pair_car)?;
let _to = expect_int(&pair_cdr)?;
let (value, _run_from, _run_to) = ct_lookup_and_range(table, from)?;
Ok(value)
}
_ => Err(signal(
"error",
vec![Value::string(
"Invalid RANGE argument to `char-table-range'",
)],
)),
}
}
pub(crate) fn ct_lookup(table: &Value, ch: i64) -> EvalResult {
if !table.is_vector() {
return Err(wrong_type("char-table-p", table));
}
let vec = table.as_vector_data().unwrap().clone();
if let Some(val) = ct_get_char(&vec, ch) {
if !val.is_nil() {
return Ok(val);
}
}
let default = vec[CT_DEFAULT];
let parent = vec[CT_PARENT];
if !default.is_nil() {
Ok(default)
} else if is_char_table(&parent) {
ct_lookup(&parent, ch)
} else {
Ok(Value::NIL)
}
}
fn ct_lookup_and_range(table: &Value, ch: i64) -> Result<(Value, i64, i64), Flow> {
if !is_char_table(table) {
return Err(wrong_type("char-table-p", table));
}
for run in ct_effective_runs(table) {
if ch >= run.start && ch <= run.end {
return Ok((run.value, run.start, run.end));
}
}
Ok((Value::NIL, 0, MAX_CHAR))
}
pub(crate) fn char_table_ref_and_range(table: &Value, ch: i64) -> Result<(Value, i64, i64), Flow> {
ct_lookup_and_range(table, ch)
}
pub(crate) fn builtin_char_table_parent(args: Vec<Value>) -> EvalResult {
expect_args("char-table-parent", &args, 1)?;
let table = &args[0];
if !is_char_table(table) {
return Err(wrong_type("char-table-p", table));
}
let vec = table.as_vector_data().unwrap();
Ok(vec[CT_PARENT])
}
pub(crate) fn char_table_local_entries(table: &Value) -> Result<Vec<(Value, Value)>, Flow> {
if !is_char_table(table) {
return Err(wrong_type("char-table-p", table));
}
let vec = table.as_vector_data().unwrap().clone();
let start = ct_data_start(&vec);
let mut out = Vec::new();
let mut i = start;
while i + 1 < vec.len() {
match vec[i].kind() {
ValueKind::Fixnum(_) | ValueKind::Cons => out.push((vec[i], vec[i + 1])),
_ => {}
}
i += 2;
}
Ok(out)
}
pub(crate) fn builtin_set_char_table_parent(args: Vec<Value>) -> EvalResult {
expect_args("set-char-table-parent", &args, 2)?;
let table = &args[0];
let parent = &args[1];
if !is_char_table(table) {
return Err(wrong_type("char-table-p", table));
}
if !parent.is_nil() && !is_char_table(parent) {
return Err(wrong_type("char-table-p", parent));
}
if !parent.is_nil() {
let mut cursor = *parent;
while is_char_table(&cursor) {
if cursor.is_vector() && table.is_vector() {
if std::ptr::eq(
cursor.as_vector_data().unwrap() as *const _,
table.as_vector_data().unwrap() as *const _,
) {
return Err(signal(
"error",
vec![Value::string(
"Attempt to make a chartable be its own parent",
)],
));
}
}
let vec = cursor.as_vector_data().unwrap().clone();
cursor = vec[CT_PARENT];
}
}
let _ = table.set_vector_slot(CT_PARENT, *parent);
Ok(*parent)
}
pub(crate) fn for_each_char_table_mapping(
table: &Value,
mut f: impl FnMut(Value, Value) -> Result<(), Flow>,
) -> Result<(), Flow> {
if !is_char_table(table) {
return Err(wrong_type("char-table-p", table));
}
let shared_range = Value::cons(Value::fixnum(0), Value::fixnum(MAX_CHAR));
for run in ct_effective_runs(table) {
shared_range.set_car(Value::fixnum(run.start));
shared_range.set_cdr(Value::fixnum(run.end));
if run.value.is_nil() {
continue;
}
let key = if run.start == run.end {
Value::fixnum(run.start)
} else {
shared_range
};
f(key, run.value)?;
}
Ok(())
}
pub(crate) fn builtin_map_char_table(eval: &mut Context, args: Vec<Value>) -> EvalResult {
expect_args("map-char-table", &args, 2)?;
let func = args[0];
let table = args[1];
for_each_char_table_mapping(&table, |key, value| {
let _ = eval.apply(func, vec![key, value])?;
Ok(())
})?;
Ok(Value::NIL)
}
fn ct_resolved_entries(table: &Value) -> Vec<(Value, Value)> {
ct_effective_runs(table)
.into_iter()
.filter(|run| !run.value.is_nil())
.map(|run| (run_key(run.start, run.end), run.value))
.collect()
}
#[derive(Clone, Copy)]
struct RawEntry {
start: i64,
end: i64,
value: Value,
}
#[derive(Clone, Copy, Debug, PartialEq)]
struct EffectiveRun {
start: i64,
end: i64,
value: Value,
}
fn ct_collect_raw_entries(vec: &[Value]) -> Vec<RawEntry> {
let start = ct_data_start(vec);
let mut raws = Vec::new();
let mut i = start;
while i + 1 < vec.len() {
match vec[i].kind() {
ValueKind::Fixnum(ch) => raws.push(RawEntry {
start: ch,
end: ch,
value: vec[i + 1],
}),
ValueKind::Cons => {
let pair_car = vec[i].cons_car();
let pair_cdr = vec[i].cons_cdr();
if let (Some(min), Some(max)) = (pair_car.as_fixnum(), pair_cdr.as_fixnum()) {
raws.push(RawEntry {
start: min,
end: max,
value: vec[i + 1],
});
}
}
_ => {}
}
i += 2;
}
raws
}
fn ct_local_value_at(raws: &[RawEntry], ch: i64) -> Option<Value> {
let mut value = None;
for raw in raws {
if ch >= raw.start && ch <= raw.end {
value = Some(raw.value);
}
}
value
}
fn ct_effective_value_at(
raws: &[RawEntry],
default: Value,
parent_runs: &[EffectiveRun],
ch: i64,
) -> Value {
if let Some(local) = ct_local_value_at(raws, ch) {
if !local.is_nil() {
return local;
}
}
if !default.is_nil() {
return default;
}
effective_runs_value_at(parent_runs, ch).unwrap_or(Value::NIL)
}
fn effective_runs_value_at(entries: &[EffectiveRun], ch: i64) -> Option<Value> {
for run in entries {
if ch >= run.start && ch <= run.end {
return Some(run.value);
}
}
None
}
fn ct_effective_runs(table: &Value) -> Vec<EffectiveRun> {
if !table.is_vector() {
return vec![EffectiveRun {
start: 0,
end: MAX_CHAR,
value: Value::NIL,
}];
};
let vec = table.as_vector_data().unwrap().clone();
let raws = ct_collect_raw_entries(&vec);
let default = vec[CT_DEFAULT];
let parent = vec[CT_PARENT];
let parent_runs = if is_char_table(&parent) {
ct_effective_runs(&parent)
} else {
vec![EffectiveRun {
start: 0,
end: MAX_CHAR,
value: Value::NIL,
}]
};
let mut boundaries = std::collections::BTreeSet::new();
boundaries.insert(0);
boundaries.insert(MAX_CHAR.saturating_add(1));
for raw in &raws {
boundaries.insert(raw.start);
boundaries.insert(raw.end.saturating_add(1).min(MAX_CHAR.saturating_add(1)));
}
for run in &parent_runs {
boundaries.insert(run.start);
boundaries.insert(run.end.saturating_add(1).min(MAX_CHAR.saturating_add(1)));
}
let boundary_vec = boundaries.into_iter().collect::<Vec<_>>();
let mut runs: Vec<EffectiveRun> = Vec::new();
for window in boundary_vec.windows(2) {
let start = window[0];
let end_exclusive = window[1];
if start > MAX_CHAR || end_exclusive <= start {
continue;
}
let end = end_exclusive.saturating_sub(1).min(MAX_CHAR);
let value = ct_effective_value_at(&raws, default, &parent_runs, start);
match runs.last_mut() {
Some(last) if last.value == value && start == last.end.saturating_add(1) => {
last.end = end;
}
_ => runs.push(EffectiveRun { start, end, value }),
}
}
if runs.is_empty() {
vec![EffectiveRun {
start: 0,
end: MAX_CHAR,
value: Value::NIL,
}]
} else {
runs
}
}
fn run_key(start: i64, end: i64) -> Value {
if start == end {
Value::fixnum(start)
} else {
Value::cons(Value::fixnum(start), Value::fixnum(end))
}
}
pub(crate) fn for_each_non_nil_char_table_run<F>(table: &Value, mut f: F)
where
F: FnMut(Value, Value),
{
if !is_char_table(table) {
return;
}
for run in ct_effective_runs(table) {
if run.value.is_nil() {
continue;
}
f(run_key(run.start, run.end), run.value);
}
}
const GNU_CHAR_TABLE_CONTENT_BLOCKS: i64 = 64;
const GNU_CHAR_TABLE_BLOCK_CHARS: i64 = 1 << 16;
fn uniform_run_value(runs: &[EffectiveRun], start: i64, end: i64) -> Option<Value> {
runs.iter()
.find(|run| start >= run.start && end <= run.end)
.map(|run| run.value)
}
pub(crate) fn char_table_external_slots(table: &Value) -> Option<Vec<Value>> {
if !is_char_table(table) {
return None;
}
if !table.is_vector() {
return None;
};
let vec = table.as_vector_data().unwrap().clone();
let runs = ct_effective_runs(table);
let extra_count = match vec[CT_EXTRA_COUNT].kind() {
ValueKind::Fixnum(n) if n >= 0 => n as usize,
_ => 0,
};
let mut slots = Vec::with_capacity(4 + GNU_CHAR_TABLE_CONTENT_BLOCKS as usize + extra_count);
slots.push(vec[CT_DEFAULT]);
slots.push(vec[CT_PARENT]);
slots.push(vec[CT_SUBTYPE]);
slots.push(uniform_run_value(&runs, 0, 127).unwrap_or(Value::NIL));
for idx in 0..GNU_CHAR_TABLE_CONTENT_BLOCKS {
let start = idx * GNU_CHAR_TABLE_BLOCK_CHARS;
let end = (start + GNU_CHAR_TABLE_BLOCK_CHARS - 1).min(MAX_CHAR);
slots.push(uniform_run_value(&runs, start, end).unwrap_or(Value::NIL));
}
for extra_idx in 0..extra_count {
slots.push(vec[CT_EXTRA_START + extra_idx]);
}
Some(slots)
}
pub(crate) fn builtin_char_table_extra_slot(args: Vec<Value>) -> EvalResult {
expect_args("char-table-extra-slot", &args, 2)?;
let table = &args[0];
let n = expect_int(&args[1])?;
if !is_char_table(table) {
return Err(wrong_type("char-table-p", table));
}
let v = table.as_vector_data().unwrap().clone();
let extra_count = match v[CT_EXTRA_COUNT].kind() {
ValueKind::Fixnum(c) => c,
_ => 0,
};
if n < 0 || n >= extra_count {
return Err(signal("args-out-of-range", vec![args[0], args[1]]));
}
Ok(v[CT_EXTRA_START + n as usize])
}
pub(crate) fn builtin_set_char_table_extra_slot(args: Vec<Value>) -> EvalResult {
expect_args("set-char-table-extra-slot", &args, 3)?;
let table = &args[0];
let n = expect_int(&args[1])?;
let value = &args[2];
if !is_char_table(table) {
return Err(wrong_type("char-table-p", table));
}
let v = table.as_vector_data().unwrap();
let extra_count = match v[CT_EXTRA_COUNT].kind() {
ValueKind::Fixnum(c) => c,
_ => 0,
};
if n < 0 || n >= extra_count {
return Err(signal("args-out-of-range", vec![args[0], args[1]]));
}
let slot_idx = CT_EXTRA_START + n as usize;
let _ = table.set_vector_slot(slot_idx, *value);
Ok(*value)
}
pub(crate) fn builtin_char_table_subtype(args: Vec<Value>) -> EvalResult {
expect_args("char-table-subtype", &args, 1)?;
let table = &args[0];
if !is_char_table(table) {
return Err(wrong_type("char-table-p", table));
}
let vec = table.as_vector_data().unwrap();
Ok(vec[CT_SUBTYPE])
}
pub(crate) fn builtin_make_bool_vector(args: Vec<Value>) -> EvalResult {
expect_args("make-bool-vector", &args, 2)?;
let length = expect_int(&args[0])?;
if length < 0 {
return Err(signal("args-out-of-range", vec![args[0]]));
}
let init_val = if args[1].is_truthy() {
Value::fixnum(1)
} else {
Value::fixnum(0)
};
let len = length as usize;
let mut vec = Vec::with_capacity(2 + len);
vec.push(Value::symbol(BOOL_VECTOR_TAG));
vec.push(Value::fixnum(length));
for _ in 0..len {
vec.push(init_val);
}
Ok(Value::vector(vec))
}
pub(crate) fn builtin_bool_vector(args: Vec<Value>) -> EvalResult {
let bits: Vec<bool> = args.into_iter().map(|v| v.is_truthy()).collect();
Ok(bv_from_bits(&bits))
}
pub(crate) fn builtin_bool_vector_p(args: Vec<Value>) -> EvalResult {
expect_args("bool-vector-p", &args, 1)?;
Ok(Value::bool_val(is_bool_vector(&args[0])))
}
fn bv_length(vec: &[Value]) -> i64 {
match vec[BV_SIZE].kind() {
ValueKind::Fixnum(n) => n,
_ => 0,
}
}
fn bv_bits(vec: &[Value]) -> Vec<bool> {
let len = bv_length(vec) as usize;
let mut bits = Vec::with_capacity(len);
for i in 0..len {
let v = &vec[2 + i];
bits.push(v.as_fixnum().map_or(false, |n| n != 0));
}
bits
}
pub(crate) fn builtin_bool_vector_count_population(args: Vec<Value>) -> EvalResult {
expect_args("bool-vector-count-population", &args, 1)?;
let (bits, _len) = extract_bv_bits(&args[0])?;
let count = bits.iter().filter(|&&b| b).count();
Ok(Value::fixnum(count as i64))
}
fn extract_bv_bits(value: &Value) -> Result<(Vec<bool>, i64), Flow> {
if !is_bool_vector(value) {
return Err(wrong_type("bool-vector-p", value));
}
let vec = value.as_vector_data().unwrap().clone();
let len = bv_length(&vec);
let bits = bv_bits(&vec);
Ok((bits, len))
}
fn bv_from_bits(bits: &[bool]) -> Value {
let len = bits.len();
let mut vec = Vec::with_capacity(2 + len);
vec.push(Value::symbol(BOOL_VECTOR_TAG));
vec.push(Value::fixnum(len as i64));
for &b in bits {
vec.push(Value::fixnum(if b { 1 } else { 0 }));
}
Value::vector(vec)
}
pub(crate) fn builtin_bool_vector_intersection(args: Vec<Value>) -> EvalResult {
expect_min_args("bool-vector-intersection", &args, 2)?;
expect_max_args("bool-vector-intersection", &args, 3)?;
let (bits_a, len_a) = extract_bv_bits(&args[0])?;
let (bits_b, len_b) = extract_bv_bits(&args[1])?;
if len_a != len_b {
return Err(signal(
"wrong-length-argument",
vec![Value::fixnum(len_a), Value::fixnum(len_b)],
));
}
let result_bits: Vec<bool> = bits_a
.iter()
.zip(bits_b.iter())
.map(|(&a, &b)| a && b)
.collect();
if args.len() == 3 {
store_bv_result_with_expected_lengths(&args[2], &result_bits, &[len_a, len_b])?;
Ok(args[2])
} else {
Ok(bv_from_bits(&result_bits))
}
}
pub(crate) fn builtin_bool_vector_union(args: Vec<Value>) -> EvalResult {
expect_min_args("bool-vector-union", &args, 2)?;
expect_max_args("bool-vector-union", &args, 3)?;
let (bits_a, len_a) = extract_bv_bits(&args[0])?;
let (bits_b, len_b) = extract_bv_bits(&args[1])?;
if len_a != len_b {
return Err(signal(
"wrong-length-argument",
vec![Value::fixnum(len_a), Value::fixnum(len_b)],
));
}
let result_bits: Vec<bool> = bits_a
.iter()
.zip(bits_b.iter())
.map(|(&a, &b)| a || b)
.collect();
if args.len() == 3 {
store_bv_result_with_expected_lengths(&args[2], &result_bits, &[len_a, len_b])?;
Ok(args[2])
} else {
Ok(bv_from_bits(&result_bits))
}
}
pub(crate) fn builtin_bool_vector_exclusive_or(args: Vec<Value>) -> EvalResult {
expect_min_args("bool-vector-exclusive-or", &args, 2)?;
expect_max_args("bool-vector-exclusive-or", &args, 3)?;
let (bits_a, len_a) = extract_bv_bits(&args[0])?;
let (bits_b, len_b) = extract_bv_bits(&args[1])?;
if len_a != len_b {
return Err(signal(
"wrong-length-argument",
vec![Value::fixnum(len_a), Value::fixnum(len_b)],
));
}
let result_bits: Vec<bool> = bits_a
.iter()
.zip(bits_b.iter())
.map(|(&a, &b)| a ^ b)
.collect();
if args.len() == 3 {
store_bv_result_with_expected_lengths(&args[2], &result_bits, &[len_a, len_b])?;
Ok(args[2])
} else {
Ok(bv_from_bits(&result_bits))
}
}
pub(crate) fn builtin_bool_vector_not(args: Vec<Value>) -> EvalResult {
expect_min_args("bool-vector-not", &args, 1)?;
expect_max_args("bool-vector-not", &args, 2)?;
let (bits, len_a) = extract_bv_bits(&args[0])?;
let result_bits: Vec<bool> = bits.into_iter().map(|b| !b).collect();
if args.len() == 2 {
store_bv_result_with_expected_lengths(&args[1], &result_bits, &[len_a])?;
Ok(args[1])
} else {
Ok(bv_from_bits(&result_bits))
}
}
pub(crate) fn builtin_bool_vector_set_difference(args: Vec<Value>) -> EvalResult {
expect_min_args("bool-vector-set-difference", &args, 2)?;
expect_max_args("bool-vector-set-difference", &args, 3)?;
let (bits_a, len_a) = extract_bv_bits(&args[0])?;
let (bits_b, len_b) = extract_bv_bits(&args[1])?;
if len_a != len_b {
return Err(signal(
"wrong-length-argument",
vec![Value::fixnum(len_a), Value::fixnum(len_b)],
));
}
let result_bits: Vec<bool> = bits_a
.iter()
.zip(bits_b.iter())
.map(|(&a, &b)| a && !b)
.collect();
if args.len() == 3 {
store_bv_result_with_expected_lengths(&args[2], &result_bits, &[len_a, len_b])?;
Ok(args[2])
} else {
Ok(bv_from_bits(&result_bits))
}
}
pub(crate) fn builtin_bool_vector_count_consecutive(args: Vec<Value>) -> EvalResult {
expect_args("bool-vector-count-consecutive", &args, 3)?;
let (bits, len) = extract_bv_bits(&args[0])?;
let target = args[1].is_truthy();
let start = expect_wholenump(&args[2])?;
if start > len {
return Err(signal(
"args-out-of-range",
vec![args[0], Value::fixnum(start)],
));
}
let mut count = 0usize;
for bit in bits.iter().skip(start as usize) {
if *bit != target {
break;
}
count += 1;
}
Ok(Value::fixnum(count as i64))
}
pub(crate) fn builtin_bool_vector_subsetp(args: Vec<Value>) -> EvalResult {
expect_args("bool-vector-subsetp", &args, 2)?;
let (bits_a, len_a) = extract_bv_bits(&args[0])?;
let (bits_b, len_b) = extract_bv_bits(&args[1])?;
if len_a != len_b {
return Err(signal(
"wrong-length-argument",
vec![
Value::fixnum(len_a),
Value::fixnum(len_b),
Value::fixnum(len_b),
],
));
}
let is_subset = bits_a.iter().zip(bits_b.iter()).all(|(&a, &b)| !a || b);
Ok(Value::bool_val(is_subset))
}
fn store_bv_result_with_expected_lengths(
dest: &Value,
bits: &[bool],
expected_lengths: &[i64],
) -> Result<(), Flow> {
if !is_bool_vector(dest) {
return Err(wrong_type("bool-vector-p", dest));
}
let v = dest.as_vector_data().unwrap().clone();
let len = bv_length(&v) as usize;
if len != bits.len() {
let mut payload: Vec<Value> = expected_lengths
.iter()
.copied()
.map(Value::fixnum)
.collect();
payload.push(Value::fixnum(len as i64));
return Err(signal("wrong-length-argument", payload));
}
let mut slots = dest.as_vector_data().cloned().unwrap_or_default();
for (i, &b) in bits.iter().enumerate() {
slots[2 + i] = Value::fixnum(if b { 1 } else { 0 });
}
let _ = dest.replace_vector_data(slots);
Ok(())
}
#[cfg(test)]
#[path = "chartable_test.rs"]
mod tests;