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
use std::collections::BTreeMap;

use lexpr::Value;

use crate::{
    graph::{
        NodeId,
        ShaderGraph,
    },
    lisp::Val,
};

pub type FnDef = (Vec<String>, Vec<Value>);

#[derive(Debug)]
pub struct Scope<T> {
    items: Vec<BTreeMap<String, T>>,
}

impl<T> Scope<T> {
    pub fn new() -> Self {
        Scope {
            items: vec![BTreeMap::new()],
        }
    }

    pub fn get(&self, name: &str) -> Result<&T, String> {
        for item in self.items.iter().rev() {
            if let Some(item) = item.get(name) {
                return Ok(item);
            }
        }

        Err(format!("Item `{}` is not defined", name))
    }

    pub fn set(&mut self, name: String, item: T) {
        self.items.last_mut().unwrap().insert(name, item);
    }

    pub fn enter_scope(&mut self) { self.items.push(BTreeMap::new()) }

    pub fn exit_scope(&mut self) -> BTreeMap<String, T> {
        self.items.pop().unwrap()
    }
}

pub type ExternalFn =
    Box<dyn Fn(&mut ShaderGraph, &[NodeId]) -> Result<NodeId, String>>;
pub type External = BTreeMap<String, ExternalFn>;

// Note that functions are not first class.
// We're trying to describe a graph,
// not make a turing complete language ;)
pub struct Env {
    /// Maps name to value, separate from function scope.
    vars:      Scope<Val>,
    /// Maps name to args, body.
    functions: Scope<FnDef>,
    /// Maps shader name to shader source.
    shaders:   BTreeMap<String, String>,
    /// Maps names to rust functions that construct
    /// subgraphs.
    external:  External,
}

impl std::fmt::Debug for Env {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Env")
            .field("vars", &self.vars)
            .field("functions", &self.functions)
            .field("shaders", &self.shaders.keys().collect::<Vec<&String>>())
            .field("external", &self.external.keys().collect::<Vec<&String>>())
            .finish()
    }
}

impl Env {
    pub fn new(shaders: BTreeMap<String, String>, external: External) -> Env {
        Env {
            vars: Scope::new(),
            functions: Scope::new(),
            shaders,
            external,
        }
    }

    pub fn get(&self, name: &str) -> Result<&Val, String> {
        self.vars.get(name)
    }

    pub fn set(&mut self, name: String, item: Val) { self.vars.set(name, item) }

    pub fn get_fn(&self, name: &str) -> Result<&FnDef, String> {
        self.functions.get(name)
    }

    pub fn set_fn(&mut self, name: String, item: FnDef) {
        self.functions.set(name, item)
    }

    pub fn enter_scope(&mut self) {
        self.vars.enter_scope();
        self.functions.enter_scope();
    }

    pub fn exit_scope(&mut self) {
        self.vars.exit_scope();
        self.functions.exit_scope();
    }

    pub fn shader(&self, name: &str) -> Result<&String, String> {
        self.shaders.get(name).ok_or(format!(
            "Could not load shader `{}`, it is not defined",
            name
        ))
    }

    pub fn external(&self, name: &str) -> Result<&ExternalFn, String> {
        self.external.get(name).ok_or(format!(
            "Could not load external function `{}`, it is not defined",
            name
        ))
    }
}