1use std::rc::Rc;
5
6use nifty_runtime::{
7 Backdrop, ButtonStyle, Icon, ImageSize, LabelStyle, Size, StackStyle, ToggleStyle,
8};
9pub use nifty_runtime::{
10 DialogKind, DownloadCommand, DownloadField, Menu, MenuEntry, Shortcut, WindowAction,
11};
12pub use nifty_runtime::{StateId, StateSet};
13use nifty_syntax::Span;
14pub use nifty_syntax::ast::BinaryOp;
15
16macro_rules! id {
17 ($name:ident) => {
18 #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
19 pub struct $name(pub usize);
20 };
21}
22
23id!(StructId);
24id!(FnId);
25id!(ViewId);
26id!(LocalId);
27id!(WindowId);
28
29#[derive(Debug, Default)]
30pub struct Program {
31 pub structs: Vec<StructDef>,
32 pub states: Vec<StateDef>,
33 pub functions: Vec<FnDef>,
34 pub views: Vec<ViewDef>,
35 pub windows: Vec<WindowDef>,
36 pub menu_bar: MenuBar,
37}
38
39#[derive(Debug, Default)]
41pub struct MenuBar {
42 pub menus: Vec<Menu<Block>>,
43 pub locals: Vec<Type>,
45}
46
47#[derive(Debug)]
48pub struct StructDef {
49 pub name: String,
50 pub fields: Vec<FieldDef>,
51}
52
53#[derive(Debug)]
54pub struct FieldDef {
55 pub name: String,
56 pub ty: Type,
57}
58
59#[derive(Debug)]
60pub struct StateDef {
61 pub name: String,
62 pub ty: Type,
63 pub init: Expr,
64}
65
66#[derive(Debug)]
68pub struct FnDef {
69 pub name: String,
70 pub params: usize,
71 pub return_type: Type,
72 pub body: Block,
73 pub locals: Vec<Type>,
75}
76
77#[derive(Debug)]
78pub struct ViewDef {
79 pub name: String,
80 pub params: usize,
81 pub body: Nodes,
82 pub locals: Vec<Type>,
84}
85
86#[derive(Debug)]
88pub struct NavItem {
89 pub title: Rc<Dynamic>,
90 pub value: Rc<Dynamic>,
91 pub selection: Rc<Binding>,
92 pub icon: Icon,
93 pub badge: Option<Rc<Dynamic>>,
94 pub deps: StateSet,
96}
97
98#[derive(Debug)]
99pub struct WindowDef {
100 pub title: String,
101 pub starts_open: bool,
103 pub size: Size,
104 pub backdrop: Backdrop,
105 pub style: StackStyle,
108 pub children: Nodes,
109 pub locals: Vec<Type>,
111}
112
113#[derive(Clone, Debug, PartialEq, Eq, Hash)]
114pub enum Type {
115 Int,
116 Float,
117 Bool,
118 String,
119 List(Rc<Type>),
120 Struct(StructId),
121 Void,
122 Error,
124}
125
126pub const PRIMITIVE_TYPES: &[Type] = &[Type::Int, Type::Float, Type::Bool, Type::String];
128
129impl Type {
130 #[must_use]
131 pub fn is_error(&self) -> bool {
132 matches!(self, Self::Error)
133 }
134
135 #[must_use]
137 pub fn primitive(name: &str) -> Option<Self> {
138 PRIMITIVE_TYPES
139 .iter()
140 .find(|primitive| primitive.primitive_name() == Some(name))
141 .cloned()
142 }
143
144 #[must_use]
146 pub const fn primitive_name(&self) -> Option<&'static str> {
147 match self {
148 Self::Int => Some("int"),
149 Self::Float => Some("float"),
150 Self::Bool => Some("bool"),
151 Self::String => Some("string"),
152 Self::List(_) | Self::Struct(_) | Self::Void | Self::Error => None,
153 }
154 }
155}
156
157pub type Block = Rc<[Stmt]>;
158pub type Nodes = Rc<[Node]>;
159
160#[derive(Debug)]
161pub enum Stmt {
162 Let(LocalId, Expr),
163 Assign {
164 place: Place,
165 op: Option<BinaryOp>,
167 value: Expr,
168 },
169 If {
170 branches: Vec<(Expr, Block)>,
171 otherwise: Option<Block>,
172 },
173 For {
174 head: Loop,
175 body: Block,
176 },
177 Return(Option<Expr>),
178 Expr(Expr),
179}
180
181#[derive(Debug)]
183pub struct Loop {
184 pub item: LocalId,
186 pub index: Option<LocalId>,
188 pub iterable: Iterable,
189}
190
191#[derive(Debug)]
194pub enum Iterable {
195 Place(Place),
196 Value(Expr),
197 Range {
199 start: Expr,
200 end: Expr,
201 },
202}
203
204#[derive(Debug)]
206pub struct Place {
207 pub root: PlaceRoot,
208 pub path: Vec<PlaceStep>,
209 pub ty: Type,
210 pub span: Span,
211}
212
213#[derive(Clone, Copy, Debug, PartialEq, Eq)]
214pub enum PlaceRoot {
215 State(StateId),
216 Local(LocalId),
217}
218
219#[derive(Debug)]
220pub enum PlaceStep {
221 Field(usize),
222 Index(Expr),
223}
224
225#[derive(Debug)]
226pub struct Expr {
227 pub kind: ExprKind,
228 pub ty: Type,
229 pub span: Span,
230}
231
232#[derive(Debug)]
233pub enum ExprKind {
234 Int(i64),
235 Float(f64),
236 Bool(bool),
237 String(Rc<str>),
238 Interpolate(Vec<Segment>),
239 State(StateId),
240 Local(LocalId),
241 List(Vec<Expr>),
242 Construct(StructId, Vec<Expr>),
243 Field(Box<Expr>, usize),
244 Index(Box<Expr>, Box<Expr>),
245 Not(Box<Expr>),
246 Negate(Box<Expr>),
247 Binary(BinaryOp, Box<Expr>, Box<Expr>),
248 Call(FnId, Vec<Expr>),
249 Len(Box<Expr>),
250 IsEmpty(Box<Expr>),
251 Push(Place, Box<Expr>),
252 Remove(Place, Box<Expr>),
253 Download(Box<Download>),
254 FormatBytes(Box<Expr>),
256 DownloadCommand(DownloadCommand, Box<Expr>),
258 Dialog(Box<Dialog>),
259 Window(WindowAction, WindowId),
261 Error,
263}
264
265#[derive(Debug)]
272pub struct Download {
273 pub url: Expr,
274 pub folder: Expr,
275 pub name: Expr,
276 pub connections: Expr,
277 pub headers: Expr,
279 pub retries: Expr,
280 pub bindings: Vec<(DownloadField, Place)>,
282}
283
284#[derive(Debug)]
288pub struct Dialog {
289 pub kind: DialogKind,
290 pub answer: Place,
291 pub title: Expr,
292 pub types: Expr,
293 pub folder: Expr,
294 pub name: Expr,
295}
296
297#[derive(Debug)]
298pub enum Segment {
299 Text(Rc<str>),
300 Value(Expr),
301}
302
303#[derive(Debug)]
305pub struct Dynamic {
306 pub expr: Expr,
307 pub deps: StateSet,
308}
309
310#[derive(Debug)]
312pub struct Binding {
313 pub place: Place,
314 pub deps: StateSet,
315}
316
317#[derive(Debug)]
320pub enum Node {
321 Stack {
323 style: StackStyle,
324 children: Nodes,
325 scrolls: bool,
326 },
327 Label {
328 text: Rc<Dynamic>,
329 style: LabelStyle,
330 },
331 Button {
333 title: Rc<Dynamic>,
334 style: ButtonStyle,
335 icon: Icon,
336 selected: Option<Rc<Dynamic>>,
337 action: Block,
338 },
339 TextField {
342 value: Rc<Binding>,
343 placeholder: String,
344 lines: u32,
345 action: Option<Block>,
346 },
347 Checkbox {
349 checked: Rc<Binding>,
350 title: Option<Rc<Dynamic>>,
351 style: ToggleStyle,
352 },
353 Slider {
355 value: Rc<Binding>,
356 min: i64,
357 max: i64,
358 },
359 Picker {
361 selection: Rc<Binding>,
362 options: Rc<Dynamic>,
363 deps: StateSet,
365 },
366 NavItem(NavItem),
367 Image {
369 path: Rc<Dynamic>,
370 size: ImageSize,
371 },
372 Progress {
375 value: Option<Rc<Dynamic>>,
376 },
377 Spacer,
378 Divider,
379 ContextMenu {
381 entries: Vec<MenuEntry<Block>>,
382 },
383 DropTarget {
386 dropped: Rc<Binding>,
387 action: Option<Block>,
388 },
389 If {
391 branches: Vec<(Rc<Expr>, Nodes)>,
392 otherwise: Nodes,
393 deps: StateSet,
394 },
395 For {
397 head: Rc<Loop>,
398 body: Nodes,
399 deps: StateSet,
400 },
401 View {
403 view: ViewId,
404 args: Rc<[Expr]>,
405 deps: StateSet,
406 },
407}