1#[derive(Debug, Clone, PartialEq)]
8pub struct Program {
9 pub in_decls: Vec<InDecl>,
10 pub out_decls: Vec<OutDecl>,
11 pub wires: Vec<Wire>,
12 pub destructuring_wires: Vec<DestructuringWire>,
13 pub msg_decls: Vec<MsgDecl>,
14 pub out_assignments: Vec<OutAssignment>,
15 pub direct_connections: Vec<DirectConnection>,
16 pub feedback_decls: Vec<FeedbackDecl>,
17 pub feedback_assignments: Vec<FeedbackAssignment>,
18 pub state_decls: Vec<StateDecl>,
19 pub state_assignments: Vec<StateAssignment>,
20}
21
22impl Default for Program {
23 fn default() -> Self {
24 Self::new()
25 }
26}
27
28impl Program {
29 pub fn new() -> Self {
30 Self {
31 in_decls: Vec::new(),
32 out_decls: Vec::new(),
33 wires: Vec::new(),
34 destructuring_wires: Vec::new(),
35 msg_decls: Vec::new(),
36 out_assignments: Vec::new(),
37 direct_connections: Vec::new(),
38 feedback_decls: Vec::new(),
39 feedback_assignments: Vec::new(),
40 state_decls: Vec::new(),
41 state_assignments: Vec::new(),
42 }
43 }
44}
45
46#[derive(Debug, Clone, PartialEq, Default)]
48pub struct Span {
49 pub start_line: usize,
50 pub start_column: usize,
51 pub end_line: usize,
52 pub end_column: usize,
53}
54
55#[derive(Debug, Clone, Copy, PartialEq, Eq)]
57pub enum PortType {
58 Signal,
59 Float,
60 Int,
61 Bang,
62 List,
63 Symbol,
64}
65
66impl PortType {
67 pub fn parse(s: &str) -> Option<Self> {
68 match s {
69 "signal" => Some(Self::Signal),
70 "float" => Some(Self::Float),
71 "int" => Some(Self::Int),
72 "bang" => Some(Self::Bang),
73 "list" => Some(Self::List),
74 "symbol" => Some(Self::Symbol),
75 _ => None,
76 }
77 }
78
79 pub fn is_signal(&self) -> bool {
80 matches!(self, Self::Signal)
81 }
82}
83
84#[derive(Debug, Clone, PartialEq)]
86pub struct InDecl {
87 pub index: u32,
88 pub name: String,
89 pub port_type: PortType,
90}
91
92#[derive(Debug, Clone, PartialEq)]
94pub struct OutDecl {
95 pub index: u32,
96 pub name: String,
97 pub port_type: PortType,
98 pub value: Option<Expr>,
99}
100
101#[derive(Debug, Clone, PartialEq)]
104pub struct Wire {
105 pub name: String,
106 pub value: Expr,
107 pub span: Option<Span>,
108 pub attrs: Vec<AttrPair>,
109}
110
111#[derive(Debug, Clone, PartialEq)]
113pub struct OutAssignment {
114 pub index: u32,
115 pub value: Expr,
116 pub span: Option<Span>,
117}
118
119#[derive(Debug, Clone, PartialEq)]
121pub struct DirectConnection {
122 pub target: InputPortAccess,
123 pub value: Expr,
124}
125
126#[derive(Debug, Clone, PartialEq)]
128pub struct InputPortAccess {
129 pub object: String,
130 pub index: u32,
131}
132
133#[derive(Debug, Clone, PartialEq)]
135pub struct OutputPortAccess {
136 pub object: String,
137 pub index: u32,
138}
139
140#[derive(Debug, Clone, PartialEq)]
142pub struct DestructuringWire {
143 pub names: Vec<String>,
144 pub value: Expr,
145 pub span: Option<Span>,
146}
147
148#[derive(Debug, Clone, PartialEq)]
150pub struct FeedbackDecl {
151 pub name: String,
152 pub port_type: PortType,
153 pub span: Option<Span>,
154}
155
156#[derive(Debug, Clone, PartialEq)]
158pub struct FeedbackAssignment {
159 pub target: String,
160 pub value: Expr,
161 pub span: Option<Span>,
162}
163
164#[derive(Debug, Clone, PartialEq)]
166pub struct StateDecl {
167 pub name: String,
168 pub port_type: PortType,
169 pub init_value: Expr,
170 pub span: Option<Span>,
171}
172
173#[derive(Debug, Clone, PartialEq)]
175pub struct StateAssignment {
176 pub name: String,
177 pub value: Expr,
178 pub span: Option<Span>,
179}
180
181#[derive(Debug, Clone, PartialEq)]
184pub struct MsgDecl {
185 pub name: String,
186 pub content: String,
187 pub span: Option<Span>,
188 pub attrs: Vec<AttrPair>,
189}
190
191#[derive(Debug, Clone, PartialEq)]
193pub struct CallArg {
194 pub name: Option<String>,
196 pub value: Expr,
198}
199
200impl CallArg {
201 pub fn positional(value: Expr) -> Self {
203 CallArg { name: None, value }
204 }
205
206 pub fn named(name: impl Into<String>, value: Expr) -> Self {
208 CallArg {
209 name: Some(name.into()),
210 value,
211 }
212 }
213}
214
215#[derive(Debug, Clone, PartialEq)]
217pub enum Expr {
218 Call { object: String, args: Vec<CallArg> },
220 Ref(String),
222 Lit(LitValue),
224 OutputPortAccess(OutputPortAccess),
226 Tuple(Vec<Expr>),
228}
229
230#[derive(Debug, Clone, PartialEq)]
232pub enum LitValue {
233 Int(i64),
234 Float(f64),
235 Str(String),
236}
237
238#[derive(Debug, Clone, PartialEq)]
240pub struct AttrPair {
241 pub key: String,
242 pub value: AttrValue,
243}
244
245#[derive(Debug, Clone, PartialEq)]
247pub enum AttrValue {
248 Int(i64),
249 Float(f64),
250 Str(String),
251 Ident(String),
252}
253
254#[cfg(test)]
255mod tests {
256 use super::*;
257
258 #[test]
259 fn test_port_type_from_str() {
260 assert_eq!(PortType::parse("signal"), Some(PortType::Signal));
261 assert_eq!(PortType::parse("float"), Some(PortType::Float));
262 assert_eq!(PortType::parse("int"), Some(PortType::Int));
263 assert_eq!(PortType::parse("bang"), Some(PortType::Bang));
264 assert_eq!(PortType::parse("unknown"), None);
265 }
266
267 #[test]
268 fn test_program_new() {
269 let prog = Program::new();
270 assert!(prog.in_decls.is_empty());
271 assert!(prog.out_decls.is_empty());
272 assert!(prog.wires.is_empty());
273 assert!(prog.out_assignments.is_empty());
274 }
275
276 #[test]
277 fn test_build_l2_ast() {
278 let prog = Program {
280 in_decls: vec![InDecl {
281 index: 0,
282 name: "freq".to_string(),
283 port_type: PortType::Float,
284 }],
285 out_decls: vec![OutDecl {
286 index: 0,
287 name: "audio".to_string(),
288 port_type: PortType::Signal,
289 value: None,
290 }],
291 wires: vec![
292 Wire {
293 name: "osc".to_string(),
294 value: Expr::Call {
295 object: "cycle~".to_string(),
296 args: vec![CallArg::positional(Expr::Ref("freq".to_string()))],
297 },
298 span: None,
299 attrs: vec![],
300 },
301 Wire {
302 name: "amp".to_string(),
303 value: Expr::Call {
304 object: "*~".to_string(),
305 args: vec![
306 CallArg::positional(Expr::Ref("osc".to_string())),
307 CallArg::positional(Expr::Lit(LitValue::Float(0.5))),
308 ],
309 },
310 span: None,
311 attrs: vec![],
312 },
313 ],
314 destructuring_wires: Vec::new(),
315 msg_decls: Vec::new(),
316 out_assignments: vec![OutAssignment {
317 index: 0,
318 value: Expr::Ref("amp".to_string()),
319 span: None,
320 }],
321 direct_connections: Vec::new(),
322 feedback_decls: Vec::new(),
323 feedback_assignments: Vec::new(),
324 state_decls: Vec::new(),
325 state_assignments: Vec::new(),
326 };
327
328 assert_eq!(prog.in_decls.len(), 1);
329 assert_eq!(prog.out_decls.len(), 1);
330 assert_eq!(prog.wires.len(), 2);
331 assert_eq!(prog.out_assignments.len(), 1);
332 }
333
334 #[test]
335 fn test_call_arg_helpers() {
336 let pos = CallArg::positional(Expr::Lit(LitValue::Int(440)));
337 assert_eq!(pos.name, None);
338 assert_eq!(pos.value, Expr::Lit(LitValue::Int(440)));
339
340 let named = CallArg::named("freq", Expr::Lit(LitValue::Int(440)));
341 assert_eq!(named.name, Some("freq".to_string()));
342 assert_eq!(named.value, Expr::Lit(LitValue::Int(440)));
343 }
344}