use crate::ty::{Confidence, Effect, Type};
use serde::{Deserialize, Serialize};
use sha2::{Digest, Sha256};
use std::collections::BTreeSet;
use std::fmt;
#[derive(Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub struct NodeHash(String);
impl NodeHash {
pub fn as_str(&self) -> &str {
&self.0
}
pub(crate) fn from_raw(s: String) -> Self {
NodeHash(s)
}
pub fn parse(s: &str) -> Self {
NodeHash(s.to_string())
}
}
impl fmt::Display for NodeHash {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct Param {
pub name: String,
pub ty: Type,
pub min_confidence: Confidence,
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct Produces {
pub ty: Type,
pub confidence: Confidence,
}
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub enum BinOp {
Add,
Sub,
Mul,
Div,
Mod,
Eq,
Neq,
Lt,
Le,
Gt,
Ge,
And,
Or,
}
impl BinOp {
pub fn is_comparison(self) -> bool {
use BinOp::*;
matches!(self, Eq | Neq | Lt | Le | Gt | Ge)
}
pub fn is_logical(self) -> bool {
matches!(self, BinOp::And | BinOp::Or)
}
pub fn symbol(self) -> &'static str {
match self {
BinOp::Add => "+",
BinOp::Sub => "-",
BinOp::Mul => "*",
BinOp::Div => "/",
BinOp::Mod => "%",
BinOp::Eq => "==",
BinOp::Neq => "!=",
BinOp::Lt => "<",
BinOp::Le => "<=",
BinOp::Gt => ">",
BinOp::Ge => ">=",
BinOp::And => "&&",
BinOp::Or => "||",
}
}
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub struct MatchArm {
pub case: String,
pub bindings: Vec<String>,
pub body: NodeHash,
}
#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
pub enum Node {
Lit(i64),
FloatLit(u64),
FloatOp {
op: BinOp,
lhs: NodeHash,
rhs: NodeHash,
},
IntToFloat(NodeHash),
FloatToInt(NodeHash),
DecimalLit(i64),
DecimalOp {
op: BinOp,
lhs: NodeHash,
rhs: NodeHash,
},
IntToDecimal(NodeHash),
DecimalToInt(NodeHash),
DecimalRaw(NodeHash),
Bool(bool),
Not(NodeHash),
Str(String),
StrLen(NodeHash),
StrLower(NodeHash),
StrFromCode(NodeHash),
StrConcat(NodeHash, NodeHash),
StrSlice {
s: NodeHash,
start: NodeHash,
len: NodeHash,
},
StrEq(NodeHash, NodeHash),
StrContains {
haystack: NodeHash,
needle: NodeHash,
},
StrStartsWith {
s: NodeHash,
prefix: NodeHash,
},
StrIndexOf {
haystack: NodeHash,
needle: NodeHash,
},
NumberToStr(NodeHash),
StrToNumber(NodeHash),
StrToNumberOpt(NodeHash),
Now,
List(Vec<NodeHash>),
ListEmpty { elem: Type },
ListCons { head: NodeHash, tail: NodeHash },
OptionSome(NodeHash),
OptionNone { elem: Type },
OptionElse { opt: NodeHash, default: NodeHash },
OptionMatch {
opt: NodeHash,
some_bind: String,
some_body: NodeHash,
none_body: NodeHash,
},
ListTryGet { list: NodeHash, index: NodeHash },
ListLen(NodeHash),
ListGet { list: NodeHash, index: NodeHash },
Map(Vec<(NodeHash, NodeHash)>),
MapGet { map: NodeHash, key: NodeHash },
MapTryGet { map: NodeHash, key: NodeHash },
MapLen(NodeHash),
Log(NodeHash),
Publish(NodeHash),
SetHeader { name: NodeHash, value: NodeHash },
Rand,
MutNew(NodeHash),
MutGet(NodeHash),
MutSet { cell: NodeHash, value: NodeHash },
DiskWrite { path: NodeHash, content: NodeHash },
DiskRead(NodeHash),
NetGet(NodeHash),
DbQuery {
sql: NodeHash,
params: NodeHash,
},
Ref(String),
Call { func: String, args: Vec<NodeHash> },
FuncRef(String),
CallValue {
callee: NodeHash,
args: Vec<NodeHash>,
},
Lambda {
params: Vec<Param>,
body: NodeHash,
},
Hole { expects: String },
BinOp {
op: BinOp,
lhs: NodeHash,
rhs: NodeHash,
},
Fail(String),
Handle {
body: NodeHash,
handlers: Vec<(String, NodeHash)>,
},
If {
cond: NodeHash,
then_branch: NodeHash,
else_branch: NodeHash,
},
Step { binding: String, value: NodeHash },
Function {
name: String,
type_params: Vec<String>,
params: Vec<Param>,
produces: Produces,
requires: BTreeSet<Effect>,
on_failure: Vec<String>,
body: Vec<NodeHash>,
result: NodeHash,
},
Module {
name: String,
types: Vec<NodeHash>,
functions: Vec<NodeHash>,
},
RecordDef {
name: String,
fields: Vec<(String, Type)>,
},
Record {
type_name: String,
fields: Vec<(String, NodeHash)>,
},
Field {
base: NodeHash,
type_name: String,
field: String,
},
VariantDef {
name: String,
cases: Vec<(String, Vec<(String, Type)>)>,
},
Variant {
type_name: String,
case: String,
fields: Vec<(String, NodeHash)>,
},
Match {
scrutinee: NodeHash,
type_name: String,
arms: Vec<MatchArm>,
},
}
impl Node {
pub(crate) fn canonical_bytes(&self) -> Vec<u8> {
serde_json::to_vec(self).expect("Node serialization is infallible")
}
pub fn hash(&self) -> NodeHash {
let mut hasher = Sha256::new();
hasher.update(self.canonical_bytes());
NodeHash(hex(&hasher.finalize()))
}
}
fn hex(bytes: &[u8]) -> String {
use std::fmt::Write;
let mut s = String::with_capacity(bytes.len() * 2);
for b in bytes {
write!(s, "{:02x}", b).expect("writing to a String cannot fail");
}
s
}