vesper/
t_expr.rs

1// Vesper: declarative human-readable structural language
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Written in 2024 by
6//     Dr Maxim Orlovsky <orlovsky@ubideco.org>
7//
8// Copyright (C) 2024 UBIDECO Institute, Switzerland
9//
10// Licensed under the Apache License, Version 2.0 (the "License");
11// you may not use this file except in compliance with the License.
12// You may obtain a copy of the License at
13//
14//     http://www.apache.org/licenses/LICENSE-2.0
15//
16// Unless required by applicable law or agreed to in writing, software
17// distributed under the License is distributed on an "AS IS" BASIS,
18// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19// See the License for the specific language governing permissions and
20// limitations under the License.
21
22use std::fmt::{Display, Formatter};
23
24use amplify::confinement::{SmallVec, TinyVec};
25use strict_encoding::Ident;
26
27pub trait Predicate: Clone + Eq {
28    type Attr: Attribute;
29}
30
31pub trait Expression: Clone + Eq + Display {}
32
33pub trait Attribute: Clone + Eq {
34    type Expression: Expression;
35
36    fn is_named(&self) -> bool { self.name().is_some() }
37    fn name(&self) -> Option<Ident>;
38    fn value(&self) -> AttrVal<Self::Expression>;
39}
40
41#[derive(Clone, Eq, PartialEq, Debug, Display)]
42#[display(inner)]
43pub enum AttrVal<E: Expression> {
44    Ident(Ident),
45    Expr(E),
46}
47
48#[derive(Clone, Eq, PartialEq, Debug)]
49pub struct TExpr<P: Predicate> {
50    pub subject: Ident,
51    pub predicate: P,
52    pub attributes: SmallVec<P::Attr>,
53    pub content: TinyVec<Box<TExpr<P>>>,
54    pub comment: Option<String>,
55}
56
57impl<P: Predicate> TExpr<P> {
58    pub fn display(&self) -> TExprDisplay<P>
59    where P: Display {
60        TExprDisplay {
61            expr: self,
62            indent: 0,
63            tab: s!("  "),
64        }
65    }
66}
67
68pub struct TExprDisplay<'expr, P: Predicate>
69where P: Display
70{
71    expr: &'expr TExpr<P>,
72    indent: usize,
73    tab: String,
74}
75
76impl<'expr, P: Predicate> TExprDisplay<'expr, P>
77where P: Display
78{
79    pub fn indented(parent: &Self, expr: &'expr TExpr<P>) -> TExprDisplay<'expr, P> {
80        TExprDisplay {
81            expr,
82            indent: parent.indent + 1,
83            tab: parent.tab.clone(),
84        }
85    }
86}
87
88impl<'expr, P: Predicate> Display for TExprDisplay<'expr, P>
89where P: Display
90{
91    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
92        const MAX_LINE_VARS: usize = 8;
93
94        let expr = self.expr;
95        let attrs = &expr.attributes;
96
97        let indent = self.tab.repeat(self.indent);
98        write!(f, "{indent}{} {}", expr.predicate, expr.subject)?;
99
100        if attrs.len() > MAX_LINE_VARS {
101            write!(f, " {{\n{indent}{}", self.tab)?;
102        } else if !attrs.is_empty() {
103            f.write_str(", ")?;
104        }
105        for (pos, attr) in expr.attributes.iter().enumerate() {
106            if pos == 1 || (pos > 0 && pos % MAX_LINE_VARS != 1) {
107                f.write_str(", ")?;
108            }
109            if let Some(name) = attr.name() {
110                write!(f, "{name} ")?;
111            }
112            write!(f, "{}", attr.value())?;
113
114            if pos > 0 && pos % MAX_LINE_VARS == 0 {
115                write!(f, "\n{indent}{}", self.tab)?;
116            }
117        }
118        if attrs.len() > MAX_LINE_VARS {
119            write!(f, "\n{indent}}}")?;
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}