devalang_wasm/language/syntax/parser/driver/
routing.rs

1use crate::language::syntax::ast::{Statement, StatementKind, Value};
2use crate::language::syntax::parser::driver::effects::parse_chained_effects;
3use anyhow::{Result, anyhow};
4
5pub fn parse_routing_command(line_number: usize) -> Result<Statement> {
6    // The "routing" keyword itself indicates a block-based routing declaration
7    // The body will be filled in during indentation parsing in the main driver
8    Ok(Statement::new(
9        StatementKind::Routing { body: Vec::new() },
10        Value::Null,
11        0,
12        line_number,
13        1,
14    ))
15}
16
17/// Parse routing block statements (node, fx, route, duck, sidechain)
18pub fn parse_routing_statement<'a>(line: &str, line_number: usize) -> Result<Statement> {
19    let trimmed = line.trim();
20
21    // node <name> [= <alias>]
22    if trimmed.starts_with("node ") {
23        let rest = trimmed[5..].trim();
24        if let Some((name, alias)) = rest.split_once('=') {
25            let name = name.trim().trim_end_matches(':').to_string();
26            let alias = alias.trim().trim_end_matches(':').to_string();
27            return Ok(Statement::new(
28                StatementKind::RoutingNode {
29                    name,
30                    alias: Some(alias),
31                },
32                Value::Null,
33                0,
34                line_number,
35                1,
36            ));
37        } else {
38            let name = rest.trim_end_matches(':').to_string();
39            return Ok(Statement::new(
40                StatementKind::RoutingNode { name, alias: None },
41                Value::Null,
42                0,
43                line_number,
44                1,
45            ));
46        }
47    }
48
49    // fx <target> -> effect1 -> effect2 ...
50    if trimmed.starts_with("fx ") {
51        let rest = trimmed[3..].trim();
52        if let Some((target, effects_str)) = rest.split_once("->") {
53            let target = target.trim().to_string();
54            let effects_chain = format!("->{}", effects_str);
55            let effects = parse_chained_effects(&effects_chain)?;
56            return Ok(Statement::new(
57                StatementKind::RoutingFx { target, effects },
58                Value::Null,
59                0,
60                line_number,
61                1,
62            ));
63        } else {
64            return Err(anyhow!(
65                "fx statement requires effects after '->': {}",
66                trimmed
67            ));
68        }
69    }
70
71    // route <source> to <dest> with effect(...)
72    if trimmed.starts_with("route ") {
73        let rest = trimmed[6..].trim();
74        if let Some((source_part, rest)) = rest.split_once(" to ") {
75            let source = source_part.trim().to_string();
76            if let Some((dest_part, effect_part)) = rest.split_once(" with ") {
77                let destination = dest_part.trim().to_string();
78                let effect_str = effect_part.trim().trim_end_matches(':');
79                let effects = parse_single_routing_effect(effect_str)?;
80                return Ok(Statement::new(
81                    StatementKind::RoutingRoute {
82                        source,
83                        destination,
84                        effects: Some(effects),
85                    },
86                    Value::Null,
87                    0,
88                    line_number,
89                    1,
90                ));
91            } else {
92                let destination = rest.trim().trim_end_matches(':').to_string();
93                return Ok(Statement::new(
94                    StatementKind::RoutingRoute {
95                        source,
96                        destination,
97                        effects: None,
98                    },
99                    Value::Null,
100                    0,
101                    line_number,
102                    1,
103                ));
104            }
105        } else {
106            return Err(anyhow!(
107                "route statement requires format: route <source> to <dest> [with effect(...)]: {}",
108                trimmed
109            ));
110        }
111    }
112
113    // duck <source> to <dest> with effect(...)
114    if trimmed.starts_with("duck ") {
115        let rest = trimmed[5..].trim();
116        if let Some((source_part, rest)) = rest.split_once(" to ") {
117            let source = source_part.trim().to_string();
118            if let Some((dest_part, effect_part)) = rest.split_once(" with ") {
119                let destination = dest_part.trim().to_string();
120                let effect_str = effect_part.trim().trim_end_matches(':');
121                let effect = parse_single_routing_effect(effect_str)?;
122                return Ok(Statement::new(
123                    StatementKind::RoutingDuck {
124                        source,
125                        destination,
126                        effect,
127                    },
128                    Value::Null,
129                    0,
130                    line_number,
131                    1,
132                ));
133            } else {
134                return Err(anyhow!(
135                    "duck statement requires 'with' clause: {}",
136                    trimmed
137                ));
138            }
139        } else {
140            return Err(anyhow!(
141                "duck statement requires format: duck <source> to <dest> with effect(...): {}",
142                trimmed
143            ));
144        }
145    }
146
147    // sidechain <source> to <dest> with effect(...)
148    if trimmed.starts_with("sidechain ") {
149        let rest = trimmed[10..].trim();
150        if let Some((source_part, rest)) = rest.split_once(" to ") {
151            let source = source_part.trim().to_string();
152            if let Some((dest_part, effect_part)) = rest.split_once(" with ") {
153                let destination = dest_part.trim().to_string();
154                let effect_str = effect_part.trim().trim_end_matches(':');
155                let effect = parse_single_routing_effect(effect_str)?;
156                return Ok(Statement::new(
157                    StatementKind::RoutingSidechain {
158                        source,
159                        destination,
160                        effect,
161                    },
162                    Value::Null,
163                    0,
164                    line_number,
165                    1,
166                ));
167            } else {
168                return Err(anyhow!(
169                    "sidechain statement requires 'with' clause: {}",
170                    trimmed
171                ));
172            }
173        } else {
174            return Err(anyhow!(
175                "sidechain statement requires format: sidechain <source> to <dest> with effect(...): {}",
176                trimmed
177            ));
178        }
179    }
180
181    Err(anyhow!("Unknown routing statement: {}", trimmed))
182}
183
184/// Parse a single routing effect like "effect({ param: value, ... })"
185fn parse_single_routing_effect(effect_str: &str) -> Result<Value> {
186    let effect_str = effect_str.trim();
187    if effect_str.is_empty() {
188        return Err(anyhow!("Empty effect definition"));
189    }
190
191    // Handle function-like syntax: effect_name(param1, param2, ...)
192    if let Some((name, params_str)) = effect_str.split_once('(') {
193        let name = name.trim().to_string();
194        let params_str = params_str.trim_end_matches(')');
195
196        // Handle parameter map
197        if params_str.contains('{') && params_str.contains('}') {
198            let map_str = params_str.trim_matches(|c| c == '{' || c == '}');
199            let mut params_map = std::collections::HashMap::new();
200
201            for pair in map_str.split(',') {
202                if let Some((key, value)) = pair.split_once(':') {
203                    let key = key.trim().to_string();
204                    let value = value.trim();
205
206                    let value = if value == "true" {
207                        Value::Boolean(true)
208                    } else if value == "false" {
209                        Value::Boolean(false)
210                    } else if let Ok(num) = value.parse::<f32>() {
211                        Value::Number(num)
212                    } else {
213                        Value::String(value.trim_matches('"').to_string())
214                    };
215
216                    params_map.insert(key, value);
217                }
218            }
219
220            let mut result = std::collections::HashMap::new();
221            result.insert(name, Value::Map(params_map));
222            return Ok(Value::Map(result));
223        } else if let Ok(num) = params_str.parse::<f32>() {
224            let mut result = std::collections::HashMap::new();
225            result.insert(name, Value::Number(num));
226            return Ok(Value::Map(result));
227        } else {
228            let mut result = std::collections::HashMap::new();
229            result.insert(
230                name,
231                Value::String(params_str.trim_matches('"').to_string()),
232            );
233            return Ok(Value::Map(result));
234        }
235    } else {
236        let mut result = std::collections::HashMap::new();
237        result.insert(effect_str.to_string(), Value::Null);
238        return Ok(Value::Map(result));
239    }
240}