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}