pub mod ast;
pub mod compiled;
pub mod error;
pub mod loader;
pub mod nfa_compiler;
pub mod parser;
pub mod symbol_expander;
pub use ast::{
Directive, FileMetadata, ImportDirective, LLreFile, LLreFlags, ResolvedImport, SymbolTable,
};
pub use error::{LLreError, LLreErrorKind, LLreResult, Position};
pub use loader::{load_file, load_file_with_config, Loader, LoaderConfig};
pub use nfa_compiler::{
compile, compile_pattern, compile_pattern_with_flags, compile_with_options, is_match,
is_match_multiline, CompileOptions, CompiledNFA,
};
pub use parser::{parse_str, Parser};
pub use symbol_expander::expand_pattern_symbols;
#[cfg(feature = "serialization")]
pub use compiled::{from_bytes, load, save, to_bytes, CompiledMetadata, MAGIC, VERSION};
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_full_workflow() {
let file = parse_str(
r#"
@name "Test Pattern"
@version "1.0"
^hello$
"#,
)
.expect("Failed to parse");
assert_eq!(file.metadata.name, Some("Test Pattern".to_string()));
assert_eq!(file.metadata.version, Some("1.0".to_string()));
let compiled = compile(&file).expect("Failed to compile");
assert!(compiled.matches("hello"));
assert!(!compiled.matches("world"));
}
#[test]
fn test_quick_match() {
assert!(is_match("[a-z]+", "hello").expect("Failed"));
assert!(!is_match("[a-z]+", "123").expect("Failed"));
}
#[test]
fn test_anchors() {
assert!(is_match("^hello", "hello world").expect("Failed"));
assert!(!is_match("^hello", "say hello").expect("Failed"));
assert!(is_match("world$", "hello world").expect("Failed"));
assert!(!is_match("world$", "world hello").expect("Failed"));
assert!(is_match("^hello$", "hello").expect("Failed"));
assert!(!is_match("^hello$", "hello world").expect("Failed"));
}
#[test]
fn test_multiline_flag() {
let file = parse_str(
r#"
@flags multiline
^line$
"#,
)
.expect("Failed to parse");
let compiled = compile(&file).expect("Failed to compile");
assert!(compiled.multiline);
assert!(compiled.matches("line"));
}
#[test]
fn test_imports_parsed() {
let file = parse_str(
r#"
@import "symbols.llev"
@import "english.llev" as en
^test$
"#,
)
.expect("Failed to parse");
assert_eq!(file.imports.len(), 2);
assert_eq!(file.imports[0].path, "symbols.llev");
assert!(file.imports[0].alias.is_none());
assert_eq!(file.imports[1].path, "english.llev");
assert_eq!(file.imports[1].alias, Some("en".to_string()));
}
#[test]
fn test_compile_options() {
let file = parse_str("^[a-z]+$").expect("Failed to parse");
let options = CompileOptions {
max_states: Some(1000),
optimize: true,
..Default::default()
};
let compiled = compile_with_options(&file, &options).expect("Failed to compile");
assert!(compiled.matches("hello"));
}
#[test]
fn test_character_classes() {
assert!(is_match("[aeiou]+", "aeiou").expect("Failed"));
assert!(!is_match("[aeiou]+", "xyz").expect("Failed"));
assert!(is_match("[^aeiou]+", "xyz").expect("Failed"));
assert!(!is_match("[^aeiou]+", "aeiou").expect("Failed"));
}
#[test]
fn test_quantifiers() {
assert!(is_match("a*", "").expect("Failed"));
assert!(is_match("a*", "aaa").expect("Failed"));
assert!(!is_match("^a+$", "").expect("Failed"));
assert!(is_match("a+", "aaa").expect("Failed"));
assert!(is_match("ab?c", "ac").expect("Failed"));
assert!(is_match("ab?c", "abc").expect("Failed"));
assert!(is_match("^a{3}$", "aaa").expect("Failed"));
assert!(!is_match("^a{3}$", "aa").expect("Failed"));
}
#[test]
fn test_alternation() {
assert!(is_match("cat|dog", "cat").expect("Failed"));
assert!(is_match("cat|dog", "dog").expect("Failed"));
assert!(!is_match("^(cat|dog)$", "bird").expect("Failed"));
}
#[test]
fn test_groups() {
assert!(is_match("(ab)+", "abab").expect("Failed"));
assert!(!is_match("^(ab)+$", "aba").expect("Failed"));
}
#[test]
fn test_escape_sequences() {
assert!(is_match(r"a\.b", "a.b").expect("Failed"));
assert!(!is_match(r"^a\.b$", "axb").expect("Failed"));
}
#[test]
fn test_complex_pattern() {
let pattern = r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$";
assert!(is_match(pattern, "test@example.com").expect("Failed"));
assert!(is_match(pattern, "user.name@sub.domain.org").expect("Failed"));
assert!(!is_match(pattern, "invalid").expect("Failed"));
assert!(!is_match(pattern, "@example.com").expect("Failed"));
}
}