1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use crate::common::{
    span::Spanned,
    data::Data,
};

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct UniqueSymbol(pub usize);

/// A pattern that mirrors the structure of some Data.
#[derive(Debug, Clone, PartialEq)]
pub enum SSTPattern {
    Symbol(UniqueSymbol),
    Data(Data),
    Label(String, Box<Spanned<SSTPattern>>), // TODO: usize for label
    Tuple(Vec<Spanned<SSTPattern>>),
    // Where {
    //     pattern: Box<ASTPattern>,
    //     expression: Box<AST>,
    // },
}

#[derive(Debug, Clone, PartialEq)]
pub struct Scope {
    pub locals:    Vec<UniqueSymbol>,
    pub nonlocals: Vec<UniqueSymbol>,
}

impl Scope {
    pub fn new() -> Scope {
        Scope {
            locals:    vec![],
            nonlocals: vec![],
        }
    }

    pub fn is_local(&self, unique_symbol: UniqueSymbol) -> bool {
        self.locals.contains(&unique_symbol)
    }

    pub fn is_nonlocal(&self, unique_symbol: UniqueSymbol) -> bool {
        self.nonlocals.contains(&unique_symbol)
    }

    pub fn local_index(&self, unique_symbol: UniqueSymbol) -> Option<usize> {
        self.locals.iter().position(|l| l == &unique_symbol)
    }

    pub fn nonlocal_index(&self, unique_symbol: UniqueSymbol) -> Option<usize> {
        self.nonlocals.iter().position(|l| l == &unique_symbol)
    }
}

impl Default for Scope {
    fn default() -> Self {
        Self::new()
    }
}

/// Represents an item in a hoisted `SST`.
/// Each langauge-level construct has it's own `SST` variant.
/// Note that symbols have been substituted.
/// At this point in compilation the scope of each local is known.
#[derive(Debug, Clone, PartialEq)]
pub enum SST {
    Symbol(UniqueSymbol),
    Data(Data),
    Block(Vec<Spanned<SST>>),
    Assign {
        pattern:    Box<Spanned<SSTPattern>>,
        expression: Box<Spanned<SST>>,
    },
    Lambda {
        pattern:    Box<Spanned<SSTPattern>>,
        expression: Box<Spanned<SST>>,
        scope:      Scope,
    },
    Call {
        fun: Box<Spanned<SST>>,
        arg: Box<Spanned<SST>>,
    },
    Label(String, Box<Spanned<SST>>),
    Tuple(Vec<Spanned<SST>>),
    FFI {
        name:       String,
        expression: Box<Spanned<SST>>,
    },
}

impl SST {
    /// Shortcut for creating an `SST::Assign` variant.
    pub fn assign(
        pattern:    Spanned<SSTPattern>,
        expression: Spanned<SST>
    ) -> SST {
        SST::Assign {
            pattern:    Box::new(pattern),
            expression: Box::new(expression)
        }
    }

    /// Shortcut for creating an `SST::Lambda` variant.
    pub fn lambda(
        pattern:    Spanned<SSTPattern>,
        expression: Spanned<SST>,
        scope:      Scope,
    ) -> SST {
        SST::Lambda {
            pattern:    Box::new(pattern),
            expression: Box::new(expression),
            scope,
        }
    }

    /// Shortcut for creating a `SST::Label` variant.
    pub fn label(name: &str, expression: Spanned<SST>) -> SST {
        SST::Label(name.to_string(), Box::new(expression))
    }

    /// Shortcut for creating a `SST::Lambda` variant.
    pub fn call(fun: Spanned<SST>, arg: Spanned<SST>) -> SST {
        SST::Call {
            fun: Box::new(fun),
            arg: Box::new(arg),
        }
    }

    // Shortcut for creating an `SST::FFI` variant.
    pub fn ffi(name: &str, expression: Spanned<SST>) -> SST {
        SST::FFI {
            name: name.to_string(),
            expression: Box::new(expression),
        }
    }
}