1use crate::lexer::{LexError, Lexer};
6use crate::tokens::{Token, TokenType};
7use flutmax_ast::{
8 AttrPair, AttrValue, CallArg, DestructuringWire, DirectConnection, Expr, FeedbackAssignment,
9 FeedbackDecl, InDecl, InputPortAccess, LitValue, MsgDecl, OutAssignment, OutDecl,
10 OutputPortAccess, PortType, Program, Span, StateAssignment, StateDecl, Wire,
11};
12
13#[derive(Debug)]
18pub enum ParseError {
19 Lex(LexError),
20 Syntax {
21 message: String,
22 line: usize,
23 column: usize,
24 },
25}
26
27impl From<LexError> for ParseError {
28 fn from(e: LexError) -> Self {
29 ParseError::Lex(e)
30 }
31}
32
33impl std::fmt::Display for ParseError {
34 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
35 match self {
36 ParseError::Lex(e) => write!(f, "{}", e),
37 ParseError::Syntax {
38 message,
39 line,
40 column,
41 } => write!(f, "Parse error at {}:{}: {}", line, column, message),
42 }
43 }
44}
45
46impl std::error::Error for ParseError {}
47
48pub struct FlutmaxParser {
53 tokens: Vec<Token>,
54 pos: usize,
55 implicit_in_index: u32,
57 implicit_out_index: u32,
59}
60
61impl FlutmaxParser {
62 pub fn parse(source: &str) -> Result<Program, ParseError> {
65 let (program, errors) = Self::parse_with_errors(source)?;
66 if let Some(first_err) = errors.into_iter().next() {
67 return Err(first_err);
68 }
69 Ok(program)
70 }
71
72 pub fn parse_with_errors(source: &str) -> Result<(Program, Vec<ParseError>), ParseError> {
75 let tokens = Lexer::tokenize(source)?;
76 let mut parser = FlutmaxParser {
77 tokens,
78 pos: 0,
79 implicit_in_index: 0,
80 implicit_out_index: 0,
81 };
82 let (program, errors) = parser.parse_program_recovering();
83 Ok((program, errors))
84 }
85
86 fn peek(&self) -> &Token {
89 &self.tokens[self.pos.min(self.tokens.len() - 1)]
90 }
91
92 fn peek_type(&self) -> TokenType {
93 self.peek().token_type
94 }
95
96 fn peek_at(&self, offset: usize) -> &Token {
97 let idx = (self.pos + offset).min(self.tokens.len() - 1);
98 &self.tokens[idx]
99 }
100
101 fn advance(&mut self) -> &Token {
102 let tok = &self.tokens[self.pos.min(self.tokens.len() - 1)];
103 if self.pos < self.tokens.len() {
104 self.pos += 1;
105 }
106 tok
107 }
108
109 fn expect(&mut self, tt: TokenType) -> Result<Token, ParseError> {
110 let tok = self.peek().clone();
111 if tok.token_type == tt {
112 self.advance();
113 Ok(tok)
114 } else {
115 Err(self.error(format!(
116 "Expected {:?}, got {:?} '{}'",
117 tt, tok.token_type, tok.lexeme
118 )))
119 }
120 }
121
122 fn check(&self, tt: TokenType) -> bool {
123 self.peek_type() == tt
124 }
125
126 fn error(&self, message: String) -> ParseError {
127 let tok = self.peek();
128 ParseError::Syntax {
129 message,
130 line: tok.line,
131 column: tok.column,
132 }
133 }
134
135 fn is_statement_start(&self) -> bool {
137 matches!(
138 self.peek_type(),
139 TokenType::Wire
140 | TokenType::In
141 | TokenType::Out
142 | TokenType::Msg
143 | TokenType::State
144 | TokenType::Feedback
145 )
146 }
147
148 fn make_span_from(&self, start: &Token, end: &Token) -> Option<Span> {
149 Some(Span {
150 start_line: start.line,
151 start_column: start.column,
152 end_line: end.line,
153 end_column: end.column + end.lexeme.len(),
154 })
155 }
156
157 fn previous(&self) -> &Token {
159 &self.tokens[(self.pos - 1).max(0)]
160 }
161
162 fn parse_program_recovering(&mut self) -> (Program, Vec<ParseError>) {
166 let mut program = Program::new();
167 let mut errors = Vec::new();
168
169 while !self.check(TokenType::Eof) {
170 match self.parse_statement(&mut program) {
171 Ok(()) => {}
172 Err(e) => {
173 errors.push(e);
174 while !self.check(TokenType::Eof)
176 && !self.check(TokenType::Semicolon)
177 && !self.is_statement_start()
178 {
179 self.advance();
180 }
181 if self.check(TokenType::Semicolon) {
182 self.advance(); }
184 }
186 }
187 }
188
189 (program, errors)
190 }
191
192 fn parse_statement(&mut self, program: &mut Program) -> Result<(), ParseError> {
194 match self.peek_type() {
195 TokenType::Wire => self.parse_wire_or_destructuring(program),
196 TokenType::In => self.parse_in_decl_or_nothing(program),
197 TokenType::Out => self.parse_out_decl_or_assignment(program),
198 TokenType::Msg => self.parse_msg_declaration(program),
199 TokenType::Feedback => self.parse_feedback(program),
200 TokenType::State => self.parse_state(program),
201 TokenType::Identifier => self.parse_direct_connection(program),
202 _ => {
203 let tok = self.peek().clone();
204 Err(self.error(format!(
205 "Unexpected token {:?} '{}'",
206 tok.token_type, tok.lexeme
207 )))
208 }
209 }
210 }
211
212 fn parse_in_decl_or_nothing(&mut self, program: &mut Program) -> Result<(), ParseError> {
217 self.expect(TokenType::In)?;
219
220 if self.check(TokenType::NumberLit) {
221 let index = self.parse_integer()?;
223 self.expect(TokenType::LParen)?;
224 let name = self.expect_identifier()?;
225 self.expect(TokenType::RParen)?;
226 self.expect(TokenType::Colon)?;
227 let port_type = self.parse_port_type()?;
228 self.expect(TokenType::Semicolon)?;
229
230 program.in_decls.push(InDecl {
231 index,
232 name,
233 port_type,
234 });
235 } else {
236 let name = self.expect_identifier()?;
238 self.expect(TokenType::Colon)?;
239 let port_type = self.parse_port_type()?;
240 self.expect(TokenType::Semicolon)?;
241
242 let index = self.implicit_in_index;
243 self.implicit_in_index += 1;
244
245 program.in_decls.push(InDecl {
246 index,
247 name,
248 port_type,
249 });
250 }
251
252 Ok(())
253 }
254
255 fn parse_out_decl_or_assignment(&mut self, program: &mut Program) -> Result<(), ParseError> {
261 let start_tok = self.peek().clone();
262 self.expect(TokenType::Out)?;
263
264 if self.check(TokenType::LBracket) {
265 self.expect(TokenType::LBracket)?;
267 let index = self.parse_integer()?;
268 self.expect(TokenType::RBracket)?;
269 self.expect(TokenType::Eq)?;
270 let value = self.parse_expression()?;
271 let end_tok = self.expect(TokenType::Semicolon)?;
272
273 program.out_assignments.push(OutAssignment {
274 index,
275 value,
276 span: self.make_span_from(&start_tok, &end_tok),
277 });
278 } else if self.check(TokenType::NumberLit) {
279 let index = self.parse_integer()?;
281 self.expect(TokenType::LParen)?;
282 let name = self.expect_identifier()?;
283 self.expect(TokenType::RParen)?;
284 self.expect(TokenType::Colon)?;
285 let port_type = self.parse_port_type()?;
286 let value = if self.check(TokenType::Eq) {
287 self.advance(); Some(self.parse_expression()?)
289 } else {
290 None
291 };
292 self.expect(TokenType::Semicolon)?;
293
294 program.out_decls.push(OutDecl {
295 index,
296 name,
297 port_type,
298 value,
299 });
300 } else {
301 let name = self.expect_identifier()?;
303 self.expect(TokenType::Colon)?;
304 let port_type = self.parse_port_type()?;
305 let value = if self.check(TokenType::Eq) {
306 self.advance(); Some(self.parse_expression()?)
308 } else {
309 None
310 };
311 self.expect(TokenType::Semicolon)?;
312
313 let index = self.implicit_out_index;
314 self.implicit_out_index += 1;
315
316 program.out_decls.push(OutDecl {
317 index,
318 name,
319 port_type,
320 value,
321 });
322 }
323
324 Ok(())
325 }
326
327 fn parse_wire_or_destructuring(&mut self, program: &mut Program) -> Result<(), ParseError> {
332 let start_tok = self.peek().clone();
333 self.expect(TokenType::Wire)?;
334
335 if self.check(TokenType::LParen) {
336 self.expect(TokenType::LParen)?;
338 let mut names = Vec::new();
339 names.push(self.expect_identifier()?);
340 while self.check(TokenType::Comma) {
341 self.advance(); names.push(self.expect_identifier()?);
343 }
344 self.expect(TokenType::RParen)?;
345 self.expect(TokenType::Eq)?;
346 let value = self.parse_expression()?;
347 let end_tok = self.expect(TokenType::Semicolon)?;
348
349 program.destructuring_wires.push(DestructuringWire {
350 names,
351 value,
352 span: self.make_span_from(&start_tok, &end_tok),
353 });
354 } else {
355 let name = self.expect_identifier()?;
357 self.expect(TokenType::Eq)?;
358 let value = self.parse_expression()?;
359
360 let attrs = if self.check(TokenType::DotAttrLParen) {
362 self.parse_attr_chain()?
363 } else {
364 vec![]
365 };
366
367 let end_tok = self.expect(TokenType::Semicolon)?;
368
369 program.wires.push(Wire {
370 name,
371 value,
372 span: self.make_span_from(&start_tok, &end_tok),
373 attrs,
374 });
375 }
376
377 Ok(())
378 }
379
380 fn parse_msg_declaration(&mut self, program: &mut Program) -> Result<(), ParseError> {
385 let start_tok = self.peek().clone();
386 self.expect(TokenType::Msg)?;
387 let name = self.expect_identifier()?;
388 self.expect(TokenType::Eq)?;
389
390 let content_tok = self.expect(TokenType::StringLit)?;
391 let content = unescape_string_content(&content_tok.lexeme);
392
393 let attrs = if self.check(TokenType::DotAttrLParen) {
394 self.parse_attr_chain()?
395 } else {
396 vec![]
397 };
398
399 let end_tok = self.expect(TokenType::Semicolon)?;
400
401 program.msg_decls.push(MsgDecl {
402 name,
403 content,
404 span: self.make_span_from(&start_tok, &end_tok),
405 attrs,
406 });
407 Ok(())
408 }
409
410 fn parse_feedback(&mut self, program: &mut Program) -> Result<(), ParseError> {
415 let start_tok = self.peek().clone();
416 self.expect(TokenType::Feedback)?;
417 let name = self.expect_identifier()?;
418
419 if self.check(TokenType::Colon) {
420 self.advance(); let port_type = self.parse_port_type()?;
423 let end_tok = self.expect(TokenType::Semicolon)?;
424
425 program.feedback_decls.push(FeedbackDecl {
426 name,
427 port_type,
428 span: self.make_span_from(&start_tok, &end_tok),
429 });
430 } else {
431 self.expect(TokenType::Eq)?;
433 let value = self.parse_expression()?;
434 let end_tok = self.expect(TokenType::Semicolon)?;
435
436 program.feedback_assignments.push(FeedbackAssignment {
437 target: name,
438 value,
439 span: self.make_span_from(&start_tok, &end_tok),
440 });
441 }
442
443 Ok(())
444 }
445
446 fn parse_state(&mut self, program: &mut Program) -> Result<(), ParseError> {
451 let start_tok = self.peek().clone();
452 self.expect(TokenType::State)?;
453 let name = self.expect_identifier()?;
454
455 if self.check(TokenType::Colon) {
456 self.advance(); let port_type = self.parse_control_type()?;
459 self.expect(TokenType::Eq)?;
460 let init_value = self.parse_expression()?;
461 let end_tok = self.expect(TokenType::Semicolon)?;
462
463 program.state_decls.push(StateDecl {
464 name,
465 port_type,
466 init_value,
467 span: self.make_span_from(&start_tok, &end_tok),
468 });
469 } else {
470 self.expect(TokenType::Eq)?;
472 let value = self.parse_expression()?;
473 let end_tok = self.expect(TokenType::Semicolon)?;
474
475 program.state_assignments.push(StateAssignment {
476 name,
477 value,
478 span: self.make_span_from(&start_tok, &end_tok),
479 });
480 }
481
482 Ok(())
483 }
484
485 fn parse_direct_connection(&mut self, program: &mut Program) -> Result<(), ParseError> {
490 let object = self.expect_identifier()?;
491 self.expect(TokenType::Dot)?;
492 self.expect(TokenType::In)?;
493 let index = if self.check(TokenType::LBracket) {
495 self.advance(); let idx = self.parse_integer()?;
497 self.expect(TokenType::RBracket)?;
498 idx
499 } else {
500 0
501 };
502 self.expect(TokenType::Eq)?;
503 let value = self.parse_expression()?;
504 self.expect(TokenType::Semicolon)?;
505
506 program.direct_connections.push(DirectConnection {
507 target: InputPortAccess { object, index },
508 value,
509 });
510 Ok(())
511 }
512
513 fn parse_expression(&mut self) -> Result<Expr, ParseError> {
522 match self.peek_type() {
523 TokenType::LParen => self.parse_tuple_expression(),
524 TokenType::NumberLit => self.parse_number_literal(),
525 TokenType::StringLit => self.parse_string_literal(),
526 TokenType::Identifier | TokenType::Operator => self.parse_name_based_expression(),
527 TokenType::In
530 | TokenType::Out
531 | TokenType::Int
532 | TokenType::Float
533 | TokenType::Bang
534 | TokenType::List
535 | TokenType::Symbol
536 | TokenType::Signal
537 | TokenType::State
538 | TokenType::Msg
539 | TokenType::Feedback
540 | TokenType::Wire => self.parse_name_based_expression(),
541 _ => {
542 let tok = self.peek().clone();
543 Err(self.error(format!(
544 "Expected expression, got {:?} '{}'",
545 tok.token_type, tok.lexeme
546 )))
547 }
548 }
549 }
550
551 fn parse_name_based_expression(&mut self) -> Result<Expr, ParseError> {
554 let name = self.parse_object_name()?;
557
558 if self.check(TokenType::LParen) {
559 self.advance(); let args = if self.check(TokenType::RParen) {
562 vec![]
563 } else {
564 self.parse_argument_list()?
565 };
566 self.expect(TokenType::RParen)?;
567 Ok(Expr::Call { object: name, args })
568 } else if self.check(TokenType::Dot) {
569 if self.peek_at(1).token_type == TokenType::Out
572 && self.peek_at(2).token_type == TokenType::LBracket
573 {
574 self.advance(); self.advance(); self.expect(TokenType::LBracket)?;
577 let index = self.parse_integer()?;
578 self.expect(TokenType::RBracket)?;
579 Ok(Expr::OutputPortAccess(OutputPortAccess {
580 object: name,
581 index,
582 }))
583 } else {
584 Ok(Expr::Ref(name))
587 }
588 } else {
589 Ok(Expr::Ref(name))
591 }
592 }
593
594 fn parse_object_name(&mut self) -> Result<String, ParseError> {
600 let mut name = String::new();
601
602 match self.peek_type() {
603 TokenType::Identifier | TokenType::Operator => {
604 name.push_str(&self.advance().lexeme.clone());
605 }
606 TokenType::In
608 | TokenType::Out
609 | TokenType::Int
610 | TokenType::Float
611 | TokenType::Bang
612 | TokenType::List
613 | TokenType::Symbol
614 | TokenType::Signal
615 | TokenType::State
616 | TokenType::Msg
617 | TokenType::Feedback
618 | TokenType::Wire => {
619 name.push_str(&self.advance().lexeme.clone());
620 }
621 _ => {
622 return Err(self.error(format!(
623 "Expected identifier or operator, got {:?}",
624 self.peek_type()
625 )));
626 }
627 }
628
629 while self.check(TokenType::Dot) {
632 let after_dot = &self.peek_at(1);
634 match after_dot.token_type {
635 TokenType::In => {
636 if self.peek_at(2).token_type == TokenType::LBracket {
638 break;
639 }
640 self.advance(); name.push('.');
643 name.push_str(&self.advance().lexeme.clone());
644 }
645 TokenType::Out => {
646 if self.peek_at(2).token_type == TokenType::LBracket {
648 break;
649 }
650 self.advance(); name.push('.');
652 name.push_str(&self.advance().lexeme.clone());
653 }
654 TokenType::Identifier => {
655 self.advance(); name.push('.');
657 name.push_str(&self.advance().lexeme.clone());
658 }
659 TokenType::NumberLit => {
660 self.advance(); name.push('.');
665 let num_part = self.advance().lexeme.clone();
667 name.push_str(&num_part);
668 if self.check(TokenType::Identifier) {
671 let prev_end = self.tokens[self.pos - 1].column
673 + self.tokens[self.pos - 1].lexeme.len();
674 let next_start = self.peek().column;
675 if prev_end == next_start
676 && self.tokens[self.pos - 1].line == self.peek().line
677 {
678 name.push_str(&self.advance().lexeme.clone());
679 }
680 }
681 }
682 TokenType::Operator => {
683 self.advance(); name.push('.');
686 name.push_str(&self.advance().lexeme.clone());
687 }
688 TokenType::Bang
691 | TokenType::Float
692 | TokenType::Int
693 | TokenType::List
694 | TokenType::Symbol
695 | TokenType::Signal
696 | TokenType::State
697 | TokenType::Msg
698 | TokenType::Feedback
699 | TokenType::Wire => {
700 self.advance(); name.push('.');
702 name.push_str(&self.advance().lexeme.clone());
703 }
704 _ => break,
705 }
706 }
707
708 if self.check(TokenType::Eq) && name.contains('.') {
711 let prev_end = self.previous().column + self.previous().lexeme.len();
712 let eq_col = self.peek().column;
713 if prev_end == eq_col
714 && self.previous().line == self.peek().line
715 && self.peek_at(1).token_type == TokenType::LParen
716 {
717 self.advance(); name.push('=');
719 }
720 }
721
722 if self.check(TokenType::Tilde) {
724 let prev_end_col = self.previous().column + self.previous().lexeme.len();
726 let tilde_col = self.peek().column;
727 if prev_end_col == tilde_col && self.previous().line == self.peek().line {
728 self.advance(); name.push('~');
730 }
731 }
732
733 Ok(name)
734 }
735
736 fn parse_argument_list(&mut self) -> Result<Vec<CallArg>, ParseError> {
738 let mut args = Vec::new();
739 args.push(self.parse_call_arg()?);
740 while self.check(TokenType::Comma) {
741 self.advance(); args.push(self.parse_call_arg()?);
743 }
744 Ok(args)
745 }
746
747 fn parse_call_arg(&mut self) -> Result<CallArg, ParseError> {
749 if self.check_named_arg_ahead() {
752 let name = self.expect_identifier()?;
753 self.expect(TokenType::Colon)?;
754 let value = self.parse_expression()?;
755 Ok(CallArg::named(name, value))
756 } else {
757 let value = self.parse_expression()?;
758 Ok(CallArg::positional(value))
759 }
760 }
761
762 fn check_named_arg_ahead(&self) -> bool {
768 let cur = self.peek_type();
769 if cur != TokenType::Identifier && !is_contextual_identifier(cur) {
770 return false;
771 }
772 if self.peek_at(1).token_type != TokenType::Colon {
773 return false;
774 }
775 true
776 }
777
778 fn parse_tuple_expression(&mut self) -> Result<Expr, ParseError> {
781 self.expect(TokenType::LParen)?;
782 let mut elements = Vec::new();
783 elements.push(self.parse_expression()?);
784
785 if !self.check(TokenType::Comma) {
787 return Err(self.error("Tuple must have at least 2 elements".to_string()));
788 }
789 while self.check(TokenType::Comma) {
790 self.advance();
791 elements.push(self.parse_expression()?);
792 }
793 self.expect(TokenType::RParen)?;
794
795 Ok(Expr::Tuple(elements))
796 }
797
798 fn parse_number_literal(&mut self) -> Result<Expr, ParseError> {
799 let tok = self.expect(TokenType::NumberLit)?;
800 let text = &tok.lexeme;
801
802 if text.contains('.') || text.contains('e') || text.contains('E') {
803 let val: f64 = text.parse().map_err(|_| ParseError::Syntax {
804 message: format!("Invalid float literal '{}'", text),
805 line: tok.line,
806 column: tok.column,
807 })?;
808 Ok(Expr::Lit(LitValue::Float(val)))
809 } else {
810 let val: i64 = text.parse().map_err(|_| ParseError::Syntax {
811 message: format!("Invalid integer literal '{}'", text),
812 line: tok.line,
813 column: tok.column,
814 })?;
815 Ok(Expr::Lit(LitValue::Int(val)))
816 }
817 }
818
819 fn parse_string_literal(&mut self) -> Result<Expr, ParseError> {
820 let tok = self.expect(TokenType::StringLit)?;
821 let content = unescape_string_content(&tok.lexeme);
822 Ok(Expr::Lit(LitValue::Str(content)))
823 }
824
825 fn parse_attr_chain(&mut self) -> Result<Vec<AttrPair>, ParseError> {
829 self.expect(TokenType::DotAttrLParen)?;
830 let mut pairs = Vec::new();
831
832 if !self.check(TokenType::RParen) {
833 pairs.push(self.parse_attr_pair()?);
834 while self.check(TokenType::Comma) {
835 self.advance();
836 pairs.push(self.parse_attr_pair()?);
837 }
838 }
839
840 self.expect(TokenType::RParen)?;
841 Ok(pairs)
842 }
843
844 fn parse_attr_pair(&mut self) -> Result<AttrPair, ParseError> {
845 let key = self.expect_identifier()?;
846 self.expect(TokenType::Colon)?;
847 let value = self.parse_attr_value()?;
848 Ok(AttrPair { key, value })
849 }
850
851 fn parse_attr_value(&mut self) -> Result<AttrValue, ParseError> {
852 match self.peek_type() {
853 TokenType::NumberLit => {
854 let tok = self.advance().clone();
855 let text = &tok.lexeme;
856 if text.contains('.') || text.contains('e') || text.contains('E') {
857 let val: f64 = text.parse().map_err(|_| ParseError::Syntax {
858 message: format!("Invalid float literal '{}'", text),
859 line: tok.line,
860 column: tok.column,
861 })?;
862 Ok(AttrValue::Float(val))
863 } else {
864 let val: i64 = text.parse().map_err(|_| ParseError::Syntax {
865 message: format!("Invalid integer literal '{}'", text),
866 line: tok.line,
867 column: tok.column,
868 })?;
869 Ok(AttrValue::Int(val))
870 }
871 }
872 TokenType::StringLit => {
873 let tok = self.advance().clone();
874 let content = unescape_string_content(&tok.lexeme);
875 Ok(AttrValue::Str(content))
876 }
877 TokenType::Identifier => {
878 let tok = self.advance().clone();
879 Ok(AttrValue::Ident(tok.lexeme))
880 }
881 _ => Err(self.error(format!(
882 "Expected attribute value (number, string, or identifier), got {:?}",
883 self.peek_type()
884 ))),
885 }
886 }
887
888 fn parse_integer(&mut self) -> Result<u32, ParseError> {
892 let tok = self.expect(TokenType::NumberLit)?;
893 tok.lexeme.parse().map_err(|_| ParseError::Syntax {
894 message: format!("Expected integer, got '{}'", tok.lexeme),
895 line: tok.line,
896 column: tok.column,
897 })
898 }
899
900 fn expect_identifier(&mut self) -> Result<String, ParseError> {
905 let tok = self.peek().clone();
906 if tok.token_type == TokenType::Identifier || is_contextual_identifier(tok.token_type) {
907 self.advance();
908 Ok(tok.lexeme)
909 } else {
910 Err(self.error(format!(
911 "Expected identifier, got {:?} '{}'",
912 tok.token_type, tok.lexeme
913 )))
914 }
915 }
916
917 fn parse_port_type(&mut self) -> Result<PortType, ParseError> {
919 let tok = self.peek().clone();
920 let pt = match tok.token_type {
921 TokenType::Signal => PortType::Signal,
922 TokenType::Float => PortType::Float,
923 TokenType::Int => PortType::Int,
924 TokenType::Bang => PortType::Bang,
925 TokenType::List => PortType::List,
926 TokenType::Symbol => PortType::Symbol,
927 _ => {
928 return Err(self.error(format!(
929 "Expected port type (signal/float/int/bang/list/symbol), got '{}'",
930 tok.lexeme
931 )));
932 }
933 };
934 self.advance();
935 Ok(pt)
936 }
937
938 fn parse_control_type(&mut self) -> Result<PortType, ParseError> {
940 let tok = self.peek().clone();
941 let pt = match tok.token_type {
942 TokenType::Float => PortType::Float,
943 TokenType::Int => PortType::Int,
944 TokenType::Bang => PortType::Bang,
945 TokenType::List => PortType::List,
946 TokenType::Symbol => PortType::Symbol,
947 _ => {
948 return Err(self.error(format!(
949 "Expected control type (float/int/bang/list/symbol), got '{}'",
950 tok.lexeme
951 )));
952 }
953 };
954 self.advance();
955 Ok(pt)
956 }
957}
958
959fn is_contextual_identifier(tt: TokenType) -> bool {
968 matches!(
969 tt,
970 TokenType::Msg
971 | TokenType::Signal
972 | TokenType::Float
973 | TokenType::Int
974 | TokenType::Bang
975 | TokenType::List
976 | TokenType::Symbol
977 | TokenType::State
978 | TokenType::Feedback
979 | TokenType::In
980 | TokenType::Out
981 | TokenType::Wire
982 )
983}
984
985fn unescape_string_content(lexeme: &str) -> String {
991 let inner = &lexeme[1..lexeme.len() - 1];
993 let mut result = String::with_capacity(inner.len());
994 let mut chars = inner.chars();
995 while let Some(ch) = chars.next() {
996 if ch == '\\' {
997 match chars.next() {
998 Some('n') => result.push('\n'),
999 Some('t') => result.push('\t'),
1000 Some('\\') => result.push('\\'),
1001 Some('"') => result.push('"'),
1002 Some(other) => {
1003 result.push('\\');
1004 result.push(other);
1005 }
1006 None => result.push('\\'),
1007 }
1008 } else {
1009 result.push(ch);
1010 }
1011 }
1012 result
1013}
1014
1015#[cfg(test)]
1020mod tests {
1021 use super::*;
1022
1023 fn parse(source: &str) -> Program {
1025 FlutmaxParser::parse(source).expect("parse failed")
1026 }
1027
1028 #[test]
1031 fn test_l1_minimal() {
1032 let prog = parse(
1033 r#"
1034out 0 (audio): signal;
1035wire osc = cycle~(440);
1036out[0] = osc;
1037"#,
1038 );
1039
1040 assert_eq!(prog.out_decls.len(), 1);
1041 assert_eq!(prog.out_decls[0].index, 0);
1042 assert_eq!(prog.out_decls[0].name, "audio");
1043 assert_eq!(prog.out_decls[0].port_type, PortType::Signal);
1044
1045 assert_eq!(prog.wires.len(), 1);
1046 assert_eq!(prog.wires[0].name, "osc");
1047 assert_eq!(
1048 prog.wires[0].value,
1049 Expr::Call {
1050 object: "cycle~".to_string(),
1051 args: vec![CallArg::positional(Expr::Lit(LitValue::Int(440)))],
1052 }
1053 );
1054 assert!(prog.wires[0].span.is_some());
1055
1056 assert_eq!(prog.out_assignments.len(), 1);
1057 assert_eq!(prog.out_assignments[0].index, 0);
1058 assert_eq!(prog.out_assignments[0].value, Expr::Ref("osc".to_string()));
1059 assert!(prog.out_assignments[0].span.is_some());
1060
1061 assert!(prog.in_decls.is_empty());
1062 assert!(prog.direct_connections.is_empty());
1063 }
1064
1065 #[test]
1068 fn test_l2_simple_synth() {
1069 let prog = parse(
1070 r#"
1071// L2_simple_synth.flutmax
1072in 0 (freq): float;
1073out 0 (audio): signal;
1074
1075wire osc = cycle~(freq);
1076wire amp = mul~(osc, 0.5);
1077
1078out[0] = amp;
1079"#,
1080 );
1081
1082 assert_eq!(prog.in_decls.len(), 1);
1083 assert_eq!(prog.in_decls[0].name, "freq");
1084 assert_eq!(prog.in_decls[0].port_type, PortType::Float);
1085
1086 assert_eq!(prog.out_decls.len(), 1);
1087 assert_eq!(prog.wires.len(), 2);
1088 assert_eq!(prog.wires[0].name, "osc");
1089 assert_eq!(
1090 prog.wires[0].value,
1091 Expr::Call {
1092 object: "cycle~".to_string(),
1093 args: vec![CallArg::positional(Expr::Ref("freq".to_string()))],
1094 }
1095 );
1096 assert_eq!(prog.wires[1].name, "amp");
1097 assert_eq!(
1098 prog.wires[1].value,
1099 Expr::Call {
1100 object: "mul~".to_string(),
1101 args: vec![
1102 CallArg::positional(Expr::Ref("osc".to_string())),
1103 CallArg::positional(Expr::Lit(LitValue::Float(0.5))),
1104 ],
1105 }
1106 );
1107 assert_eq!(prog.out_assignments.len(), 1);
1108 }
1109
1110 #[test]
1113 fn test_l3b_control_fanout() {
1114 let prog = parse(
1115 r#"
1116wire trigger = button();
1117wire counter = counter(trigger);
1118wire msg = print(counter);
1119
1120node_a.in[0] = trigger;
1121node_b.in[0] = trigger;
1122"#,
1123 );
1124
1125 assert_eq!(prog.wires.len(), 3);
1126 assert_eq!(
1127 prog.wires[0].value,
1128 Expr::Call {
1129 object: "button".to_string(),
1130 args: vec![],
1131 }
1132 );
1133
1134 assert_eq!(prog.direct_connections.len(), 2);
1135 assert_eq!(prog.direct_connections[0].target.object, "node_a");
1136 assert_eq!(prog.direct_connections[0].target.index, 0);
1137 assert_eq!(
1138 prog.direct_connections[0].value,
1139 Expr::Ref("trigger".to_string())
1140 );
1141 assert_eq!(prog.direct_connections[1].target.object, "node_b");
1142 }
1143
1144 #[test]
1147 fn test_multiple_in_out_ports() {
1148 let prog = parse(
1149 r#"
1150in 0 (input_sig): signal;
1151in 1 (cutoff): float;
1152in 2 (q_factor): float;
1153
1154out 0 (lowpass): signal;
1155out 1 (highpass): signal;
1156"#,
1157 );
1158
1159 assert_eq!(prog.in_decls.len(), 3);
1160 assert_eq!(prog.in_decls[0].name, "input_sig");
1161 assert_eq!(prog.in_decls[0].port_type, PortType::Signal);
1162 assert_eq!(prog.in_decls[1].name, "cutoff");
1163 assert_eq!(prog.in_decls[1].port_type, PortType::Float);
1164 assert_eq!(prog.in_decls[2].name, "q_factor");
1165
1166 assert_eq!(prog.out_decls.len(), 2);
1167 assert_eq!(prog.out_decls[0].name, "lowpass");
1168 assert_eq!(prog.out_decls[1].name, "highpass");
1169 }
1170
1171 #[test]
1174 fn test_nested_object_call() {
1175 let prog = parse("wire sig = biquad~(cycle~(440), 1000, 0.7);");
1176
1177 assert_eq!(prog.wires.len(), 1);
1178 assert_eq!(
1179 prog.wires[0].value,
1180 Expr::Call {
1181 object: "biquad~".to_string(),
1182 args: vec![
1183 CallArg::positional(Expr::Call {
1184 object: "cycle~".to_string(),
1185 args: vec![CallArg::positional(Expr::Lit(LitValue::Int(440)))],
1186 }),
1187 CallArg::positional(Expr::Lit(LitValue::Int(1000))),
1188 CallArg::positional(Expr::Lit(LitValue::Float(0.7))),
1189 ],
1190 }
1191 );
1192 }
1193
1194 #[test]
1197 fn test_string_literal() {
1198 let prog = parse(r#"wire msg = print("hello world");"#);
1199 assert_eq!(
1200 prog.wires[0].value,
1201 Expr::Call {
1202 object: "print".to_string(),
1203 args: vec![CallArg::positional(Expr::Lit(LitValue::Str(
1204 "hello world".to_string()
1205 )))],
1206 }
1207 );
1208 }
1209
1210 #[test]
1213 fn test_zero_arg_object_call() {
1214 let prog = parse("wire btn = button();");
1215 assert_eq!(
1216 prog.wires[0].value,
1217 Expr::Call {
1218 object: "button".to_string(),
1219 args: vec![],
1220 }
1221 );
1222 }
1223
1224 #[test]
1227 fn test_empty_source() {
1228 let prog = parse("");
1229 assert!(prog.in_decls.is_empty());
1230 assert!(prog.out_decls.is_empty());
1231 assert!(prog.wires.is_empty());
1232 assert!(prog.out_assignments.is_empty());
1233 assert!(prog.direct_connections.is_empty());
1234 }
1235
1236 #[test]
1239 fn test_comments_ignored() {
1240 let prog = parse(
1241 r#"
1242// This is a comment
1243wire osc = cycle~(440);
1244// Another comment
1245out 0 (audio): signal;
1246"#,
1247 );
1248 assert_eq!(prog.wires.len(), 1);
1249 assert_eq!(prog.out_decls.len(), 1);
1250 }
1251
1252 #[test]
1255 fn test_span_populated() {
1256 let prog = parse("wire osc = cycle~(440);");
1257 assert!(prog.wires[0].span.is_some());
1258 let span = prog.wires[0].span.as_ref().unwrap();
1259 assert_eq!(span.start_line, 1);
1260 assert_eq!(span.start_column, 1);
1261 }
1262
1263 #[test]
1264 fn test_out_assignment_span_populated() {
1265 let prog = parse("out 0 (audio): signal;\nwire osc = cycle~(440);\nout[0] = osc;");
1266 assert!(prog.out_assignments[0].span.is_some());
1267 let span = prog.out_assignments[0].span.as_ref().unwrap();
1268 assert_eq!(span.start_line, 3);
1269 }
1270
1271 #[test]
1274 fn test_tuple_expression() {
1275 let prog = parse("wire t = (a, b, c);");
1276 assert_eq!(
1277 prog.wires[0].value,
1278 Expr::Tuple(vec![
1279 Expr::Ref("a".to_string()),
1280 Expr::Ref("b".to_string()),
1281 Expr::Ref("c".to_string()),
1282 ])
1283 );
1284 }
1285
1286 #[test]
1287 fn test_tuple_two_elements() {
1288 let prog = parse("wire pair = (x, y);");
1289 assert_eq!(
1290 prog.wires[0].value,
1291 Expr::Tuple(vec![Expr::Ref("x".to_string()), Expr::Ref("y".to_string()),])
1292 );
1293 }
1294
1295 #[test]
1296 fn test_tuple_with_literals() {
1297 let prog = parse("wire nums = (1, 2, 3);");
1298 assert_eq!(
1299 prog.wires[0].value,
1300 Expr::Tuple(vec![
1301 Expr::Lit(LitValue::Int(1)),
1302 Expr::Lit(LitValue::Int(2)),
1303 Expr::Lit(LitValue::Int(3)),
1304 ])
1305 );
1306 }
1307
1308 #[test]
1309 fn test_destructuring_wire() {
1310 let prog = parse("wire (a, b, c) = unpack(coords);");
1311 assert_eq!(prog.destructuring_wires.len(), 1);
1312 let dw = &prog.destructuring_wires[0];
1313 assert_eq!(dw.names, vec!["a", "b", "c"]);
1314 assert_eq!(
1315 dw.value,
1316 Expr::Call {
1317 object: "unpack".to_string(),
1318 args: vec![CallArg::positional(Expr::Ref("coords".to_string()))],
1319 }
1320 );
1321 assert!(dw.span.is_some());
1322 }
1323
1324 #[test]
1325 fn test_destructuring_wire_two_names() {
1326 let prog = parse("wire (x, y) = data;");
1327 assert_eq!(prog.destructuring_wires.len(), 1);
1328 let dw = &prog.destructuring_wires[0];
1329 assert_eq!(dw.names, vec!["x", "y"]);
1330 assert_eq!(dw.value, Expr::Ref("data".to_string()));
1331 }
1332
1333 #[test]
1334 fn test_destructuring_wire_with_tuple_value() {
1335 let prog = parse("wire (a, b) = (x, y);");
1336 let dw = &prog.destructuring_wires[0];
1337 assert_eq!(dw.names, vec!["a", "b"]);
1338 assert_eq!(
1339 dw.value,
1340 Expr::Tuple(vec![Expr::Ref("x".to_string()), Expr::Ref("y".to_string()),])
1341 );
1342 }
1343
1344 #[test]
1345 fn test_l4_tuple_full() {
1346 let prog = parse(
1347 r#"
1348in 0 (x): float;
1349in 1 (y): float;
1350in 2 (z): float;
1351out 0 (coords): list;
1352
1353wire packed = (x, y, z);
1354out[0] = packed;
1355"#,
1356 );
1357
1358 assert_eq!(prog.in_decls.len(), 3);
1359 assert_eq!(prog.out_decls.len(), 1);
1360 assert_eq!(prog.wires.len(), 1);
1361 assert_eq!(
1362 prog.wires[0].value,
1363 Expr::Tuple(vec![
1364 Expr::Ref("x".to_string()),
1365 Expr::Ref("y".to_string()),
1366 Expr::Ref("z".to_string()),
1367 ])
1368 );
1369 assert_eq!(prog.out_assignments.len(), 1);
1370 }
1371
1372 #[test]
1373 fn test_l5_destructure_full() {
1374 let prog = parse(
1375 r#"
1376in 0 (coords): list;
1377out 0 (x): float;
1378out 1 (y): float;
1379
1380wire (a, b) = unpack(coords);
1381out[0] = a;
1382out[1] = b;
1383"#,
1384 );
1385
1386 assert_eq!(prog.in_decls.len(), 1);
1387 assert_eq!(prog.out_decls.len(), 2);
1388 assert_eq!(prog.destructuring_wires.len(), 1);
1389 assert_eq!(prog.destructuring_wires[0].names, vec!["a", "b"]);
1390 assert_eq!(prog.out_assignments.len(), 2);
1391 }
1392
1393 #[test]
1396 fn test_feedback_declaration() {
1397 let prog = parse("feedback fb: signal;");
1398 assert_eq!(prog.feedback_decls.len(), 1);
1399 assert_eq!(prog.feedback_decls[0].name, "fb");
1400 assert_eq!(prog.feedback_decls[0].port_type, PortType::Signal);
1401 assert!(prog.feedback_decls[0].span.is_some());
1402 }
1403
1404 #[test]
1405 fn test_feedback_assignment() {
1406 let prog = parse("feedback fb = tapin~(mixed, 1000);");
1407 assert_eq!(prog.feedback_assignments.len(), 1);
1408 assert_eq!(prog.feedback_assignments[0].target, "fb");
1409 assert_eq!(
1410 prog.feedback_assignments[0].value,
1411 Expr::Call {
1412 object: "tapin~".to_string(),
1413 args: vec![
1414 CallArg::positional(Expr::Ref("mixed".to_string())),
1415 CallArg::positional(Expr::Lit(LitValue::Int(1000))),
1416 ],
1417 }
1418 );
1419 assert!(prog.feedback_assignments[0].span.is_some());
1420 }
1421
1422 #[test]
1423 fn test_feedback_full_patch() {
1424 let prog = parse(
1425 r#"
1426in 0 (input): signal;
1427out 0 (output): signal;
1428
1429feedback fb: signal;
1430wire delayed = tapout~(fb, 500);
1431wire mixed = add~(input, mul~(delayed, 0.3));
1432feedback fb = tapin~(mixed, 1000);
1433out[0] = mixed;
1434"#,
1435 );
1436
1437 assert_eq!(prog.feedback_decls.len(), 1);
1438 assert_eq!(prog.feedback_decls[0].name, "fb");
1439 assert_eq!(prog.wires.len(), 2);
1440 assert_eq!(prog.feedback_assignments.len(), 1);
1441 assert_eq!(prog.out_assignments.len(), 1);
1442 }
1443
1444 #[test]
1447 fn test_output_port_access_in_wire() {
1448 let prog = parse("wire x = node.out[0];");
1449 match &prog.wires[0].value {
1450 Expr::OutputPortAccess(opa) => {
1451 assert_eq!(opa.object, "node");
1452 assert_eq!(opa.index, 0);
1453 }
1454 other => panic!("expected OutputPortAccess, got {:?}", other),
1455 }
1456 }
1457
1458 #[test]
1459 fn test_output_port_access_in_call_arg() {
1460 let prog = parse("wire y = mul~(node.out[0], 0.5);");
1461 if let Expr::Call { args, .. } = &prog.wires[0].value {
1462 match &args[0].value {
1463 Expr::OutputPortAccess(opa) => {
1464 assert_eq!(opa.object, "node");
1465 assert_eq!(opa.index, 0);
1466 }
1467 other => panic!("expected OutputPortAccess, got {:?}", other),
1468 }
1469 } else {
1470 panic!("expected Call");
1471 }
1472 }
1473
1474 #[test]
1475 fn test_output_port_access_higher_index() {
1476 let prog = parse("wire z = node.out[2];");
1477 match &prog.wires[0].value {
1478 Expr::OutputPortAccess(opa) => {
1479 assert_eq!(opa.object, "node");
1480 assert_eq!(opa.index, 2);
1481 }
1482 other => panic!("expected OutputPortAccess, got {:?}", other),
1483 }
1484 }
1485
1486 #[test]
1487 fn test_input_port_access_in_direct_connection() {
1488 let prog = parse("node_a.in[0] = trigger;");
1489 assert_eq!(prog.direct_connections.len(), 1);
1490 assert_eq!(prog.direct_connections[0].target.object, "node_a");
1491 assert_eq!(prog.direct_connections[0].target.index, 0);
1492 }
1493
1494 #[test]
1495 fn test_direct_connection_index_omitted() {
1496 let prog = parse("tap_l.in = add~(input, fb_r);");
1498 assert_eq!(prog.direct_connections.len(), 1);
1499 assert_eq!(prog.direct_connections[0].target.object, "tap_l");
1500 assert_eq!(prog.direct_connections[0].target.index, 0);
1501 }
1502
1503 #[test]
1504 fn test_direct_connection_index_explicit() {
1505 let prog = parse("filter.in[2] = resonance;");
1507 assert_eq!(prog.direct_connections.len(), 1);
1508 assert_eq!(prog.direct_connections[0].target.object, "filter");
1509 assert_eq!(prog.direct_connections[0].target.index, 2);
1510 }
1511
1512 #[test]
1515 fn test_state_declaration_int() {
1516 let prog = parse("state counter: int = 0;");
1517 assert_eq!(prog.state_decls.len(), 1);
1518 assert_eq!(prog.state_decls[0].name, "counter");
1519 assert_eq!(prog.state_decls[0].port_type, PortType::Int);
1520 assert_eq!(prog.state_decls[0].init_value, Expr::Lit(LitValue::Int(0)));
1521 assert!(prog.state_decls[0].span.is_some());
1522 }
1523
1524 #[test]
1525 fn test_state_declaration_float() {
1526 let prog = parse("state volume: float = 0.5;");
1527 assert_eq!(prog.state_decls[0].name, "volume");
1528 assert_eq!(prog.state_decls[0].port_type, PortType::Float);
1529 assert_eq!(
1530 prog.state_decls[0].init_value,
1531 Expr::Lit(LitValue::Float(0.5))
1532 );
1533 }
1534
1535 #[test]
1536 fn test_state_assignment() {
1537 let prog = parse("state counter = next;");
1538 assert_eq!(prog.state_assignments.len(), 1);
1539 assert_eq!(prog.state_assignments[0].name, "counter");
1540 assert_eq!(
1541 prog.state_assignments[0].value,
1542 Expr::Ref("next".to_string())
1543 );
1544 assert!(prog.state_assignments[0].span.is_some());
1545 }
1546
1547 #[test]
1548 fn test_state_assignment_with_call() {
1549 let prog = parse("state counter = add(counter, 1);");
1550 assert_eq!(
1551 prog.state_assignments[0].value,
1552 Expr::Call {
1553 object: "add".to_string(),
1554 args: vec![
1555 CallArg::positional(Expr::Ref("counter".to_string())),
1556 CallArg::positional(Expr::Lit(LitValue::Int(1))),
1557 ],
1558 }
1559 );
1560 }
1561
1562 #[test]
1563 fn test_state_full_counter_patch() {
1564 let prog = parse(
1565 r#"
1566state counter: int = 0;
1567wire next = add(counter, 1);
1568state counter = next;
1569out 0 (count): int;
1570out[0] = next;
1571"#,
1572 );
1573 assert_eq!(prog.state_decls.len(), 1);
1574 assert_eq!(prog.wires.len(), 1);
1575 assert_eq!(prog.state_assignments.len(), 1);
1576 assert_eq!(prog.out_decls.len(), 1);
1577 assert_eq!(prog.out_assignments.len(), 1);
1578 }
1579
1580 #[test]
1583 fn test_dotted_identifier_object_call() {
1584 let prog = parse("wire vid = jit.gl.videoplane();");
1585 assert_eq!(
1586 prog.wires[0].value,
1587 Expr::Call {
1588 object: "jit.gl.videoplane".to_string(),
1589 args: vec![],
1590 }
1591 );
1592 }
1593
1594 #[test]
1595 fn test_dotted_identifier_with_args() {
1596 let prog = parse("wire dial = live.dial(0.5);");
1597 assert_eq!(
1598 prog.wires[0].value,
1599 Expr::Call {
1600 object: "live.dial".to_string(),
1601 args: vec![CallArg::positional(Expr::Lit(LitValue::Float(0.5)))],
1602 }
1603 );
1604 }
1605
1606 #[test]
1607 fn test_dotted_identifier_does_not_conflict_with_port_access() {
1608 let prog = parse(
1609 r#"
1610wire node = cycle~(440);
1611wire x = node.out[0];
1612node.in[0] = 440;
1613"#,
1614 );
1615
1616 assert_eq!(prog.wires.len(), 2);
1617 assert_eq!(
1618 prog.wires[1].value,
1619 Expr::OutputPortAccess(OutputPortAccess {
1620 object: "node".to_string(),
1621 index: 0,
1622 })
1623 );
1624 assert_eq!(prog.direct_connections.len(), 1);
1625 assert_eq!(prog.direct_connections[0].target.object, "node");
1626 assert_eq!(prog.direct_connections[0].target.index, 0);
1627 }
1628
1629 #[test]
1632 fn test_hyphenated_identifier() {
1633 let prog = parse("wire x = drunk-walk(10);");
1634 assert_eq!(
1635 prog.wires[0].value,
1636 Expr::Call {
1637 object: "drunk-walk".to_string(),
1638 args: vec![CallArg::positional(Expr::Lit(LitValue::Int(10)))],
1639 }
1640 );
1641 }
1642
1643 #[test]
1646 fn test_msg_declaration() {
1647 let prog = parse(r#"msg click = "bang";"#);
1648 assert_eq!(prog.msg_decls.len(), 1);
1649 assert_eq!(prog.msg_decls[0].name, "click");
1650 assert_eq!(prog.msg_decls[0].content, "bang");
1651 assert!(prog.msg_decls[0].span.is_some());
1652 }
1653
1654 #[test]
1655 fn test_msg_declaration_with_template() {
1656 let prog = parse(r#"msg format = "set $1 $2";"#);
1657 assert_eq!(prog.msg_decls[0].content, "set $1 $2");
1658 }
1659
1660 #[test]
1661 fn test_msg_declaration_multiple() {
1662 let prog = parse(
1663 r#"
1664msg bang_msg = "bang";
1665msg set_msg = "set 42";
1666"#,
1667 );
1668 assert_eq!(prog.msg_decls.len(), 2);
1669 assert_eq!(prog.msg_decls[0].content, "bang");
1670 assert_eq!(prog.msg_decls[1].content, "set 42");
1671 }
1672
1673 #[test]
1674 fn test_msg_with_wire_and_connection() {
1675 let prog = parse(
1676 r#"
1677msg click = "bang";
1678wire btn = button();
1679btn.in[0] = click;
1680"#,
1681 );
1682 assert_eq!(prog.msg_decls.len(), 1);
1683 assert_eq!(prog.wires.len(), 1);
1684 assert_eq!(prog.direct_connections.len(), 1);
1685 assert_eq!(
1686 prog.direct_connections[0].value,
1687 Expr::Ref("click".to_string())
1688 );
1689 }
1690
1691 #[test]
1694 fn test_wire_with_attrs() {
1695 let prog = parse(r#"wire w = flonum(x).attr(minimum: 0., maximum: 100.);"#);
1696 let wire = &prog.wires[0];
1697 assert_eq!(wire.attrs.len(), 2);
1698 assert_eq!(wire.attrs[0].key, "minimum");
1699 assert_eq!(wire.attrs[0].value, AttrValue::Float(0.0));
1700 assert_eq!(wire.attrs[1].key, "maximum");
1701 assert_eq!(wire.attrs[1].value, AttrValue::Float(100.0));
1702 }
1703
1704 #[test]
1705 fn test_wire_without_attrs() {
1706 let prog = parse("wire osc = cycle~(440);");
1707 assert!(prog.wires[0].attrs.is_empty());
1708 }
1709
1710 #[test]
1711 fn test_wire_with_string_attr() {
1712 let prog = parse(r#"wire dial = live.dial().attr(parameter_longname: "Cutoff");"#);
1713 assert_eq!(prog.wires[0].attrs.len(), 1);
1714 assert_eq!(prog.wires[0].attrs[0].key, "parameter_longname");
1715 assert_eq!(
1716 prog.wires[0].attrs[0].value,
1717 AttrValue::Str("Cutoff".to_string())
1718 );
1719 }
1720
1721 #[test]
1722 fn test_wire_with_ident_attr() {
1723 let prog = parse("wire osc = cycle~(freq).attr(phase: half);");
1724 assert_eq!(prog.wires[0].attrs[0].key, "phase");
1725 assert_eq!(
1726 prog.wires[0].attrs[0].value,
1727 AttrValue::Ident("half".to_string())
1728 );
1729 }
1730
1731 #[test]
1732 fn test_wire_with_int_attr() {
1733 let prog = parse("wire w = flonum(x).attr(minimum: 0, maximum: 100);");
1734 assert_eq!(prog.wires[0].attrs[0].value, AttrValue::Int(0));
1735 assert_eq!(prog.wires[0].attrs[1].value, AttrValue::Int(100));
1736 }
1737
1738 #[test]
1739 fn test_msg_with_attrs() {
1740 let prog = parse(r#"msg click = "bang".attr(patching_rect: 100.);"#);
1741 let msg = &prog.msg_decls[0];
1742 assert_eq!(msg.attrs.len(), 1);
1743 assert_eq!(msg.attrs[0].key, "patching_rect");
1744 assert_eq!(msg.attrs[0].value, AttrValue::Float(100.0));
1745 }
1746
1747 #[test]
1748 fn test_msg_without_attrs() {
1749 let prog = parse(r#"msg click = "bang";"#);
1750 assert!(prog.msg_decls[0].attrs.is_empty());
1751 }
1752
1753 #[test]
1754 fn test_wire_multiline_attrs() {
1755 let prog = parse(
1756 r#"
1757wire dial = live.dial().attr(
1758 parameter_longname: "Cutoff",
1759 parameter_shortname: "Cut",
1760 minimum: 20.,
1761 maximum: 20000.
1762);
1763"#,
1764 );
1765 assert_eq!(prog.wires[0].attrs.len(), 4);
1766 assert_eq!(prog.wires[0].attrs[0].key, "parameter_longname");
1767 assert_eq!(prog.wires[0].attrs[1].key, "parameter_shortname");
1768 assert_eq!(prog.wires[0].attrs[2].key, "minimum");
1769 assert_eq!(prog.wires[0].attrs[3].key, "maximum");
1770 }
1771
1772 #[test]
1775 fn test_negative_integer() {
1776 let prog = parse("wire x = foo(-7);");
1777 assert_eq!(
1778 prog.wires[0].value,
1779 Expr::Call {
1780 object: "foo".to_string(),
1781 args: vec![CallArg::positional(Expr::Lit(LitValue::Int(-7)))],
1782 }
1783 );
1784 }
1785
1786 #[test]
1787 fn test_negative_float() {
1788 let prog = parse("wire x = foo(-3.14);");
1789 assert_eq!(
1790 prog.wires[0].value,
1791 Expr::Call {
1792 object: "foo".to_string(),
1793 args: vec![CallArg::positional(Expr::Lit(LitValue::Float(-3.14)))],
1794 }
1795 );
1796 }
1797
1798 #[test]
1801 fn test_operator_object_call() {
1802 let prog = parse("wire x = ?(a, b);");
1803 assert_eq!(
1804 prog.wires[0].value,
1805 Expr::Call {
1806 object: "?".to_string(),
1807 args: vec![
1808 CallArg::positional(Expr::Ref("a".to_string())),
1809 CallArg::positional(Expr::Ref("b".to_string())),
1810 ],
1811 }
1812 );
1813 }
1814
1815 #[test]
1816 fn test_mul_operator_call() {
1817 let prog = parse("wire x = *(a, b);");
1818 assert_eq!(
1819 prog.wires[0].value,
1820 Expr::Call {
1821 object: "*".to_string(),
1822 args: vec![
1823 CallArg::positional(Expr::Ref("a".to_string())),
1824 CallArg::positional(Expr::Ref("b".to_string())),
1825 ],
1826 }
1827 );
1828 }
1829
1830 #[test]
1833 fn test_simple_fm_tilde() {
1834 let prog = parse(
1835 r##"
1836in 0 (Carrier_frequency): float;
1837in 1 (Harmonicity_ratio): float;
1838in 2 (Modulation_index): float;
1839out 0 (FM_signal): signal;
1840
1841wire w_1 = mul~("#1");
1842wire w_2 = mul~("#2");
1843wire w_3 = cycle~(w_1);
1844wire w_4 = mul~(w_3, w_2);
1845wire w_5 = add~(Carrier_frequency, w_4);
1846wire w_6 = cycle~(w_5);
1847
1848w_1.in[1] = Harmonicity_ratio;
1849w_1.in[0] = Carrier_frequency;
1850w_2.in[0] = w_1;
1851w_2.in[1] = Modulation_index;
1852
1853out[0] = w_6;
1854"##,
1855 );
1856
1857 assert_eq!(prog.in_decls.len(), 3);
1858 assert_eq!(prog.out_decls.len(), 1);
1859 assert_eq!(prog.wires.len(), 6);
1860 assert_eq!(prog.direct_connections.len(), 4);
1861 assert_eq!(prog.out_assignments.len(), 1);
1862
1863 assert_eq!(
1865 prog.wires[0].value,
1866 Expr::Call {
1867 object: "mul~".to_string(),
1868 args: vec![CallArg::positional(Expr::Lit(LitValue::Str(
1869 "#1".to_string()
1870 )))],
1871 }
1872 );
1873 }
1874
1875 #[test]
1878 fn test_synth_fm_voice() {
1879 let prog = parse(
1880 r#"
1881in 0 (MIDi_key___and_velocity): float;
1882in 1 (frequency_bend): float;
1883in 2 (additional_modulation_depth): float;
1884out 0 (voice_output): signal;
1885
1886msg msg_1 = "0 100".attr(background: 0, bgcolor2: "0.867 0.867 0.867 1.0", gradient: 0);
1887msg msg_2 = "setdomain $1".attr(background: 0, bgcolor2: "0.867 0.867 0.867 1.0", gradient: 0);
1888
1889wire w_1 = unpack(MIDi_key___and_velocity).attr(background: 0);
1890wire w_2 = add~(8.0).attr(background: 0);
1891wire w_3 = mtof(w_1.out[0]).attr(background: 0);
1892wire w_4 = select(0).attr(background: 0);
1893
1894w_2.in[0] = additional_modulation_depth;
1895w_4.in[0] = w_1.out[1];
1896
1897out[0] = w_4;
1898"#,
1899 );
1900
1901 assert_eq!(prog.in_decls.len(), 3);
1902 assert_eq!(prog.out_decls.len(), 1);
1903 assert_eq!(prog.msg_decls.len(), 2);
1904 assert_eq!(prog.wires.len(), 4);
1905
1906 assert_eq!(prog.msg_decls[0].content, "0 100");
1908 assert_eq!(prog.msg_decls[0].attrs.len(), 3);
1909 assert_eq!(prog.msg_decls[0].attrs[0].key, "background");
1910
1911 assert_eq!(
1913 prog.wires[2].value,
1914 Expr::Call {
1915 object: "mtof".to_string(),
1916 args: vec![CallArg::positional(Expr::OutputPortAccess(
1917 OutputPortAccess {
1918 object: "w_1".to_string(),
1919 index: 0,
1920 }
1921 ))],
1922 }
1923 );
1924
1925 assert_eq!(
1927 prog.direct_connections[1].value,
1928 Expr::OutputPortAccess(OutputPortAccess {
1929 object: "w_1".to_string(),
1930 index: 1,
1931 })
1932 );
1933 }
1934
1935 #[test]
1938 fn test_wire_with_complex_string_arg() {
1939 let prog = parse(r#"wire w_6 = t("b", "f", "f");"#);
1940 assert_eq!(
1941 prog.wires[0].value,
1942 Expr::Call {
1943 object: "t".to_string(),
1944 args: vec![
1945 CallArg::positional(Expr::Lit(LitValue::Str("b".to_string()))),
1946 CallArg::positional(Expr::Lit(LitValue::Str("f".to_string()))),
1947 CallArg::positional(Expr::Lit(LitValue::Str("f".to_string()))),
1948 ],
1949 }
1950 );
1951 }
1952
1953 #[test]
1956 fn test_wire_with_escaped_string() {
1957 let prog = parse(r#"wire w = expr("pow($f1/127.\\,4.)");"#);
1958 assert_eq!(
1959 prog.wires[0].value,
1960 Expr::Call {
1961 object: "expr".to_string(),
1962 args: vec![CallArg::positional(Expr::Lit(LitValue::Str(
1963 "pow($f1/127.\\,4.)".to_string()
1964 )))],
1965 }
1966 );
1967 }
1968
1969 #[test]
1972 fn test_trailing_dot_float() {
1973 let prog = parse("wire x = foo(100.);");
1974 assert_eq!(
1975 prog.wires[0].value,
1976 Expr::Call {
1977 object: "foo".to_string(),
1978 args: vec![CallArg::positional(Expr::Lit(LitValue::Float(100.0)))],
1979 }
1980 );
1981 }
1982
1983 #[test]
1986 fn test_scientific_notation() {
1987 let prog = parse("wire x = foo(1e-6);");
1988 assert_eq!(
1989 prog.wires[0].value,
1990 Expr::Call {
1991 object: "foo".to_string(),
1992 args: vec![CallArg::positional(Expr::Lit(LitValue::Float(1e-6)))],
1993 }
1994 );
1995 }
1996
1997 #[test]
2000 fn test_msg_identifier_direct_connection() {
2001 let prog = parse("msg_1.in[0] = w_4.out[0];");
2003 assert_eq!(prog.direct_connections.len(), 1);
2004 assert_eq!(prog.direct_connections[0].target.object, "msg_1");
2005 assert_eq!(
2006 prog.direct_connections[0].value,
2007 Expr::OutputPortAccess(OutputPortAccess {
2008 object: "w_4".to_string(),
2009 index: 0,
2010 })
2011 );
2012 }
2013
2014 #[test]
2017 fn test_implicit_in_single() {
2018 let prog = parse("in freq: float;");
2019 assert_eq!(prog.in_decls.len(), 1);
2020 assert_eq!(prog.in_decls[0].index, 0);
2021 assert_eq!(prog.in_decls[0].name, "freq");
2022 assert_eq!(prog.in_decls[0].port_type, PortType::Float);
2023 }
2024
2025 #[test]
2026 fn test_implicit_in_multiple() {
2027 let prog = parse("in freq: float;\nin cutoff: float;");
2028 assert_eq!(prog.in_decls.len(), 2);
2029 assert_eq!(prog.in_decls[0].index, 0);
2030 assert_eq!(prog.in_decls[0].name, "freq");
2031 assert_eq!(prog.in_decls[1].index, 1);
2032 assert_eq!(prog.in_decls[1].name, "cutoff");
2033 }
2034
2035 #[test]
2036 fn test_implicit_out_single() {
2037 let prog = parse("out audio: signal;");
2038 assert_eq!(prog.out_decls.len(), 1);
2039 assert_eq!(prog.out_decls[0].index, 0);
2040 assert_eq!(prog.out_decls[0].name, "audio");
2041 assert_eq!(prog.out_decls[0].port_type, PortType::Signal);
2042 }
2043
2044 #[test]
2045 fn test_implicit_out_multiple() {
2046 let prog = parse("out left: signal;\nout right: signal;");
2047 assert_eq!(prog.out_decls.len(), 2);
2048 assert_eq!(prog.out_decls[0].index, 0);
2049 assert_eq!(prog.out_decls[0].name, "left");
2050 assert_eq!(prog.out_decls[1].index, 1);
2051 assert_eq!(prog.out_decls[1].name, "right");
2052 }
2053
2054 #[test]
2055 fn test_explicit_index_still_works() {
2056 let prog = parse("in 5 (freq): float;");
2057 assert_eq!(prog.in_decls.len(), 1);
2058 assert_eq!(prog.in_decls[0].index, 5);
2059 assert_eq!(prog.in_decls[0].name, "freq");
2060 }
2061
2062 #[test]
2063 fn test_implicit_separate_counters_in_out() {
2064 let prog = parse("in a: float;\nout x: signal;\nin b: float;\nout y: signal;");
2066 assert_eq!(prog.in_decls.len(), 2);
2067 assert_eq!(prog.in_decls[0].index, 0);
2068 assert_eq!(prog.in_decls[0].name, "a");
2069 assert_eq!(prog.in_decls[1].index, 1);
2070 assert_eq!(prog.in_decls[1].name, "b");
2071 assert_eq!(prog.out_decls.len(), 2);
2072 assert_eq!(prog.out_decls[0].index, 0);
2073 assert_eq!(prog.out_decls[0].name, "x");
2074 assert_eq!(prog.out_decls[1].index, 1);
2075 assert_eq!(prog.out_decls[1].name, "y");
2076 }
2077
2078 #[test]
2079 fn test_implicit_all_port_types() {
2080 let prog = parse(
2081 "in a: signal;\nin b: float;\nin c: int;\nin d: bang;\nin e: list;\nin f: symbol;",
2082 );
2083 assert_eq!(prog.in_decls.len(), 6);
2084 assert_eq!(prog.in_decls[0].port_type, PortType::Signal);
2085 assert_eq!(prog.in_decls[1].port_type, PortType::Float);
2086 assert_eq!(prog.in_decls[2].port_type, PortType::Int);
2087 assert_eq!(prog.in_decls[3].port_type, PortType::Bang);
2088 assert_eq!(prog.in_decls[4].port_type, PortType::List);
2089 assert_eq!(prog.in_decls[5].port_type, PortType::Symbol);
2090 for (i, decl) in prog.in_decls.iter().enumerate() {
2091 assert_eq!(decl.index, i as u32);
2092 }
2093 }
2094
2095 #[test]
2096 fn test_implicit_in_full_patch() {
2097 let prog = parse(
2098 r#"
2099in carrier_freq: float;
2100in harmonicity: float;
2101out fm_signal: signal;
2102
2103wire osc = cycle~(carrier_freq);
2104out[0] = osc;
2105"#,
2106 );
2107 assert_eq!(prog.in_decls.len(), 2);
2108 assert_eq!(prog.in_decls[0].index, 0);
2109 assert_eq!(prog.in_decls[0].name, "carrier_freq");
2110 assert_eq!(prog.in_decls[1].index, 1);
2111 assert_eq!(prog.in_decls[1].name, "harmonicity");
2112 assert_eq!(prog.out_decls.len(), 1);
2113 assert_eq!(prog.out_decls[0].index, 0);
2114 assert_eq!(prog.out_decls[0].name, "fm_signal");
2115 assert_eq!(prog.wires.len(), 1);
2116 assert_eq!(prog.out_assignments.len(), 1);
2117 }
2118
2119 #[test]
2122 fn test_out_decl_inline_value_implicit() {
2123 let prog = parse(
2125 r#"
2126wire osc = cycle~(440);
2127out audio: signal = osc;
2128"#,
2129 );
2130 assert_eq!(prog.out_decls.len(), 1);
2131 assert_eq!(prog.out_decls[0].index, 0);
2132 assert_eq!(prog.out_decls[0].name, "audio");
2133 assert_eq!(prog.out_decls[0].port_type, PortType::Signal);
2134 assert_eq!(prog.out_decls[0].value, Some(Expr::Ref("osc".to_string())));
2135 assert!(prog.out_assignments.is_empty());
2137 }
2138
2139 #[test]
2140 fn test_out_decl_inline_value_explicit() {
2141 let prog = parse(
2143 r#"
2144wire osc = cycle~(440);
2145out 0 (audio): signal = osc;
2146"#,
2147 );
2148 assert_eq!(prog.out_decls.len(), 1);
2149 assert_eq!(prog.out_decls[0].index, 0);
2150 assert_eq!(prog.out_decls[0].name, "audio");
2151 assert_eq!(prog.out_decls[0].port_type, PortType::Signal);
2152 assert_eq!(prog.out_decls[0].value, Some(Expr::Ref("osc".to_string())));
2153 assert!(prog.out_assignments.is_empty());
2154 }
2155
2156 #[test]
2157 fn test_out_decl_without_value_backward_compat() {
2158 let prog = parse(
2160 r#"
2161out audio: signal;
2162wire osc = cycle~(440);
2163out[0] = osc;
2164"#,
2165 );
2166 assert_eq!(prog.out_decls.len(), 1);
2167 assert_eq!(prog.out_decls[0].value, None);
2168 assert_eq!(prog.out_assignments.len(), 1);
2169 assert_eq!(prog.out_assignments[0].index, 0);
2170 }
2171
2172 #[test]
2173 fn test_out_decl_inline_with_call_expr() {
2174 let prog = parse(
2176 r#"
2177out audio: signal = cycle~(440);
2178"#,
2179 );
2180 assert_eq!(prog.out_decls.len(), 1);
2181 assert_eq!(prog.out_decls[0].name, "audio");
2182 assert_eq!(
2183 prog.out_decls[0].value,
2184 Some(Expr::Call {
2185 object: "cycle~".to_string(),
2186 args: vec![CallArg::positional(Expr::Lit(LitValue::Int(440)))],
2187 })
2188 );
2189 assert!(prog.out_assignments.is_empty());
2190 }
2191
2192 #[test]
2193 fn test_out_assignment_unchanged() {
2194 let prog = parse(
2196 r#"
2197out audio: signal;
2198wire osc = cycle~(440);
2199out[0] = osc;
2200"#,
2201 );
2202 assert_eq!(prog.out_decls.len(), 1);
2203 assert_eq!(prog.out_decls[0].value, None);
2204 assert_eq!(prog.out_assignments.len(), 1);
2205 assert_eq!(prog.out_assignments[0].index, 0);
2206 assert_eq!(prog.out_assignments[0].value, Expr::Ref("osc".to_string()));
2207 }
2208
2209 #[test]
2210 fn test_out_decl_inline_multiple() {
2211 let prog = parse(
2213 r#"
2214wire left = cycle~(440);
2215wire right = cycle~(880);
2216out left_out: signal = left;
2217out right_out: signal = right;
2218"#,
2219 );
2220 assert_eq!(prog.out_decls.len(), 2);
2221 assert_eq!(prog.out_decls[0].index, 0);
2222 assert_eq!(prog.out_decls[0].name, "left_out");
2223 assert_eq!(prog.out_decls[0].value, Some(Expr::Ref("left".to_string())));
2224 assert_eq!(prog.out_decls[1].index, 1);
2225 assert_eq!(prog.out_decls[1].name, "right_out");
2226 assert_eq!(
2227 prog.out_decls[1].value,
2228 Some(Expr::Ref("right".to_string()))
2229 );
2230 assert!(prog.out_assignments.is_empty());
2231 }
2232
2233 #[test]
2236 fn test_named_args_single() {
2237 let prog = parse("wire x = cycle~(freq: 440);");
2238 assert_eq!(prog.wires.len(), 1);
2239 if let Expr::Call { object, args } = &prog.wires[0].value {
2240 assert_eq!(object, "cycle~");
2241 assert_eq!(args.len(), 1);
2242 assert_eq!(args[0].name, Some("freq".to_string()));
2243 assert_eq!(args[0].value, Expr::Lit(LitValue::Int(440)));
2244 } else {
2245 panic!("expected Call");
2246 }
2247 }
2248
2249 #[test]
2250 fn test_named_args_multiple() {
2251 let prog = parse("wire x = biquad~(input: osc, freq: cutoff, q: resonance);");
2252 if let Expr::Call { args, .. } = &prog.wires[0].value {
2253 assert_eq!(args.len(), 3);
2254 assert_eq!(args[0].name, Some("input".to_string()));
2255 assert_eq!(args[0].value, Expr::Ref("osc".to_string()));
2256 assert_eq!(args[1].name, Some("freq".to_string()));
2257 assert_eq!(args[1].value, Expr::Ref("cutoff".to_string()));
2258 assert_eq!(args[2].name, Some("q".to_string()));
2259 assert_eq!(args[2].value, Expr::Ref("resonance".to_string()));
2260 } else {
2261 panic!("expected Call");
2262 }
2263 }
2264
2265 #[test]
2266 fn test_mixed_positional_and_named_args() {
2267 let prog = parse("wire x = biquad~(osc, freq: cutoff, 0.7);");
2268 if let Expr::Call { args, .. } = &prog.wires[0].value {
2269 assert_eq!(args.len(), 3);
2270 assert_eq!(args[0].name, None);
2271 assert_eq!(args[0].value, Expr::Ref("osc".to_string()));
2272 assert_eq!(args[1].name, Some("freq".to_string()));
2273 assert_eq!(args[1].value, Expr::Ref("cutoff".to_string()));
2274 assert_eq!(args[2].name, None);
2275 assert_eq!(args[2].value, Expr::Lit(LitValue::Float(0.7)));
2276 } else {
2277 panic!("expected Call");
2278 }
2279 }
2280
2281 #[test]
2282 fn test_named_arg_with_literal() {
2283 let prog = parse(r#"wire x = print(msg: "hello");"#);
2284 if let Expr::Call { args, .. } = &prog.wires[0].value {
2285 assert_eq!(args.len(), 1);
2286 assert_eq!(args[0].name, Some("msg".to_string()));
2287 assert_eq!(args[0].value, Expr::Lit(LitValue::Str("hello".to_string())));
2288 } else {
2289 panic!("expected Call");
2290 }
2291 }
2292
2293 #[test]
2294 fn test_positional_args_still_work() {
2295 let prog = parse("wire x = cycle~(440);");
2297 if let Expr::Call { args, .. } = &prog.wires[0].value {
2298 assert_eq!(args.len(), 1);
2299 assert_eq!(args[0].name, None);
2300 assert_eq!(args[0].value, Expr::Lit(LitValue::Int(440)));
2301 } else {
2302 panic!("expected Call");
2303 }
2304 }
2305}