panfix/
op.rs

1use crate::{Token, TokenId};
2use std::fmt;
3
4/// Precedence level. Smaller is tighter / wins.
5pub type Prec = u16;
6
7/// Whether an operator takes an argument on the left and/or on the right. For example:
8///
9/// - `3` takes no arguments, so it is a `Nilfix` operator.
10/// - `_ [ _ ]` (indexing) takes an argument only on the left, so it is a `Suffix` operator. (The
11///   "internal argument" between the brackets does not count towards the fixity.)
12/// - `! _` (not) takes an argument only on the right, so it is a `Prefix` oeprator.
13/// - `_ - _` takes an argument on both sides, so it is an infix operator.
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15pub enum Fixity {
16    Nilfix,
17    Prefix,
18    Suffix,
19    Infix,
20}
21
22/// Whether an operator is left or right associative. For example:
23///
24/// - Subtraction is left associative because `0 - 10 - 1` is equal to `(0 - 10) - 1 = -11` rather
25///   than `0 - (10 - 1) = -9`.
26/// - `_ . _` (field access) is also left associative because `person.birthdate.year` is
27///   equal to `(person.birthdate).year` and not `person.(birthdate.year)`.
28/// - Right associativity is less common, but in languages that allow chained variable declarations,
29///   `x = y = 3` is right-associative. If it were left-associative, it would be equal to `(x = y) =
30///   3`, which would attempt to set `x` equal to `y` before `y` had been defined.  Instead it is
31///   right-associative (`InfixR`) and thus equal to `x = (y = 3)`.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
33pub enum Assoc {
34    Left,
35    Right,
36}
37
38#[derive(Debug, Clone)]
39pub(crate) struct Op<T: Token> {
40    pub(crate) token: T,
41    pub(crate) fixity: Fixity,
42    pub(crate) assoc: Assoc,
43    pub(crate) prec: Prec,
44    // computed
45    pub(crate) arity: usize,
46    pub(crate) left_prec: Option<Prec>,
47    pub(crate) right_prec: Option<Prec>,
48}
49
50impl<T: Token> Op<T> {
51    pub(crate) fn new(
52        token: T,
53        fixity: Fixity,
54        assoc: Assoc,
55        prec: Prec,
56        num_tokens: usize,
57    ) -> Op<T> {
58        assert_ne!(token, T::LEX_ERROR);
59        assert_ne!(token, T::BLANK);
60        assert_ne!(token, T::JUXTAPOSE);
61        Op::new_unchecked(token, fixity, assoc, prec, num_tokens)
62    }
63
64    pub(crate) fn new_atom(token: T, _token_id: TokenId) -> Op<T> {
65        Op::new_unchecked(token, Fixity::Nilfix, Assoc::Left, 0, 1)
66    }
67
68    pub(crate) fn new_error() -> Op<T> {
69        Op::new_unchecked(T::LEX_ERROR, Fixity::Nilfix, Assoc::Left, 0, 1)
70    }
71
72    pub(crate) fn new_blank() -> Op<T> {
73        Op::new_unchecked(T::BLANK, Fixity::Nilfix, Assoc::Left, 0, 1)
74    }
75
76    pub(crate) fn new_juxtapose(assoc: Assoc, prec: Prec) -> Op<T> {
77        Op::new_unchecked(T::JUXTAPOSE, Fixity::Infix, assoc, prec, 1)
78    }
79
80    fn new_unchecked(
81        token: T,
82        fixity: Fixity,
83        assoc: Assoc,
84        prec: Prec,
85        num_tokens: usize,
86    ) -> Op<T> {
87        use Assoc::{Left, Right};
88        use Fixity::{Infix, Nilfix, Prefix, Suffix};
89
90        let (left_prec, right_prec) = match (fixity, assoc) {
91            (Nilfix, _) => (None, None),
92            (Prefix, Left) => (None, Some(prec)),
93            (Prefix, Right) => (None, Some(prec + 1)),
94            (Suffix, Left) => (Some(prec + 1), None),
95            (Suffix, Right) => (Some(prec), None),
96            (Infix, Left) => (Some(prec + 1), Some(prec)),
97            (Infix, Right) => (Some(prec), Some(prec + 1)),
98        };
99        let arity = match fixity {
100            Nilfix => num_tokens - 1,
101            Prefix | Suffix => num_tokens,
102            Infix => num_tokens + 1,
103        };
104        Op {
105            token,
106            fixity,
107            assoc,
108            prec,
109            arity,
110            left_prec,
111            right_prec,
112        }
113    }
114}
115
116impl<T: Token> fmt::Display for Op<T> {
117    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
118        write!(f, "{}", self.token)
119    }
120}