use super::instruction::{InstructionRegistry, ScriptError};
#[derive(Debug)]
pub struct AstNode {
pub line_num: usize,
pub command: String,
pub args: Vec<String>,
}
#[derive(Debug, Clone)]
pub struct ParserConfig {
pub strict_mode: bool,
}
impl Default for ParserConfig {
fn default() -> Self {
Self { strict_mode: true }
}
}
pub struct Parser<'a> {
config: ParserConfig,
registry: &'a InstructionRegistry,
}
impl<'a> Parser<'a> {
pub fn new(config: ParserConfig, registry: &'a InstructionRegistry) -> Self {
Self { config, registry }
}
pub fn parse(&self, text: &str) -> Result<Vec<AstNode>, ScriptError> {
let mut nodes = Vec::new();
for (line_num, line) in text.lines().enumerate() {
let line = line.trim();
if line.is_empty() || line.starts_with('#') || line.starts_with("//") {
continue;
}
let node = self.parse_line(line, line_num + 1)?;
nodes.push(node);
}
Ok(nodes)
}
fn parse_line(&self, line: &str, line_num: usize) -> Result<AstNode, ScriptError> {
let parts: Vec<&str> = line.split_whitespace().collect();
if parts.is_empty() {
return Err(ScriptError::ParseError(format!(
"Empty line at {}",
line_num
)));
}
let command = parts[0].to_lowercase();
let args = parts[1..].iter().map(|s| s.to_string()).collect();
if self.config.strict_mode && !self.registry.has_instruction(&command) {
return Err(ScriptError::ParseError(format!(
"Unknown command '{}' at line {}",
command, line_num
)));
}
Ok(AstNode {
line_num,
command,
args,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_simple_command() {
let registry = InstructionRegistry::new();
let config = ParserConfig { strict_mode: false };
let parser = Parser::new(config, ®istry);
let ast = parser.parse("key a").unwrap();
assert_eq!(ast.len(), 1);
assert_eq!(ast[0].command, "key");
assert_eq!(ast[0].args, vec!["a"]);
}
#[test]
fn test_parse_with_comments() {
let registry = InstructionRegistry::new();
let config = ParserConfig { strict_mode: false };
let parser = Parser::new(config, ®istry);
let ast = parser
.parse("// comment\nkey a\n// another comment")
.unwrap();
assert_eq!(ast.len(), 1);
assert_eq!(ast[0].command, "key");
}
}