use crate::language::syntax::ast::Value;
use anyhow::Result;
use std::collections::HashMap;
pub fn parse_function_args(args_str: &str) -> Result<Vec<Value>> {
let mut args = Vec::new();
let mut current_arg = String::new();
let mut depth = 0; let mut in_string = false;
for ch in args_str.chars() {
match ch {
'"' => {
in_string = !in_string;
current_arg.push(ch);
}
'[' | '{' if !in_string => {
depth += 1;
current_arg.push(ch);
}
']' | '}' if !in_string => {
depth -= 1;
current_arg.push(ch);
}
',' if depth == 0 && !in_string => {
if !current_arg.trim().is_empty() {
args.push(parse_single_arg(current_arg.trim())?);
current_arg.clear();
}
}
_ => {
current_arg.push(ch);
}
}
}
if !current_arg.trim().is_empty() {
args.push(parse_single_arg(current_arg.trim())?);
}
Ok(args)
}
pub fn parse_single_arg(arg: &str) -> Result<Value> {
let arg = arg.trim();
if arg.starts_with('"') && arg.ends_with('"') {
return Ok(Value::String(arg[1..arg.len() - 1].to_string()));
}
if arg.starts_with('[') && arg.ends_with(']') {
let inner = &arg[1..arg.len() - 1];
let items = parse_function_args(inner)?;
return Ok(Value::Array(items));
}
if arg.starts_with('{') && arg.ends_with('}') {
let inner = &arg[1..arg.len() - 1];
let mut map = HashMap::new();
for pair in inner.split(',') {
if let Some(colon_idx) = pair.find(':') {
let key = pair[..colon_idx].trim().trim_matches('"');
let value = parse_single_arg(pair[colon_idx + 1..].trim())?;
map.insert(key.to_string(), value);
}
}
return Ok(Value::Map(map));
}
if let Ok(num) = arg.parse::<f32>() {
return Ok(Value::Number(num));
}
if let Some(open_paren) = arg.find('(') {
if arg.ends_with(')') {
let name = arg[..open_paren].trim().to_string();
let args_str = &arg[open_paren + 1..arg.len() - 1];
let parsed_args = if args_str.trim().is_empty() {
Vec::new()
} else {
parse_function_args(args_str)?
};
return Ok(Value::Call {
name,
args: parsed_args,
});
}
}
match arg.to_lowercase().as_str() {
"true" => return Ok(Value::Boolean(true)),
"false" => return Ok(Value::Boolean(false)),
_ => {}
}
Ok(Value::Identifier(arg.to_string()))
}
pub fn parse_synth_definition(input: &str) -> Result<Value> {
let input = input.trim();
let input = if input == "synth" {
""
} else {
input.strip_prefix("synth ").map(str::trim).unwrap_or(input)
};
let (waveform_or_plugin, params_str) = if let Some(brace_idx) = input.find('{') {
let before_brace = input[..brace_idx].trim();
let params = &input[brace_idx..];
(before_brace, params)
} else {
return Ok(Value::Map({
let mut map = HashMap::new();
map.insert("type".to_string(), Value::String("synth".to_string()));
if input.is_empty() {
} else if input.starts_with("plugin.") {
let parts: Vec<&str> = input.split('.').collect();
if parts.len() >= 3 {
map.insert(
"plugin_author".to_string(),
Value::String(parts[1].to_string()),
);
map.insert(
"plugin_name".to_string(),
Value::String(parts[2].to_string()),
);
if parts.len() >= 4 {
map.insert(
"plugin_export".to_string(),
Value::String(parts[3].to_string()),
);
}
}
} else if input.contains('.') {
map.insert("_plugin_ref".to_string(), Value::String(input.to_string()));
} else {
map.insert("_deprecated_shorthand".to_string(), Value::Boolean(true));
map.insert("waveform".to_string(), Value::String(input.to_string()));
}
map
}));
};
let params_str = params_str.trim_matches(|c| c == '{' || c == '}').trim();
let mut params_map = HashMap::new();
params_map.insert("type".to_string(), Value::String("synth".to_string()));
if !waveform_or_plugin.is_empty() {
if waveform_or_plugin.contains('.') {
params_map.insert(
"_plugin_ref".to_string(),
Value::String(waveform_or_plugin.to_string()),
);
if waveform_or_plugin.starts_with("plugin.") {
let parts: Vec<&str> = waveform_or_plugin.split('.').collect();
if parts.len() >= 3 {
params_map.insert(
"plugin_author".to_string(),
Value::String(parts[1].to_string()),
);
params_map.insert(
"plugin_name".to_string(),
Value::String(parts[2].to_string()),
);
if parts.len() >= 4 {
params_map.insert(
"plugin_export".to_string(),
Value::String(parts[3].to_string()),
);
}
}
}
} else {
params_map.insert(
"waveform".to_string(),
Value::String(waveform_or_plugin.to_string()),
);
}
}
if !params_str.is_empty() {
let mut cleaned_lines = Vec::new();
for line in params_str.lines() {
if let Some(comment_pos) = line.find("//") {
let clean_line = &line[..comment_pos];
if !clean_line.trim().is_empty() {
cleaned_lines.push(clean_line);
}
} else if !line.trim().is_empty() {
cleaned_lines.push(line);
}
}
let cleaned = cleaned_lines.join("\n");
let normalized = cleaned.replace('\n', ",").replace('\r', "");
for pair in normalized.split(',') {
let pair = pair.trim();
if pair.is_empty() {
continue;
}
let parts: Vec<&str> = pair.split(':').collect();
if parts.len() >= 2 {
let key = parts[0].trim().to_string();
let value_part = parts[1..].join(":");
let value_str = value_part.trim().trim_matches(',').trim_matches('"');
if value_str.starts_with('[') {
if let Ok(array_val) = parse_array_value(value_str) {
params_map.insert(key, array_val);
continue;
}
}
if let Ok(num) = value_str.parse::<f32>() {
params_map.insert(key, Value::Number(num));
} else {
params_map.insert(key, Value::String(value_str.to_string()));
}
}
}
}
Ok(Value::Map(params_map))
}
pub fn parse_array_value(input: &str) -> Result<Value> {
let input = input.trim().trim_matches(|c| c == '[' || c == ']').trim();
if input.is_empty() {
return Ok(Value::Array(Vec::new()));
}
if input.contains("..") {
let parts: Vec<&str> = input.split("..").collect();
if parts.len() == 2 {
let start_str = parts[0].trim();
let end_str = parts[1].trim();
if let (Ok(start), Ok(end)) = (start_str.parse::<f32>(), end_str.parse::<f32>()) {
return Ok(Value::Range {
start: Box::new(Value::Number(start)),
end: Box::new(Value::Number(end)),
});
}
}
}
let mut items = Vec::new();
let mut depth = 0;
let mut current = String::new();
for ch in input.chars() {
match ch {
'{' => {
depth += 1;
current.push(ch);
}
'}' => {
depth -= 1;
current.push(ch);
if depth == 0 && !current.trim().is_empty() {
if let Ok(obj) = parse_map_value(¤t) {
items.push(obj);
}
current.clear();
}
}
',' if depth == 0 => {
let token = current.trim();
if !token.is_empty() {
if let Ok(val) = parse_single_arg(token) {
if let Value::Map(_) = val {
items.push(val);
} else {
let mut map = HashMap::new();
let idx = items.len();
map.insert("index".to_string(), Value::Number(idx as f32));
map.insert("value".to_string(), val);
items.push(Value::Map(map));
}
}
}
current.clear();
continue;
}
_ => {
current.push(ch);
}
}
}
if !current.trim().is_empty() {
let token = current.trim();
if !token.is_empty() {
if let Ok(val) = parse_single_arg(token) {
items.push(val);
}
}
}
Ok(Value::Array(items))
}
pub fn parse_map_value(input: &str) -> Result<Value> {
let input = input.trim().trim_matches(|c| c == '{' || c == '}').trim();
let mut map = HashMap::new();
for pair in input.split(',') {
let pair = pair.trim();
if pair.is_empty() {
continue;
}
let parts: Vec<&str> = pair.split(':').collect();
if parts.len() >= 2 {
let key = parts[0].trim().to_string();
let value_part = parts[1..].join(":");
let value_clean = if let Some(comment_pos) = value_part.find("//") {
&value_part[..comment_pos]
} else {
&value_part
};
let value_str = value_clean.trim().trim_matches('"').trim_matches('\'');
if let Ok(num) = value_str.parse::<f32>() {
map.insert(key, Value::Number(num));
} else {
map.insert(key, Value::String(value_str.to_string()));
}
}
}
Ok(Value::Map(map))
}
fn find_word_boundary(text: &str, keyword: &str) -> Option<usize> {
text.rfind(keyword).and_then(|idx| {
let before_ok =
idx == 0 || !text[..idx].ends_with(|c: char| c.is_alphanumeric() || c == '_');
let after_idx = idx + keyword.len();
let after_ok = after_idx >= text.len()
|| !text[after_idx..].starts_with(|c: char| c.is_alphanumeric() || c == '_');
if before_ok && after_ok {
Some(idx)
} else {
None
}
})
}
pub fn parse_condition(condition_str: &str) -> Result<Value> {
let condition_str = condition_str.trim();
if let Some(idx) = condition_str.rfind("||") {
let left = parse_condition(&condition_str[..idx].trim())?;
let right = parse_condition(&condition_str[idx + 2..].trim())?;
let mut map = HashMap::new();
map.insert("operator".to_string(), Value::String("||".to_string()));
map.insert("left".to_string(), left);
map.insert("right".to_string(), right);
return Ok(Value::Map(map));
}
if let Some(idx) = find_word_boundary(condition_str, " or ") {
let left = parse_condition(&condition_str[..idx].trim())?;
let right = parse_condition(&condition_str[idx + 4..].trim())?;
let mut map = HashMap::new();
map.insert("operator".to_string(), Value::String("||".to_string()));
map.insert("left".to_string(), left);
map.insert("right".to_string(), right);
return Ok(Value::Map(map));
}
if let Some(idx) = condition_str.rfind("&&") {
let left = parse_condition(&condition_str[..idx].trim())?;
let right = parse_condition(&condition_str[idx + 2..].trim())?;
let mut map = HashMap::new();
map.insert("operator".to_string(), Value::String("&&".to_string()));
map.insert("left".to_string(), left);
map.insert("right".to_string(), right);
return Ok(Value::Map(map));
}
if let Some(idx) = find_word_boundary(condition_str, " and ") {
let left = parse_condition(&condition_str[..idx].trim())?;
let right = parse_condition(&condition_str[idx + 5..].trim())?;
let mut map = HashMap::new();
map.insert("operator".to_string(), Value::String("&&".to_string()));
map.insert("left".to_string(), left);
map.insert("right".to_string(), right);
return Ok(Value::Map(map));
}
if condition_str.starts_with('!') {
let inner = parse_condition(&condition_str[1..].trim())?;
let mut map = HashMap::new();
map.insert("operator".to_string(), Value::String("!".to_string()));
map.insert("operand".to_string(), inner);
return Ok(Value::Map(map));
}
if condition_str.starts_with("not ") {
let inner = parse_condition(&condition_str[4..].trim())?;
let mut map = HashMap::new();
map.insert("operator".to_string(), Value::String("!".to_string()));
map.insert("operand".to_string(), inner);
return Ok(Value::Map(map));
}
let operators = vec![">=", "<=", "==", "!=", ">", "<"];
for op in operators {
if let Some(idx) = condition_str.find(op) {
let left = condition_str[..idx].trim();
let right = condition_str[idx + op.len()..].trim();
let mut map = HashMap::new();
map.insert("operator".to_string(), Value::String(op.to_string()));
map.insert(
"left".to_string(),
if let Ok(num) = left.parse::<f32>() {
Value::Number(num)
} else {
Value::Identifier(left.to_string())
},
);
map.insert(
"right".to_string(),
if let Ok(num) = right.parse::<f32>() {
Value::Number(num)
} else {
Value::Identifier(right.to_string())
},
);
return Ok(Value::Map(map));
}
}
Ok(Value::Identifier(condition_str.to_string()))
}