mirror of
https://github.com/microsoft/regorus.git
synced 2026-08-05 02:16:11 +00:00
- `std` feature is enabled by default - By default enable #![no_std] compilation - Import std create if `std` feature is enabled or if testing - Use core, alloc types - Make it clear where std types are being used - In no std, use BTreeMap in place of HashMap. HashMap is not available in no std due to lack of a secure random number generator Note: The project does not yet compile without std feature being specified. But it's really close to being able to do so. Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
112 lines
3.4 KiB
Rust
112 lines
3.4 KiB
Rust
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
use crate::ast::{Expr, Ref};
|
|
use crate::builtins;
|
|
use crate::builtins::utils::{ensure_args_count, ensure_string, ensure_string_collection};
|
|
use crate::lexer::Span;
|
|
use crate::value::Value;
|
|
use crate::*;
|
|
|
|
use anyhow::{bail, Result};
|
|
//use glob::{Pattern, MatchOptions};
|
|
use wax::{Glob, Pattern};
|
|
|
|
pub fn register(m: &mut builtins::BuiltinsMap<&'static str, builtins::BuiltinFcn>) {
|
|
m.insert("glob.match", (glob_match, 3));
|
|
m.insert("glob.quote_meta", (quote_meta, 1));
|
|
}
|
|
|
|
const PLACE_HOLDER: &str = "\0";
|
|
|
|
fn suppress_unix_style_delimiter(s: &str) -> Result<String> {
|
|
// Replace unix-style delimiter with placeholder so that no delimiter is
|
|
// encountered during glob matching.
|
|
Ok(s.replace('/', PLACE_HOLDER))
|
|
}
|
|
|
|
fn make_delimiters_unix_style(s: &str, delimiters: &[char]) -> Result<String> {
|
|
if s.contains(PLACE_HOLDER) {
|
|
bail!("string contains internal glob placeholder");
|
|
}
|
|
|
|
let has_unix_style = delimiters.contains(&'/');
|
|
|
|
let mut s = if !has_unix_style {
|
|
suppress_unix_style_delimiter(s)?
|
|
} else {
|
|
s.to_string()
|
|
};
|
|
|
|
for d in delimiters {
|
|
if *d == ':' {
|
|
s = s.replace(*d, PLACE_HOLDER);
|
|
} else if *d != '/' {
|
|
// Insert / before occurances of delimiter.
|
|
s = s.replace(*d, format!("/{}/", *d).as_str());
|
|
}
|
|
}
|
|
|
|
Ok(s)
|
|
}
|
|
|
|
fn make_glob<'a>(pattern: &'a str, span: &'a Span) -> Result<Glob<'a>> {
|
|
Glob::new(pattern).or_else(|_| bail!(span.error("invalid glob")))
|
|
}
|
|
|
|
fn glob_match(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
|
let name = "glob.match";
|
|
ensure_args_count(span, name, params, args, 3)?;
|
|
|
|
let pattern = ensure_string(name, ¶ms[0], &args[0])?;
|
|
let value = ensure_string(name, ¶ms[2], &args[2])?;
|
|
|
|
let pattern = pattern.as_ref();
|
|
let value = value.as_ref();
|
|
|
|
if let Value::Null = &args[1] {
|
|
// Ensure that / is not treated as a delimiter.
|
|
let value = suppress_unix_style_delimiter(value)?;
|
|
let pattern = suppress_unix_style_delimiter(pattern)?;
|
|
|
|
let glob = make_glob(&pattern, params[0].span())?;
|
|
return Ok(Value::Bool(glob.is_match(&value[..])));
|
|
}
|
|
|
|
let delimiters = if let Value::Array(_) = &args[1] {
|
|
ensure_string_collection(name, ¶ms[1], &args[1])?
|
|
} else {
|
|
bail!(params[1]
|
|
.span()
|
|
.error(format!("{name} requires string array").as_str()));
|
|
};
|
|
|
|
if delimiters.iter().any(|d| d.len() > 1) {
|
|
bail!(params[1]
|
|
.span()
|
|
.error("delimiters must be single character"));
|
|
}
|
|
|
|
let mut delimiters: Vec<char> = delimiters.iter().filter_map(|d| d.chars().next()).collect();
|
|
|
|
if delimiters.is_empty() {
|
|
delimiters.push('.');
|
|
}
|
|
|
|
let pattern = make_delimiters_unix_style(pattern, &delimiters)?;
|
|
let value = make_delimiters_unix_style(value, &delimiters)?;
|
|
|
|
let glob = make_glob(&pattern, params[0].span())?;
|
|
Ok(Value::Bool(glob.is_match(&value[..])))
|
|
}
|
|
|
|
fn quote_meta(span: &Span, params: &[Ref<Expr>], args: &[Value], _strict: bool) -> Result<Value> {
|
|
let name = "glob.quote_meta";
|
|
ensure_args_count(span, name, params, args, 1)?;
|
|
|
|
let pattern = ensure_string(name, ¶ms[0], &args[0])?;
|
|
// Ensure that the glob is valid.
|
|
let _ = make_glob(&pattern, params[0].span())?;
|
|
Ok(Value::String(pattern.as_ref().replace('*', "\\*").into()))
|
|
}
|