devalang_core/core/parser/handler/identifier/
synth.rs

1use std::collections::HashMap;
2
3use devalang_types::Value;
4
5use crate::core::{
6    lexer::token::Token,
7    parser::{
8        driver::parser::Parser,
9        handler::dot::parse_dot_token,
10        statement::{Statement, StatementKind},
11    },
12    store::global::GlobalStore,
13};
14
15pub fn parse_synth_token(
16    parser: &mut Parser,
17    _current_token: Token,
18    _global_store: &mut GlobalStore,
19) -> Statement {
20    parser.advance(); // consume 'synth'
21
22    let Some(synth_token) = parser.previous_clone() else {
23        return Statement::unknown();
24    };
25
26    // Expect a provider or waveform identifier (can be dotted: alias.synth)
27    // Also accept a dot-led entity by delegating to the dot parser (e.g. .module.export)
28    // Support optional explicit waveform after a dotted provider: `synth alias.synth saw {}`
29    let synth_waveform = if let Some(first_token) = parser.peek_clone() {
30        use crate::core::lexer::token::TokenKind;
31
32        if first_token.kind == TokenKind::Dot {
33            // Parse dot-entity and extract its entity string
34            let dot_stmt = parse_dot_token(parser, _global_store);
35            // Extract entity if the parsed statement is a Trigger
36            match dot_stmt.kind {
37                StatementKind::Trigger { entity, .. } => entity,
38                _ => String::new(),
39            }
40        } else {
41            if first_token.kind != crate::core::lexer::token::TokenKind::Identifier
42                && first_token.kind != crate::core::lexer::token::TokenKind::Number
43                && first_token.kind != crate::core::lexer::token::TokenKind::Synth
44            {
45                return crate::core::parser::statement::error_from_token(
46                    first_token.clone(),
47                    "Expected identifier after 'synth'".to_string(),
48                );
49            }
50
51            // Collect dotted parts on the same line
52            let mut parts: Vec<String> = Vec::new();
53            let current_line = first_token.line;
54            loop {
55                let Some(tok) = parser.peek_clone() else {
56                    break;
57                };
58                if tok.line != current_line {
59                    break;
60                }
61                match tok.kind {
62                    crate::core::lexer::token::TokenKind::Identifier
63                    | crate::core::lexer::token::TokenKind::Number
64                    | crate::core::lexer::token::TokenKind::Synth => {
65                        parts.push(tok.lexeme.clone());
66                        parser.advance();
67                        // If next isn't a dot on same line, stop
68                        if let Some(next) = parser.peek_clone() {
69                            if !(next.line == current_line
70                                && next.kind == crate::core::lexer::token::TokenKind::Dot)
71                            {
72                                break;
73                            }
74                        } else {
75                            break;
76                        }
77                    }
78                    crate::core::lexer::token::TokenKind::Dot => {
79                        parser.advance();
80                    }
81                    _ => break,
82                }
83            }
84
85            // The joined parts form either:
86            // - a simple waveform name (e.g. "sine")
87            // - a provider entity (e.g. "author.synth")
88            let first = parts.join(".");
89
90            // If this looks like a dotted provider, try to consume an optional explicit
91            // waveform identifier on the same line (e.g. `synth author.synth saw`). If none
92            // is provided, fall back to the original behaviour (use the joined string as
93            // the waveform identifier) to remain backward-compatible.
94            if first.contains('.') {
95                // peek next token on same line
96                if let Some(next_tok) = parser.peek_clone() {
97                    if next_tok.line == current_line
98                        && matches!(
99                            next_tok.kind,
100                            crate::core::lexer::token::TokenKind::Identifier
101                                | crate::core::lexer::token::TokenKind::Number
102                                | crate::core::lexer::token::TokenKind::Synth
103                        )
104                    {
105                        // consume waveform token
106                        let waveform = next_tok.lexeme.clone();
107                        parser.advance();
108                        // store as "provider.waveform" style? keep only waveform for compatibility
109                        waveform
110                    } else {
111                        // no explicit waveform -> use the provider string (old behaviour)
112                        first
113                    }
114                } else {
115                    first
116                }
117            } else {
118                // simple waveform name
119                first
120            }
121        }
122    } else {
123        return crate::core::parser::statement::error_from_token(
124            synth_token,
125            "Expected identifier after 'synth'".to_string(),
126        );
127    };
128
129    // Skip formatting before optional parameters map
130    while parser.check_token(crate::core::lexer::token::TokenKind::Newline)
131        || parser.check_token(crate::core::lexer::token::TokenKind::Indent)
132        || parser.check_token(crate::core::lexer::token::TokenKind::Dedent)
133        || parser.check_token(crate::core::lexer::token::TokenKind::Whitespace)
134    {
135        parser.advance();
136    }
137
138    // Expect synth optional parameters map
139    let parameters = if let Some(params) = parser.parse_map_value() {
140        // If parameters are provided, we expect a map
141        if let Value::Map(map) = params {
142            map
143        } else {
144            return crate::core::parser::statement::error_from_token(
145                synth_token,
146                "Expected a map for synth parameters".to_string(),
147            );
148        }
149    } else {
150        // If no parameters are provided, we can still create the statement with an empty map
151        HashMap::new()
152    };
153
154    Statement {
155        kind: StatementKind::Synth,
156        value: Value::Map(HashMap::from([
157            ("entity".to_string(), Value::String("synth".to_string())),
158            (
159                "value".to_string(),
160                Value::Map(HashMap::from([
161                    // Store waveform as identifier to allow resolution from variables/exports
162                    ("waveform".to_string(), Value::Identifier(synth_waveform)),
163                    ("parameters".to_string(), Value::Map(parameters)),
164                ])),
165            ),
166        ])),
167        indent: synth_token.indent,
168        line: synth_token.line,
169        column: synth_token.column,
170    }
171}