mirror of
https://github.com/storytold/cadcraft.git
synced 2026-10-24 10:56:51 -06:00
AutoLISP: = compares the symbols type returns
`(= (type x) 'LIST)` is how an AutoLISP routine tests a type, because `type` returns a symbol. `=` coerced both of its arguments to numbers, so the symbol failed the coercion and the routine stopped at its first type test: _$ (= (type 1) 'INT) bad argument type: numberp: INT `=` and `/=` now compare numbers and strings as before, and any other atom -- a symbol, `nil`, an entity name, a selection set, a file handle -- by what it is, the way `eq` does. Two consequences, both closer to AutoLISP than raising was: a mismatch of kinds answers `nil` rather than failing, so `(= 1 "1")` and `(= '(1) '(1))` answer (comparing list structure is `equal`'s job), and `(= nil nil)` is `T`. The ordering comparisons still take numbers and strings only, as AutoLISP has them: `(< 'a 'b)` remains `bad argument type: numberp: A`. Fix: #1597
This commit is contained in:
@@ -307,11 +307,32 @@ fn chain(a: &[Value], ok: fn(std::cmp::Ordering) -> bool) -> R {
|
||||
}
|
||||
Ok(Value::t())
|
||||
}
|
||||
/// `=` and `/=`: numbers and strings compare as [`compare`] does, and any other atom compares by
|
||||
/// what it is, as `eq` does. AutoLISP answers `(= (type x) 'LIST)` -- the idiom for a type test,
|
||||
/// since `type` returns a symbol -- where coercing that symbol to a number failed the whole
|
||||
/// routine with "bad argument type" instead (#1597). A mismatch is `nil`, not an error, so
|
||||
/// `(= 1 "1")` and `(= '(1) '(1))` answer too. The ordering comparisons stay numbers and strings
|
||||
/// only, as AutoLISP has them.
|
||||
fn eq_chain(a: &[Value], want_equal: bool) -> R {
|
||||
arity(a, 1, usize::MAX)?;
|
||||
for w in a.windows(2) {
|
||||
let [x, y] = w else { continue };
|
||||
let same = match (x, y) {
|
||||
(Value::Str(_), Value::Str(_)) => compare(x, y)?.is_eq(),
|
||||
_ if x.as_number().is_some() && y.as_number().is_some() => compare(x, y)?.is_eq(),
|
||||
_ => eq(x, y),
|
||||
};
|
||||
if same != want_equal {
|
||||
return Ok(Value::Nil);
|
||||
}
|
||||
}
|
||||
Ok(Value::t())
|
||||
}
|
||||
fn num_eq(a: &[Value]) -> R {
|
||||
chain(a, |o| o.is_eq())
|
||||
eq_chain(a, true)
|
||||
}
|
||||
fn num_ne(a: &[Value]) -> R {
|
||||
chain(a, |o| o.is_ne())
|
||||
eq_chain(a, false)
|
||||
}
|
||||
fn lt(a: &[Value]) -> R {
|
||||
chain(a, |o| o.is_lt())
|
||||
|
||||
@@ -70,6 +70,39 @@ fn arithmetic_follows_autolisp() {
|
||||
assert_eq!(eval(&mut s, "(+ 1 nil)"), Err("bad argument type: numberp: nil".into()));
|
||||
}
|
||||
|
||||
/// `=` answers for the symbols `type` returns, which is how AutoLISP routines test a type:
|
||||
/// `(= (type x) 'INT)`. Coercing the symbol to a number failed the whole routine with "bad
|
||||
/// argument type" instead, so a routine written for AutoLISP stopped at its first type test
|
||||
/// (#1597). The ordering comparisons are still numbers and strings, as AutoLISP has them.
|
||||
#[test]
|
||||
fn equality_compares_symbols_as_autolisp_does() {
|
||||
let mut s = session();
|
||||
assert_eq!(ok(&mut s, "(= (type 1) 'INT)"), "T");
|
||||
assert_eq!(ok(&mut s, "(= (type 1.0) 'INT)"), "nil");
|
||||
assert_eq!(ok(&mut s, "(= (type '(1 2)) 'LIST)"), "T");
|
||||
assert_eq!(ok(&mut s, "(= (type \"a\") 'STR)"), "T");
|
||||
assert_eq!(ok(&mut s, "(/= (type 1) 'REAL)"), "T");
|
||||
assert_eq!(ok(&mut s, "(= 'a 'a 'a)"), "T");
|
||||
assert_eq!(ok(&mut s, "(= 'a 'b)"), "nil");
|
||||
|
||||
// The routine from the report runs.
|
||||
ok(&mut s, "(defun isint (a) (if (= (type a) 'INT) T nil))");
|
||||
assert_eq!(ok(&mut s, "(isint 3)"), "T");
|
||||
assert_eq!(ok(&mut s, "(isint 3.0)"), "nil");
|
||||
assert_eq!(ok(&mut s, "(isint \"3\")"), "nil");
|
||||
|
||||
// A mismatch of kinds is nil, not an error; numbers and strings are unchanged.
|
||||
assert_eq!(ok(&mut s, "(= 1 \"1\")"), "nil");
|
||||
assert_eq!(ok(&mut s, "(= '(1) '(1))"), "nil", "`=` is not `equal`");
|
||||
assert_eq!(ok(&mut s, "(= nil nil)"), "T");
|
||||
assert_eq!(ok(&mut s, "(= 1 1.0)"), "T");
|
||||
assert_eq!(ok(&mut s, "(= \"a\" \"a\" \"a\")"), "T");
|
||||
assert_eq!(ok(&mut s, "(= 1 1 2)"), "nil");
|
||||
|
||||
// Ordering still refuses what AutoLISP refuses.
|
||||
assert_eq!(eval(&mut s, "(< 'a 'b)"), Err("bad argument type: numberp: A".into()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lists_and_strings() {
|
||||
let mut s = session();
|
||||
|
||||
Reference in New Issue
Block a user