use std::cmp::Ordering;
use std::ffi::{CStr, CString};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::os::raw::c_char;
use std::ptr;
use crate::error::{ClingoError, Error, check};
use crate::fun::{Fun, SymbolicArgs};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum SymbolValue {
Infimum,
Number(i32),
String(&'static str),
Function {
name: &'static str,
arguments: Vec<Symbol>,
positive: bool,
},
Supremum,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SymbolType {
Infimum,
Number,
String,
Function,
Supremum,
}
#[derive(Clone, Copy)]
pub struct Symbol(pub(crate) clingo_sys::clingo_symbol_t);
impl Symbol {
pub fn raw(self) -> clingo_sys::clingo_symbol_t {
self.0
}
pub unsafe fn from_raw(raw: clingo_sys::clingo_symbol_t) -> Self {
Symbol(raw)
}
pub fn number(n: i32) -> Self {
let mut sym: clingo_sys::clingo_symbol_t = 0;
unsafe { clingo_sys::clingo_symbol_create_number(n, &mut sym) };
Symbol(sym)
}
pub fn infimum() -> Self {
let mut sym: clingo_sys::clingo_symbol_t = 0;
unsafe { clingo_sys::clingo_symbol_create_infimum(&mut sym) };
Symbol(sym)
}
pub fn supremum() -> Self {
let mut sym: clingo_sys::clingo_symbol_t = 0;
unsafe { clingo_sys::clingo_symbol_create_supremum(&mut sym) };
Symbol(sym)
}
pub fn string(s: &str) -> Result<Self, Error> {
let c_str = CString::new(s)?;
let mut sym: clingo_sys::clingo_symbol_t = 0;
check(unsafe { clingo_sys::clingo_symbol_create_string(c_str.as_ptr(), &mut sym) })?;
Ok(Symbol(sym))
}
pub fn id(name: &str, positive: bool) -> Result<Self, Error> {
let c_name = CString::new(name)?;
let mut sym: clingo_sys::clingo_symbol_t = 0;
check(unsafe { clingo_sys::clingo_symbol_create_id(c_name.as_ptr(), positive, &mut sym) })?;
Ok(Symbol(sym))
}
pub fn function(name: &str, args: &[Symbol], positive: bool) -> Result<Self, Error> {
let c_name = CString::new(name)?;
let mut sym: clingo_sys::clingo_symbol_t = 0;
let raw_args: Vec<clingo_sys::clingo_symbol_t> = args.iter().map(|s| s.0).collect();
check(unsafe {
clingo_sys::clingo_symbol_create_function(
c_name.as_ptr(),
raw_args.as_ptr(),
raw_args.len(),
positive,
&mut sym,
)
})?;
Ok(Symbol(sym))
}
pub fn parse(s: &str) -> Result<Self, Error> {
let c_str = CString::new(s)?;
let mut sym: clingo_sys::clingo_symbol_t = 0;
check(unsafe {
clingo_sys::clingo_parse_term(c_str.as_ptr(), None, ptr::null_mut(), 20, &mut sym)
})?;
Ok(Symbol(sym))
}
pub fn symbol_type(self) -> SymbolType {
let t = unsafe { clingo_sys::clingo_symbol_type(self.0) };
match t as u32 {
clingo_sys::clingo_symbol_type_e_clingo_symbol_type_infimum => SymbolType::Infimum,
clingo_sys::clingo_symbol_type_e_clingo_symbol_type_number => SymbolType::Number,
clingo_sys::clingo_symbol_type_e_clingo_symbol_type_string => SymbolType::String,
clingo_sys::clingo_symbol_type_e_clingo_symbol_type_function => SymbolType::Function,
clingo_sys::clingo_symbol_type_e_clingo_symbol_type_supremum => SymbolType::Supremum,
_ => unreachable!("unknown clingo symbol type: {}", t),
}
}
pub fn as_number(self) -> Option<i32> {
let mut n: i32 = 0;
if unsafe { clingo_sys::clingo_symbol_number(self.0, &mut n) } {
Some(n)
} else {
None
}
}
pub fn name(self) -> Option<&'static str> {
let mut ptr: *const c_char = ptr::null();
if unsafe { clingo_sys::clingo_symbol_name(self.0, &mut ptr) } {
Some(
unsafe { CStr::from_ptr(ptr) }
.to_str()
.expect("clingo symbol name not UTF-8"),
)
} else {
None
}
}
pub fn as_string(self) -> Option<&'static str> {
let mut ptr: *const c_char = ptr::null();
if unsafe { clingo_sys::clingo_symbol_string(self.0, &mut ptr) } {
Some(
unsafe { CStr::from_ptr(ptr) }
.to_str()
.expect("clingo symbol string not UTF-8"),
)
} else {
None
}
}
pub fn arguments(self) -> Option<Vec<Symbol>> {
unsafe { self.gen_args(|raw| raw.iter().map(|&s| Symbol(s)).collect()) }
}
pub fn arity(self) -> Option<usize> {
unsafe { self.gen_args(|raw| raw.len()) }
}
unsafe fn gen_args<T>(self, f: impl Fn(&[u64]) -> T) -> Option<T> {
let mut ptr: *const clingo_sys::clingo_symbol_t = ptr::null();
let mut len: usize = 0;
if unsafe { clingo_sys::clingo_symbol_arguments(self.0, &mut ptr, &mut len) } {
if len == 0 {
return Some(f(&[]));
}
Some(f(unsafe { std::slice::from_raw_parts(ptr, len) }))
} else {
None
}
}
pub fn is_positive(self) -> Option<bool> {
let mut positive = false;
if unsafe { clingo_sys::clingo_symbol_is_positive(self.0, &mut positive) } {
Some(positive)
} else {
None
}
}
pub fn value(self) -> SymbolValue {
match self.symbol_type() {
SymbolType::Infimum => SymbolValue::Infimum,
SymbolType::Supremum => SymbolValue::Supremum,
SymbolType::Number => SymbolValue::Number(self.as_number().unwrap()),
SymbolType::String => SymbolValue::String(self.as_string().unwrap()),
SymbolType::Function => SymbolValue::Function {
name: self.name().unwrap(),
arguments: self.arguments().unwrap(),
positive: self.is_positive().unwrap(),
},
}
}
pub fn as_fun<Args: SymbolicArgs>(self) -> Option<Fun<Args>> {
if self.symbol_type() != SymbolType::Function || self.is_positive() != Some(true) {
return None;
}
let args = Args::from_symbols(self.arguments()?)?;
Some(Fun(self.name().unwrap(), args))
}
pub fn to_string_lossy(self) -> Result<String, ClingoError> {
let mut size: usize = 0;
check(unsafe { clingo_sys::clingo_symbol_to_string_size(self.0, &mut size) })?;
let mut buf = vec![0u8; size];
check(unsafe {
clingo_sys::clingo_symbol_to_string(self.0, buf.as_mut_ptr() as *mut c_char, size)
})?;
buf.pop(); Ok(String::from_utf8_lossy(&buf).into_owned())
}
}
impl PartialEq for Symbol {
fn eq(&self, other: &Self) -> bool {
unsafe { clingo_sys::clingo_symbol_is_equal_to(self.0, other.0) }
}
}
impl Eq for Symbol {}
impl PartialOrd for Symbol {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Symbol {
fn cmp(&self, other: &Self) -> Ordering {
if self == other {
Ordering::Equal
} else if unsafe { clingo_sys::clingo_symbol_is_less_than(self.0, other.0) } {
Ordering::Less
} else {
Ordering::Greater
}
}
}
impl Hash for Symbol {
fn hash<H: Hasher>(&self, state: &mut H) {
let h = unsafe { clingo_sys::clingo_symbol_hash(self.0) };
state.write_usize(h);
}
}
impl fmt::Debug for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Symbol({})", self.to_string_lossy().unwrap())
}
}
impl fmt::Display for Symbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.to_string_lossy().unwrap())
}
}