use serde::{Deserialize, Deserializer, Serialize};
use super::types::Type;
use super::{Language, Span};
#[allow(clippy::trivially_copy_pass_by_ref)] const fn is_false(value: &bool) -> bool {
!*value
}
fn numeral<'de, D: Deserializer<'de>>(deserializer: D) -> Result<String, D::Error> {
#[derive(Deserialize)]
#[serde(untagged)]
enum Numeral {
Text(String),
Number(serde_json::Number),
}
Ok(match Numeral::deserialize(deserializer)? {
Numeral::Text(text) => text,
Numeral::Number(number) => number.to_string(),
})
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SExpr {
#[serde(flatten)]
pub node: SNode,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
#[serde(default, skip_serializing_if = "is_false")]
pub block: bool,
#[serde(rename = "tagTest", default, skip_serializing_if = "Option::is_none")]
pub tag_test: Option<Box<STagTest>>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct STagTest {
pub object: SExpr,
pub tag: String,
pub data: SData,
pub negated: bool,
}
impl SExpr {
#[must_use]
pub const fn new(node: SNode, span: Option<Span>) -> Self {
Self {
node,
span,
block: false,
tag_test: None,
}
}
#[must_use]
pub fn name(name: &str) -> Self {
Self::new(
SNode::Name {
path: vec![name.to_owned()],
},
None,
)
}
#[must_use]
pub fn simple_name(&self) -> Option<&str> {
match &self.node {
SNode::Name { path } if path.len() == 1 => Some(&path[0]),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum UnaryOp {
Not,
Neg,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum BinaryOp {
Add,
Sub,
Mul,
Div,
Rem,
Eq,
Ne,
Lt,
Le,
Gt,
Ge,
And,
Or,
Concat,
Plus,
}
impl BinaryOp {
#[must_use]
pub const fn name(self) -> &'static str {
match self {
Self::Add => "add",
Self::Sub => "sub",
Self::Mul => "mul",
Self::Div => "div",
Self::Rem => "rem",
Self::Eq => "eq",
Self::Ne => "ne",
Self::Lt => "lt",
Self::Le => "le",
Self::Gt => "gt",
Self::Ge => "ge",
Self::And => "and",
Self::Or => "or",
Self::Concat => "concat",
Self::Plus => "plus",
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Rounding {
Trunc,
Euclid,
Floor,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum ShowStyle {
#[serde(rename = "lean")]
Lean,
#[serde(rename = "rocq")]
Rocq,
#[serde(rename = "rust")]
Rust,
#[serde(rename = "js-console")]
JsConsole,
#[serde(rename = "js-template")]
JsTemplate,
#[serde(rename = "js-format-first")]
JsFormatFirst,
#[serde(rename = "js-format-number")]
JsFormatNumber,
#[serde(rename = "js-format-integer")]
JsFormatInteger,
}
impl ShowStyle {
#[must_use]
pub const fn is_console(self) -> bool {
matches!(
self,
Self::JsConsole | Self::JsFormatFirst | Self::JsFormatNumber | Self::JsFormatInteger
)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub enum Flavor {
Exact,
Checked,
Clamp,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "k", rename_all = "camelCase", rename_all_fields = "camelCase")]
pub enum SNode {
Num {
value: String,
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
ty: Option<Type>,
#[serde(default, skip_serializing_if = "is_false")]
negative: bool,
#[serde(default, skip_serializing_if = "is_false")]
unit: bool,
},
Bool {
value: bool,
},
Str {
value: String,
},
Unit,
Name {
path: Vec<String>,
},
DotCtor {
name: String,
},
App {
#[serde(rename = "fn")]
func: Box<SExpr>,
args: Vec<SExpr>,
},
Field {
object: Box<SExpr>,
field: String,
},
Unary {
op: UnaryOp,
arg: Box<SExpr>,
},
Binary {
op: BinaryOp,
left: Box<SExpr>,
right: Box<SExpr>,
#[serde(default, skip_serializing_if = "Option::is_none")]
rounding: Option<Rounding>,
},
If {
cond: Box<SExpr>,
then: Box<SExpr>,
#[serde(rename = "else")]
otherwise: Box<SExpr>,
},
Let {
name: String,
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
ty: Option<Type>,
value: Box<SExpr>,
body: Box<SExpr>,
},
Match {
scrutinees: Vec<SExpr>,
rows: Vec<SRow>,
},
Match1 {
scrutinee: Box<SExpr>,
cases: Vec<SCase>,
},
ToString {
arg: Box<SExpr>,
},
Show {
arg: Box<SExpr>,
style: ShowStyle,
},
Cast {
arg: Box<SExpr>,
to: Type,
#[serde(default, skip_serializing_if = "Option::is_none")]
from: Option<Type>,
flavor: Flavor,
},
Abort {
message: String,
},
Print {
expr: Box<SExpr>,
style: ShowStyle,
body: Box<SExpr>,
},
CtorObject {
tag: String,
#[serde(with = "ordered_fields")]
fields: Vec<(String, SExpr)>,
},
Array {
items: Vec<SArrayItem>,
#[serde(default, skip_serializing_if = "Option::is_none")]
element: Option<Type>,
},
Index {
object: Box<SExpr>,
index: Box<SExpr>,
},
Length {
object: Box<SExpr>,
integer: bool,
},
Math {
op: String,
name: String,
args: Vec<SArrayItem>,
},
Cons {
head: Box<SExpr>,
tail: Box<SExpr>,
},
Nil,
List {
items: Vec<SExpr>,
},
Tag {
tag: String,
},
Default,
}
impl SNode {
#[must_use]
pub const fn kind(&self) -> &'static str {
match self {
Self::Num { .. } => "num",
Self::Bool { .. } => "bool",
Self::Str { .. } => "str",
Self::Unit => "unit",
Self::Name { .. } => "name",
Self::DotCtor { .. } => "dotCtor",
Self::App { .. } => "app",
Self::Field { .. } => "field",
Self::Unary { .. } => "unary",
Self::Binary { .. } => "binary",
Self::If { .. } => "if",
Self::Let { .. } => "let",
Self::Match { .. } => "match",
Self::Match1 { .. } => "match1",
Self::ToString { .. } => "toString",
Self::Show { .. } => "show",
Self::Cast { .. } => "cast",
Self::Abort { .. } => "abort",
Self::Print { .. } => "print",
Self::CtorObject { .. } => "ctorObject",
Self::Array { .. } => "array",
Self::Index { .. } => "index",
Self::Length { .. } => "length",
Self::Math { .. } => "math",
Self::Cons { .. } => "cons",
Self::Nil => "nil",
Self::List { .. } => "list",
Self::Tag { .. } => "tag",
Self::Default => "default",
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SArrayItem {
pub spread: bool,
pub value: SExpr,
}
mod ordered_fields {
use std::fmt;
use serde::de::{MapAccess, Visitor};
use serde::ser::SerializeMap;
use serde::{Deserializer, Serializer};
use super::SExpr;
#[allow(clippy::ptr_arg)]
pub fn serialize<S: Serializer>(
fields: &Vec<(String, SExpr)>,
serializer: S,
) -> Result<S::Ok, S::Error> {
let mut map = serializer.serialize_map(Some(fields.len()))?;
for (name, value) in fields {
map.serialize_entry(name, value)?;
}
map.end()
}
pub fn deserialize<'de, D: Deserializer<'de>>(
deserializer: D,
) -> Result<Vec<(String, SExpr)>, D::Error> {
struct Fields;
impl<'de> Visitor<'de> for Fields {
type Value = Vec<(String, SExpr)>;
fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str("an object of fields")
}
fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
let mut fields = Vec::new();
while let Some(entry) = map.next_entry()? {
fields.push(entry);
}
Ok(fields)
}
}
deserializer.deserialize_map(Fields)
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SRow {
pub patterns: Vec<SPattern>,
pub body: SExpr,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SCase {
pub pattern: SCasePattern,
pub body: SExpr,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "k", rename_all = "camelCase", rename_all_fields = "camelCase")]
pub enum SCasePattern {
Wild,
Bind {
name: String,
},
NatZero,
NatSucc {
name: String,
},
Ctor {
path: Vec<String>,
binds: Vec<Option<String>>,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SPattern {
#[serde(flatten)]
pub node: SPatternNode,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "k", rename_all = "camelCase", rename_all_fields = "camelCase")]
pub enum SPatternNode {
Wild,
BindOrCtor {
name: String,
},
Bind {
name: String,
},
Ctor {
path: Vec<String>,
args: Vec<SPattern>,
},
NumLit {
#[serde(deserialize_with = "numeral")]
value: String,
#[serde(default, skip_serializing_if = "is_false")]
negative: bool,
},
BoolLit {
value: bool,
#[serde(default, skip_serializing_if = "is_false")]
negative: bool,
},
StrLit {
value: String,
},
NatAdd {
inner: Box<SPattern>,
add: String,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SBinder {
pub name: String,
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
pub ty: Option<Type>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rocq_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SProp {
#[serde(flatten)]
pub node: SPropNode,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SComparison {
pub left: SExpr,
pub right: SExpr,
#[serde(default, skip_serializing_if = "is_false")]
pub reference: bool,
#[serde(default, rename = "sameValue", skip_serializing_if = "is_false")]
pub same_value: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "p", rename_all = "camelCase", rename_all_fields = "camelCase")]
pub enum SPropNode {
Forall {
binders: Vec<SBinder>,
body: Box<SProp>,
},
And {
left: Box<SProp>,
right: Box<SProp>,
},
Or {
left: Box<SProp>,
right: Box<SProp>,
},
Implies {
left: Box<SProp>,
right: Box<SProp>,
},
Not {
arg: Box<SProp>,
},
Bool {
expr: SExpr,
},
Eq(SComparison),
Ne(SComparison),
Lt(SComparison),
Le(SComparison),
Gt(SComparison),
Ge(SComparison),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SRule {
pub name: String,
pub reverse: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "t", rename_all = "camelCase", rename_all_fields = "camelCase")]
pub enum SStep {
Compute {
#[serde(default, skip_serializing_if = "Option::is_none")]
tactic: Option<String>,
},
Arith {
#[serde(default, skip_serializing_if = "Option::is_none")]
tactic: Option<String>,
},
Intro {
names: Vec<String>,
},
Unfold {
names: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
tactic: Option<String>,
},
Simp {
rules: Vec<SRule>,
#[serde(default, skip_serializing_if = "Option::is_none")]
only: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
tactic: Option<String>,
},
Rewrite {
rules: Vec<SRule>,
#[serde(default, skip_serializing_if = "Option::is_none")]
only: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
tactic: Option<String>,
},
Induction(SSplit),
Cases(SSplit),
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SSplit {
pub variable: String,
pub cases: Vec<SSplitCase>,
#[serde(default, skip_serializing_if = "is_false")]
pub positional: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub blocks: Option<usize>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SSplitCase {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ctor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub index: Option<usize>,
pub binds: Vec<Option<String>>,
pub steps: Vec<SStep>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SProof {
pub steps: Vec<SStep>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub source: Option<String>,
pub source_language: Language,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SField {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(rename = "type")]
pub ty: Type,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rocq_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SCtor {
pub name: String,
pub fields: Vec<SField>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Guard {
pub message: String,
pub order: usize,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct SParam {
pub name: String,
#[serde(default, rename = "default", skip_serializing_if = "Option::is_none")]
pub default_value: Option<SExpr>,
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
pub ty: Option<Type>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub guard: Option<Guard>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub rocq_type: Option<String>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SData {
pub name: String,
pub ctors: Vec<SCtor>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
#[serde(default, skip_serializing_if = "is_false")]
pub generated: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SFn {
pub name: String,
pub params: Vec<SParam>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ret: Option<Type>,
pub body: SExpr,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
#[serde(default, skip_serializing_if = "is_false")]
pub generated: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct STheorem {
pub name: String,
pub binders: Vec<SBinder>,
pub prop: SProp,
pub proof: SProof,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SModule {
pub name: String,
pub items: Vec<SItem>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "k", rename_all = "camelCase")]
pub enum SItem {
Data(SData),
Fn(SFn),
Theorem(STheorem),
Module(SModule),
}
impl SItem {
#[must_use]
pub fn name(&self) -> &str {
match self {
Self::Data(item) => &item.name,
Self::Fn(item) => &item.name,
Self::Theorem(item) => &item.name,
Self::Module(item) => &item.name,
}
}
#[must_use]
pub const fn span(&self) -> Option<Span> {
match self {
Self::Data(item) => item.span,
Self::Fn(item) => item.span,
Self::Theorem(item) => item.span,
Self::Module(item) => item.span,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(tag = "k", rename_all = "camelCase", rename_all_fields = "camelCase")]
pub enum SEffect {
Print {
expr: SExpr,
style: ShowStyle,
#[serde(default, skip_serializing_if = "Option::is_none")]
span: Option<Span>,
},
Let {
name: String,
#[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
ty: Option<Type>,
value: SExpr,
#[serde(default, skip_serializing_if = "is_false")]
constant: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
span: Option<Span>,
},
Assert {
prop: SProp,
#[serde(default, skip_serializing_if = "Option::is_none")]
span: Option<Span>,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SMain {
pub effects: Vec<SEffect>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub span: Option<Span>,
#[serde(rename = "sequentialAsync", default, skip_serializing_if = "is_false")]
pub sequential_async: bool,
}
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct SProgram {
pub language: Language,
pub items: Vec<SItem>,
#[serde(default)]
pub main: Option<SMain>,
}