use super::super::helpers::{parse_array_value, parse_condition};
use crate::language::syntax::ast::{Statement, StatementKind, Value};
use anyhow::{Result, anyhow};
use std::collections::HashMap;
use std::iter::Iterator;
pub fn parse_pattern(
mut parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let name = parts
.next()
.ok_or_else(|| anyhow!("pattern requires a name"))?
.as_ref()
.to_string();
let mut target = None;
let mut pattern_str = None;
let mut options: HashMap<String, Value> = HashMap::new();
if let Some(word) = parts.next() {
if word.as_ref() == "with" {
target = parts.next().map(|v| v.as_ref().to_string());
let rest: Vec<String> = parts.map(|s| s.as_ref().to_string()).collect();
let joined = rest.join(" ");
if let Some(brace_start) = joined.find('{') {
if let Some(brace_end) = joined.rfind('}') {
let options_str = &joined[brace_start + 1..brace_end];
for pair in options_str.split(',') {
let parts: Vec<&str> = pair.split(':').collect();
if parts.len() == 2 {
let key = parts[0].trim().to_string();
let value_str = parts[1].trim();
let value = if let Ok(num) = value_str.parse::<f32>() {
Value::Number(num)
} else if value_str == "true" {
Value::Boolean(true)
} else if value_str == "false" {
Value::Boolean(false)
} else if value_str.starts_with('"') && value_str.ends_with('"') {
Value::String(value_str.trim_matches('"').to_string())
} else {
Value::Identifier(value_str.to_string())
};
options.insert(key, value);
}
}
let after_brace = joined[brace_end + 1..].trim();
if let Some(eq_pos) = after_brace.find('=') {
let pattern_part = after_brace[eq_pos + 1..].trim();
pattern_str = Some(pattern_part.trim_matches('"').to_string());
}
} else {
return Err(anyhow!("Unclosed brace in pattern options"));
}
} else {
if let Some(eq_pos) = joined.find('=') {
let pattern_part = joined[eq_pos + 1..].trim();
pattern_str = Some(pattern_part.trim_matches('"').to_string());
}
}
}
}
let value = if !options.is_empty() {
let mut map = options;
if let Some(pat) = pattern_str {
map.insert("pattern".to_string(), Value::String(pat));
}
Value::Map(map)
} else {
pattern_str.map(Value::String).unwrap_or(Value::Null)
};
Ok(Statement::new(
StatementKind::Pattern { name, target },
value,
0,
line_number,
1,
))
}
pub fn parse_pattern_shorthand(line: &str, line_number: usize) -> Result<Option<Statement>> {
let Some(colon_idx) = line.find(':') else {
return Ok(None);
};
let Some(eq_idx) = line.find('=') else {
return Ok(None);
};
if colon_idx > eq_idx {
return Ok(None);
}
let name = line[..colon_idx].trim();
if name.is_empty()
|| name.contains(char::is_whitespace)
|| !name.chars().all(|c| c.is_alphanumeric() || c == '_')
{
return Ok(None);
}
let target_expr = line[colon_idx + 1..eq_idx].trim();
let pattern_part = line[eq_idx + 1..].trim();
if target_expr.is_empty()
|| !(pattern_part.starts_with('"')
&& pattern_part.ends_with('"')
&& pattern_part.len() >= 2)
{
return Ok(None);
}
let mut target = target_expr.to_string();
let mut options: HashMap<String, Value> = HashMap::new();
fn insert_option(options: &mut HashMap<String, Value>, method: &str, args: &[Value]) {
if let Some(first) = args.first() {
match first {
Value::Number(_) => {
options.insert(method.to_string(), first.clone());
}
Value::Map(map) => {
for (key, value) in map.iter() {
if matches!(value, Value::Number(_)) {
options.insert(key.clone(), value.clone());
}
}
}
_ => {}
}
}
}
if target_expr.contains("->") {
let stmt =
crate::language::syntax::parser::driver::parse_arrow_call(target_expr, line_number)?;
if let Value::Map(chain_container) = stmt.value {
if let Some(Value::String(parsed_target)) = chain_container.get("target") {
target = parsed_target.clone();
}
if let Some(Value::String(method)) = chain_container.get("method") {
if let Some(Value::Array(args)) = chain_container.get("args") {
insert_option(&mut options, method, args);
}
}
if let Some(Value::Array(chain)) = chain_container.get("chain") {
for item in chain {
if let Value::Map(call_map) = item {
if let (Some(Value::String(method)), Some(Value::Array(args))) =
(call_map.get("method"), call_map.get("args"))
{
insert_option(&mut options, method, args);
}
}
}
}
}
}
let pattern = pattern_part.trim_matches('"').to_string();
let value = if options.is_empty() {
Value::String(pattern)
} else {
options.insert("pattern".to_string(), Value::String(pattern));
Value::Map(options)
};
Ok(Some(Statement::new(
StatementKind::Pattern {
name: name.to_string(),
target: Some(target),
},
value,
0,
line_number,
1,
)))
}
pub fn parse_group(
mut parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let name = parts
.next()
.ok_or_else(|| anyhow!("group requires a name"))?
.as_ref()
.trim_end_matches(':')
.to_string();
let next = parts.next();
let duration = if next.as_ref().map(|w| w.as_ref()) == Some("during") {
let duration_str = parts
.map(|p| p.as_ref().to_string())
.collect::<Vec<_>>()
.join(" ")
.trim_end_matches(':')
.to_string();
Some(super::super::duration::parse_duration_token(&duration_str)?)
} else {
None
};
Ok(Statement::new(
StatementKind::Group {
name: name.clone(),
body: Vec::new(),
duration,
},
Value::Identifier(name),
0,
line_number,
1,
))
}
pub fn parse_automate(
mut parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let target = parts
.next()
.ok_or_else(|| anyhow!("automate requires a target"))?
.as_ref()
.trim_end_matches(':')
.to_string();
let mut mode: Option<String> = None;
if let Some(word) = parts.next() {
if word.as_ref() == "mode" {
if let Some(m) = parts.next() {
mode = Some(m.as_ref().trim_end_matches(':').to_string());
}
}
}
let mut map = HashMap::new();
if let Some(m) = mode {
map.insert("mode".to_string(), Value::String(m));
}
Ok(Statement::new(
StatementKind::Automate { target },
Value::Map(map),
0,
line_number,
1,
))
}
pub fn parse_loop(
parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let all_parts: Vec<String> = parts.map(|p| p.as_ref().to_string()).collect();
let count = if all_parts.is_empty() {
Value::Null
} else {
let count_str = all_parts.join(" ").trim_end_matches(':').to_string();
if let Ok(num) = count_str.parse::<f32>() {
Value::Number(num)
} else if count_str.contains('(') && count_str.ends_with(')') {
if let Some(open_idx) = count_str.find('(') {
let name = count_str[..open_idx].to_string();
let inside = &count_str[open_idx + 1..count_str.len() - 1];
if name == "pass" && !inside.trim().is_empty() {
if let Ok(duration) =
crate::language::syntax::parser::driver::parse_duration_token(inside.trim())
{
Value::Duration(duration)
} else {
let args = if inside.trim().is_empty() {
Vec::new()
} else {
crate::language::syntax::parser::driver::parse_function_args(inside)?
};
Value::Call { name, args }
}
} else {
let args = if inside.trim().is_empty() {
Vec::new()
} else {
crate::language::syntax::parser::driver::parse_function_args(inside)?
};
Value::Call { name, args }
}
} else {
Value::Identifier(count_str.to_string())
}
} else {
Value::Identifier(count_str)
}
};
Ok(Statement::new(
StatementKind::Loop {
count,
body: Vec::new(), },
Value::Null,
0,
line_number,
1,
))
}
pub fn parse_for(
parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let parts_vec: Vec<String> = parts.map(|s| s.as_ref().to_string()).collect();
if parts_vec.is_empty() {
return Err(anyhow!("for loop requires a variable name"));
}
let variable = parts_vec[0].clone();
if parts_vec.len() < 2 || parts_vec[1] != "in" {
return Err(anyhow!("Expected 'in' after variable in for loop"));
}
let iterable_str = parts_vec[2..].join(" ");
let iterable_str = iterable_str.trim_end_matches(':').trim();
let iterable = if iterable_str.starts_with('[') && iterable_str.ends_with(']') {
parse_array_value(iterable_str)?
} else {
if let Ok(num) = iterable_str.parse::<f32>() {
Value::Number(num)
} else {
Value::Identifier(iterable_str.to_string())
}
};
Ok(Statement::new(
StatementKind::For {
variable,
iterable,
body: Vec::new(), },
Value::Null,
0,
line_number,
1,
))
}
pub fn parse_function(line: &str, line_number: usize) -> Result<Statement> {
let after_kw = line
.trim()
.strip_prefix("function")
.ok_or_else(|| anyhow!("function parsing error"))?
.trim();
if let Some(paren_idx) = after_kw.find('(') {
let name = after_kw[..paren_idx].trim().to_string();
if let Some(close_idx) = after_kw.rfind(')') {
let args_str = &after_kw[paren_idx + 1..close_idx];
let params: Vec<String> = if args_str.trim().is_empty() {
Vec::new()
} else {
args_str
.split(',')
.map(|s| s.trim().trim_end_matches(':').to_string())
.filter(|s| !s.is_empty())
.collect()
};
return Ok(Statement::new(
StatementKind::Function {
name: name.clone(),
parameters: params,
body: Vec::new(),
},
Value::Identifier(name),
0,
line_number,
1,
));
}
}
Err(anyhow!("Invalid function declaration"))
}
pub fn parse_if(
parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let condition_str = parts
.map(|s| s.as_ref().to_string())
.collect::<Vec<_>>()
.join(" ");
let condition_str = condition_str.trim_end_matches(':').trim();
let condition = parse_condition(condition_str)?;
Ok(Statement::new(
StatementKind::If {
condition,
body: Vec::new(), else_body: None,
},
Value::Null,
0,
line_number,
1,
))
}
pub fn parse_while(
parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let condition_str = parts
.map(|s| s.as_ref().to_string())
.collect::<Vec<_>>()
.join(" ");
let condition_str = condition_str.trim_end_matches(':').trim();
let condition = parse_condition(condition_str)?;
Ok(Statement::new(
StatementKind::While {
condition,
body: Vec::new(), },
Value::Null,
0,
line_number,
1,
))
}
pub fn parse_else(line: &str, line_number: usize) -> Result<Statement> {
if line.trim().starts_with("else if") {
let Some(condition_str) = line.trim().strip_prefix("else if") else {
return Err(anyhow::anyhow!(
"Invalid else-if statement at line {}",
line_number
));
};
let condition_str = condition_str.trim();
let condition_str = condition_str.trim_end_matches(':').trim();
let condition = parse_condition(condition_str)?;
Ok(Statement::new(
StatementKind::If {
condition,
body: Vec::new(), else_body: None,
},
Value::String("else-if".to_string()),
0,
line_number,
1,
))
} else {
Ok(Statement::new(
StatementKind::Comment, Value::String("else".to_string()),
0,
line_number,
1,
))
}
}
pub fn parse_call(
line: &str,
mut parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let first_token = parts
.next()
.ok_or_else(|| anyhow!("call requires a target"))?
.as_ref()
.to_string();
let mut call_name = first_token.clone();
if line.find('(').is_some() {
if let Some(after_call) = line.trim().strip_prefix("call") {
let snippet = after_call.trim();
if let Some(pidx) = snippet.find('(') {
call_name = snippet[..pidx].trim().to_string();
}
}
}
if line.contains('=') {
let eq_parts: Vec<&str> = line.splitn(2, '=').collect();
if eq_parts.len() == 2 {
let target = eq_parts[0]
.trim()
.strip_prefix("call")
.unwrap_or(eq_parts[0])
.trim()
.to_string();
let pattern = eq_parts[1].trim().trim_matches('"').to_string();
let mut map = HashMap::new();
map.insert("inline_pattern".to_string(), Value::Boolean(true));
map.insert("target".to_string(), Value::String(target.clone()));
map.insert("pattern".to_string(), Value::String(pattern));
return Ok(Statement::new(
StatementKind::Call {
name: target,
args: Vec::new(),
},
Value::Map(map),
0,
line_number,
1,
));
}
}
if let Some(paren_idx) = line.find('(') {
if let Some(close_idx) = line.rfind(')') {
let args_str = &line[paren_idx + 1..close_idx];
let args = if args_str.trim().is_empty() {
Vec::new()
} else {
crate::language::syntax::parser::driver::parse_function_args(args_str)?
};
return Ok(Statement::new(
StatementKind::Call {
name: call_name,
args,
},
Value::Null,
0,
line_number,
1,
));
}
}
Ok(Statement::new(
StatementKind::Call {
name: call_name,
args: Vec::new(),
},
Value::Null,
0,
line_number,
1,
))
}
pub fn parse_break(
_parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
Ok(Statement::new(
StatementKind::Break,
Value::Null,
0,
line_number,
1,
))
}
pub fn parse_spawn(
mut parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let name = parts
.next()
.ok_or_else(|| anyhow!("spawn requires a target"))?
.as_ref()
.to_string();
Ok(Statement::new(
StatementKind::Spawn {
name,
args: Vec::new(),
},
Value::Null,
0,
line_number,
1,
))
}
pub fn parse_on(
mut parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let raw = parts
.next()
.ok_or_else(|| anyhow!("on statement requires an event name"))?
.as_ref()
.to_string();
let mut event_name = raw.clone();
let mut args_vec: Vec<Value> = Vec::new();
if let Some(open_idx) = raw.find('(') {
if raw.ends_with(')') {
let base = raw[..open_idx].to_string();
let inside = &raw[open_idx + 1..raw.len() - 1];
if let Ok(n) = inside.trim().parse::<f32>() {
args_vec.push(Value::Number(n));
}
event_name = base;
}
}
let next_word = parts.next();
let once = next_word.map(|w| w.as_ref() == "once").unwrap_or(false);
if once {
args_vec.push(Value::String("once".to_string()));
}
let args = if !args_vec.is_empty() {
Some(args_vec)
} else {
None
};
Ok(Statement::new(
StatementKind::On {
event: event_name,
args,
body: Vec::new(), },
Value::Null,
0,
line_number,
1,
))
}
pub fn parse_emit(
line: &str,
mut parts: impl Iterator<Item = impl AsRef<str>>,
line_number: usize,
) -> Result<Statement> {
let event_name = parts
.next()
.ok_or_else(|| anyhow!("emit statement requires an event name"))?
.as_ref()
.to_string();
let remainder = line.splitn(2, &event_name).nth(1).unwrap_or("").trim();
let payload = if remainder.starts_with('{') && remainder.ends_with('}') {
let inner = &remainder[1..remainder.len() - 1];
let mut map = HashMap::new();
for pair in inner.split(',') {
let parts: Vec<&str> = pair.split(':').collect();
if parts.len() == 2 {
let key = parts[0].trim().to_string();
let value_str = parts[1].trim();
let value = if value_str.starts_with('"') && value_str.ends_with('"') {
Value::String(value_str.trim_matches('"').to_string())
} else if let Ok(num) = value_str.parse::<f32>() {
Value::Number(num)
} else {
Value::Identifier(value_str.to_string())
};
map.insert(key, value);
}
}
Some(Value::Map(map))
} else {
None
};
Ok(Statement::new(
StatementKind::Emit {
event: event_name,
payload,
},
Value::Null,
0,
line_number,
1,
))
}