Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/stdlib/helpers.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::interpreter::error::RuntimeError;
use crate::interpreter::value::Value;
use crate::pattern::CompiledPattern;
use std::cell::RefCell;
use std::rc::Rc;
use std::sync::Arc;
Expand Down Expand Up @@ -596,6 +597,15 @@ generate_expect!(
|dt: &Rc<chrono::NaiveDateTime>| Rc::clone(dt)
);

generate_expect!(
/// Extracts a compiled pattern value from a WFL Value, returning it as a reference-counted CompiledPattern.
expect_pattern,
Pattern,
Rc<CompiledPattern>,
"a pattern",
|p: &Rc<CompiledPattern>| Rc::clone(p)
);
Comment on lines +600 to +607

/// Helper for unary list operations (List -> Value)
///
/// Handles argument count checking, type extraction, and operation execution.
Expand Down
187 changes: 28 additions & 159 deletions src/stdlib/pattern.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use super::helpers::{check_arg_count, expect_pattern, expect_text};
use crate::interpreter::environment::Environment;
use crate::interpreter::error::RuntimeError;
use crate::interpreter::value::Value;
Expand All @@ -19,74 +20,22 @@ pub fn register(env: &mut Environment) {
/// Native function: pattern_matches(text, pattern) -> boolean
/// Tests if text matches the given compiled pattern
pub fn pattern_matches_native(args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::new(
"pattern_matches requires exactly 2 arguments (text, pattern)".to_string(),
0,
0,
));
}

let text_str = match &args[0] {
Value::Text(s) => s.as_ref(),
_ => {
return Err(RuntimeError::new(
"First argument to pattern_matches must be text".to_string(),
0,
0,
));
}
};
check_arg_count("pattern_matches", &args, 2)?;
let text = expect_text(&args[0])?;
let compiled_pattern = expect_pattern(&args[1])?;

let compiled_pattern = match &args[1] {
Value::Pattern(p) => p,
_ => {
return Err(RuntimeError::new(
"Second argument to pattern_matches must be a compiled pattern".to_string(),
0,
0,
));
}
};

let matches = compiled_pattern.matches(text_str);
let matches = compiled_pattern.matches(text.as_ref());
Ok(Value::Bool(matches))
}

/// Native function: pattern_find(text, pattern) -> object or null
/// Finds the first match of pattern in text
pub fn pattern_find_native(args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::new(
"pattern_find requires exactly 2 arguments (text, pattern)".to_string(),
0,
0,
));
}
check_arg_count("pattern_find", &args, 2)?;
let text = expect_text(&args[0])?;
let compiled_pattern = expect_pattern(&args[1])?;

let text_str = match &args[0] {
Value::Text(s) => s.as_ref(),
_ => {
return Err(RuntimeError::new(
"First argument to pattern_find must be text".to_string(),
0,
0,
));
}
};

let compiled_pattern = match &args[1] {
Value::Pattern(p) => p,
_ => {
return Err(RuntimeError::new(
"Second argument to pattern_find must be a compiled pattern".to_string(),
0,
0,
));
}
};

