smarter match finding
This commit is contained in:
parent
4bcd7a2801
commit
066d927f3b
5 changed files with 125 additions and 41 deletions
36
.vscode/launch.json
vendored
Normal file
36
.vscode/launch.json
vendored
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
{
|
||||||
|
// Use IntelliSense to learn about possible attributes.
|
||||||
|
// Hover to view descriptions of existing attributes.
|
||||||
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
|
"version": "0.2.0",
|
||||||
|
"configurations": [
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug executable 'eyes'",
|
||||||
|
"cargo": {
|
||||||
|
"args": ["build", "--bin=eyes", "--package=eyes"],
|
||||||
|
"filter": {
|
||||||
|
"name": "eyes",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "lldb",
|
||||||
|
"request": "launch",
|
||||||
|
"name": "Debug unit tests in executable 'eyes'",
|
||||||
|
"cargo": {
|
||||||
|
"args": ["test", "--no-run", "--bin=eyes", "--package=eyes"],
|
||||||
|
"filter": {
|
||||||
|
"name": "eyes",
|
||||||
|
"kind": "bin"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"args": [],
|
||||||
|
"cwd": "${workspaceFolder}"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -2,5 +2,6 @@
|
||||||
name = "eyes"
|
name = "eyes"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
license-file="LICENSE.txt"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
|
109
src/lib.rs
109
src/lib.rs
|
@ -1,30 +1,42 @@
|
||||||
pub struct Parser<'a> {
|
pub struct Parser<'a> {
|
||||||
_input: &'a str,
|
|
||||||
_pattern: &'a str,
|
|
||||||
captures: Vec<&'a str>,
|
captures: Vec<&'a str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Parser<'a> {
|
impl<'a> Parser<'a> {
|
||||||
pub fn new(input: &'a str, pattern: &'a str) -> Self {
|
pub fn new(input: &'a str, template: &'a str) -> Self {
|
||||||
let splits = pattern
|
let patterns = template
|
||||||
.split("{}")
|
.split("{}")
|
||||||
.filter(|pat| pat != &"")
|
.filter(|pat| pat != &"")
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let mut captures = vec![input];
|
let mut captures = vec![input];
|
||||||
|
|
||||||
for pat in splits {
|
for (i, pat) in patterns.iter().enumerate() {
|
||||||
captures = captures
|
let last = captures.pop().unwrap();
|
||||||
.iter()
|
let (mut left, mut right) = last.split_once(pat).unwrap();
|
||||||
.map(|sub| sub.split(pat))
|
|
||||||
.flatten()
|
// if the right side of the split doesn't contain the pattern,
|
||||||
.collect();
|
// we don't have to check if we can expand the match
|
||||||
|
if right.contains(pat) {
|
||||||
|
let mut pattern_index = right.find(pat).unwrap() + left.len();
|
||||||
|
let next_pattern_index = right.find(patterns[i + 1]).unwrap() + left.len();
|
||||||
|
|
||||||
|
while next_pattern_index > pattern_index {
|
||||||
|
let (left_side, _) = input.split_at(pattern_index + 1);
|
||||||
|
left = left_side;
|
||||||
|
let (_, right_side) = input.split_at(pattern_index + 1 + pat.len());
|
||||||
|
right = right_side;
|
||||||
|
|
||||||
|
pattern_index = right.find(pat).unwrap_or(input.len()) + left.len();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self {
|
if !left.is_empty() {
|
||||||
_input: input,
|
captures.push(left);
|
||||||
_pattern: pattern,
|
|
||||||
captures: captures[1..].to_vec(),
|
|
||||||
}
|
}
|
||||||
|
captures.push(right);
|
||||||
|
}
|
||||||
|
|
||||||
|
Self { captures }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn captures(&self) -> Vec<&'a str> {
|
pub fn captures(&self) -> Vec<&'a str> {
|
||||||
|
@ -36,15 +48,11 @@ impl<'a> Parser<'a> {
|
||||||
macro_rules! parse {
|
macro_rules! parse {
|
||||||
($input: expr, $pattern: tt, $($type:ty),*) => {
|
($input: expr, $pattern: tt, $($type:ty),*) => {
|
||||||
{
|
{
|
||||||
let mut parser = eyes::Parser::new($input, $pattern);
|
let parser = $crate::Parser::new($input, $pattern);
|
||||||
let mut captures = parser.captures();
|
let captures = parser.captures();
|
||||||
captures.reverse();
|
let mut iter = captures.iter();
|
||||||
|
|
||||||
(
|
($(iter.next().unwrap().parse::<$type>().unwrap()),*)
|
||||||
$({
|
|
||||||
captures.pop().unwrap().parse::<$type>().unwrap()
|
|
||||||
},)*
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -53,15 +61,56 @@ macro_rules! parse {
|
||||||
macro_rules! try_parse {
|
macro_rules! try_parse {
|
||||||
($input: expr, $pattern: tt, $($type:ty),*) => {
|
($input: expr, $pattern: tt, $($type:ty),*) => {
|
||||||
{
|
{
|
||||||
let mut parser = eyes::Parser::new($input, $pattern);
|
let parser = $crate::Parser::new($input, $pattern);
|
||||||
let mut captures = parser.captures();
|
let captures = parser.captures();
|
||||||
captures.reverse();
|
let mut iter = captures.iter();
|
||||||
|
|
||||||
(
|
($(iter.next().unwrap().parse::<$type>()),*)
|
||||||
$({
|
|
||||||
captures.pop().unwrap().parse::<$type>()
|
|
||||||
},)*
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn simple_test() {
|
||||||
|
// Test where the patterns in the template are all different
|
||||||
|
let input = "#lol @ 338,7643: 20.2x24.5";
|
||||||
|
let template = "#{} @ {},{}: {}x{}";
|
||||||
|
|
||||||
|
println!("input: '{}'", input);
|
||||||
|
println!("pattern: '{}'", template);
|
||||||
|
|
||||||
|
let (id, x, y, w, h) = parse!(input, template, String, isize, isize, f64, f64);
|
||||||
|
|
||||||
|
println!("id: {:?}", id);
|
||||||
|
println!("x: {:?}", x);
|
||||||
|
println!("y: {:?}", y);
|
||||||
|
println!("w: {:?}", w);
|
||||||
|
println!("h: {:?}", h);
|
||||||
|
|
||||||
|
assert_eq!((id.as_str(), x, y, w, h), ("lol", 338, 7643, 20.2, 24.5));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn tries_to_expand_correctly() {
|
||||||
|
let input = "turn off 660,55 through 986,197";
|
||||||
|
let template = "{} {},{} through {},{}";
|
||||||
|
|
||||||
|
println!("input: '{}'", input);
|
||||||
|
println!("pattern: '{}'", template);
|
||||||
|
|
||||||
|
let (op, x1, y1, x2, y2) = try_parse!(input, template, String, usize, usize, usize, usize);
|
||||||
|
|
||||||
|
println!("op: {:?}", op);
|
||||||
|
println!("p1: {:?}", (&x1, &y1));
|
||||||
|
println!("p2: {:?}", (&x2, &y2));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
(op.unwrap().as_str(), x1, y1, x2, y2),
|
||||||
|
("turn off", Ok(660), Ok(55), Ok(986), Ok(197))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -1,17 +1,15 @@
|
||||||
use eyes::*;
|
use eyes::*;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let input = "#loll1 @ 338,764: 20x24";
|
let input = "turn off 660,55 through 986,197";
|
||||||
let pattern = "#{} @ {},{}: {}x{}";
|
let template = "{} {},{} through {},{}";
|
||||||
|
|
||||||
println!("input: {}", input);
|
println!("input: '{}'", input);
|
||||||
println!("pattern: {}", pattern);
|
println!("pattern: '{}'", template);
|
||||||
|
|
||||||
let (id, x, y, w, h) = parse!(input, pattern, String, f64, f32, usize, isize);
|
let (op, x1, y1, x2, y2) = try_parse!(input, template, String, usize, usize, usize, usize);
|
||||||
|
|
||||||
println!("id: {:?}", id);
|
println!("op: {:?}", op);
|
||||||
println!("x: {:?}", x);
|
println!("p1: {:?}", (x1, y1));
|
||||||
println!("y: {:?}", y);
|
println!("p2: {:?}", (x2, y2));
|
||||||
println!("w: {:?}", w);
|
|
||||||
println!("h: {:?}", h);
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue