Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 43 additions & 29 deletions source/units/Goccia.Builtins.GlobalRegExp.pas
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ constructor TGocciaGlobalRegExp.Create(const AName: string;
function RequireRegExpThis(const AThisValue: TGocciaValue;
const AMethodName: string): TGocciaObjectValue;
begin
if not IsRegExpValue(AThisValue) then
if not IsRegExpInstance(AThisValue) then
ThrowTypeError(AMethodName + ' requires a RegExp object');
Result := TGocciaObjectValue(AThisValue);
end;
Expand Down Expand Up @@ -685,9 +685,9 @@ function TGocciaGlobalRegExp.RegExpConstructorFn(
const AArgs: TGocciaArgumentsCollection;
const AThisValue: TGocciaValue): TGocciaValue;
var
PatternArg: TGocciaValue;
PatternArg, PropVal: TGocciaValue;
Pattern, Flags: string;
IsConstructCall: Boolean;
IsConstructCall, PatternIsRegExp: Boolean;
begin
Pattern := '';
Flags := '';
Expand All @@ -696,21 +696,31 @@ function TGocciaGlobalRegExp.RegExpConstructorFn(
if AArgs.Length > 0 then
begin
PatternArg := AArgs.GetElement(0);
if IsRegExpValue(PatternArg) then
PatternIsRegExp := IsRegExp(PatternArg);

// §22.2.3.1 step 2b: non-construct, regexp-like, no flags, same constructor
if PatternIsRegExp and not IsConstructCall and
((AArgs.Length <= 1) or
(AArgs.GetElement(1) is TGocciaUndefinedLiteralValue)) and
(TGocciaObjectValue(PatternArg).GetProperty(PROP_CONSTRUCTOR) =
FRegExpConstructor) then
Exit(PatternArg);

// §22.2.3.1 steps 3–4: read source/flags when regexp-like
if PatternIsRegExp then
begin
if not IsConstructCall and
((AArgs.Length <= 1) or
(AArgs.GetElement(1) is TGocciaUndefinedLiteralValue)) then
Exit(PatternArg);

Pattern := TGocciaObjectValue(PatternArg).GetProperty(PROP_SOURCE)
.ToStringLiteral.Value;
PropVal := TGocciaObjectValue(PatternArg).GetProperty(PROP_SOURCE);
if not (PropVal is TGocciaUndefinedLiteralValue) then
Pattern := PropVal.ToStringLiteral.Value;
if (AArgs.Length > 1) and
not (AArgs.GetElement(1) is TGocciaUndefinedLiteralValue) then
Flags := AArgs.GetElement(1).ToStringLiteral.Value
else
Flags := TGocciaObjectValue(PatternArg).GetProperty(PROP_FLAGS)
.ToStringLiteral.Value;
begin
PropVal := TGocciaObjectValue(PatternArg).GetProperty(PROP_FLAGS);
if not (PropVal is TGocciaUndefinedLiteralValue) then
Flags := PropVal.ToStringLiteral.Value;
end;
end
else
begin
Expand All @@ -729,7 +739,7 @@ function TGocciaGlobalRegExp.RegExpConstruct(
const ANewTarget: TGocciaValue): TGocciaValue;
var
Proto: TGocciaObjectValue;
PatternArg: TGocciaValue;
PatternArg, PropVal: TGocciaValue;
Pattern, Flags: string;
PatternIsRegExp, FlagsProvided: Boolean;
begin
Expand All @@ -739,18 +749,22 @@ function TGocciaGlobalRegExp.RegExpConstruct(
FlagsProvided := (AArgs.Length > 1) and
not (AArgs.GetElement(1) is TGocciaUndefinedLiteralValue);

// §22.2.4.1 step 3: if pattern is a RegExp, capture source before step 6
// §22.2.4.1 steps 1, 3–4: IsRegExp check, then read source/flags if regexp-like
if AArgs.Length > 0 then
begin
PatternArg := AArgs.GetElement(0);
if IsRegExpValue(PatternArg) then
PatternIsRegExp := IsRegExp(PatternArg);
if PatternIsRegExp then
begin
PatternIsRegExp := True;
Pattern := TGocciaObjectValue(PatternArg).GetProperty(PROP_SOURCE)
.ToStringLiteral.Value;
PropVal := TGocciaObjectValue(PatternArg).GetProperty(PROP_SOURCE);
if not (PropVal is TGocciaUndefinedLiteralValue) then
Pattern := PropVal.ToStringLiteral.Value;
if not FlagsProvided then
Flags := TGocciaObjectValue(PatternArg).GetProperty(PROP_FLAGS)
.ToStringLiteral.Value;
begin
PropVal := TGocciaObjectValue(PatternArg).GetProperty(PROP_FLAGS);
if not (PropVal is TGocciaUndefinedLiteralValue) then
Flags := PropVal.ToStringLiteral.Value;
end;
end;
end;

Expand All @@ -774,7 +788,7 @@ function TGocciaGlobalRegExp.RegExpExec(const AArgs: TGocciaArgumentsCollection;
Input: string;
MatchValue: TGocciaValue;
begin
if not IsRegExpValue(AThisValue) then
if not IsRegExpInstance(AThisValue) then
ThrowTypeError(SErrorRegExpExecNonRegExp, SSuggestRegExpThisType);

if AArgs.Length > 0 then
Expand All @@ -795,7 +809,7 @@ function TGocciaGlobalRegExp.RegExpTest(const AArgs: TGocciaArgumentsCollection;
Input: string;
MatchValue: TGocciaValue;
begin
if not IsRegExpValue(AThisValue) then
if not IsRegExpInstance(AThisValue) then
ThrowTypeError(SErrorRegExpTestNonRegExp, SSuggestRegExpThisType);

if AArgs.Length > 0 then
Expand All @@ -812,7 +826,7 @@ function TGocciaGlobalRegExp.RegExpToStringMethod(
const AArgs: TGocciaArgumentsCollection;
const AThisValue: TGocciaValue): TGocciaValue;
begin
if not IsRegExpValue(AThisValue) then
if not IsRegExpInstance(AThisValue) then
ThrowTypeError(SErrorRegExpToStringNonRegExp, SSuggestRegExpThisType);

Result := TGocciaStringLiteralValue.Create(RegExpObjectToString(AThisValue));
Expand All @@ -829,7 +843,7 @@ function TGocciaGlobalRegExp.RegExpSymbolMatch(
ResultArray: TGocciaArrayValue;
MatchIndex, MatchEnd, NextIndex: Integer;
begin
if not IsRegExpValue(AThisValue) then
if not IsRegExpInstance(AThisValue) then
ThrowTypeError(SErrorRegExpMatchNonRegExp, SSuggestRegExpThisType);

if AArgs.Length > 0 then
Expand Down Expand Up @@ -869,7 +883,7 @@ function TGocciaGlobalRegExp.RegExpSymbolMatchAll(
RegexClone: TGocciaObjectValue;
IsGlobal: Boolean;
begin
if not IsRegExpValue(AThisValue) then
if not IsRegExpInstance(AThisValue) then
ThrowTypeError(SErrorRegExpMatchAllNonRegExp, SSuggestRegExpThisType);

if AArgs.Length > 0 then
Expand All @@ -896,7 +910,7 @@ function TGocciaGlobalRegExp.RegExpSymbolReplace(
MatchArray: TGocciaObjectValue;
MatchIndex, MatchEnd, NextIndex, SearchIndex, OutputIndex: Integer;
begin
if not IsRegExpValue(AThisValue) then
if not IsRegExpInstance(AThisValue) then
ThrowTypeError(SErrorRegExpReplaceNonRegExp, SSuggestRegExpThisType);

if AArgs.Length > 0 then
Expand Down Expand Up @@ -961,7 +975,7 @@ function TGocciaGlobalRegExp.RegExpSymbolSearch(
MatchArray: TGocciaObjectValue;
MatchIndex, MatchEnd, NextIndex: Integer;
begin
if not IsRegExpValue(AThisValue) then
if not IsRegExpInstance(AThisValue) then
ThrowTypeError(SErrorRegExpSearchNonRegExp, SSuggestRegExpThisType);

if AArgs.Length > 0 then
Expand Down Expand Up @@ -992,7 +1006,7 @@ function TGocciaGlobalRegExp.RegExpSymbolSplit(
LastMatchWasZeroWidth: Boolean;
I: Integer;
begin
if not IsRegExpValue(AThisValue) then
if not IsRegExpInstance(AThisValue) then
ThrowTypeError(SErrorRegExpSplitNonRegExp, SSuggestRegExpThisType);

if AArgs.Length > 0 then
Expand Down
4 changes: 2 additions & 2 deletions source/units/Goccia.Builtins.TestingLibrary.pas
Original file line number Diff line number Diff line change
Expand Up @@ -902,7 +902,7 @@ function TGocciaExpectationValue.ToMatch(const AArgs: TGocciaArgumentsCollection
end;

Expected := AArgs.GetElement(0);
if not (Expected is TGocciaStringLiteralValue) and not IsRegExpValue(Expected) then
if not (Expected is TGocciaStringLiteralValue) and not IsRegExpInstance(Expected) then
begin
if FIsNegated then
TGocciaTestAssertions(FTestAssertions).AssertionPassed('toMatch')
Expand All @@ -915,7 +915,7 @@ function TGocciaExpectationValue.ToMatch(const AArgs: TGocciaArgumentsCollection
end;

ActualString := FormatForDisplay(FActualValue);
if IsRegExpValue(Expected) then
if IsRegExpInstance(Expected) then
begin
ExpectedDescription := RegExpObjectToString(Expected);
Matches := MatchRegExpObject(Expected, ActualString, 0, False, False,
Expand Down
1 change: 1 addition & 0 deletions source/units/Goccia.Error.Messages.pas
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,7 @@ interface
SErrorMatchAllRequiresGlobalRegExp = 'String.prototype.matchAll requires a global RegExp';
SErrorSymbolMatchAllNotCallable = '@@matchAll is not callable';
SErrorSymbolSearchNotCallable = '@@search is not callable';
SErrorFirstArgMustNotBeRegExp = 'First argument to %s must not be a regular expression';
SErrorInvalidRepeatCount = 'Invalid count value: %s';
SErrorInvalidNormalizationForm = 'The normalization form should be one of NFC, NFD, NFKC, NFKD';
SErrorStringPrototypeRequiresNonNullish = 'String.prototype method requires that ''this'' not be null or undefined';
Expand Down
1 change: 1 addition & 0 deletions source/units/Goccia.Error.Suggestions.pas
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,7 @@ interface
// Runtime errors — string
SSuggestWellKnownSymbolCallable = 'the well-known Symbol method must be a function on the object';
SSuggestReplaceAllGlobalFlag = 'add the ''g'' flag to the RegExp: /pattern/g';
SSuggestUseMatchOrSearch = 'use String.prototype.match or String.prototype.search instead';
SSuggestRepeatCountRange = 'the count must be a non-negative finite integer';
SSuggestNormalizationForm = 'valid forms are NFC, NFD, NFKC, or NFKD';

Expand Down
2 changes: 1 addition & 1 deletion source/units/Goccia.REPL.Formatter.pas
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ function FormatREPLValue(const AValue: TGocciaValue;
end;

// RegExp — JSON.stringify would show empty object
if IsRegExpValue(AValue) then
if IsRegExpInstance(AValue) then
Exit(Colorize(RegExpObjectToString(AValue), ANSI_RED, AUseColor));

// Maps — JSON.stringify can't see internal entries
Expand Down
22 changes: 18 additions & 4 deletions source/units/Goccia.RegExp.Runtime.pas
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ interface
function GetRegExpPrototype: TGocciaValue;
procedure SetRegExpPrototype(const APrototype: TGocciaValue);

function IsRegExpValue(const AValue: TGocciaValue): Boolean;
function IsRegExpInstance(const AValue: TGocciaValue): Boolean;
function IsRegExp(const AValue: TGocciaValue): Boolean;
function CreateRegExpObject(const APattern, AFlags: string): TGocciaValue;
function CloneRegExpObject(const AValue: TGocciaValue): TGocciaValue;
function MatchRegExpObjectOnce(const AValue: TGocciaValue; const AInput: string;
Expand Down Expand Up @@ -117,12 +118,24 @@ function BuildMatchArray(const AInput: string;
Result := MatchArray;
end;

function IsRegExpValue(const AValue: TGocciaValue): Boolean;
function IsRegExpInstance(const AValue: TGocciaValue): Boolean;
begin
if not (AValue is TGocciaObjectValue) then
Exit(False);
Result := TGocciaObjectValue(AValue).HasOwnProperty(PROP_SOURCE) and
TGocciaObjectValue(AValue).HasOwnProperty(PROP_FLAGS);
Result := TGocciaObjectValue(AValue).HasRegExpData;
end;

function IsRegExp(const AValue: TGocciaValue): Boolean;
var
Matcher: TGocciaValue;
begin
if not (AValue is TGocciaObjectValue) then
Exit(False);
Matcher := TGocciaObjectValue(AValue).GetSymbolProperty(
TGocciaSymbolValue.WellKnownMatch);
if not (Matcher is TGocciaUndefinedLiteralValue) then
Exit(Matcher.ToBooleanLiteral.Value);
Result := IsRegExpInstance(AValue);
end;

function CreateRegExpObject(const APattern, AFlags: string): TGocciaValue;
Expand All @@ -141,6 +154,7 @@ function CreateRegExpObject(const APattern, AFlags: string): TGocciaValue;
Source := NormalizeRegExpSource(APattern);
CanonicalFlags := CanonicalizeRegExpFlags(AFlags);
Obj := TGocciaObjectValue.Create(GRegExpPrototype);
Obj.HasRegExpData := True;
Obj.DefineProperty(PROP_SOURCE,
TGocciaPropertyDescriptorData.Create(
TGocciaStringLiteralValue.Create(Source), []));
Expand Down
2 changes: 2 additions & 0 deletions source/units/Goccia.Values.ObjectValue.pas
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ TGocciaObjectValue = class(TGocciaValue)
FSealed: Boolean;
FExtensible: Boolean;
FHasErrorData: Boolean;
FHasRegExpData: Boolean;
public
class procedure InitializeSharedPrototype;
class function GetSharedObjectPrototype: TGocciaObjectValue; static;
Expand Down Expand Up @@ -96,6 +97,7 @@ TGocciaObjectValue = class(TGocciaValue)
property Sealed: Boolean read FSealed;
property Extensible: Boolean read FExtensible;
property HasErrorData: Boolean read FHasErrorData write FHasErrorData;
property HasRegExpData: Boolean read FHasRegExpData write FHasRegExpData;
published
function ObjectPrototypeToString(const AArgs: TGocciaArgumentsCollection; const AThisValue: TGocciaValue): TGocciaValue;
function ObjectPrototypeIsPrototypeOf(const AArgs: TGocciaArgumentsCollection; const AThisValue: TGocciaValue): TGocciaValue;
Expand Down
Loading
Loading