match compiled_pattern.find(text_str) {
match compiled_pattern.find(text.as_ref()) {
Some(match_result) => {
let mut result_map = HashMap::new();
result_map.insert(
Expand Down Expand Up @@ -120,37 +69,11 @@ pub fn pattern_find_native(args: Vec<Value>) -> Result<Value, RuntimeError> {
/// Native function: pattern_find_all(text, pattern) -> list
/// Finds all matches of pattern in text
pub fn pattern_find_all_native(args: Vec<Value>) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::new(
"pattern_find_all requires exactly 2 arguments (text, pattern)".to_string(),
0,
0,
));
}

let text_str = match &args[0] {
Value::Text(s) => s.as_ref(),
_ => {
return Err(RuntimeError::new(
"First argument to pattern_find_all must be text".to_string(),
0,
0,
));
}
};
check_arg_count("pattern_find_all", &args, 2)?;
let text = expect_text(&args[0])?;
let compiled_pattern = expect_pattern(&args[1])?;

let compiled_pattern = match &args[1] {
Value::Pattern(p) => p,
_ => {
return Err(RuntimeError::new(
"Second argument to pattern_find_all must be a compiled pattern".to_string(),
0,
0,
));
}
};

let matches = compiled_pattern.find_all(text_str);
let matches = compiled_pattern.find_all(text.as_ref());
let mut result_list = Vec::new();

for match_result in matches {
Expand Down Expand Up @@ -189,49 +112,17 @@ pub fn native_pattern_replace(
line: usize,
column: usize,
) -> Result<Value, RuntimeError> {
if args.len() != 3 {
return Err(RuntimeError::new(
"pattern_replace requires exactly 3 arguments".to_string(),
line,
column,
));
}

let text = match &args[0] {
Value::Text(t) => t.as_ref(),
_ => {
return Err(RuntimeError::new(
"First argument must be text".to_string(),
line,
column,
));
}
};

let _pattern = match &args[1] {
Value::Pattern(p) => p.as_ref(),
_ => {
return Err(RuntimeError::new(
"Second argument must be a pattern".to_string(),
line,
column,
));
}
};
check_arg_count("pattern_replace", &args, 3)
.map_err(|e| RuntimeError::new(e.message, line, column))?;

let _replacement = match &args[2] {
Value::Text(t) => t.as_ref(),
_ => {
return Err(RuntimeError::new(
"Third argument must be text".to_string(),
line,
column,
));
}
};
let text = expect_text(&args[0]).map_err(|e| RuntimeError::new(e.message, line, column))?;
let _pattern =
expect_pattern(&args[1]).map_err(|e| RuntimeError::new(e.message, line, column))?;
let _replacement =
expect_text(&args[2]).map_err(|e| RuntimeError::new(e.message, line, column))?;
Comment on lines +115 to +122

// TODO: Update to use new pattern system for replacement
Ok(Value::Text(Arc::from(text)))
Ok(Value::Text(Arc::clone(&text)))
}

/// Native function for pattern splitting (called by interpreter)
Expand All @@ -240,42 +131,20 @@ pub fn native_pattern_split(
line: usize,
column: usize,
) -> Result<Value, RuntimeError> {
if args.len() != 2 {
return Err(RuntimeError::new(
"pattern_split requires exactly 2 arguments".to_string(),
line,
column,
));
}
check_arg_count("pattern_split", &args, 2)
.map_err(|e| RuntimeError::new(e.message, line, column))?;

let text = match &args[0] {
Value::Text(t) => t.as_ref(),
_ => {
return Err(RuntimeError::new(
"First argument must be text".to_string(),
line,
column,
));
}
};

let pattern = match &args[1] {
Value::Pattern(p) => p,
_ => {
return Err(RuntimeError::new(
"Second argument must be a pattern".to_string(),
line,
column,
));
}
};
let text_arc = expect_text(&args[0]).map_err(|e| RuntimeError::new(e.message, line, column))?;
let text = text_arc.as_ref();
let pattern =
expect_pattern(&args[1]).map_err(|e| RuntimeError::new(e.message, line, column))?;
Comment on lines +134 to +140

// Find all matches of the pattern in the text
let matches = pattern.find_all(text);

// If no matches, return the entire text as a single element
if matches.is_empty() {
let parts = vec![Value::Text(Arc::from(text))];
let parts = vec![Value::Text(Arc::clone(&text_arc))];
return Ok(Value::List(Rc::new(RefCell::new(parts))));
}

Expand Down
8 changes: 4 additions & 4 deletions src/stdlib/pattern_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ mod tests {
result
.unwrap_err()
.to_string()
.contains("exactly 2 arguments")
.contains("expects 2 arguments")
);
}

Expand All @@ -33,7 +33,7 @@ mod tests {
result
.unwrap_err()
.to_string()
.contains("exactly 2 arguments")
.contains("expects 2 arguments")
);
}

Expand All @@ -45,7 +45,7 @@ mod tests {
result
.unwrap_err()
.to_string()
.contains("exactly 2 arguments")
.contains("expects 2 arguments")
);
}

Expand All @@ -57,6 +57,6 @@ mod tests {
];
let result = pattern_matches_native(args);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("First argument"));
assert!(result.unwrap_err().to_string().contains("Expected text"));
}
}
Loading