Merge branch 'mthom:master' into patch-5

This commit is contained in:
Dan Rose
2025-07-08 18:03:16 -05:00
committed by GitHub
10 changed files with 204 additions and 255 deletions

View File

@@ -24,7 +24,6 @@ hostname = ["dep:hostname"]
tls = ["dep:native-tls"]
http = ["dep:warp", "dep:reqwest"]
crypto-full = []
"rust-version-1.80" = []
[build-dependencies]
indexmap = "2.3.0"

View File

@@ -5088,11 +5088,21 @@ run_propagator(pmod(X,Y,Z), MState) -->
; nonvar(Z), nonvar(X) ->
( Z > 0 ->
( X < 0 -> true
; X >= Z
; X >= Z,
% due to X = Z+Y*_ and Y > Z
( X-Z > 0 ->
X-Z > Z
; true
)
)
; Z < 0 ->
( X > 0 -> true
; X =< Z
; X =< Z,
% due to X = Z+Y*_ and Y < Z
( X-Z < 0 ->
X-Z < Z
; true
)
)
; Z =:= 0 % Multiple solutions so do nothing special.
),
@@ -5100,7 +5110,23 @@ run_propagator(pmod(X,Y,Z), MState) -->
YU < X, X =< 0 } -> kill(MState), Z =:= X
; { fd_get(Y, _, n(YL), _, _),
YL > X, X >= 0 } -> kill(MState), Z =:= X
; ( Z > 0 ->
; ( Z > 0, X < 0 ->
{ fd_get(Y, YD, YPs),
YMin is Z+1,
YMax is Z-X,
domain_remove_smaller_than(YD, YMin, YD1),
domain_remove_greater_than(YD1, YMax, YD2) },
fd_put(Y, YD2, YPs)
% queue_goal((Y #> Z, Y #=< Z-X))
; Z < 0, X > 0 ->
{ fd_get(Y, YD, YPs),
YMax is Z-1,
YMin is Z-X,
domain_remove_greater_than(YD, YMax, YD1),
domain_remove_smaller_than(YD1, YMin, YD2) },
fd_put(Y, YD2, YPs)
% queue_goal((Y #< Z, Y #>= Z-X))
; Z > 0 ->
{ fd_get(Y, YD, YPs),
YMin is Z + 1,
domain_remove_smaller_than(YD, YMin, YD1) },
@@ -5112,7 +5138,16 @@ run_propagator(pmod(X,Y,Z), MState) -->
domain_remove_greater_than(YD, YMax, YD1) },
fd_put(Y, YD1, YPs)
% queue_goal(Y #< Z)
; true
; Z =:= 0,
( X =:= 0 ->
kill(MState) % trivial
; % only 4 solutions {-abs(X),-1,1,abs(X)}
{ YL is -abs(X), YU is abs(X),
fd_get(Y, YD0, YPs),
domain_remove_smaller_than(YD0, YL, YD1),
domain_remove_greater_than(YD1, YU, YD) },
fd_put(Y, YD, YPs)
)
)
)
; run_propagator(pmodz(X,Y,Z), MState),

View File

@@ -12,8 +12,8 @@ to learn more about them.
[op(1105, xfy, '|'),
phrase/2,
phrase/3,
phrase/4,
phrase/5,
phrase//2,
phrase//3,
seq//1,
seqq//1,
... //0,
@@ -29,9 +29,9 @@ to learn more about them.
:- meta_predicate phrase(2, ?, ?).
:- meta_predicate phrase(2, ?, ?, ?).
:- meta_predicate(phrase(3, ?, ?, ?)).
:- meta_predicate phrase(2, ?, ?, ?, ?).
:- meta_predicate(phrase(4, ?, ?, ?, ?)).
:- meta_predicate(','(2, 2, ?, ?)).

View File

@@ -26,7 +26,6 @@
:- op(200, xfy, ^).
:- op(500, yfx, /\).
:- op(500, yfx, \/).
:- op(500, yfx, xor).
:- op(400, yfx, div).
:- op(400, yfx, //).
:- op(400, yfx, rdiv).

View File

@@ -118,32 +118,6 @@ fn current_dir() -> PathBuf {
}
}
#[cfg(not(feature = "rust-version-1.80"))]
mod libraries {
use indexmap::IndexMap;
use std::sync::OnceLock;
fn libraries() -> &'static IndexMap<&'static str, &'static str> {
static LIBRARIES: OnceLock<IndexMap<&'static str, &'static str>> = OnceLock::new();
LIBRARIES.get_or_init(|| {
let mut m = IndexMap::new();
include!(concat!(env!("OUT_DIR"), "/libraries.rs"));
m
})
}
pub(crate) fn contains(name: &str) -> bool {
libraries().contains_key(name)
}
pub(crate) fn get(name: &str) -> Option<&'static str> {
libraries().get(name).copied()
}
}
#[cfg(feature = "rust-version-1.80")]
mod libraries {
use indexmap::IndexMap;
use std::sync::LazyLock;

View File

@@ -911,27 +911,16 @@ impl MachineState {
use crate::parser::lexer::*;
let nx = self.store(self.deref(self.registers[2]));
let add_dot = !string.ends_with('.');
let cursor = std::io::Cursor::new(string);
let iter = std::io::Read::chain(cursor, {
let mut dot_buf: [u8; '.'.len_utf8()] = [0u8];
if add_dot {
'.'.encode_utf8(&mut dot_buf);
}
std::io::Cursor::new(dot_buf)
});
let iter = std::io::Cursor::new(string);
let mut lexer = Lexer::new(CharReader::new(iter), self);
let mut tokens = vec![];
match lexer.next_token() {
match lexer.next_number_token() {
Ok(token @ Token::Literal(Literal::Atom(atom!("-")) | Literal::Char('-'))) => {
tokens.push(token);
if let Ok(token) = lexer.next_token() {
if let Ok(token) = lexer.next_number_token() {
tokens.push(token);
}
}
@@ -979,9 +968,6 @@ impl MachineState {
break;
}
Ok('.') => {
lexer.skip_char('.');
}
Ok(c) => {
let (line_num, col_num) = (lexer.line_num, lexer.col_num);

View File

@@ -432,6 +432,7 @@ pub enum ParserError {
BackQuotedString(usize, usize),
IO(IOError),
IncompleteReduction(usize, usize),
InfiniteFloat(usize, usize),
InvalidSingleQuotedCharacter(char),
LexicalError(lexical::Error),
MissingQuote(usize, usize),
@@ -447,6 +448,7 @@ impl ParserError {
match self {
&ParserError::BackQuotedString(line_num, col_num)
| &ParserError::IncompleteReduction(line_num, col_num)
| &ParserError::InfiniteFloat(line_num, col_num)
| &ParserError::MissingQuote(line_num, col_num)
| &ParserError::NonPrologChar(line_num, col_num)
| &ParserError::ParseBigInt(line_num, col_num)
@@ -463,6 +465,9 @@ impl ParserError {
ParserError::InvalidSingleQuotedCharacter(..) => {
atom!("invalid_single_quoted_character")
}
ParserError::InfiniteFloat(..) => {
atom!("infinite_float")
}
ParserError::IO(e) if e.kind() == ErrorKind::UnexpectedEof => {
atom!("unexpected_end_of_file")
}

View File

@@ -1,3 +1,5 @@
use crate::arena::F64Ptr;
use crate::arena::TypedArenaPtr;
use crate::atom_table::*;
pub use crate::machine::machine_state::*;
use crate::parser::ast::*;
@@ -49,6 +51,55 @@ impl Token {
}
}
#[derive(Debug)]
enum Number {
BigInt(TypedArenaPtr<Integer>),
Fixnum(Fixnum),
Float(F64Ptr),
}
impl Number {
#[inline]
fn to_literal(self) -> Literal {
match self {
Number::BigInt(ibig) => Literal::Integer(ibig),
Number::Fixnum(fixnum) => Literal::Fixnum(fixnum),
Number::Float(f) => Literal::Float(f.as_offset()),
}
}
}
#[derive(Debug)]
enum NumberToken {
Number(Number),
Partial(String),
}
impl NumberToken {
#[inline]
fn to_token(self) -> Option<Token> {
match self {
NumberToken::Number(number) => Some(Token::Literal(number.to_literal())),
NumberToken::Partial(_) => None,
}
}
}
macro_rules! try_nt {
($token:expr, $e: expr) => {{
match $e {
Ok(c) => c,
Err(e) => {
return if e.is_unexpected_eof() {
Ok(NumberToken::Partial($token))
} else {
Err(e)
}
}
}
}};
}
pub struct Lexer<'a, R> {
pub(crate) reader: R,
pub(crate) machine_st: &'a mut MachineState,
@@ -443,7 +494,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
}
}
fn hexadecimal_constant(&mut self, start: char) -> Result<Token, ParserError> {
fn hexadecimal_constant(&mut self, start: char) -> Result<NumberToken, ParserError> {
self.skip_char(start);
let mut c = self.lookahead_char()?;
@@ -454,31 +505,21 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if hexadecimal_digit_char!(c) {
self.skip_char(c);
token.push(c);
c = self.lookahead_char()?;
c = try_nt!(token, self.lookahead_char());
} else {
break;
}
}
i64::from_str_radix(&token, 16)
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| {
Integer::from_str_radix(&token, 16)
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
self.parse_integer_by_radix(&token, 16)
.map(NumberToken::Number)
} else {
self.return_char(start);
Err(ParserError::ParseBigInt(self.line_num, self.col_num))
}
}
fn octal_constant(&mut self, start: char) -> Result<Token, ParserError> {
fn octal_constant(&mut self, start: char) -> Result<NumberToken, ParserError> {
self.skip_char(start);
let mut c = self.lookahead_char()?;
@@ -489,31 +530,21 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if octal_digit_char!(c) {
self.skip_char(c);
token.push(c);
c = self.lookahead_char()?;
c = try_nt!(token, self.lookahead_char());
} else {
break;
}
}
i64::from_str_radix(&token, 8)
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| {
Integer::from_str_radix(&token, 8)
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
self.parse_integer_by_radix(&token, 8)
.map(NumberToken::Number)
} else {
self.return_char(start);
Err(ParserError::ParseBigInt(self.line_num, self.col_num))
}
}
fn binary_constant(&mut self, start: char) -> Result<Token, ParserError> {
fn binary_constant(&mut self, start: char) -> Result<NumberToken, ParserError> {
self.skip_char(start);
let mut c = self.lookahead_char()?;
@@ -524,24 +555,14 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if binary_digit_char!(c) {
self.skip_char(c);
token.push(c);
c = self.lookahead_char()?;
c = try_nt!(token, self.lookahead_char());
} else {
break;
}
}
i64::from_str_radix(&token, 2)
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| {
Integer::from_str_radix(&token, 2)
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
self.parse_integer_by_radix(&token, 2)
.map(NumberToken::Number)
} else {
self.return_char(start);
Err(ParserError::ParseBigInt(self.line_num, self.col_num))
@@ -634,15 +655,10 @@ impl<'a, R: CharRead> Lexer<'a, R> {
}
}
fn vacate_with_float(&mut self, mut token: String) -> Result<Token, ParserError> {
fn vacate_with_float(&mut self, mut token: String) -> Result<Number, ParserError> {
self.return_char(token.pop().unwrap());
let n = parse_float_lossy(&token)?;
Ok(Token::Literal(Literal::from(float_alloc!(
n,
self.machine_st.arena
))))
Ok(Number::Float(float_alloc!(n, self.machine_st.arena)))
}
fn skip_underscore_in_number(&mut self) -> Result<char, ParserError> {
@@ -663,17 +679,43 @@ impl<'a, R: CharRead> Lexer<'a, R> {
}
}
pub fn number_token(&mut self, leading_c: char) -> Result<Token, ParserError> {
fn parse_integer_by_radix(
&mut self,
token: &String,
radix: u32,
) -> Result<Number, ParserError> {
i64::from_str_radix(&token, radix)
.map(|n| {
Fixnum::build_with_checked(n)
.map(Number::Fixnum)
.unwrap_or_else(|_| {
Number::BigInt(arena_alloc!(Integer::from(n), &mut self.machine_st.arena))
})
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| Number::BigInt(arena_alloc!(n, &mut self.machine_st.arena)))
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
}
#[inline]
fn parse_integer(&mut self, token: &String) -> Result<Number, ParserError> {
self.parse_integer_by_radix(token, 10)
}
fn number_token(&mut self, leading_c: char) -> Result<NumberToken, ParserError> {
let mut token = String::with_capacity(16);
self.skip_char(leading_c);
token.push(leading_c);
let mut c = self.skip_underscore_in_number()?;
let mut c = try_nt!(token, self.skip_underscore_in_number());
while decimal_digit_char!(c) {
token.push(c);
self.skip_char(c);
c = self.skip_underscore_in_number()?;
c = try_nt!(token, self.skip_underscore_in_number());
}
if decimal_point_char!(c) {
@@ -681,31 +723,17 @@ impl<'a, R: CharRead> Lexer<'a, R> {
if self.reader.peek_char().is_none() {
self.return_char('.');
token
.parse::<i64>()
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
self.parse_integer(&token).map(NumberToken::Number)
} else if decimal_digit_char!(self.lookahead_char()?) {
token.push('.');
token.push(self.read_char()?);
let mut c = self.lookahead_char()?;
let mut c = try_nt!(token, self.lookahead_char());
while decimal_digit_char!(c) {
token.push(c);
self.skip_char(c);
c = self.lookahead_char()?;
c = try_nt!(token, self.lookahead_char());
}
if exponent_char!(c) {
@@ -713,12 +741,12 @@ impl<'a, R: CharRead> Lexer<'a, R> {
token.push(c);
let c = match self.lookahead_char() {
Err(_) => return self.vacate_with_float(token),
Err(_) => return self.vacate_with_float(token).map(NumberToken::Number),
Ok(c) => c,
};
if !sign_char!(c) && !decimal_digit_char!(c) {
return self.vacate_with_float(token);
return self.vacate_with_float(token).map(NumberToken::Number);
}
if sign_char!(c) {
@@ -728,14 +756,14 @@ impl<'a, R: CharRead> Lexer<'a, R> {
let c = match self.lookahead_char() {
Err(_) => {
self.return_char(token.pop().unwrap());
return self.vacate_with_float(token);
return self.vacate_with_float(token).map(NumberToken::Number);
}
Ok(c) => c,
};
if !decimal_digit_char!(c) {
self.return_char(token.pop().unwrap());
return self.vacate_with_float(token);
return self.vacate_with_float(token).map(NumberToken::Number);
}
}
@@ -746,7 +774,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
token.push(c);
loop {
c = self.lookahead_char()?;
c = try_nt!(token, self.lookahead_char());
if decimal_digit_char!(c) {
self.skip_char(c);
@@ -757,60 +785,29 @@ impl<'a, R: CharRead> Lexer<'a, R> {
}
let n = parse_float_lossy(&token)?;
Ok(Token::Literal(Literal::from(float_alloc!(
Ok(NumberToken::Number(Number::Float(float_alloc!(
n,
self.machine_st.arena
))))
} else {
return self.vacate_with_float(token);
return self.vacate_with_float(token).map(NumberToken::Number);
}
} else {
let n = parse_float_lossy(&token)?;
Ok(Token::Literal(Literal::from(float_alloc!(
Ok(NumberToken::Number(Number::Float(float_alloc!(
n,
self.machine_st.arena
))))
}
} else {
self.return_char('.');
token
.parse::<i64>()
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
self.parse_integer(&token).map(NumberToken::Number)
}
} else if token.starts_with('0') && token.len() == 1 {
if c == 'x' {
self.hexadecimal_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e {
token
.parse::<i64>()
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
self.parse_integer(&token).map(NumberToken::Number)
} else {
Err(e)
}
@@ -818,24 +815,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
} else if c == 'o' {
self.octal_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e {
token
.parse::<i64>()
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
self.parse_integer(&token).map(NumberToken::Number)
} else {
Err(e)
}
@@ -843,24 +823,7 @@ impl<'a, R: CharRead> Lexer<'a, R> {
} else if c == 'b' {
self.binary_constant(c).or_else(|e| {
if let ParserError::ParseBigInt(..) = e {
token
.parse::<i64>()
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
self.parse_integer(&token).map(NumberToken::Number)
} else {
Err(e)
}
@@ -877,14 +840,14 @@ impl<'a, R: CharRead> Lexer<'a, R> {
self.skip_char(c);
self.return_char('\'');
return Ok(Token::Literal(Literal::Fixnum(Fixnum::build_with(0))));
return Ok(NumberToken::Number(Number::Fixnum(Fixnum::build_with(0))));
} else {
self.return_char('\\');
}
}
self.get_single_quoted_char()
.map(|c| Token::Literal(Literal::Fixnum(Fixnum::build_with(c as i64))))
.map(|c| NumberToken::Number(Number::Fixnum(Fixnum::build_with(c as i64))))
.or_else(|err| {
match err {
ParserError::UnexpectedChar('\'', ..) => {}
@@ -892,57 +855,13 @@ impl<'a, R: CharRead> Lexer<'a, R> {
}
self.return_char(c);
token
.parse::<i64>()
.map(|n| {
Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena))
})
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| {
ParserError::ParseBigInt(self.line_num, self.col_num)
})
})
self.parse_integer(&token).map(NumberToken::Number)
})
} else {
token
.parse::<i64>()
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
self.parse_integer(&token).map(NumberToken::Number)
}
} else {
token
.parse::<i64>()
.map(|n| Token::Literal(fixnum!(Literal, n, &mut self.machine_st.arena)))
.or_else(|_| {
token
.parse::<Integer>()
.map(|n| {
Token::Literal(Literal::Integer(arena_alloc!(
n,
&mut self.machine_st.arena
)))
})
.map_err(|_| ParserError::ParseBigInt(self.line_num, self.col_num))
})
self.parse_integer(&token).map(NumberToken::Number)
}
}
@@ -1001,6 +920,29 @@ impl<'a, R: CharRead> Lexer<'a, R> {
}
}
pub fn next_number_token(&mut self) -> Result<Token, ParserError> {
self.scan_for_layout()?;
let c = self.lookahead_char()?;
if !decimal_digit_char!(c) {
return self.name_token(c);
}
match self.number_token(c) {
Ok(NumberToken::Partial(token_string)) => match self.parse_integer(&token_string) {
Ok(n) => Ok(Token::Literal(n.to_literal())),
Err(_) => {
let n = parse_float_lossy(&token_string)?;
Ok(Token::Literal(Literal::Float(
float_alloc!(n, self.machine_st.arena).as_offset(),
)))
}
},
Ok(NumberToken::Number(n)) => return Ok(Token::Literal(n.to_literal())),
Err(e) => return Err(e),
}
}
pub fn next_token(&mut self) -> Result<Token, ParserError> {
let layout_inserted = self.scan_for_layout()?;
let cr = self.lookahead_char();
@@ -1053,7 +995,10 @@ impl<'a, R: CharRead> Lexer<'a, R> {
}
if decimal_digit_char!(c) {
return self.number_token(c);
return self.number_token(c).and_then(|nt| match nt.to_token() {
Some(token) => Ok(token),
None => Err(ParserError::unexpected_eof()),
});
}
if c == ']' {

View File

@@ -976,11 +976,17 @@ impl<'a, R: CharRead> Parser<'a, R> {
Token::Literal(Literal::Rational(n)) => {
self.negate_number(n, negate_rat_rc, |r, _| Literal::Rational(r))
}
Token::Literal(Literal::Float(n)) => self.negate_number(
Token::Literal(Literal::Float(n)) if F64Ptr::from_offset(n).is_infinite() => {
return Err(ParserError::InfiniteFloat(
self.lexer.line_num,
self.lexer.col_num,
));
}
Token::Literal(Literal::Float(n)) => self.negate_number(
**n.as_ptr(),
|n, _| -n,
|n, arena| Literal::from(float_alloc!(n, arena)),
),
),
Token::Literal(c) => {
let atomized = atomize_constant(&self.lexer.machine_st.atom_tbl, c);

View File

@@ -258,14 +258,14 @@ test_and_or_xor(X, Y, AndExpected, OrExpected, XorExpected) :-
Or2 is Y \/ X,
Or == OrExpected,
Or == Or2,
Xor is X xor Y,
Xor2 is Y xor X,
Xor is xor(X, Y),
Xor2 is xor(Y, X),
Xor == XorExpected,
Xor2 == Xor,
call(is, And3, X /\ Y),
call(is, Or3, X \/ Y),
call(is, Xor3, X xor Y),
call(is, Xor3, xor(X, Y)),
And3 == And,
Or3 == Or,
Xor3 == Xor.
@@ -613,7 +613,7 @@ test("and_or_xor", (
]), arithmetic_tests:test_and_or_xor(X, Y, AndExpected, OrExpected, XorExpected)),
\+ catch(_ is 1 /\ 2.0, _, false),
\+ catch(_ is 1 \/ 2.0, _, false),
\+ catch(_ is 1 xor 2.0, _, false)
\+ catch(_ is xor(1, 2.0), _, false)
)).
test("mod_rem", (