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#[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#[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#[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#[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#[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}