///|
/// Escape regex metacharacters in a string.
fn escape_regex(s : String) -> String {
let buf = StringBuilder::new()
for ch in s {
if "^$[]()\\{}.|?*+".contains(ch.to_string()) {
buf.write_char('\\')
}
buf.write_char(ch)
}
buf.to_string()
}
///|
/// Compile a cucumber expression AST to a regex pattern string.
pub fn compile(
node : Node,
registry : ParamTypeRegistry,
) -> String raise ExpressionError {
match node {
ExpressionNode(children) => {
let buf = StringBuilder::new()
buf.write_char('^')
for child in children {
buf.write_string(compile(child, registry))
}
buf.write_char('$')
buf.to_string()
}
TextNode(text) => escape_regex(text)
ParameterNode(name) =>
match registry.get(name) {
Some(entry) => {
let buf = StringBuilder::new()
buf.write_char('(')
for i, pat in entry.patterns {
if i > 0 {
buf.write_char('|')
}
buf.write_string("(?:")
buf.write_string(pat.to_string())
buf.write_char(')')
}
buf.write_char(')')
buf.to_string()
}
None =>
raise ExpressionError::UnknownParameterType(
name~,
message="Unknown parameter type: {" + name + "}",
)
}
OptionalNode(children) => {
let buf = StringBuilder::new()
buf.write_string("(?:")
for child in children {
buf.write_string(compile(child, registry))
}
buf.write_string(")?")
buf.to_string()
}
AlternationNode(alternatives) => {
let buf = StringBuilder::new()
buf.write_string("(?:")
for i, arm in alternatives {
if i > 0 {
buf.write_char('|')
}
for node in arm {
buf.write_string(compile(node, registry))
}
}
buf.write_char(')')
buf.to_string()
}
}
}
///|
/// Parse a cucumber expression and compile it to a regex pattern string.
pub fn compile_expression(
expression : String,
registry? : ParamTypeRegistry = ParamTypeRegistry::default(),
) -> String raise ExpressionError {
let ast = parse_expression(expression)
compile(ast, registry)
}