Make values hashable

This commit is contained in:
Laurenz 2022-02-17 12:50:54 +01:00
parent c7a9bac992
commit 91e45458e3
11 changed files with 219 additions and 31 deletions

View File

@ -20,7 +20,7 @@ macro_rules! array {
} }
/// An array of values with clone-on-write value semantics. /// An array of values with clone-on-write value semantics.
#[derive(Default, Clone, PartialEq)] #[derive(Default, Clone, PartialEq, Hash)]
pub struct Array(Arc<Vec<Value>>); pub struct Array(Arc<Vec<Value>>);
impl Array { impl Array {

View File

@ -1,4 +1,5 @@
use std::fmt::{self, Debug, Formatter, Write}; use std::fmt::{self, Debug, Formatter, Write};
use std::hash::{Hash, Hasher};
use super::{Args, EvalContext, Func, StyleMap, Template, Value}; use super::{Args, EvalContext, Func, StyleMap, Template, Value};
use crate::diag::TypResult; use crate::diag::TypResult;
@ -103,6 +104,13 @@ impl PartialEq for Class {
} }
} }
impl Hash for Class {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.construct as usize).hash(state);
(self.set as usize).hash(state);
}
}
/// Construct an instance of a class. /// Construct an instance of a class.
pub trait Construct { pub trait Construct {
/// Construct an instance of this class from the arguments. /// Construct an instance of this class from the arguments.

View File

@ -19,7 +19,7 @@ macro_rules! dict {
} }
/// A dictionary from strings to values with clone-on-write value semantics. /// A dictionary from strings to values with clone-on-write value semantics.
#[derive(Default, Clone, PartialEq)] #[derive(Default, Clone, PartialEq, Hash)]
pub struct Dict(Arc<BTreeMap<EcoString, Value>>); pub struct Dict(Arc<BTreeMap<EcoString, Value>>);
impl Dict { impl Dict {

View File

@ -1,4 +1,5 @@
use std::fmt::{self, Debug, Formatter, Write}; use std::fmt::{self, Debug, Formatter, Write};
use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use super::{Cast, Eval, EvalContext, Scope, Value}; use super::{Cast, Eval, EvalContext, Scope, Value};
@ -8,10 +9,11 @@ use crate::syntax::{Span, Spanned};
use crate::util::EcoString; use crate::util::EcoString;
/// An evaluatable function. /// An evaluatable function.
#[derive(Clone)] #[derive(Clone, Hash)]
pub struct Func(Arc<Repr>); pub struct Func(Arc<Repr>);
/// The different kinds of function representations. /// The different kinds of function representations.
#[derive(Hash)]
enum Repr { enum Repr {
/// A native rust function. /// A native rust function.
Native(Native), Native(Native),
@ -89,7 +91,14 @@ struct Native {
pub func: fn(&mut EvalContext, &mut Args) -> TypResult<Value>, pub func: fn(&mut EvalContext, &mut Args) -> TypResult<Value>,
} }
impl Hash for Native {
fn hash<H: Hasher>(&self, state: &mut H) {
(self.func as usize).hash(state);
}
}
/// A user-defined closure. /// A user-defined closure.
#[derive(Hash)]
pub struct Closure { pub struct Closure {
/// The name of the closure. /// The name of the closure.
pub name: Option<EcoString>, pub name: Option<EcoString>,
@ -138,7 +147,7 @@ impl Closure {
} }
/// Evaluated arguments to a function. /// Evaluated arguments to a function.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Hash)]
pub struct Args { pub struct Args {
/// The span of the whole argument list. /// The span of the whole argument list.
pub span: Span, pub span: Span,
@ -147,7 +156,7 @@ pub struct Args {
} }
/// An argument to a function call: `12` or `draw: false`. /// An argument to a function call: `12` or `draw: false`.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Hash)]
pub struct Arg { pub struct Arg {
/// The span of the whole argument. /// The span of the whole argument.
pub span: Span, pub span: Span,

View File

@ -1,6 +1,7 @@
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::HashMap; use std::collections::BTreeMap;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::hash::{Hash, Hasher};
use std::iter; use std::iter;
use std::sync::Arc; use std::sync::Arc;
@ -68,7 +69,7 @@ impl<'a> Scopes<'a> {
#[derive(Default, Clone)] #[derive(Default, Clone)]
pub struct Scope { pub struct Scope {
/// The mapping from names to slots. /// The mapping from names to slots.
values: HashMap<EcoString, Slot>, values: BTreeMap<EcoString, Slot>,
} }
impl Scope { impl Scope {
@ -126,6 +127,16 @@ impl Scope {
} }
} }
impl Hash for Scope {
fn hash<H: Hasher>(&self, state: &mut H) {
self.values.len().hash(state);
for (name, value) in self.values.iter() {
name.hash(state);
value.borrow().hash(state);
}
}
}
impl Debug for Scope { impl Debug for Scope {
fn fmt(&self, f: &mut Formatter) -> fmt::Result { fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_str("Scope ")?; f.write_str("Scope ")?;

View File

@ -1,7 +1,7 @@
use std::any::Any; use std::any::Any;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::fmt::{self, Debug, Formatter}; use std::fmt::{self, Debug, Formatter};
use std::hash::Hash; use std::hash::{Hash, Hasher};
use std::sync::Arc; use std::sync::Arc;
use super::{ops, Args, Array, Class, Dict, Func, Template}; use super::{ops, Args, Array, Class, Dict, Func, Template};
@ -173,6 +173,33 @@ impl PartialOrd for Value {
} }
} }
impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
Self::None => {}
Self::Auto => {}
Self::Bool(v) => v.hash(state),
Self::Int(v) => v.hash(state),
Self::Float(v) => v.to_bits().hash(state),
Self::Length(v) => v.hash(state),
Self::Angle(v) => v.hash(state),
Self::Relative(v) => v.hash(state),
Self::Linear(v) => v.hash(state),
Self::Fractional(v) => v.hash(state),
Self::Color(v) => v.hash(state),
Self::Str(v) => v.hash(state),
Self::Array(v) => v.hash(state),
Self::Dict(v) => v.hash(state),
Self::Template(v) => v.hash(state),
Self::Func(v) => v.hash(state),
Self::Args(v) => v.hash(state),
Self::Class(v) => v.hash(state),
Self::Dyn(v) => v.hash(state),
}
}
}
impl From<i32> for Value { impl From<i32> for Value {
fn from(v: i32) -> Self { fn from(v: i32) -> Self {
Self::Int(v as i64) Self::Int(v as i64)
@ -210,14 +237,14 @@ impl From<Dynamic> for Value {
} }
/// A dynamic value. /// A dynamic value.
#[derive(Clone)] #[derive(Clone, Hash)]
pub struct Dynamic(Arc<dyn Bounds>); pub struct Dynamic(Arc<dyn Bounds>);
impl Dynamic { impl Dynamic {
/// Create a new instance from any value that satisifies the required bounds. /// Create a new instance from any value that satisifies the required bounds.
pub fn new<T>(any: T) -> Self pub fn new<T>(any: T) -> Self
where where
T: Type + Debug + PartialEq + Sync + Send + 'static, T: Type + Debug + PartialEq + Hash + Sync + Send + 'static,
{ {
Self(Arc::new(any)) Self(Arc::new(any))
} }
@ -254,11 +281,12 @@ trait Bounds: Debug + Sync + Send + 'static {
fn as_any(&self) -> &dyn Any; fn as_any(&self) -> &dyn Any;
fn dyn_eq(&self, other: &Dynamic) -> bool; fn dyn_eq(&self, other: &Dynamic) -> bool;
fn dyn_type_name(&self) -> &'static str; fn dyn_type_name(&self) -> &'static str;
fn hash64(&self) -> u64;
} }
impl<T> Bounds for T impl<T> Bounds for T
where where
T: Type + Debug + PartialEq + Sync + Send + 'static, T: Type + Debug + PartialEq + Hash + Sync + Send + 'static,
{ {
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self self
@ -275,6 +303,21 @@ where
fn dyn_type_name(&self) -> &'static str { fn dyn_type_name(&self) -> &'static str {
T::TYPE_NAME T::TYPE_NAME
} }
fn hash64(&self) -> u64 {
// Also hash the TypeId since nodes with different types but
// equal data should be different.
let mut state = fxhash::FxHasher64::default();
self.type_id().hash(&mut state);
self.hash(&mut state);
state.finish()
}
}
impl Hash for dyn Bounds {
fn hash<H: Hasher>(&self, state: &mut H) {
state.write_u64(self.hash64());
}
} }
/// The type of a value. /// The type of a value.

View File

@ -123,7 +123,7 @@ impl Sum for Angle {
} }
} }
/// Different units of angular measurement. /// Different units of angular measurement.
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum AngularUnit { pub enum AngularUnit {
/// Radians. /// Radians.
Rad, Rad,

View File

@ -211,7 +211,7 @@ impl<'a> Sum<&'a Self> for Length {
} }
/// Different units of length measurement. /// Different units of length measurement.
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub enum LengthUnit { pub enum LengthUnit {
/// Points. /// Points.
Pt, Pt,

View File

@ -30,7 +30,7 @@ macro_rules! node {
node!{$(#[$attr])* $name: NodeKind::$variant} node!{$(#[$attr])* $name: NodeKind::$variant}
}; };
($(#[$attr:meta])* $name:ident: $variants:pat) => { ($(#[$attr:meta])* $name:ident: $variants:pat) => {
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Hash)]
#[repr(transparent)] #[repr(transparent)]
$(#[$attr])* $(#[$attr])*
pub struct $name(RedNode); pub struct $name(RedNode);
@ -138,7 +138,7 @@ impl EmphNode {
} }
/// A raw block with optional syntax highlighting: `` `...` ``. /// A raw block with optional syntax highlighting: `` `...` ``.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Hash)]
pub struct RawNode { pub struct RawNode {
/// An optional identifier specifying the language to syntax-highlight in. /// An optional identifier specifying the language to syntax-highlight in.
pub lang: Option<EcoString>, pub lang: Option<EcoString>,
@ -151,7 +151,7 @@ pub struct RawNode {
} }
/// A math formula: `$a^2 + b^2 = c^2$`. /// A math formula: `$a^2 + b^2 = c^2$`.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Hash)]
pub struct MathNode { pub struct MathNode {
/// The formula between the dollars / brackets. /// The formula between the dollars / brackets.
pub formula: EcoString, pub formula: EcoString,
@ -213,7 +213,7 @@ impl EnumNode {
} }
/// An expression. /// An expression.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Hash)]
pub enum Expr { pub enum Expr {
/// A literal: `1`, `true`, ... /// A literal: `1`, `true`, ...
Lit(Lit), Lit(Lit),
@ -504,7 +504,7 @@ impl UnaryExpr {
} }
/// A unary operator. /// A unary operator.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum UnOp { pub enum UnOp {
/// The plus operator: `+`. /// The plus operator: `+`.
Pos, Pos,
@ -573,7 +573,7 @@ impl BinaryExpr {
} }
/// A binary operator. /// A binary operator.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum BinOp { pub enum BinOp {
/// The addition operator: `+`. /// The addition operator: `+`.
Add, Add,
@ -707,7 +707,7 @@ impl BinOp {
} }
/// The associativity of a binary operator. /// The associativity of a binary operator.
#[derive(Debug, Copy, Clone, Eq, PartialEq)] #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
pub enum Associativity { pub enum Associativity {
/// Left-associative: `a + b + c` is equivalent to `(a + b) + c`. /// Left-associative: `a + b + c` is equivalent to `(a + b) + c`.
Left, Left,
@ -745,7 +745,7 @@ impl CallArgs {
} }
/// An argument to a function call. /// An argument to a function call.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Hash)]
pub enum CallArg { pub enum CallArg {
/// A positional argument: `12`. /// A positional argument: `12`.
Pos(Expr), Pos(Expr),
@ -814,7 +814,7 @@ impl ClosureExpr {
} }
/// A parameter to a closure. /// A parameter to a closure.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Hash)]
pub enum ClosureParam { pub enum ClosureParam {
/// A positional parameter: `x`. /// A positional parameter: `x`.
Pos(Ident), Pos(Ident),
@ -1059,7 +1059,7 @@ impl ImportExpr {
} }
/// The items that ought to be imported from a file. /// The items that ought to be imported from a file.
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq, Hash)]
pub enum Imports { pub enum Imports {
/// All items in the scope of the file should be imported. /// All items in the scope of the file should be imported.
Wildcard, Wildcard,

View File

@ -6,6 +6,7 @@ mod pretty;
mod span; mod span;
use std::fmt::{self, Debug, Display, Formatter}; use std::fmt::{self, Debug, Display, Formatter};
use std::hash::{Hash, Hasher};
use std::ops::Range; use std::ops::Range;
use std::sync::Arc; use std::sync::Arc;
@ -21,7 +22,7 @@ use crate::source::SourceId;
use crate::util::EcoString; use crate::util::EcoString;
/// An inner or leaf node in the untyped green tree. /// An inner or leaf node in the untyped green tree.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Hash)]
pub enum Green { pub enum Green {
/// A reference-counted inner node. /// A reference-counted inner node.
Node(Arc<GreenNode>), Node(Arc<GreenNode>),
@ -101,7 +102,7 @@ impl Debug for Green {
} }
/// An inner node in the untyped green tree. /// An inner node in the untyped green tree.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Hash)]
pub struct GreenNode { pub struct GreenNode {
/// Node metadata. /// Node metadata.
data: GreenData, data: GreenData,
@ -209,7 +210,7 @@ impl Debug for GreenNode {
} }
/// Data shared between inner and leaf nodes. /// Data shared between inner and leaf nodes.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Hash)]
pub struct GreenData { pub struct GreenData {
/// What kind of node this is (each kind would have its own struct in a /// What kind of node this is (each kind would have its own struct in a
/// strongly typed AST). /// strongly typed AST).
@ -250,7 +251,7 @@ impl Debug for GreenData {
/// A owned wrapper for a green node with span information. /// A owned wrapper for a green node with span information.
/// ///
/// Owned variant of [`RedRef`]. Can be [cast](Self::cast) to an AST node. /// Owned variant of [`RedRef`]. Can be [cast](Self::cast) to an AST node.
#[derive(Clone, PartialEq)] #[derive(Clone, PartialEq, Hash)]
pub struct RedNode { pub struct RedNode {
id: SourceId, id: SourceId,
offset: usize, offset: usize,
@ -325,7 +326,7 @@ impl Debug for RedNode {
/// A borrowed wrapper for a [`GreenNode`] with span information. /// A borrowed wrapper for a [`GreenNode`] with span information.
/// ///
/// Borrowed variant of [`RedNode`]. Can be [cast](Self::cast) to an AST node. /// Borrowed variant of [`RedNode`]. Can be [cast](Self::cast) to an AST node.
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq, Hash)]
pub struct RedRef<'a> { pub struct RedRef<'a> {
id: SourceId, id: SourceId,
offset: usize, offset: usize,
@ -716,7 +717,7 @@ pub enum NodeKind {
} }
/// Where in a node an error should be annotated. /// Where in a node an error should be annotated.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum ErrorPos { pub enum ErrorPos {
/// At the start of the node. /// At the start of the node.
Start, Start,
@ -932,3 +933,119 @@ impl Display for NodeKind {
f.pad(self.as_str()) f.pad(self.as_str())
} }
} }
impl Hash for NodeKind {
fn hash<H: Hasher>(&self, state: &mut H) {
std::mem::discriminant(self).hash(state);
match self {
Self::LeftBracket => {}
Self::RightBracket => {}
Self::LeftBrace => {}
Self::RightBrace => {}
Self::LeftParen => {}
Self::RightParen => {}
Self::Star => {}
Self::Underscore => {}
Self::Comma => {}
Self::Semicolon => {}
Self::Colon => {}
Self::Plus => {}
Self::Minus => {}
Self::Slash => {}
Self::Eq => {}
Self::EqEq => {}
Self::ExclEq => {}
Self::Lt => {}
Self::LtEq => {}
Self::Gt => {}
Self::GtEq => {}
Self::PlusEq => {}
Self::HyphEq => {}
Self::StarEq => {}
Self::SlashEq => {}
Self::Not => {}
Self::And => {}
Self::Or => {}
Self::With => {}
Self::Dots => {}
Self::Arrow => {}
Self::None => {}
Self::Auto => {}
Self::Let => {}
Self::Set => {}
Self::Show => {}
Self::Wrap => {}
Self::If => {}
Self::Else => {}
Self::For => {}
Self::In => {}
Self::As => {}
Self::While => {}
Self::Break => {}
Self::Continue => {}
Self::Return => {}
Self::Import => {}
Self::Include => {}
Self::From => {}
Self::Markup(c) => c.hash(state),
Self::Space(n) => n.hash(state),
Self::Linebreak => {}
Self::Parbreak => {}
Self::Text(s) => s.hash(state),
Self::TextInLine(s) => s.hash(state),
Self::NonBreakingSpace => {}
Self::EnDash => {}
Self::EmDash => {}
Self::Escape(c) => c.hash(state),
Self::Strong => {}
Self::Emph => {}
Self::Raw(raw) => raw.hash(state),
Self::Math(math) => math.hash(state),
Self::List => {}
Self::Heading => {}
Self::Enum => {}
Self::EnumNumbering(num) => num.hash(state),
Self::Ident(v) => v.hash(state),
Self::Bool(v) => v.hash(state),
Self::Int(v) => v.hash(state),
Self::Float(v) => v.to_bits().hash(state),
Self::Length(v, u) => (v.to_bits(), u).hash(state),
Self::Angle(v, u) => (v.to_bits(), u).hash(state),
Self::Percentage(v) => v.to_bits().hash(state),
Self::Fraction(v) => v.to_bits().hash(state),
Self::Str(v) => v.hash(state),
Self::Array => {}
Self::Dict => {}
Self::Named => {}
Self::Template => {}
Self::Group => {}
Self::Block => {}
Self::Unary => {}
Self::Binary => {}
Self::Call => {}
Self::CallArgs => {}
Self::Spread => {}
Self::Closure => {}
Self::ClosureParams => {}
Self::WithExpr => {}
Self::LetExpr => {}
Self::SetExpr => {}
Self::ShowExpr => {}
Self::WrapExpr => {}
Self::IfExpr => {}
Self::WhileExpr => {}
Self::ForExpr => {}
Self::ForPattern => {}
Self::ImportExpr => {}
Self::ImportItems => {}
Self::IncludeExpr => {}
Self::BreakExpr => {}
Self::ContinueExpr => {}
Self::ReturnExpr => {}
Self::LineComment => {}
Self::BlockComment => {}
Self::Error(pos, msg) => (pos, msg).hash(state),
Self::Unknown(src) => src.hash(state),
}
}
}

View File

@ -5,7 +5,7 @@ use std::ops::Range;
use crate::source::SourceId; use crate::source::SourceId;
/// A value with the span it corresponds to in the source code. /// A value with the span it corresponds to in the source code.
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Spanned<T> { pub struct Spanned<T> {
/// The spanned value. /// The spanned value.
pub v: T, pub v: T,
@ -46,7 +46,7 @@ impl<T: Debug> Debug for Spanned<T> {
} }
/// Bounds of a slice of source code. /// Bounds of a slice of source code.
#[derive(Copy, Clone, Eq, PartialEq)] #[derive(Copy, Clone, Eq, PartialEq, Hash)]
pub struct Span { pub struct Span {
/// The id of the source file. /// The id of the source file.
pub source: SourceId, pub source: SourceId,