Skip to main content

sequoia_seq/
ir.rs

1#[derive(Clone, Debug)]
2pub struct SeqApp {
3    pub title: String,
4
5    pub size: (f32, f32),
6
7    pub theme: ThemeName,
8
9    pub windows: Vec<SeqWindow>,
10
11    pub deferred: Vec<DeferredDecl>,
12
13    pub styles: Vec<StyleDecl>,
14
15    pub components: Vec<ComponentDef>,
16
17    pub states: Vec<StateDecl>,
18
19    pub fns: Vec<SeqFn>,
20
21    pub imports: Vec<ImportDecl>,
22
23    pub app_line: usize,
24
25    pub has_title: bool,
26
27    pub has_size: bool,
28}
29
30#[derive(Clone, Debug)]
31pub struct SeqFile {
32    pub imports: Vec<ImportDecl>,
33
34    pub components: Vec<ComponentDef>,
35
36    pub app: Option<SeqApp>,
37}
38
39#[derive(Clone, Debug)]
40pub struct ImportDecl {
41    pub path: String,
42
43    pub line: usize,
44}
45
46#[derive(Clone, Copy, Debug, PartialEq, Eq)]
47pub enum Visibility {
48    Pub,
49
50    Priv,
51}
52
53#[derive(Clone, Debug)]
54pub struct DeferredDecl {
55    pub kind: DeferredKind,
56
57    pub name: String,
58
59    pub line: usize,
60
61    pub sig: Vec<String>,
62}
63
64#[derive(Clone, Copy, Debug, PartialEq, Eq)]
65pub enum DeferredKind {
66    Prop,
67
68    On,
69
70    Style,
71}
72
73#[derive(Clone, Debug)]
74pub struct StyleDecl {
75    pub name: String,
76
77    pub line: usize,
78
79    pub target: String,
80
81    pub base: Option<String>,
82
83    pub args: Vec<SeqArg>,
84}
85
86#[derive(Clone, Debug)]
87pub struct CompParam {
88    pub name: String,
89
90    pub line: usize,
91
92    pub ty: String,
93}
94
95#[derive(Clone, Debug)]
96pub struct ComponentDef {
97    pub name: String,
98
99    pub line: usize,
100
101    pub params: Vec<CompParam>,
102
103    pub body: Vec<SeqNode>,
104
105    pub visibility: Visibility,
106
107    pub file: String,
108}
109
110#[derive(Clone, Debug, PartialEq, Eq)]
111pub enum ThemeName {
112    Dark,
113    Light,
114
115    File(String),
116}
117
118#[derive(Clone, Debug)]
119pub struct SeqWindow {
120    pub name: String,
121
122    pub line: usize,
123
124    pub size: Option<(f32, f32)>,
125
126    pub nodes: Vec<SeqNode>,
127}
128
129#[derive(Clone, Debug)]
130pub struct SeqNode {
131    pub kind: String,
132
133    pub line: usize,
134
135    pub label: Option<String>,
136
137    pub args: Vec<SeqArg>,
138
139    pub children: Vec<SeqNode>,
140}
141
142#[derive(Clone, Debug)]
143pub struct SeqArg {
144    pub name: String,
145
146    pub line: usize,
147
148    pub value: SeqValue,
149}
150
151#[derive(Clone, Debug)]
152pub enum SeqValue {
153    Str(String),
154
155    Num(f32),
156
157    Bool(bool),
158
159    Color(String),
160
161    Theme(String),
162
163    Length(LengthVal),
164
165    Ident(String),
166
167    Callback { action: String, args: Vec<SeqValue> },
168
169    CallSeq { name: String, args: Vec<SeqValue> },
170
171    Dynamic(String),
172
173    Env(String),
174
175    ItemField(String),
176
177    Tuple(Vec<SeqValue>),
178
179    Array(Vec<SeqValue>),
180
181    Expr(SeqExpr),
182}
183
184impl SeqValue {
185    pub(crate) fn fold_exprs(self) -> Self {
186        match self {
187            Self::Expr(expr) => match evaluate_seq_expr(&expr) {
188                Ok(value) => value.fold_exprs(),
189                Err(_) => Self::Expr(expr),
190            },
191            Self::Tuple(items) => Self::Tuple(items.into_iter().map(Self::fold_exprs).collect()),
192            Self::Array(items) => Self::Array(items.into_iter().map(Self::fold_exprs).collect()),
193            Self::Callback { action, args } => Self::Callback {
194                action,
195                args: args.into_iter().map(Self::fold_exprs).collect(),
196            },
197            Self::CallSeq { name, args } => Self::CallSeq {
198                name,
199                args: args.into_iter().map(Self::fold_exprs).collect(),
200            },
201            other => other,
202        }
203    }
204}
205
206pub(crate) fn evaluate_seq_expr(expr: &SeqExpr) -> Result<SeqValue, String> {
207    match &expr.kind {
208        SeqExprKind::Int(i) => Ok(SeqValue::Num(*i as f32)),
209        SeqExprKind::Float(f) => {
210            #[allow(clippy::cast_possible_truncation)]
211            let value = *f as f32;
212            if value.is_finite() {
213                Ok(SeqValue::Num(value))
214            } else {
215                Err("non-finite float".to_string())
216            }
217        }
218        SeqExprKind::Str(s) => Ok(SeqValue::Str(s.clone())),
219        SeqExprKind::Bool(b) => Ok(SeqValue::Bool(*b)),
220        SeqExprKind::Tuple(items) => {
221            let mut out = Vec::with_capacity(items.len());
222            for item in items {
223                out.push(evaluate_seq_expr(item)?);
224            }
225            Ok(SeqValue::Tuple(out))
226        }
227        SeqExprKind::Array(items) => {
228            let mut out = Vec::with_capacity(items.len());
229            for item in items {
230                out.push(evaluate_seq_expr(item)?);
231            }
232            Ok(SeqValue::Array(out))
233        }
234        SeqExprKind::Unary { op, expr } => {
235            let v = evaluate_seq_expr(expr)?;
236            match (op, &v) {
237                (UnOp::Neg, SeqValue::Num(n)) => Ok(SeqValue::Num(-n)),
238                (UnOp::Not, SeqValue::Bool(b)) => Ok(SeqValue::Bool(!b)),
239                _ => Err("invalid unary operation".to_string()),
240            }
241        }
242        SeqExprKind::Binary { op, lhs, rhs } => {
243            let l = evaluate_seq_expr(lhs)?;
244            let r = evaluate_seq_expr(rhs)?;
245            match (op, &l, &r) {
246                (BinOp::Add, SeqValue::Str(a), SeqValue::Str(b)) => {
247                    Ok(SeqValue::Str(format!("{a}{b}")))
248                }
249                (BinOp::Add, SeqValue::Num(a), SeqValue::Num(b)) => Ok(SeqValue::Num(a + b)),
250                (BinOp::Sub, SeqValue::Num(a), SeqValue::Num(b)) => Ok(SeqValue::Num(a - b)),
251                (BinOp::Mul, SeqValue::Num(a), SeqValue::Num(b)) => Ok(SeqValue::Num(a * b)),
252                (BinOp::Div, SeqValue::Num(a), SeqValue::Num(b)) => {
253                    if *b == 0.0 {
254                        Err("division by zero".to_string())
255                    } else {
256                        Ok(SeqValue::Num(a / b))
257                    }
258                }
259                (BinOp::Rem, SeqValue::Num(a), SeqValue::Num(b)) => {
260                    if *b == 0.0 {
261                        Err("remainder by zero".to_string())
262                    } else {
263                        Ok(SeqValue::Num(a % b))
264                    }
265                }
266                (BinOp::Eq, _, _) => Ok(SeqValue::Bool(values_eq(&l, &r))),
267                (BinOp::Ne, _, _) => Ok(SeqValue::Bool(!values_eq(&l, &r))),
268                (BinOp::Lt, SeqValue::Num(a), SeqValue::Num(b)) => Ok(SeqValue::Bool(a < b)),
269                (BinOp::Le, SeqValue::Num(a), SeqValue::Num(b)) => Ok(SeqValue::Bool(a <= b)),
270                (BinOp::Gt, SeqValue::Num(a), SeqValue::Num(b)) => Ok(SeqValue::Bool(a > b)),
271                (BinOp::Ge, SeqValue::Num(a), SeqValue::Num(b)) => Ok(SeqValue::Bool(a >= b)),
272                (BinOp::And, SeqValue::Bool(a), SeqValue::Bool(b)) => Ok(SeqValue::Bool(*a && *b)),
273                (BinOp::Or, SeqValue::Bool(a), SeqValue::Bool(b)) => Ok(SeqValue::Bool(*a || *b)),
274                _ => Err("invalid binary operation".to_string()),
275            }
276        }
277        SeqExprKind::Ternary { cond, then, els } => {
278            let c = evaluate_seq_expr(cond)?;
279            match c {
280                SeqValue::Bool(true) => evaluate_seq_expr(then),
281                SeqValue::Bool(false) => evaluate_seq_expr(els),
282                _ => Err("ternary condition must be Bool".to_string()),
283            }
284        }
285        SeqExprKind::Var(name) => match name.as_str() {
286            "true" => Ok(SeqValue::Bool(true)),
287            "false" => Ok(SeqValue::Bool(false)),
288            "fit" => Ok(SeqValue::Length(LengthVal::Fit)),
289            _ => Ok(SeqValue::Ident(name.clone())),
290        },
291        SeqExprKind::Field { base, field } => match base.as_str() {
292            "env" => Ok(SeqValue::Env(field.clone())),
293            "theme" => Ok(SeqValue::Theme(field.clone())),
294            "item" => Ok(SeqValue::ItemField(field.clone())),
295            _ => Ok(SeqValue::Ident(format!("{base}.{field}"))),
296        },
297        SeqExprKind::Call { name, args } => match name.as_str() {
298            "fill" if args.len() == 1 => {
299                if let SeqValue::Num(n) = evaluate_seq_expr(&args[0])? {
300                    Ok(SeqValue::Length(LengthVal::Fill(n)))
301                } else {
302                    Err("fill requires number".to_string())
303                }
304            }
305            "px" if args.len() == 1 => {
306                if let SeqValue::Num(n) = evaluate_seq_expr(&args[0])? {
307                    Ok(SeqValue::Length(LengthVal::Px(n)))
308                } else {
309                    Err("px requires number".to_string())
310                }
311            }
312            "percent" if args.len() == 1 => {
313                if let SeqValue::Num(n) = evaluate_seq_expr(&args[0])? {
314                    Ok(SeqValue::Length(LengthVal::Percent(n)))
315                } else {
316                    Err("percent requires number".to_string())
317                }
318            }
319            "dynamic" if args.len() == 1 => match evaluate_seq_expr(&args[0])? {
320                SeqValue::Ident(s) | SeqValue::Str(s) => Ok(SeqValue::Dynamic(s)),
321                _ => Err("dynamic requires string or ident".to_string()),
322            },
323            "min" | "max" if args.len() == 2 => {
324                let a = evaluate_seq_expr(&args[0])?;
325                let b = evaluate_seq_expr(&args[1])?;
326                match (&a, &b) {
327                    (SeqValue::Num(x), SeqValue::Num(y)) => {
328                        let val = if name == "max" { x.max(*y) } else { x.min(*y) };
329                        Ok(SeqValue::Num(val))
330                    }
331                    _ => Err(format!("{name} requires numbers")),
332                }
333            }
334            "clamp" if args.len() == 3 => {
335                let v = evaluate_seq_expr(&args[0])?;
336                let lo = evaluate_seq_expr(&args[1])?;
337                let hi = evaluate_seq_expr(&args[2])?;
338                match (&v, &lo, &hi) {
339                    (SeqValue::Num(v), SeqValue::Num(lo), SeqValue::Num(hi)) => {
340                        Ok(SeqValue::Num((*v).max(*lo).min(*hi)))
341                    }
342                    _ => Err("clamp requires numbers".to_string()),
343                }
344            }
345            "powf" if args.len() == 2 => {
346                let a = evaluate_seq_expr(&args[0])?;
347                let b = evaluate_seq_expr(&args[1])?;
348                match (&a, &b) {
349                    (SeqValue::Num(x), SeqValue::Num(y)) => Ok(SeqValue::Num(x.powf(*y))),
350                    _ => Err("powf requires numbers".to_string()),
351                }
352            }
353            "len" if args.len() == 1 => {
354                let a = evaluate_seq_expr(&args[0])?;
355                match a {
356                    SeqValue::Str(s) => Ok(SeqValue::Num(s.chars().count() as f32)),
357                    _ => Err("len requires string".to_string()),
358                }
359            }
360            _ => {
361                let mut evaluated_args = Vec::with_capacity(args.len());
362                for arg in args {
363                    evaluated_args.push(evaluate_seq_expr(arg)?);
364                }
365                Ok(SeqValue::CallSeq {
366                    name: name.clone(),
367                    args: evaluated_args,
368                })
369            }
370        },
371    }
372}
373
374fn values_eq(a: &SeqValue, b: &SeqValue) -> bool {
375    match (a, b) {
376        (SeqValue::Num(x), SeqValue::Num(y)) => x == y,
377        (SeqValue::Str(x), SeqValue::Str(y)) => x == y,
378        (SeqValue::Bool(x), SeqValue::Bool(y)) => x == y,
379        _ => false,
380    }
381}
382
383#[derive(Clone, Copy, Debug)]
384pub enum LengthVal {
385    Fill(f32),
386
387    Px(f32),
388
389    Fit,
390
391    Percent(f32),
392}
393
394#[derive(Clone, Debug)]
395pub struct StateDecl {
396    pub name: String,
397
398    pub line: usize,
399
400    pub ty: String,
401
402    pub init: SeqExpr,
403}
404
405#[derive(Clone, Debug)]
406pub struct FnParam {
407    pub name: String,
408
409    pub mutable: bool,
410
411    pub line: usize,
412
413    pub ty: String,
414}
415
416#[derive(Clone, Debug)]
417pub struct SeqFn {
418    pub name: String,
419
420    pub line: usize,
421
422    pub params: Vec<FnParam>,
423
424    pub ret: Option<String>,
425
426    pub body: Vec<SeqStmt>,
427}
428
429#[derive(Clone, Debug)]
430pub enum SeqStmt {
431    Let {
432        name: String,
433
434        mutable: bool,
435
436        value: SeqExpr,
437
438        line: usize,
439    },
440
441    Assign {
442        name: String,
443
444        value: SeqExpr,
445
446        line: usize,
447    },
448
449    If {
450        cond: SeqExpr,
451
452        then: Vec<SeqStmt>,
453
454        elifs: Vec<(SeqExpr, Vec<SeqStmt>)>,
455
456        els: Option<Vec<SeqStmt>>,
457
458        line: usize,
459    },
460
461    For {
462        var: String,
463
464        source: String,
465
466        body: Vec<SeqStmt>,
467
468        line: usize,
469    },
470
471    Match {
472        value: SeqExpr,
473
474        arms: Vec<SeqMatchArm>,
475
476        line: usize,
477    },
478
479    Return {
480        value: Option<SeqExpr>,
481
482        line: usize,
483    },
484
485    Expr(SeqExpr),
486}
487
488#[derive(Clone, Debug)]
489pub struct SeqMatchArm {
490    pub pattern: MatchPat,
491
492    pub body: Vec<SeqStmt>,
493
494    pub line: usize,
495}
496
497#[derive(Clone, Debug)]
498pub enum MatchPat {
499    Str(String),
500
501    Int(i64),
502
503    Bool(bool),
504
505    Wildcard,
506}
507
508#[derive(Clone, Debug)]
509pub struct SeqExpr {
510    pub line: usize,
511
512    pub kind: SeqExprKind,
513}
514
515#[derive(Clone, Debug)]
516pub enum SeqExprKind {
517    Int(i64),
518
519    Float(f64),
520
521    Str(String),
522
523    Bool(bool),
524
525    Var(String),
526
527    Field {
528        base: String,
529
530        field: String,
531    },
532
533    Tuple(Vec<SeqExpr>),
534
535    Array(Vec<SeqExpr>),
536
537    Unary {
538        op: UnOp,
539
540        expr: Box<SeqExpr>,
541    },
542
543    Binary {
544        op: BinOp,
545
546        lhs: Box<SeqExpr>,
547
548        rhs: Box<SeqExpr>,
549    },
550
551    Call {
552        name: String,
553
554        args: Vec<SeqExpr>,
555    },
556
557    Ternary {
558        cond: Box<SeqExpr>,
559
560        then: Box<SeqExpr>,
561
562        els: Box<SeqExpr>,
563    },
564}
565
566#[derive(Clone, Copy, Debug, PartialEq, Eq)]
567pub enum UnOp {
568    Neg,
569
570    Not,
571}
572
573#[derive(Clone, Copy, Debug, PartialEq, Eq)]
574pub enum BinOp {
575    Add,
576
577    Sub,
578
579    Mul,
580
581    Div,
582
583    Rem,
584
585    Eq,
586
587    Ne,
588
589    Lt,
590
591    Le,
592
593    Gt,
594
595    Ge,
596
597    And,
598
599    Or,
600}
601
602#[must_use]
603pub fn resolve_seq_calls(mut app: SeqApp) -> SeqApp {
604    let fns: std::collections::HashSet<&str> = app.fns.iter().map(|f| f.name.as_str()).collect();
605    if fns.is_empty() {
606        return app;
607    }
608    for style in &mut app.styles {
609        for arg in &mut style.args {
610            resolve_value(&fns, &mut arg.value);
611        }
612    }
613    for window in &mut app.windows {
614        for node in &mut window.nodes {
615            resolve_node(&fns, node);
616        }
617    }
618    for comp in &mut app.components {
619        for node in &mut comp.body {
620            resolve_node(&fns, node);
621        }
622    }
623    app
624}
625
626fn resolve_node(fns: &std::collections::HashSet<&str>, node: &mut SeqNode) {
627    for arg in &mut node.args {
628        resolve_value(fns, &mut arg.value);
629    }
630    for child in &mut node.children {
631        resolve_node(fns, child);
632    }
633}
634
635fn resolve_value(fns: &std::collections::HashSet<&str>, value: &mut SeqValue) {
636    if let SeqValue::Callback { action, args } = value
637        && fns.contains(action.as_str())
638    {
639        let name = std::mem::take(action);
640        let mut taken = Vec::new();
641        std::mem::swap(args, &mut taken);
642        *value = SeqValue::CallSeq { name, args: taken };
643    }
644    match value {
645        SeqValue::Callback { args, .. } | SeqValue::CallSeq { args, .. } => {
646            for arg in args {
647                resolve_value(fns, arg);
648            }
649        }
650        SeqValue::Tuple(items) | SeqValue::Array(items) => {
651            for item in items {
652                resolve_value(fns, item);
653            }
654        }
655        SeqValue::Expr(_) => {}
656        _ => {}
657    }
658}