Skip to content

Commit

Permalink
feat: allow includes regex (#65)
Browse files Browse the repository at this point in the history
  • Loading branch information
morgante authored Mar 24, 2024
1 parent 73e866f commit 61241d6
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 31 deletions.
72 changes: 65 additions & 7 deletions crates/core/src/pattern/includes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,71 @@ impl Matcher for Includes {
context: &Context<'a>,
logs: &mut AnalysisLogs,
) -> Result<bool> {
let resolved = ResolvedPattern::from_pattern(&self.includes, state, context, logs)?;
let substring = resolved.text(&state.files)?;
let string = binding.text(&state.files)?;
if string.contains(&*substring) {
Ok(true)
} else {
Ok(false)
match &self.includes {
Pattern::Regex(pattern) => {
pattern.execute_matching(binding, state, context, logs, false)
}
Pattern::ASTNode(_)
| Pattern::List(_)
| Pattern::ListIndex(_)
| Pattern::Map(_)
| Pattern::Accessor(_)
| Pattern::Call(_)
| Pattern::File(_)
| Pattern::Files(_)
| Pattern::Bubble(_)
| Pattern::Limit(_)
| Pattern::CallBuiltIn(_)
| Pattern::CallFunction(_)
| Pattern::CallForeignFunction(_)
| Pattern::Assignment(_)
| Pattern::Accumulate(_)
| Pattern::And(_)
| Pattern::Or(_)
| Pattern::Maybe(_)
| Pattern::Any(_)
| Pattern::Not(_)
| Pattern::If(_)
| Pattern::Undefined
| Pattern::Top
| Pattern::Bottom
| Pattern::Underscore
| Pattern::StringConstant(_)
| Pattern::AstLeafNode(_)
| Pattern::IntConstant(_)
| Pattern::FloatConstant(_)
| Pattern::BooleanConstant(_)
| Pattern::Dynamic(_)
| Pattern::CodeSnippet(_)
| Pattern::Variable(_)
| Pattern::Rewrite(_)
| Pattern::Log(_)
| Pattern::Range(_)
| Pattern::Contains(_)
| Pattern::Includes(_)
| Pattern::Within(_)
| Pattern::After(_)
| Pattern::Before(_)
| Pattern::Where(_)
| Pattern::Some(_)
| Pattern::Every(_)
| Pattern::Add(_)
| Pattern::Subtract(_)
| Pattern::Multiply(_)
| Pattern::Divide(_)
| Pattern::Modulo(_)
| Pattern::Dots
| Pattern::Sequential(_)
| Pattern::Like(_) => {
let resolved = ResolvedPattern::from_pattern(&self.includes, state, context, logs)?;
let substring = resolved.text(&state.files)?;
let string = binding.text(&state.files)?;
if string.contains(&*substring) {
Ok(true)
} else {
Ok(false)
}
}
}
}
}
6 changes: 3 additions & 3 deletions crates/core/src/pattern/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ use marzano_language::{language::Language, language::SnippetNode};
use marzano_util::analysis_logs::AnalysisLogs;
use marzano_util::cursor_wrapper::CursorWrapper;
use marzano_util::position::{char_index_to_byte_index, Position, Range};
use regex::{Match, Regex};
use regex::Match;
use std::collections::{BTreeMap, HashMap};
use std::str;
use std::vec;
Expand Down Expand Up @@ -409,8 +409,8 @@ fn implicit_metavariable_regex(
&text[last as usize..range.end_byte as usize],
));
}
let regex = format!("^{}$", regex_string);
let regex = RegexLike::Regex(Regex::new(regex.as_str())?);
let regex = regex_string.to_string();
let regex = RegexLike::Regex(regex);
Ok(Some(RegexPattern::new(regex, variables)))
}

Expand Down
53 changes: 34 additions & 19 deletions crates/core/src/pattern/regex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ pub struct RegexPattern {

#[derive(Debug, Clone)]
pub enum RegexLike {
Regex(Regex),
Regex(String),
Pattern(Box<Pattern>),
}

Expand Down Expand Up @@ -66,9 +66,7 @@ impl RegexPattern {
.strip_suffix('\"')
.ok_or_else(|| anyhow!("invalid regex postfix"))?;

let regex = format!("^{}$", regex);

RegexLike::Regex(Regex::new(regex.as_str())?)
RegexLike::Regex(regex.to_string())
} else {
let back_tick_node = regex_node
.child_by_field_name("snippet")
Expand Down Expand Up @@ -135,33 +133,32 @@ impl RegexPattern {
regex, variables,
))))
}
}

