blob: 7dccc0158eb7857e1b7b7f194d5d7cf512d5fc62 [file] [log] [blame]
mjs6f821c82002-03-22 00:31:57 +00001// -*- c-basic-offset: 2 -*-
kocienda66a6d362001-08-24 14:24:45 +00002/*
3 * This file is part of the KDE libraries
4 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
darin253b57c2003-01-22 00:11:44 +00005 * Copyright (C) 2003 Apple Computer, Inc.
kocienda66a6d362001-08-24 14:24:45 +00006 *
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
11 *
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
mjs6f821c82002-03-22 00:31:57 +000020 *
kocienda66a6d362001-08-24 14:24:45 +000021 */
22
23#include <stdio.h>
24
mjs6f821c82002-03-22 00:31:57 +000025#include "value.h"
26#include "object.h"
kocienda66a6d362001-08-24 14:24:45 +000027#include "types.h"
mjs6f821c82002-03-22 00:31:57 +000028#include "interpreter.h"
29#include "operations.h"
kocienda66a6d362001-08-24 14:24:45 +000030#include "internal.h"
31#include "regexp.h"
32#include "regexp_object.h"
mjs6f821c82002-03-22 00:31:57 +000033#include "error_object.h"
kocienda66a6d362001-08-24 14:24:45 +000034
35using namespace KJS;
36
mjs6f821c82002-03-22 00:31:57 +000037// ------------------------------ RegExpPrototypeImp ---------------------------
38
39// ECMA 15.9.4
40
41RegExpPrototypeImp::RegExpPrototypeImp(ExecState *exec,
42 ObjectPrototypeImp *objProto,
43 FunctionPrototypeImp *funcProto)
darinc758b282002-11-20 21:12:14 +000044 : ObjectImp(objProto)
kocienda66a6d362001-08-24 14:24:45 +000045{
mjs6f821c82002-03-22 00:31:57 +000046 Value protect(this);
47 setInternalValue(String(""));
48
49 // The constructor will be added later in RegExpObject's constructor (?)
50
darinc758b282002-11-20 21:12:14 +000051 static const Identifier execPropertyName("exec");
52 putDirect(execPropertyName, new RegExpProtoFuncImp(exec,funcProto,RegExpProtoFuncImp::Exec, 0), DontEnum);
53 static const Identifier testPropertyName("test");
54 putDirect(testPropertyName, new RegExpProtoFuncImp(exec,funcProto,RegExpProtoFuncImp::Test, 0), DontEnum);
55 putDirect(toStringPropertyName, new RegExpProtoFuncImp(exec,funcProto,RegExpProtoFuncImp::ToString, 0), DontEnum);
mjs6f821c82002-03-22 00:31:57 +000056}
57
58// ------------------------------ RegExpProtoFuncImp ---------------------------
59
60RegExpProtoFuncImp::RegExpProtoFuncImp(ExecState *exec,
61 FunctionPrototypeImp *funcProto, int i, int len)
62 : InternalFunctionImp(funcProto), id(i)
63{
64 Value protect(this);
darinc758b282002-11-20 21:12:14 +000065 putDirect(lengthPropertyName, len, DontDelete|ReadOnly|DontEnum);
mjs6f821c82002-03-22 00:31:57 +000066}
67
68bool RegExpProtoFuncImp::implementsCall() const
69{
70 return true;
71}
72
73Value RegExpProtoFuncImp::call(ExecState *exec, Object &thisObj, const List &args)
74{
75 if (!thisObj.inherits(&RegExpImp::info)) {
76 Object err = Error::create(exec,TypeError);
77 exec->setException(err);
78 return err;
79 }
80
81 RegExpImp *reimp = static_cast<RegExpImp*>(thisObj.imp());
82 RegExp *re = reimp->regExp();
83 String s;
84 UString str;
85 switch (id) {
86 case Exec: // 15.10.6.2
87 case Test:
88 {
89 s = args[0].toString(exec);
90 int length = s.value().size();
91 Value lastIndex = thisObj.get(exec,"lastIndex");
92 int i = lastIndex.isNull() ? 0 : lastIndex.toInt32(exec);
93 bool globalFlag = thisObj.get(exec,"global").toBoolean(exec);
94 if (!globalFlag)
95 i = 0;
96 if (i < 0 || i > length) {
97 thisObj.put(exec,"lastIndex", Number(0), DontDelete | DontEnum);
darinf028f812002-06-10 20:08:04 +000098 if (id == Test)
99 return Boolean(false);
100 else
101 Null();
mjs6f821c82002-03-22 00:31:57 +0000102 }
103 RegExpObjectImp* regExpObj = static_cast<RegExpObjectImp*>(exec->interpreter()->builtinRegExp().imp());
104 int **ovector = regExpObj->registerRegexp( re, s.value() );
105
106 str = re->match(s.value(), i, 0L, ovector);
darinf028f812002-06-10 20:08:04 +0000107 regExpObj->setSubPatterns(re->subPatterns());
mjs6f821c82002-03-22 00:31:57 +0000108
109 if (id == Test)
110 return Boolean(!str.isNull());
111
112 if (str.isNull()) // no match
113 {
114 if (globalFlag)
115 thisObj.put(exec,"lastIndex",Number(0), DontDelete | DontEnum);
116 return Null();
117 }
118 else // success
119 {
120 if (globalFlag)
121 thisObj.put(exec,"lastIndex",Number( (*ovector)[1] ), DontDelete | DontEnum);
122 return regExpObj->arrayOfMatches(exec,str);
123 }
124 }
125 break;
126 case ToString:
127 s = thisObj.get(exec,"source").toString(exec);
128 str = "/";
129 str += s.value();
130 str += "/";
131 // TODO append the flags
132 return String(str);
133 }
134
135 return Undefined();
136}
137
138// ------------------------------ RegExpImp ------------------------------------
139
140const ClassInfo RegExpImp::info = {"RegExp", 0, 0, 0};
141
142RegExpImp::RegExpImp(RegExpPrototypeImp *regexpProto)
darinc758b282002-11-20 21:12:14 +0000143 : ObjectImp(regexpProto), reg(0L)
mjs6f821c82002-03-22 00:31:57 +0000144{
145}
146
147RegExpImp::~RegExpImp()
148{
149 delete reg;
150}
151
152// ------------------------------ RegExpObjectImp ------------------------------
153
154RegExpObjectImp::RegExpObjectImp(ExecState *exec,
darin74f6ed62002-07-22 05:38:39 +0000155 FunctionPrototypeImp *funcProto,
156 RegExpPrototypeImp *regProto)
157
mjs6f821c82002-03-22 00:31:57 +0000158 : InternalFunctionImp(funcProto), lastOvector(0L), lastNrSubPatterns(0)
159{
160 Value protect(this);
kocienda66a6d362001-08-24 14:24:45 +0000161 // ECMA 15.10.5.1 RegExp.prototype
darinc758b282002-11-20 21:12:14 +0000162 putDirect(prototypePropertyName, regProto, DontEnum|DontDelete|ReadOnly);
mjs6f821c82002-03-22 00:31:57 +0000163
164 // no. of arguments for constructor
darinc758b282002-11-20 21:12:14 +0000165 putDirect(lengthPropertyName, NumberImp::two(), ReadOnly|DontDelete|DontEnum);
kocienda66a6d362001-08-24 14:24:45 +0000166}
167
mjs6f821c82002-03-22 00:31:57 +0000168RegExpObjectImp::~RegExpObjectImp()
kocienda66a6d362001-08-24 14:24:45 +0000169{
darinf028f812002-06-10 20:08:04 +0000170 delete [] lastOvector;
kocienda66a6d362001-08-24 14:24:45 +0000171}
172
mjs6f821c82002-03-22 00:31:57 +0000173int **RegExpObjectImp::registerRegexp( const RegExp* re, const UString& s )
kocienda66a6d362001-08-24 14:24:45 +0000174{
mjs6f821c82002-03-22 00:31:57 +0000175 lastString = s;
darinf028f812002-06-10 20:08:04 +0000176 delete [] lastOvector;
mjs6f821c82002-03-22 00:31:57 +0000177 lastOvector = 0;
178 lastNrSubPatterns = re->subPatterns();
179 return &lastOvector;
180}
181
darin74f6ed62002-07-22 05:38:39 +0000182Object RegExpObjectImp::arrayOfMatches(ExecState *exec, const UString &result) const
mjs6f821c82002-03-22 00:31:57 +0000183{
184 List list;
185 // The returned array contains 'result' as first item, followed by the list of matches
186 list.append(String(result));
187 if ( lastOvector )
188 for ( uint i = 1 ; i < lastNrSubPatterns + 1 ; ++i )
189 {
190 UString substring = lastString.substr( lastOvector[2*i], lastOvector[2*i+1] - lastOvector[2*i] );
191 list.append(String(substring));
192 }
darin74f6ed62002-07-22 05:38:39 +0000193 Object arr = exec->interpreter()->builtinArray().construct(exec, list);
194 arr.put(exec, "index", Number(lastOvector[0]));
195 arr.put(exec, "input", String(lastString));
196 return arr;
mjs6f821c82002-03-22 00:31:57 +0000197}
198
darin880105d2002-11-19 22:02:26 +0000199Value RegExpObjectImp::get(ExecState *exec, const Identifier &p) const
mjs6f821c82002-03-22 00:31:57 +0000200{
darind0ba3282002-11-19 23:45:44 +0000201 UString s = p.ustring();
202 if (s[0] == '$' && lastOvector)
mjs6f821c82002-03-22 00:31:57 +0000203 {
204 bool ok;
darind0ba3282002-11-19 23:45:44 +0000205 unsigned long i = s.substr(1).toULong(&ok);
mjs6f821c82002-03-22 00:31:57 +0000206 if (ok)
207 {
208 if (i < lastNrSubPatterns + 1)
209 {
210 UString substring = lastString.substr( lastOvector[2*i], lastOvector[2*i+1] - lastOvector[2*i] );
211 return String(substring);
212 }
213 return String("");
214 }
215 }
darin74f6ed62002-07-22 05:38:39 +0000216 return InternalFunctionImp::get(exec, p);
mjs6f821c82002-03-22 00:31:57 +0000217}
218
219bool RegExpObjectImp::implementsConstruct() const
220{
221 return true;
222}
223
224// ECMA 15.10.4
225Object RegExpObjectImp::construct(ExecState *exec, const List &args)
226{
darinc758b282002-11-20 21:12:14 +0000227 UString p = args.isEmpty() ? UString("") : args[0].toString(exec);
darin74f6ed62002-07-22 05:38:39 +0000228 UString flags = args[1].toString(exec);
kocienda66a6d362001-08-24 14:24:45 +0000229
mjs6f821c82002-03-22 00:31:57 +0000230 RegExpPrototypeImp *proto = static_cast<RegExpPrototypeImp*>(exec->interpreter()->builtinRegExpPrototype().imp());
231 RegExpImp *dat = new RegExpImp(proto);
kocienda66a6d362001-08-24 14:24:45 +0000232 Object obj(dat); // protect from GC
233
234 bool global = (flags.find("g") >= 0);
235 bool ignoreCase = (flags.find("i") >= 0);
236 bool multiline = (flags.find("m") >= 0);
mjs6f821c82002-03-22 00:31:57 +0000237 // TODO: throw a syntax error on invalid flags
kocienda66a6d362001-08-24 14:24:45 +0000238
darinc758b282002-11-20 21:12:14 +0000239 dat->putDirect("global", global ? BooleanImp::staticTrue : BooleanImp::staticFalse);
240 dat->putDirect("ignoreCase", ignoreCase ? BooleanImp::staticTrue : BooleanImp::staticFalse);
241 dat->putDirect("multiline", multiline ? BooleanImp::staticTrue : BooleanImp::staticFalse);
kocienda66a6d362001-08-24 14:24:45 +0000242
darinc758b282002-11-20 21:12:14 +0000243 dat->putDirect("source", new StringImp(p));
244 dat->putDirect("lastIndex", NumberImp::zero(), DontDelete | DontEnum);
kocienda66a6d362001-08-24 14:24:45 +0000245
mjs6f821c82002-03-22 00:31:57 +0000246 int reflags = RegExp::None;
247 if (global)
248 reflags |= RegExp::Global;
249 if (ignoreCase)
250 reflags |= RegExp::IgnoreCase;
251 if (multiline)
252 reflags |= RegExp::Multiline;
darinc758b282002-11-20 21:12:14 +0000253 dat->setRegExp(new RegExp(p, reflags));
kocienda66a6d362001-08-24 14:24:45 +0000254
255 return obj;
256}
257
mjs6f821c82002-03-22 00:31:57 +0000258bool RegExpObjectImp::implementsCall() const
kocienda66a6d362001-08-24 14:24:45 +0000259{
mjs6f821c82002-03-22 00:31:57 +0000260 return true;
kocienda66a6d362001-08-24 14:24:45 +0000261}
262
mjs6f821c82002-03-22 00:31:57 +0000263// ECMA 15.10.3
darin74f6ed62002-07-22 05:38:39 +0000264Value RegExpObjectImp::call(ExecState *exec, Object &/*thisObj*/,
265 const List &args)
kocienda66a6d362001-08-24 14:24:45 +0000266{
darin74f6ed62002-07-22 05:38:39 +0000267 // TODO: handle RegExp argument case (15.10.3.1)
268
269 return construct(exec, args);
kocienda66a6d362001-08-24 14:24:45 +0000270}