vesper/
t_expr.rs

1// Vesper: declarative human-readable structural language
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Designed & Written in 2024-2025 by
6//     Dr Maxim Orlovsky <orlovsky@ubideco.org>
7//
8// Copyright (C) 2024-2025 Laboratories for Ubiquitous and Deterministic Computing,
9//     Institute for Distributed and Cognitive Systems, Lugano, Switzerland
10//
11// Licensed under the Apache License, Version 2.0 (the "License");
12// you may not use this file except in compliance with the License.
13// You may obtain a copy of the License at
14//
15//     http://www.apache.org/licenses/LICENSE-2.0
16//
17// Unless required by applicable law or agreed to in writing, software
18// distributed under the License is distributed on an "AS IS" BASIS,
19// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20// See the License for the specific language governing permissions and
21// limitations under the License.
22
23use std::fmt::{Display, Formatter};
24
25use amplify::confinement::{SmallVec, TinyVec};
26use strict_encoding::Ident;
27
28pub trait Predicate: Clone + Eq {
29    type Attr: Attribute;
30}
31
32pub trait Expression: Clone + Eq + Display {}
33
34pub trait Attribute: Clone + Eq {
35    type Expression: Expression;
36
37    fn is_named(&self) -> bool { self.name().is_some() }
38    fn name(&self) -> Option<Ident>;
39    fn value(&self) -> AttrVal<Self::Expression>;
40}
41
42#[derive(Clone, Eq, PartialEq, Debug, Display)]
43#[display(inner)]
44pub enum AttrVal<E: Expression> {
45    Ident(Ident),
46    Expr(E),
47}
48
49#[derive(Clone, Eq, PartialEq, Debug)]
50pub struct TExpr<P: Predicate> {
51    pub subject: Ident,
52    pub predicate: P,
53    pub attributes: SmallVec<P::Attr>,
54    pub content: TinyVec<Box<TExpr<P>>>,
55    pub comment: Option<String>,
56}
57
58impl<P: Predicate> TExpr<P> {
59    pub fn display(&self) -> TExprDisplay<'_, P>
60    where P: Display {
61        TExprDisplay {
62            expr: self,
63            indent: 0,
64            tab: s!("  "),
65        }
66    }
67}
68
69pub struct TExprDisplay<'expr, P: Predicate>
70where P: Display
71{
72    expr: &'expr TExpr<P>,
73    indent: usize,
74    tab: String,
75}
76
77impl<'expr, P: Predicate> TExprDisplay<'expr, P>
78where P: Display
79{
80    pub fn indented(parent: &Self, expr: &'expr TExpr<P>) -> TExprDisplay<'expr, P> {
81        TExprDisplay {
82            expr,
83            indent: parent.indent + 1,
84            tab: parent.tab.clone(),
85        }
86    }
87}
88
89impl<'expr, P: Predicate> Display for TExprDisplay<'expr, P>
90where P: Display
91{
92    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
93        const MAX_LINE_VARS: usize = 8;
94
95        let expr = self.expr;
96        let attrs = &expr.attributes;
97
98        let indent = self.tab.repeat(self.indent);
99        write!(f, "{indent}{} {}", expr.predicate, expr.subject)?;
100
101        if !attrs.is_empty() {
102            f.write_str(": ")?;
103        }
104
105        let mut iter = expr.attributes.iter().enumerate().peekable();
106        while let Some((pos, attr)) = iter.next() {
107            if let Some(name) = attr.name() {
108                write!(f, "{name} ")?;
109            }
110            write!(f, "{}", attr.value())?;
111
112            if iter.peek().is_some() {
113                f.write_str(",")?;
114            }
115            if pos > 0 && pos % MAX_LINE_VARS == 0 {
116                write!(f, "\n{indent}{}", self.tab)?;
117            } else {
118                f.write_str(" ")?;
119            }
120        }
121
122        if let Some(comment) = &expr.comment {
123            write!(f, " -- {comment}")?;
124        }
125        writeln!(f)?;
126
127        for expr in &expr.content {
128            let display = TExprDisplay::indented(self, expr.as_ref());
129            Display::fmt(&display, f)?;
130        }
131        Ok(())
132    }
133}
134
135#[cfg(test)]
136mod tests {
137    use std::str::FromStr;
138    use super::*;
139
140    #[derive(Copy, Clone, Eq, PartialEq, Debug, Display)]
141    #[display("predicate")]
142    struct Test;
143    impl Predicate for Test {
144        type Attr = TestAttr;
145    }
146
147    #[derive(Copy, Clone, Eq, PartialEq, Debug, Display)]
148    enum TestAttr {
149        #[display("attr1")]
150        TestAttr1,
151        #[display("attr2")]
152        TestAttr2,
153    }
154    impl Attribute for TestAttr {
155        type Expression = String;
156
157        fn name(&self) -> Option<Ident> {
158            Some(Ident::from_str(&self.to_string()).unwrap())
159        }
160
161        fn value(&self) -> AttrVal<Self::Expression> {
162            AttrVal::Expr("value".to_string())
163        }
164    }
165
166    impl Expression for String {}
167
168    #[test]
169    fn display() {
170        let expr = TExpr {
171            subject: strict_encoding::ident!("test"),
172            predicate: Test,
173            attributes: small_vec![ TestAttr::TestAttr1, TestAttr::TestAttr2 ],
174            content: Default::default(),
175            comment: None,
176        };
177        assert_eq!(expr.display().to_string(), "predicate test: attr1 value, attr2 value \n");
178    }
179}