1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/*
 * Regular expressions.
 *
 * This module fits the python re interface onto the rust regular expression
 * system.
 */
use regex::{Match, Regex};

use crate::obj::objstr::PyStringRef;
use crate::obj::objtype::PyClassRef;
use crate::pyobject::{PyObjectRef, PyRef, PyResult, PyValue};
use crate::vm::VirtualMachine;

impl PyValue for Regex {
    fn class(vm: &VirtualMachine) -> PyClassRef {
        vm.class("re", "Pattern")
    }
}

/// Inner data for a match object.
#[derive(Debug)]
struct PyMatch {
    start: usize,
    end: usize,
}

impl PyValue for PyMatch {
    fn class(vm: &VirtualMachine) -> PyClassRef {
        vm.class("re", "Match")
    }
}

type PyRegexRef = PyRef<Regex>;
type PyMatchRef = PyRef<PyMatch>;

fn re_match(pattern: PyStringRef, string: PyStringRef, vm: &VirtualMachine) -> PyResult {
    let regex = make_regex(vm, &pattern.value)?;
    do_match(vm, &regex, &string.value)
}

fn re_search(pattern: PyStringRef, string: PyStringRef, vm: &VirtualMachine) -> PyResult {
    let regex = make_regex(vm, &pattern.value)?;
    do_search(vm, &regex, &string.value)
}

fn do_match(vm: &VirtualMachine, regex: &Regex, search_text: &str) -> PyResult {
    // TODO: implement match!
    do_search(vm, regex, search_text)
}

fn do_search(vm: &VirtualMachine, regex: &Regex, search_text: &str) -> PyResult {
    match regex.find(search_text) {
        None => Ok(vm.get_none()),
        Some(result) => create_match(vm, &result),
    }
}

fn make_regex(vm: &VirtualMachine, pattern: &str) -> PyResult<Regex> {
    match Regex::new(pattern) {
        Ok(regex) => Ok(regex),
        Err(err) => Err(vm.new_value_error(format!("Error in regex: {:?}", err))),
    }
}

/// Take a found regular expression and convert it to proper match object.
fn create_match(vm: &VirtualMachine, match_value: &Match) -> PyResult {
    // let mo = vm.invoke(match_class, PyFuncArgs::default())?;
    // let txt = vm.ctx.new_str(result.as_str().to_string());
    // vm.ctx.set_attr(&mo, "str", txt);
    Ok(PyMatch {
        start: match_value.start(),
        end: match_value.end(),
    }
    .into_ref(vm)
    .into_object())
}

fn re_compile(pattern: PyStringRef, vm: &VirtualMachine) -> PyResult<Regex> {
    make_regex(vm, &pattern.value)
}

fn re_escape(pattern: PyStringRef, _vm: &VirtualMachine) -> String {
    regex::escape(&pattern.value)
}

impl PyRegexRef {
    fn match_(self, text: PyStringRef, vm: &VirtualMachine) -> PyResult {
        do_match(vm, &self, &text.value)
    }
    fn search(self, text: PyStringRef, vm: &VirtualMachine) -> PyResult {
        do_search(vm, &self, &text.value)
    }
}

impl PyMatchRef {
    fn start(self, _vm: &VirtualMachine) -> usize {
        self.start
    }
    fn end(self, _vm: &VirtualMachine) -> usize {
        self.end
    }
}

/// Create the python `re` module with all its members.
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
    let ctx = &vm.ctx;

    let match_type = py_class!(ctx, "Match", ctx.object(), {
        "start" => ctx.new_rustfunc(PyMatchRef::start),
        "end" => ctx.new_rustfunc(PyMatchRef::end)
    });

    let pattern_type = py_class!(ctx, "Pattern", ctx.object(), {
        "match" => ctx.new_rustfunc(PyRegexRef::match_),
        "search" => ctx.new_rustfunc(PyRegexRef::search)
    });

    py_module!(vm, "re", {
        "compile" => ctx.new_rustfunc(re_compile),
        "escape" => ctx.new_rustfunc(re_escape),
        "Match" => match_type,
        "match" => ctx.new_rustfunc(re_match),
        "Pattern" => pattern_type,
        "search" => ctx.new_rustfunc(re_search)
    })
}