~kameliya/rubyex

ref: 9fafd84d7cac7c0c04623ab2091788564df792d6 rubyex/vm/rregexp.cpp -rw-r--r-- 5.5 KiB
9fafd84d — Yuki Izumi match will find it anywhere in the string, even 12 years ago
                                                                                
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#include "rregexp.h"
#include <sstream>

RubyValue regexp_initialize(linked_ptr<Binding> &, RubyValue, const std::vector<RubyValue> &);
RubyValue regexp_match(linked_ptr<Binding> &, RubyValue, const std::vector<RubyValue> &);
RubyValue regexp_source(linked_ptr<Binding> &, RubyValue);
RubyValue regexp_inspect(linked_ptr<Binding> &, RubyValue);

RubyValue match_data_new(linked_ptr<Binding> &, RubyValue);
RubyValue match_data_begin(linked_ptr<Binding> &, RubyValue, const std::vector<RubyValue> &);
RubyValue match_data_end(linked_ptr<Binding> &, RubyValue, const std::vector<RubyValue> &);
RubyValue match_data_index_op(linked_ptr<Binding> &, RubyValue, const std::vector<RubyValue> &);
RubyValue match_data_captures(linked_ptr<Binding> &, RubyValue);

void RubyRegexpEI::init(RubyEnvironment &_e)
{
  RubyClass *rb_cRegexp = _e.gc.track<RubyClass>(new RubyClass(_e, "Regexp"));

  rb_cRegexp->add_method("initialize", RubyMethod::Create(regexp_initialize, 1));
  rb_cRegexp->add_method("match", RubyMethod::Create(regexp_match, 1));
  rb_cRegexp->add_method("source", RubyMethod::Create(regexp_source));
  rb_cRegexp->add_method("inspect", RubyMethod::Create(regexp_inspect));

  _e.set_global_by_name("Regexp", rb_cRegexp);
  _e.Regexp = rb_cRegexp;

  RubyClass *rb_cMatchData = _e.gc.track<RubyClass>(new RubyClass(_e, "MatchData"));

  rb_cMatchData->add_metaclass_method(_e, "new", RubyMethod::Create(match_data_new));

  rb_cMatchData->add_method("[]", RubyMethod::Create(match_data_index_op, 1));
  rb_cMatchData->add_method("begin", RubyMethod::Create(match_data_begin, 1));
  rb_cMatchData->add_method("end", RubyMethod::Create(match_data_end, 1));
  rb_cMatchData->add_method("captures", RubyMethod::Create(match_data_captures));

  _e.set_global_by_name("MatchData", rb_cMatchData);
  _e.MatchData = rb_cMatchData;
}

RubyValue regexp_initialize(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args) {
  RubyRegexp *exp = _self.get_special<RubyRegexp>();
  exp->initialize(_b, _args[0].get_string());
  return _b->environment.NIL;
}

RubyValue regexp_match(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args) {
  RubyRegexp *exp = _self.get_special<RubyRegexp>();
  const std::string &str = _args[0].get_string();
  OnigRegion *mr = onig_region_new();

  int result = onig_search(	exp->reg, (UChar *)str.c_str(), (UChar *)str.c_str() + str.size(),
				(UChar *)str.c_str(), (UChar *)str.c_str() + str.size(), mr, 0);
  if (result == ONIG_MISMATCH)
    return _b->environment.NIL;

  // XXX I probably need to use this: onig_capture_tree_traverse ?

  RubyMatchData *md = _b->environment.gc.track<RubyMatchData>(new RubyMatchData(_b->environment));
  for (int i = 0; i < mr->num_regs; ++i)
    md->add_region(str.substr(mr->beg[i], mr->end[i] - mr->beg[i]), mr->beg[i], mr->end[i]);

  onig_region_free(mr, 1);

  return O2V(md);
}

RubyValue regexp_source(linked_ptr<Binding> &_b, RubyValue _self) {
  return _b->environment.get_string(_self.get_special<RubyRegexp>()->phrase);
}

RubyValue regexp_inspect(linked_ptr<Binding> &_b, RubyValue _self) {
  RubyRegexp *exp = _self.get_special<RubyRegexp>();
  std::string &phrase = exp->phrase;

  std::ostringstream oss;
  oss << '/';
  for (unsigned int i = 0; i < phrase.size(); ++i)
    if (phrase[i] == '/')
      oss << "\\/";
    else
      oss << phrase[i];
  oss << '/';
  return _b->environment.get_string(oss.str());
}

RubyValue match_data_new(linked_ptr<Binding> &_b, RubyValue _self) {
  throw WorldException(_b, _b->environment.NoMethodError, "undefined method `new': XXX - use undef/remove");
  // XXX
}

RubyValue match_data_begin(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args) {
  // RESUME XXX CONTINUE
}

RubyValue match_data_end(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args) {
}

RubyValue match_data_index_op(linked_ptr<Binding> &_b, RubyValue _self, const std::vector<RubyValue> &_args) {
  // XXX bounds-checking wtf?
  return _b->environment.get_string(_self.get_special<RubyMatchData>()->regions[_args[0].get_fixnum()].data);
}

RubyValue match_data_captures(linked_ptr<Binding> &_b, RubyValue _self) {
  std::vector<RubyValue> captures;
  std::vector<ruby_match_region_t> &regions = _self.get_special<RubyMatchData>()->regions;
  for (std::vector<ruby_match_region_t>::const_iterator it = regions.begin() + 1; it != regions.end(); ++it)
    captures.push_back(_b->environment.get_string(it->data));

  return O2V(_b->environment.gc.track(new RubyArray(_b->environment, captures)));
}

RubyRegexp::RubyRegexp(RubyEnvironment &_e): RubyObject(_e.Regexp)
{ }

RubyRegexp::RubyRegexp(linked_ptr<Binding> &_b, const std::string &_phrase): RubyObject(_b->environment.Regexp) {
  initialize(_b, _phrase);
}

RubyRegexp::~RubyRegexp() {
  onig_free(reg);
}

void RubyRegexp::initialize(linked_ptr<Binding> &_b, const std::string &_phrase) {
  phrase = _phrase;
  OnigErrorInfo oei;

  int new_res =
    onig_new(	&reg, (UChar *)phrase.c_str(), (UChar *)phrase.c_str() + phrase.size(),
		ONIG_OPTION_NONE, ONIG_ENCODING_ASCII /* XXX later: encodings? */,
		ONIG_SYNTAX_RUBY, &oei);
  if (new_res != ONIG_NORMAL) {
    UChar *cb = new UChar[ONIG_MAX_ERROR_MESSAGE_LEN];
    onig_error_code_to_str(cb, new_res, oei);
    WorldException ex(_b, _b->environment.SyntaxError, (const char *)cb);
    delete [] cb;
    throw ex;
  }
}

RubyMatchData::RubyMatchData(RubyEnvironment &_e): RubyObject(_e.MatchData)
{ }

void RubyMatchData::add_region(const std::string &_data, int _beg, int _end) {
  ruby_match_region_t mr = {_data, _beg, _end};
  regions.push_back(mr);
}