impl Name for RegexPattern {
fn name(&self) -> &'static str {
"REGEX"
}
}

impl Matcher for RegexPattern {
// wrong, but whatever for now
fn execute<'a>(
pub(crate) fn execute_matching<'a>(
&'a self,
binding: &ResolvedPattern<'a>,
state: &mut State<'a>,
context: &Context<'a>,
logs: &mut AnalysisLogs,
must_match_entire_string: bool,
) -> Result<bool> {
let text = binding.text(&state.files)?;
let resolved_regex = match self.regex {
RegexLike::Regex(ref regex) => regex.clone(),
let resolved_regex_text = match &self.regex {
RegexLike::Regex(regex) => match must_match_entire_string {
true => format!("^{}$", regex),
false => regex.to_string(),
},
RegexLike::Pattern(ref pattern) => {
let resolved = ResolvedPattern::from_pattern(pattern, state, context, logs)?;
let text = format!("^{}$", resolved.text(&state.files)?);
Regex::new(&text)?
let text = resolved.text(&state.files)?;
match must_match_entire_string {
true => format!("^{}$", text),
false => text.to_string(),
}
}
};
let captures = match resolved_regex.captures(&text) {
let final_regex = Regex::new(&resolved_regex_text)?;
let captures = match final_regex.captures(&text) {
Some(captures) => captures,
None => return Ok(false),
};
Expand Down Expand Up @@ -230,3 +227,21 @@ impl Matcher for RegexPattern {
Ok(true)
}
}

impl Name for RegexPattern {
fn name(&self) -> &'static str {
"REGEX"
}
}

impl Matcher for RegexPattern {
fn execute<'a>(
&'a self,
binding: &ResolvedPattern<'a>,
state: &mut State<'a>,
context: &Context<'a>,
logs: &mut AnalysisLogs,
) -> Result<bool> {
self.execute_matching(binding, state, context, logs, true)
}
}
72 changes: 70 additions & 2 deletions crates/core/src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2910,6 +2910,74 @@ fn hcl_implicit_regex() {
.unwrap();
}

#[test]
fn includes_regex() {
run_test_expected({
TestArgExpected {
pattern: r#"
|language js
|
|`console.log($_)` as $haystack where {
| $haystack <: includes r"Hello"
|} => `console.log("Goodbye world!")`
|"#
.trim_margin()
.unwrap(),
source: r#"
|console.log("Hello world!");
|console.log("Hello handsome!");
|console.log("But not me, sadly.");
|console.log("Hi, Hello world!");
|"#
.trim_margin()
.unwrap(),
expected: r#"
|console.log("Goodbye world!");
|console.log("Goodbye world!");
|console.log("But not me, sadly.");
|console.log("Goodbye world!");
|"#
.trim_margin()
.unwrap(),
}
})
.unwrap();
}

#[test]
fn includes_regex_with_capture() {
run_test_expected({
TestArgExpected {
pattern: r#"
|language js
|
|`console.log($_)` as $haystack where {
| $haystack <: includes r"Hello (\w+)"($name)
|} => `console.log("Goodbye $name!")`
|"#
.trim_margin()
.unwrap(),
source: r#"
|console.log("Hello world!");
|console.log("Hello handsome!");
|console.log("But not me, sadly.");
|console.log("Hi, Hello world!");
|"#
.trim_margin()
.unwrap(),
expected: r#"
|console.log("Goodbye world!");
|console.log("Goodbye handsome!");
|console.log("But not me, sadly.");
|console.log("Goodbye world!");
|"#
.trim_margin()
.unwrap(),
}
})
.unwrap();
}

#[test]
fn parses_simple_pattern() {
let pattern = r#"
Expand All @@ -2927,7 +2995,7 @@ fn parses_simple_pattern() {
fn warning_rewrite_in_not() {
let pattern = r#"
|`async ($args) => { $body }` where {
| $body <: not contains `try` => ` try {
| $body <: not contains `try` => ` try {
| $body
| } catch { }`
|}"#
Expand Down Expand Up @@ -10743,7 +10811,7 @@ fn rewrite_to_accessor_mut() {
|language js
|
|`const $x = $foo` where {
| $cities = {},
| $cities = {},
| $kiel = `kiel`,
| $cities.germany = $kiel,
| $cities.italy = `"venice"`,
Expand Down

0 comments on commit 61241d6

Please sign in to comment.