mirror of
https://github.com/lune-org/lune.git
synced 2024-12-12 13:00:37 +00:00
Port over regex implementation from fork, do some cleanup
This commit is contained in:
parent
e11302766b
commit
12f5824da9
6 changed files with 215 additions and 7 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
@ -1522,6 +1522,7 @@ dependencies = [
|
||||||
"regex",
|
"regex",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"rustyline",
|
"rustyline",
|
||||||
|
"self_cell",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_yaml",
|
"serde_yaml",
|
||||||
|
@ -2423,6 +2424,12 @@ dependencies = [
|
||||||
"untrusted",
|
"untrusted",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "self_cell"
|
||||||
|
version = "1.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58bf37232d3bb9a2c4e641ca2a11d83b5062066f88df7fed36c28772046d65ba"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "semver"
|
name = "semver"
|
||||||
version = "0.9.0"
|
version = "0.9.0"
|
||||||
|
|
|
@ -24,7 +24,6 @@ cli = [
|
||||||
"dep:env_logger",
|
"dep:env_logger",
|
||||||
"dep:clap",
|
"dep:clap",
|
||||||
"dep:include_dir",
|
"dep:include_dir",
|
||||||
"dep:regex",
|
|
||||||
"dep:rustyline",
|
"dep:rustyline",
|
||||||
"dep:zip_next",
|
"dep:zip_next",
|
||||||
]
|
]
|
||||||
|
@ -76,7 +75,9 @@ path-clean = "1.0"
|
||||||
pathdiff = "0.2"
|
pathdiff = "0.2"
|
||||||
pin-project = "1.0"
|
pin-project = "1.0"
|
||||||
urlencoding = "2.1"
|
urlencoding = "2.1"
|
||||||
bstr = "1.9.1"
|
bstr = "1.9"
|
||||||
|
regex = "1.10"
|
||||||
|
self_cell = "1.0"
|
||||||
|
|
||||||
### RUNTIME
|
### RUNTIME
|
||||||
|
|
||||||
|
@ -133,10 +134,6 @@ env_logger = { optional = true, version = "0.11" }
|
||||||
itertools = "0.12"
|
itertools = "0.12"
|
||||||
clap = { optional = true, version = "4.1", features = ["derive"] }
|
clap = { optional = true, version = "4.1", features = ["derive"] }
|
||||||
include_dir = { optional = true, version = "0.7", features = ["glob"] }
|
include_dir = { optional = true, version = "0.7", features = ["glob"] }
|
||||||
regex = { optional = true, version = "1.7", default-features = false, features = [
|
|
||||||
"std",
|
|
||||||
"unicode-perl",
|
|
||||||
] }
|
|
||||||
rustyline = { optional = true, version = "14.0" }
|
rustyline = { optional = true, version = "14.0" }
|
||||||
zip_next = { optional = true, version = "1.1" }
|
zip_next = { optional = true, version = "1.1" }
|
||||||
|
|
||||||
|
|
76
src/lune/builtins/regex/captures.rs
Normal file
76
src/lune/builtins/regex/captures.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mlua::prelude::*;
|
||||||
|
use regex::{Captures, Regex};
|
||||||
|
use self_cell::self_cell;
|
||||||
|
|
||||||
|
use super::matches::LuaMatch;
|
||||||
|
|
||||||
|
type OptionalCaptures<'a> = Option<Captures<'a>>;
|
||||||
|
|
||||||
|
self_cell! {
|
||||||
|
struct LuaCapturesInner {
|
||||||
|
owner: Arc<String>,
|
||||||
|
#[covariant]
|
||||||
|
dependent: OptionalCaptures,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct LuaCaptures {
|
||||||
|
inner: LuaCapturesInner,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LuaCaptures {
|
||||||
|
pub fn new(pattern: &Regex, text: String) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: LuaCapturesInner::new(Arc::from(text), |owned| pattern.captures(owned.as_str())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn captures(&self) -> &Captures {
|
||||||
|
self.inner
|
||||||
|
.borrow_dependent()
|
||||||
|
.as_ref()
|
||||||
|
.expect("None captures should not be used")
|
||||||
|
}
|
||||||
|
|
||||||
|
fn num_captures(&self) -> usize {
|
||||||
|
// NOTE: Here we exclude the match for the entire regex
|
||||||
|
// pattern, only counting the named and numbered captures
|
||||||
|
self.captures().len() - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn text(&self) -> Arc<String> {
|
||||||
|
Arc::clone(self.inner.borrow_owner())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LuaUserData for LuaCaptures {
|
||||||
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("get", |_, this, n: usize| {
|
||||||
|
Ok(this
|
||||||
|
.captures()
|
||||||
|
.get(n)
|
||||||
|
.map(|m| LuaMatch::new(this.text(), m)))
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("group", |_, this, group: String| {
|
||||||
|
Ok(this
|
||||||
|
.captures()
|
||||||
|
.name(&group)
|
||||||
|
.map(|m| LuaMatch::new(this.text(), m)))
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("format", |_, this, format: String| {
|
||||||
|
let mut new = String::new();
|
||||||
|
this.captures().expand(&format, &mut new);
|
||||||
|
Ok(new)
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_meta_method(LuaMetaMethod::Type, |_, _, ()| Ok("RegexCaptures"));
|
||||||
|
methods.add_meta_method(LuaMetaMethod::Len, |_, this, ()| Ok(this.num_captures()));
|
||||||
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
|
||||||
|
Ok(format!("RegexCaptures({})", this.num_captures()))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
48
src/lune/builtins/regex/matches.rs
Normal file
48
src/lune/builtins/regex/matches.rs
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
use std::{ops::Range, sync::Arc};
|
||||||
|
|
||||||
|
use mlua::prelude::*;
|
||||||
|
use regex::Match;
|
||||||
|
|
||||||
|
pub struct LuaMatch {
|
||||||
|
text: Arc<String>,
|
||||||
|
start: usize,
|
||||||
|
end: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LuaMatch {
|
||||||
|
pub fn new(text: Arc<String>, matched: Match) -> Self {
|
||||||
|
Self {
|
||||||
|
text,
|
||||||
|
start: matched.start(),
|
||||||
|
end: matched.end(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn range(&self) -> Range<usize> {
|
||||||
|
self.start..self.end
|
||||||
|
}
|
||||||
|
|
||||||
|
fn slice(&self) -> &str {
|
||||||
|
&self.text[self.range()]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LuaUserData for LuaMatch {
|
||||||
|
fn add_fields<'lua, F: LuaUserDataFields<'lua, Self>>(fields: &mut F) {
|
||||||
|
// NOTE: Strings are 0 based in Rust but 1 based in Luau, and end of range in Rust is exclusive
|
||||||
|
fields.add_field_method_get("start", |_, this| Ok(this.start.saturating_add(1)));
|
||||||
|
fields.add_field_method_get("finish", |_, this| Ok(this.end));
|
||||||
|
fields.add_field_method_get("len", |_, this| Ok(this.range().len()));
|
||||||
|
fields.add_field_method_get("text", |_, this| Ok(this.slice().to_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("isEmpty", |_, this, ()| Ok(this.range().is_empty()));
|
||||||
|
|
||||||
|
methods.add_meta_method(LuaMetaMethod::Type, |_, _, ()| Ok("RegexMatch"));
|
||||||
|
methods.add_meta_method(LuaMetaMethod::Len, |_, this, ()| Ok(this.range().len()));
|
||||||
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
|
||||||
|
Ok(format!("RegexMatch({})", this.slice()))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,21 @@
|
||||||
|
#![allow(clippy::module_inception)]
|
||||||
|
|
||||||
use mlua::prelude::*;
|
use mlua::prelude::*;
|
||||||
|
|
||||||
use crate::lune::util::TableBuilder;
|
use crate::lune::util::TableBuilder;
|
||||||
|
|
||||||
|
mod captures;
|
||||||
|
mod matches;
|
||||||
|
mod regex;
|
||||||
|
|
||||||
|
use self::regex::LuaRegex;
|
||||||
|
|
||||||
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
|
pub fn create(lua: &Lua) -> LuaResult<LuaTable> {
|
||||||
TableBuilder::new(lua)?.build_readonly()
|
TableBuilder::new(lua)?
|
||||||
|
.with_function("new", new_regex)?
|
||||||
|
.build_readonly()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_regex(_: &Lua, pattern: String) -> LuaResult<LuaRegex> {
|
||||||
|
LuaRegex::new(pattern)
|
||||||
}
|
}
|
||||||
|
|
66
src/lune/builtins/regex/regex.rs
Normal file
66
src/lune/builtins/regex/regex.rs
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use mlua::prelude::*;
|
||||||
|
use regex::Regex;
|
||||||
|
|
||||||
|
use super::{captures::LuaCaptures, matches::LuaMatch};
|
||||||
|
|
||||||
|
pub struct LuaRegex {
|
||||||
|
inner: Regex,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LuaRegex {
|
||||||
|
pub fn new(pattern: String) -> LuaResult<Self> {
|
||||||
|
Regex::new(&pattern)
|
||||||
|
.map(|inner| Self { inner })
|
||||||
|
.map_err(LuaError::external)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LuaUserData for LuaRegex {
|
||||||
|
fn add_methods<'lua, M: LuaUserDataMethods<'lua, Self>>(methods: &mut M) {
|
||||||
|
methods.add_method("isMatch", |_, this, text: String| {
|
||||||
|
Ok(this.inner.is_match(&text))
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("find", |_, this, text: String| {
|
||||||
|
let arc = Arc::new(text);
|
||||||
|
Ok(this
|
||||||
|
.inner
|
||||||
|
.find(&arc)
|
||||||
|
.map(|m| LuaMatch::new(Arc::clone(&arc), m)))
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("captures", |_, this, text: String| {
|
||||||
|
Ok(LuaCaptures::new(&this.inner, text))
|
||||||
|
});
|
||||||
|
|
||||||
|
methods.add_method("split", |_, this, text: String| {
|
||||||
|
Ok(this
|
||||||
|
.inner
|
||||||
|
.split(&text)
|
||||||
|
.map(|s| s.to_string())
|
||||||
|
.collect::<Vec<_>>())
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: Determine whether it's desirable and / or feasible to support
|
||||||
|
// using a function or table for `replace` like in the lua string library
|
||||||
|
methods.add_method(
|
||||||
|
"replace",
|
||||||
|
|_, this, (haystack, replacer): (String, String)| {
|
||||||
|
Ok(this.inner.replace(&haystack, replacer).to_string())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
methods.add_method(
|
||||||
|
"replaceAll",
|
||||||
|
|_, this, (haystack, replacer): (String, String)| {
|
||||||
|
Ok(this.inner.replace_all(&haystack, replacer).to_string())
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
methods.add_meta_method(LuaMetaMethod::Type, |_, _, ()| Ok("Regex"));
|
||||||
|
methods.add_meta_method(LuaMetaMethod::ToString, |_, this, ()| {
|
||||||
|
Ok(format!("Regex({})", this.inner.as_str()))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue