wlambda/
prog_writer.rs

1use crate::vval::SynPos;
2use crate::ops::*;
3use crate::compiler::ResPos;
4use crate::compiler::ResValue;
5
6#[derive(Debug, Clone)]
7pub(crate) enum ResultSink {
8    WriteTo(ResPos),
9    WantResult,
10    Null,
11}
12
13impl ResultSink {
14    pub(crate) fn if_null<T>(&self, f: T) -> bool
15        where T: FnOnce(ResPos)
16    {
17        match self {
18            ResultSink::WriteTo(_) => true,
19            ResultSink::WantResult => true,
20            ResultSink::Null => {
21                let rp = ResPos::Value(ResValue::None);
22                f(rp);
23                false
24            },
25        }
26    }
27
28    pub(crate) fn if_must_store<T>(&self, f: T) -> ResPos
29        where T: FnOnce(ResPos)
30    {
31        match self {
32            ResultSink::WriteTo(rp) => {
33                f(*rp);
34                *rp
35            },
36            ResultSink::WantResult => {
37                let rp = ResPos::Stack(0);
38                f(rp);
39                rp
40            },
41            ResultSink::Null => ResPos::Value(ResValue::None),
42        }
43    }
44}
45
46pub(crate) type ProgWriteNode = Box<dyn Fn(&mut Prog, ResultSink) -> ResPos>;
47
48pub(crate) struct ProgWriter {
49    node:  ProgWriteNode,
50}
51
52impl ProgWriter {
53    pub(crate) fn eval_to(&self, prog: &mut Prog, rp: ResPos) {
54        (*self.node)(prog, ResultSink::WriteTo(rp));
55    }
56
57    pub(crate) fn eval_proxy(&self, prog: &mut Prog, rs: ResultSink) -> ResPos {
58        (*self.node)(prog, rs)
59    }
60
61    pub(crate) fn eval_nul(&self, prog: &mut Prog) {
62        let rp = (*self.node)(prog, ResultSink::Null);
63        if let ResPos::Stack(_) = rp {
64            prog.op_mov(&SynPos::empty(), rp, ResPos::Value(ResValue::None));
65        }
66    }
67
68    pub(crate) fn eval(&self, prog: &mut Prog) -> ResPos {
69        (*self.node)(prog, ResultSink::WantResult)
70    }
71}
72
73pub(crate) fn pw(f: ProgWriteNode) -> ProgWriter {
74    ProgWriter {
75        node:   Box::new(f),
76    }
77}
78
79/// Internal helper macro for the code generator. It creates a `ProgWriter`
80/// instance from the given code block.
81/// `$prog` is the variable of the
82/// [Prog] and `$store` is the supplied store type that is requested
83/// by the parent of this AST node.
84#[macro_export]
85macro_rules! pw {
86    ($prog: ident, $store: ident, $b: block) => {
87        Ok(pw(Box::new(move |$prog, $store| {
88            $b
89        })))
90    }
91}
92
93/// Internal helper macro for the code generator. It creates a `ProgWriter`
94/// instance from the given code block. `pw_null` is used in cases where
95/// there are only side effects and no return values to be stored anywhere.
96/// `$prog` is the variable of the [Prog].
97#[macro_export]
98macro_rules! pw_null {
99    ($prog: ident, $b: block) => {
100        pw_provides_result_pos!($prog, {
101            $b
102            ResPos::Value(ResValue::None)
103        })
104    }
105}
106
107/// Internal helper macro for the code generator. It creates a `ProgWriter`
108/// instance from the given code block. It's used in cases where the `ProgWriter`
109/// code in the code block `$b` defines the storage position where it wrote
110/// the results to. The calling parent then has to take care to store
111/// the value in some reasonable place or not store it at all.
112/// `$prog` is the variable of the [Prog].
113#[macro_export]
114macro_rules! pw_provides_result_pos {
115    ($prog: ident, $b: block) => {
116        pw!($prog, store, {
117            let pos = $b;
118            match store {
119                ResultSink::WriteTo(store_pos) => {
120                    $prog.op_mov(&SynPos::empty(), pos, store_pos);
121                    store_pos
122                },
123                ResultSink::WantResult => {
124                    pos
125                },
126                ResultSink::Null => {
127                    if let ResPos::Stack(_) = pos {
128                        $prog.op_mov(&SynPos::empty(), pos, ResPos::Value(ResValue::None));
129                    }
130                    ResPos::Value(ResValue::None)
131                },
132            }
133        })
134    }
135}
136
137/// Internal helper macro for the code generator. It creates a `ProgWriter`
138/// instance from the given code block. It's used where the `ProgWriter`
139/// needs it's parent to define a place to put it's result to, but it's not
140/// required to be stored.
141/// `$prog` is the variable of the [Prog].
142#[macro_export]
143macro_rules! pw_store_if_needed {
144    ($prog: ident, $pos: ident, $b: block) => {
145        pw!($prog, store, {
146            let $pos =
147                match store {
148                    ResultSink::WriteTo(store_pos) => store_pos,
149                    ResultSink::WantResult => ResPos::Stack(0),
150                    ResultSink::Null => ResPos::Value(ResValue::None),
151                };
152
153            $b;
154            $pos
155        })
156    }
157}
158
159
160/// Internal helper macro for the code generator. It creates a `ProgWriter`
161/// instance from the given code block. It's used where the `ProgWriter`
162/// needs it's parent to define a place to put it's result to.
163/// `$prog` is the variable of the [Prog].
164#[macro_export]
165macro_rules! pw_needs_storage {
166    ($prog: ident, $pos: ident, $b: block) => {
167        pw!($prog, store, {
168            match store {
169                ResultSink::WriteTo(store_pos) => {
170                    let $pos = store_pos;
171                    $b;
172                    $pos
173                },
174                ResultSink::WantResult => {
175                    let $pos = ResPos::Stack(0);
176                    $b;
177                    $pos
178                },
179                ResultSink::Null => {
180                    let $pos = ResPos::Stack(0);
181                    $b;
182                    $prog.op_mov(&SynPos::empty(), $pos, ResPos::Value(ResValue::None));
183                    ResPos::Value(ResValue::None)
184                },
185            }
186        })
187    }
188}