makepad_shader_compiler/
const_eval.rs

1use{
2    std::cell::Cell,
3    crate::{
4        makepad_live_compiler::{
5            LiveError,
6            LiveErrorOrigin,
7            live_error_origin,
8            TokenSpan
9        },
10        shader_ast::*,
11        analyse::ShaderAnalyseOptions
12    }
13};
14
15pub struct ConstEvaluator {
16    pub options: ShaderAnalyseOptions
17}
18
19impl ConstEvaluator {
20    pub fn const_eval_expr(&self, expr: &Expr) -> Result<Val, LiveError> {
21        self.try_const_eval_expr(expr).ok_or_else(|| LiveError {
22            origin:live_error_origin!(),
23            span: expr.span.into(),
24            message: String::from("expression is not const"),
25        })
26    }
27
28    pub fn try_const_eval_expr(&self, expr: &Expr) -> Option<Val> {
29        let const_val = match expr.kind {
30            ExprKind::Cond {
31                span,
32                ref expr,
33                ref expr_if_true,
34                ref expr_if_false,
35            } => self.try_const_eval_cond_expr(span, expr, expr_if_true, expr_if_false),
36            ExprKind::Bin {
37                span,
38                op,
39                ref left_expr,
40                ref right_expr,
41            } => self.try_const_eval_bin_expr(span, op, left_expr, right_expr),
42            ExprKind::Un { span, op, ref expr } => self.try_const_eval_un_expr(span, op, expr),
43            ExprKind::Field {
44                span,
45                ref expr,
46                field_ident,
47            } => self.try_const_eval_field_expr(span, expr, field_ident),
48            ExprKind::Index {
49                span,
50                ref expr,
51                ref index_expr,
52            } => self.try_const_eval_index_expr(span, expr, index_expr),
53            ExprKind::MethodCall {
54                ref arg_exprs,
55                ..
56            } => self.try_const_eval_all_call_expr(arg_exprs),
57            ExprKind::PlainCall {
58                ref arg_exprs,
59                ..
60            } => self.try_const_eval_all_call_expr(arg_exprs),
61            ExprKind::BuiltinCall {
62                ref arg_exprs,
63                ..
64            } => self.try_const_eval_all_call_expr(arg_exprs),
65            /*xprKind::ClosureCall {
66                ref arg_exprs,
67                ..
68            } => self.try_const_eval_all_call_expr(arg_exprs),*/
69            ExprKind::ClosureDef(_) => None,
70            ExprKind::ConsCall {
71                ref arg_exprs,
72                ..
73            } => self.try_const_eval_all_call_expr(arg_exprs),
74            ExprKind::Var {
75                span,
76                ref kind,
77                ..
78            } => self.try_const_eval_var_expr(span, kind),
79            ExprKind::StructCons{
80                struct_ptr,
81                span,
82                ref args
83            } => self.try_const_eval_struct_cons(struct_ptr, span, args),
84            ExprKind::Lit { span, lit } => self.try_const_eval_lit_expr(span, lit),
85        };
86        *expr.const_val.borrow_mut() = Some(const_val.clone());
87        expr.const_index.set(None);
88        const_val
89    }
90
91    fn try_const_eval_cond_expr(
92        &self,
93        _span: TokenSpan,
94        expr: &Expr,
95        expr_if_true: &Expr,
96        expr_if_false: &Expr,
97    ) -> Option<Val> {
98        let val = self.try_const_eval_expr(expr)?;
99        let val_if_true = self.try_const_eval_expr(expr_if_true)?;
100        let val_if_false = self.try_const_eval_expr(expr_if_false)?;
101        Some(if val.to_bool().unwrap() {
102            val_if_true
103        } else {
104            val_if_false
105        })
106    }
107
108    #[allow(clippy::float_cmp)]
109    fn try_const_eval_bin_expr(
110        &self,
111        _span: TokenSpan,
112        op: BinOp,
113        left_expr: &Expr,
114        right_expr: &Expr,
115    ) -> Option<Val> {
116        let left_val = self.try_const_eval_expr(left_expr);
117        let right_val = self.try_const_eval_expr(right_expr);
118        let left_val = left_val?;
119        let right_val = right_val?;
120        if self.options.no_const_collapse{
121            return None
122        }
123        match op {
124            BinOp::Or => match (&left_val, &right_val) {
125                (Val::Bool(x), Val::Bool(y)) => Some(Val::Bool(*x || *y)),
126                _ => None,
127            },
128            BinOp::And => match (&left_val, &right_val) {
129                (Val::Bool(x), Val::Bool(y)) => Some(Val::Bool(*x && *y)),
130                _ => None,
131            },
132            BinOp::Eq => match (&left_val, &right_val) {
133                (Val::Bool(x), Val::Bool(y)) => Some(Val::Bool(x == y)),
134                (Val::Int(x), Val::Int(y)) => Some(Val::Bool(x == y)),
135                (Val::Float(x), Val::Float(y)) => Some(Val::Bool(x == y)),
136                _ => None,
137            },
138            BinOp::Ne => match (&left_val, &right_val) {
139                (Val::Bool(x), Val::Bool(y)) => Some(Val::Bool(x != y)),
140                (Val::Int(x), Val::Int(y)) => Some(Val::Bool(x != y)),
141                (Val::Float(x), Val::Float(y)) => Some(Val::Bool(x != y)),
142                _ => None,
143            },
144            BinOp::Lt => match (&left_val, &right_val) {
145                (Val::Int(x), Val::Int(y)) => Some(Val::Bool(x < y)),
146                (Val::Float(x), Val::Float(y)) => Some(Val::Bool(x < y)),
147                _ => None,
148            },
149            BinOp::Le => match (&left_val, &right_val) {
150                (Val::Int(x), Val::Int(y)) => Some(Val::Bool(x <= y)),
151                (Val::Float(x), Val::Float(y)) => Some(Val::Bool(x <= y)),
152                _ => None,
153            },
154            BinOp::Gt => match (&left_val, &right_val) {
155                (Val::Int(x), Val::Int(y)) => Some(Val::Bool(x > y)),
156                (Val::Float(x), Val::Float(y)) => Some(Val::Bool(x > y)),
157                _ => None,
158            },
159            BinOp::Ge => match (&left_val, &right_val) {
160                (Val::Int(x), Val::Int(y)) => Some(Val::Bool(x >= y)),
161                (Val::Float(x), Val::Float(y)) => Some(Val::Bool(x >= y)),
162                _ => None,
163            },
164            BinOp::Add => match (&left_val, &right_val) {
165                (Val::Int(x), Val::Int(y)) => Some(Val::Int(x + y)),
166                (Val::Float(x), Val::Float(y)) => Some(Val::Float(x + y)),
167                _ => None,
168            },
169            BinOp::Sub => match (&left_val, &right_val) {
170                (Val::Int(x), Val::Int(y)) => Some(Val::Int(x - y)),
171                (Val::Float(x), Val::Float(y)) => Some(Val::Float(x - y)),
172                _ => None,
173            },
174            BinOp::Mul => match (&left_val, &right_val) {
175                (Val::Int(x), Val::Int(y)) => Some(Val::Int(x * y)),
176                (Val::Float(x), Val::Float(y)) => Some(Val::Float(x * y)),
177                _ => None,
178            },
179            BinOp::Div => match (&left_val, &right_val) {
180                (Val::Int(x), Val::Int(y)) => Some(Val::Int(x / y)),
181                (Val::Float(x), Val::Float(y)) => Some(Val::Float(x / y)),
182                _ => None,
183            },
184            _ => None,
185        }
186    }
187
188    fn try_const_eval_un_expr(&self, _span: TokenSpan, op: UnOp, expr: &Expr) -> Option<Val> {
189        let val = self.try_const_eval_expr(expr);
190        let val = val?;
191        if self.options.no_const_collapse{
192            return None
193        }
194        match op {
195            UnOp::Not => match val {
196                Val::Bool(x) => Some(Val::Bool(!x)),
197                _ => None,
198            },
199            UnOp::Neg => match val {
200                Val::Int(x) => Some(Val::Int(-x)),
201                Val::Float(x) => Some(Val::Float(-x)),
202                _ => None,
203            },
204        }
205    }
206
207    fn try_const_eval_field_expr(
208        &self,
209        _span: TokenSpan,
210        expr: &Expr,
211        _field_ident: Ident,
212    ) -> Option<Val> {
213        self.try_const_eval_expr(expr);
214        None
215    }
216
217    fn try_const_eval_index_expr(
218        &self,
219        _span: TokenSpan,
220        expr: &Expr,
221        _index_expr: &Expr,
222    ) -> Option<Val> {
223        self.try_const_eval_expr(expr);
224        None
225    }
226
227    fn try_const_eval_all_call_expr(
228        &self,
229        //_span: Span,
230        //_ident: Ident,
231        arg_exprs: &[Expr],
232    ) -> Option<Val> {
233        for arg_expr in arg_exprs {
234            self.try_const_eval_expr(arg_expr);
235        }
236        None
237    }
238
239    fn try_const_eval_var_expr(
240        &self,
241        _span: TokenSpan,
242        _kind: &Cell<Option<VarKind>>,
243        //_ident_path: IdentPath,
244    ) -> Option<Val> {
245        None
246    }
247
248    fn try_const_eval_struct_cons(
249        &self,
250        _struct_ptr: StructPtr,
251        _span: TokenSpan,
252        args: &Vec<(Ident,Expr)>,
253    ) -> Option<Val> {
254        for arg in args{
255            self.try_const_eval_expr(&arg.1);
256        }
257        None
258    }
259
260    fn try_const_eval_lit_expr(&self, _span: TokenSpan, lit: Lit) -> Option<Val> {
261        Some(lit.to_val())
262    }
263}