use std::{
collections::HashMap,
fmt::{Debug, Display, Formatter},
mem,
sync::Arc,
};
use crate::{LinkError, NamedRef, RangeList, Type};
#[derive(Clone, PartialEq, Debug)]
pub(crate) enum Annotation<Identifier> {
Atom(Identifier),
Call(AnnotationCall<Identifier>),
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) enum AnnotationArgument<Identifier> {
Array(Vec<AnnotationLiteral<Identifier>>),
Literal(AnnotationLiteral<Identifier>),
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct AnnotationCall<Identifier> {
pub(crate) id: Identifier,
pub(crate) args: Vec<AnnotationArgument<Identifier>>,
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) enum AnnotationLiteral<Identifier> {
Int(i64),
Float(f64),
Reference(NameId),
Bool(bool),
IntSet(RangeList<i64>),
FloatSet(RangeList<f64>),
String(String),
Annotation(AnnotationCall<Identifier>),
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) enum Argument {
Array(Vec<Literal>),
Literal(Literal),
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct Array<Identifier> {
pub(crate) contents: Vec<Literal>,
pub(crate) ann: Vec<Annotation<Identifier>>,
pub(crate) defined: bool,
pub(crate) introduced: bool,
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct Constraint<Identifier> {
pub(crate) id: Identifier,
pub(crate) args: Vec<Argument>,
pub(crate) defines: Option<NameId>,
pub(crate) ann: Vec<Annotation<Identifier>>,
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) enum Declaration<Identifier> {
Uninit,
Variable(Variable<Identifier>),
Array(Array<Identifier>),
}
#[derive(PartialEq, Debug)]
pub(crate) struct FlatZinc<Identifier> {
pub(crate) names: NameStore<Identifier>,
pub(crate) constraints: Vec<Constraint<Identifier>>,
pub(crate) output: Vec<NameId>,
pub(crate) solve: SolveObjective<Identifier>,
pub(crate) version: String,
}
struct Linker<Identifier, F> {
names: NameStore<Identifier>,
interner: F,
constraints: Vec<Constraint<Identifier>>,
output: Vec<NameId>,
solve: SolveObjective<Identifier>,
version: String,
resolved: Vec<Option<crate::Argument<Identifier>>>,
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) enum Literal {
Int(i64),
Float(f64),
Reference(NameId),
Bool(bool),
IntSet(RangeList<i64>),
FloatSet(RangeList<f64>),
String(String),
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) enum Method {
Satisfy,
Minimize(Literal),
Maximize(Literal),
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct NameEntry<Identifier> {
pub(crate) name: Box<str>,
pub(crate) declaration: Declaration<Identifier>,
}
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub(crate) struct NameId(usize);
#[derive(Debug)]
pub(crate) struct NameStore<Identifier> {
entries: Vec<NameEntry<Identifier>>,
lookup: HashMap<&'static str, NameId>,
}
pub(crate) struct ParserState<Identifier, F> {
pub(crate) names: NameStore<Identifier>,
pub(crate) interner: F,
#[cfg(feature = "fzn")]
pub(crate) identifier_error: Option<LinkError>,
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct SolveObjective<Identifier> {
pub(crate) method: Method,
pub(crate) ann: Vec<Annotation<Identifier>>,
}
#[derive(Clone, PartialEq, Debug)]
pub(crate) struct Variable<Identifier> {
pub(crate) ty: Type,
pub(crate) ann: Vec<Annotation<Identifier>>,
pub(crate) defined: bool,
pub(crate) introduced: bool,
}
impl<Identifier> From<Literal> for AnnotationLiteral<Identifier> {
fn from(value: Literal) -> Self {
match value {
Literal::Int(i) => AnnotationLiteral::Int(i),
Literal::Float(f) => AnnotationLiteral::Float(f),
Literal::Reference(name) => AnnotationLiteral::Reference(name),
Literal::Bool(b) => AnnotationLiteral::Bool(b),
Literal::IntSet(ranges) => AnnotationLiteral::IntSet(ranges),
Literal::FloatSet(ranges) => AnnotationLiteral::FloatSet(ranges),
Literal::String(string) => AnnotationLiteral::String(string),
}
}
}
impl<Identifier, F, E> Linker<Identifier, F>
where
Identifier: Clone,
F: FnMut(&str) -> Result<Identifier, E>,
E: Display,
{
fn create_array(
&mut self,
name: Box<str>,
array: Array<Identifier>,
) -> Result<crate::Argument<Identifier>, LinkError> {
let array = Arc::new(crate::Array {
name: name.into_string(),
contents: self.link_literals(array.contents)?,
ann: self.link_annotations(array.ann)?,
defined: array.defined,
introduced: array.introduced,
});
Ok(crate::Argument::ArrayNamed(array))
}
fn create_variable(
&mut self,
name: Box<str>,
variable: Variable<Identifier>,
) -> Result<crate::Literal<Identifier>, LinkError> {
Ok(crate::Literal::Variable(Arc::new(crate::Variable {
name: name.into_string(),
ty: variable.ty,
ann: variable
.ann
.into_iter()
.map(|a| self.link_annotation(a))
.collect::<Result<Vec<_>, _>>()?,
defined: variable.defined,
introduced: variable.introduced,
})))
}
fn link(mut self) -> Result<crate::FlatZinc<Identifier>, LinkError> {
let mut var_names = Vec::new();
let mut arr_names = Vec::new();
for (name, entry) in self.names.iter() {
match &entry.declaration {
Declaration::Variable(_) => var_names.push(name),
Declaration::Array(_) => arr_names.push(name),
_ => {}
}
}
let variables = var_names
.into_iter()
.filter_map(|name| {
self.link_name(name).transpose().map(|r| {
r.map(|r| {
let NamedRef::Variable(v) = r else {
unreachable!()
};
v
})
})
})
.collect::<Result<Vec<_>, _>>()?;
let arrays = arr_names
.into_iter()
.filter_map(|name| {
self.link_name(name).transpose().map(|r| {
r.map(|r| {
let NamedRef::Array(v) = r else {
unreachable!()
};
v
})
})
})
.collect::<Result<Vec<_>, _>>()?;
let constraints = mem::take(&mut self.constraints);
let constraints = constraints
.into_iter()
.map(|constraint| self.link_constraint(constraint))
.collect::<Result<Vec<_>, _>>()?;
let output = mem::take(&mut self.output);
let output = output
.into_iter()
.flat_map(|name| self.link_name(name).transpose())
.collect::<Result<Vec<_>, _>>()?;
let ann = mem::take(&mut self.solve.ann);
let solve = crate::SolveObjective {
method: match self.solve.method.clone() {
Method::Satisfy => crate::Method::Satisfy,
Method::Minimize(lit) => crate::Method::Minimize(self.link_literal(lit)?),
Method::Maximize(lit) => crate::Method::Maximize(self.link_literal(lit)?),
},
ann: self.link_annotations(ann)?,
};
Ok(crate::FlatZinc {
variables,
arrays,
constraints,
output,
solve,
version: self.version,
})
}
fn link_annotation(
&mut self,
annotation: Annotation<Identifier>,
) -> Result<crate::Annotation<Identifier>, LinkError> {
match annotation {
Annotation::Atom(id) => Ok(crate::Annotation::Atom(id)),
Annotation::Call(call) => Ok(crate::Annotation::Call(self.link_annotation_call(call)?)),
}
}
fn link_annotation_argument(
&mut self,
argument: AnnotationArgument<Identifier>,
) -> Result<crate::AnnotationArgument<Identifier>, LinkError> {
match argument {
AnnotationArgument::Array(values) => Ok(crate::AnnotationArgument::Array(
values
.into_iter()
.map(|value| self.link_annotation_literal(value))
.collect::<Result<Vec<_>, _>>()?,
)),
AnnotationArgument::Literal(AnnotationLiteral::Reference(name)) => {
match self.link_argument(Argument::Literal(Literal::Reference(name))) {
Ok(arg) => Ok(arg.into()),
Err(LinkError::UnknownReference(_)) => {
debug_assert!(matches!(
self.names.entries[name.index()].declaration,
Declaration::Uninit
));
let ident = (self.interner)(self.names.entries[name.index()].name.as_ref())
.map_err(|err| LinkError::IdentifierError {
ident: self.names.to_owned(name),
err: err.to_string(),
})?;
Ok(crate::AnnotationArgument::Literal(
crate::AnnotationLiteral::Annotation(crate::Annotation::Atom(ident)),
))
}
Err(err) => Err(err),
}
}
AnnotationArgument::Literal(value) => Ok(crate::AnnotationArgument::Literal(
self.link_annotation_literal(value)?,
)),
}
}
fn link_annotation_call(
&mut self,
call: AnnotationCall<Identifier>,
) -> Result<crate::AnnotationCall<Identifier>, LinkError> {
Ok(crate::AnnotationCall {
id: call.id,
args: call
.args
.into_iter()
.map(|arg| self.link_annotation_argument(arg))
.collect::<Result<Vec<_>, _>>()?,
})
}
fn link_annotation_literal(
&mut self,
literal: AnnotationLiteral<Identifier>,
) -> Result<crate::AnnotationLiteral<Identifier>, LinkError> {
Ok(match literal {
AnnotationLiteral::Int(i) => crate::AnnotationLiteral::Int(i),
AnnotationLiteral::Float(f) => crate::AnnotationLiteral::Float(f),
AnnotationLiteral::Reference(name) => {
match self.link_literal(Literal::Reference(name)) {
Ok(lit) => lit.into(),
Err(LinkError::UnknownReference(_)) => {
debug_assert!(matches!(
self.names.entries[name.index()].declaration,
Declaration::Uninit
));
let ident = (self.interner)(self.names.entries[name.index()].name.as_ref())
.map_err(|err| LinkError::IdentifierError {
ident: self.names.to_owned(name),
err: err.to_string(),
})?;
crate::AnnotationLiteral::Annotation(crate::Annotation::Atom(ident))
}
Err(err) => return Err(err),
}
}
AnnotationLiteral::Bool(b) => crate::AnnotationLiteral::Bool(b),
AnnotationLiteral::IntSet(ranges) => crate::AnnotationLiteral::IntSet(ranges),
AnnotationLiteral::FloatSet(ranges) => crate::AnnotationLiteral::FloatSet(ranges),
AnnotationLiteral::String(string) => crate::AnnotationLiteral::String(string),
AnnotationLiteral::Annotation(annotation) => crate::AnnotationLiteral::Annotation(
crate::Annotation::Call(self.link_annotation_call(annotation)?),
),
})
}
fn link_annotations(
&mut self,
annotations: Vec<Annotation<Identifier>>,
) -> Result<Vec<crate::Annotation<Identifier>>, LinkError> {
annotations
.into_iter()
.map(|annotation| self.link_annotation(annotation))
.collect()
}
fn link_argument(&mut self, arg: Argument) -> Result<crate::Argument<Identifier>, LinkError> {
Ok(match arg {
Argument::Array(lits) => crate::Argument::Array(
lits.into_iter()
.map(|lit| self.link_literal(lit))
.collect::<Result<Vec<_>, _>>()?,
),
Argument::Literal(Literal::Reference(name)) => self.resolve_name(name)?,
Argument::Literal(lit) => crate::Argument::Literal(self.link_literal(lit)?),
})
}
fn link_constraint(
&mut self,
constraint: Constraint<Identifier>,
) -> Result<crate::Constraint<Identifier>, LinkError> {
let defines = if let Some(name) = constraint.defines {
self.link_name(name)?
} else {
None
};
Ok(crate::Constraint {
id: constraint.id,
args: constraint
.args
.into_iter()
.map(|arg| self.link_argument(arg))
.collect::<Result<Vec<_>, _>>()?,
defines,
ann: self.link_annotations(constraint.ann)?,
})
}
fn link_literal(&mut self, literal: Literal) -> Result<crate::Literal<Identifier>, LinkError> {
Ok(match literal {
Literal::Int(i) => crate::Literal::Int(i),
Literal::Float(f) => crate::Literal::Float(f),
Literal::Reference(name) => self.resolve_literal_name(name)?,
Literal::Bool(b) => crate::Literal::Bool(b),
Literal::IntSet(ranges) => crate::Literal::IntSet(ranges),
Literal::FloatSet(ranges) => crate::Literal::FloatSet(ranges),
Literal::String(string) => crate::Literal::String(string),
})
}
fn link_literals(
&mut self,
literals: Vec<Literal>,
) -> Result<Vec<crate::Literal<Identifier>>, LinkError> {
literals
.into_iter()
.map(|literal| self.link_literal(literal))
.collect()
}
fn link_name(&mut self, name: NameId) -> Result<Option<NamedRef<Identifier>>, LinkError> {
Ok(match self.resolve_name(name)? {
crate::Argument::Literal(crate::Literal::Variable(var)) => {
Some(NamedRef::Variable(var))
}
crate::Argument::ArrayNamed(arr) => Some(NamedRef::Array(arr)),
crate::Argument::Literal(_) | crate::Argument::Array(_) => None,
})
}
fn new(model: FlatZinc<Identifier>, interner: F) -> Self {
let names_len = model.names.len();
Self {
names: model.names,
interner,
constraints: model.constraints,
output: model.output,
solve: model.solve,
version: model.version,
resolved: vec![None; names_len],
}
}
fn resolve_literal_name(
&mut self,
name: NameId,
) -> Result<crate::Literal<Identifier>, LinkError> {
match self.resolve_name(name)? {
crate::Argument::Literal(lit) => Ok(lit),
crate::Argument::ArrayNamed(_) => {
Err(LinkError::NestedArray(self.names.to_owned(name)))
}
crate::Argument::Array(_) => unreachable!("resolved names never produce inline arrays"),
}
}
fn resolve_name(&mut self, name: NameId) -> Result<crate::Argument<Identifier>, LinkError> {
if let Some(arg) = &self.resolved[name.index()] {
return Ok(arg.clone());
}
let entry = self.names.extract_entry(name);
let arg = match entry.declaration {
Declaration::Variable(variable) => {
crate::Argument::Literal(self.create_variable(entry.name, variable)?)
}
Declaration::Array(array) => self.create_array(entry.name, array)?,
Declaration::Uninit => {
self.names.entries[name.index()] = entry;
return Err(LinkError::UnknownReference(self.names.to_owned(name)));
}
};
self.resolved[name.index()] = Some(arg.clone());
Ok(arg)
}
}
impl NameId {
pub(crate) fn index(self) -> usize {
self.0
}
pub(crate) fn new(index: usize) -> Self {
Self(index)
}
}
impl<Identifier> NameStore<Identifier> {
fn define_name(&mut self, id: NameId, decl: Declaration<Identifier>) -> Result<(), LinkError> {
match self.entries[id.index()].declaration {
Declaration::Uninit => {
self.entries[id.index()].declaration = decl;
Ok(())
}
_ => Err(LinkError::DuplicateDefinition(self.to_owned(id))),
}
}
pub(crate) fn extract_entry(&mut self, id: NameId) -> NameEntry<Identifier> {
mem::replace(
&mut self.entries[id.index()],
NameEntry {
name: Box::default(),
declaration: Declaration::Uninit,
},
)
}
pub(crate) fn intern(&mut self, name: &str) -> NameId {
if let Some(&id) = self.lookup.get(name) {
return id;
}
let id = NameId::new(self.entries.len());
self.entries.push(NameEntry {
name: name.into(),
declaration: Declaration::Uninit,
});
let key =
unsafe { mem::transmute::<&str, &'static str>(self.entries[id.index()].name.as_ref()) };
let _ = self.lookup.insert(key, id);
id
}
pub(crate) fn iter(&self) -> impl Iterator<Item = (NameId, &NameEntry<Identifier>)> {
self.entries
.iter()
.enumerate()
.map(|(index, entry)| (NameId::new(index), entry))
}
pub(crate) fn len(&self) -> usize {
self.entries.len()
}
pub(crate) fn resolve(&self, id: NameId) -> &str {
&self.entries[id.index()].name
}
pub(crate) fn to_owned(&self, id: NameId) -> String {
self.resolve(id).to_owned()
}
}
impl<Identifier> Default for NameStore<Identifier> {
fn default() -> Self {
Self {
entries: Vec::new(),
lookup: HashMap::new(),
}
}
}
impl<Identifier: PartialEq> PartialEq for NameStore<Identifier> {
fn eq(&self, other: &Self) -> bool {
self.entries == other.entries
}
}
impl<Identifier, F> ParserState<Identifier, F> {
#[cfg(feature = "serde")]
pub(crate) fn define_array(
&mut self,
name: NameId,
array: Array<Identifier>,
) -> Result<(), LinkError> {
self.define_name(name, Declaration::Array(array))
}
pub(crate) fn define_name(
&mut self,
name: NameId,
decl: Declaration<Identifier>,
) -> Result<(), LinkError> {
self.names.define_name(name, decl)
}
#[cfg(feature = "serde")]
pub(crate) fn define_variable(
&mut self,
name: NameId,
variable: Variable<Identifier>,
) -> Result<(), LinkError> {
self.define_name(name, Declaration::Variable(variable))
}
#[cfg(feature = "fzn")]
pub(crate) fn intern_name(&mut self, name: &str) -> NameId {
self.names.intern(name)
}
pub(crate) fn into_parts(self) -> (NameStore<Identifier>, F) {
(self.names, self.interner)
}
pub(crate) fn new(interner: F) -> Self {
Self {
names: NameStore::default(),
interner,
#[cfg(feature = "fzn")]
identifier_error: None,
}
}
#[cfg(feature = "fzn")]
pub(crate) fn record_identifier_error(&mut self, ident: &str, err: String) {
self.identifier_error = Some(LinkError::IdentifierError {
ident: ident.to_owned(),
err,
});
}
#[cfg(feature = "fzn")]
pub(crate) fn take_identifier_error(&mut self) -> Option<LinkError> {
self.identifier_error.take()
}
}
impl<Identifier, F> ParserState<Identifier, F>
where
Identifier: Clone,
{
pub(crate) fn intern_identifier<E>(&mut self, ident: &str) -> Result<Identifier, String>
where
F: FnMut(&str) -> Result<Identifier, E>,
E: Display,
{
(self.interner)(ident)
.map_err(|err| format!("failed to intern identifier `{ident}`: {err}"))
}
}
impl<Identifier, F> Debug for ParserState<Identifier, F> {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ParserState").finish_non_exhaustive()
}
}
impl<Identifier: Clone> crate::FlatZinc<Identifier> {
pub(crate) fn from_intermediate<F, E>(
model: FlatZinc<Identifier>,
interner: F,
) -> Result<Self, LinkError>
where
F: FnMut(&str) -> Result<Identifier, E>,
E: Display,
{
Linker::new(model, interner).link()
}
}