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
47 changes: 41 additions & 6 deletions source/shared/IntlICU.pas
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ function TryICUGetLocaleCollations(const ALocale: string;
out ACollations: IntlTypes.TStringArray): Boolean;

function TryICUCompareStrings(const ALocale: string; const AStr1, AStr2: UnicodeString;
ASensitivity: TIntlCollatorSensitivity; AIgnorePunctuation: Boolean;
out AResult: Integer): Boolean;
ASensitivity: TIntlCollatorSensitivity; AIgnorePunctuation, ANumeric: Boolean;
const ACaseFirst: string; out AResult: Integer): Boolean;

function TryICUFormatNumber(const ALocale: string; AValue: Double;
const AOptions: TIntlNumberFormatOptions; out AFormatted: string): Boolean;
Expand Down Expand Up @@ -188,11 +188,16 @@ implementation
UCOL_TERTIARY = 2;
UCOL_QUATERNARY = 3;
UCOL_IDENTICAL = 15;
UCOL_STRENGTH = 2;
UCOL_STRENGTH = 5;
UCOL_CASE_FIRST = 2;
UCOL_CASE_LEVEL = 3;
UCOL_ALTERNATE_HANDLING = 1;
UCOL_NUMERIC_COLLATION = 7;
UCOL_OFF = 16;
UCOL_SHIFTED = 20;
UCOL_ON = 1;
UCOL_ON = 17;
UCOL_LOWER_FIRST = 24;
UCOL_UPPER_FIRST = 25;
UCOL_LESS = -1;
UCOL_EQUAL = 0;
UCOL_GREATER = 1;
Expand Down Expand Up @@ -1546,8 +1551,8 @@ function TryICUGetLocaleCollations(const ALocale: string;
end;

function TryICUCompareStrings(const ALocale: string; const AStr1, AStr2: UnicodeString;
ASensitivity: TIntlCollatorSensitivity; AIgnorePunctuation: Boolean;
out AResult: Integer): Boolean;
ASensitivity: TIntlCollatorSensitivity; AIgnorePunctuation, ANumeric: Boolean;
const ACaseFirst: string; out AResult: Integer): Boolean;
var
Status: TICUErrorCode;
Collator: Pointer;
Expand Down Expand Up @@ -1582,6 +1587,36 @@ function TryICUCompareStrings(const ALocale: string; const AStr1, AStr2: Unicode
if not ICUSucceeded(Status) then
Exit;

if ANumeric then
begin
Status := ICU_SUCCESS;
IntlFunctions.UcolSetAttribute(Collator, UCOL_NUMERIC_COLLATION, UCOL_ON, Status);
if not ICUSucceeded(Status) then
Exit;
end;

if ACaseFirst = 'upper' then
begin
Status := ICU_SUCCESS;
IntlFunctions.UcolSetAttribute(Collator, UCOL_CASE_FIRST, UCOL_UPPER_FIRST, Status);
if not ICUSucceeded(Status) then
Exit;
end
else if ACaseFirst = 'lower' then
begin
Status := ICU_SUCCESS;
IntlFunctions.UcolSetAttribute(Collator, UCOL_CASE_FIRST, UCOL_LOWER_FIRST, Status);
if not ICUSucceeded(Status) then
Exit;
end
else if ACaseFirst = 'false' then
begin
Status := ICU_SUCCESS;
IntlFunctions.UcolSetAttribute(Collator, UCOL_CASE_FIRST, UCOL_OFF, Status);
if not ICUSucceeded(Status) then
Exit;
end;

if ASensitivity = icsCase then
begin
Status := ICU_SUCCESS;
Expand Down
64 changes: 58 additions & 6 deletions source/units/Goccia.Builtins.Intl.pas
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ implementation
Goccia.Values.IntlSegmenter,
Goccia.Values.NativeFunction,
Goccia.Values.ObjectPropertyDescriptor,
Goccia.Values.SymbolValue;
Goccia.Values.SymbolValue,
Goccia.Values.ToObject;

{ TGocciaIntlBuiltin }

Expand Down Expand Up @@ -225,9 +226,7 @@ function TGocciaIntlBuiltin.SupportedValuesOf(const AArgs: TGocciaArgumentsColle
AddString('phonetic');
AddString('pinyin');
AddString('reformed');
AddString('search');
AddString('searchjl');
AddString('standard');
AddString('stroke');
AddString('trad');
AddString('unihan');
Expand Down Expand Up @@ -711,6 +710,59 @@ procedure TGocciaIntlBuiltin.RegisterLocale;

{ Collator }

function CollatorLocaleArgumentToLocale(const AArg: TGocciaValue): string;
var
Element: TGocciaValue;
Tag, Canonical: string;
FirstUnicodeExtension, SecondUnicodeExtension: Integer;
LowerTag, Tail: string;
begin
// TODO: CollatorLocaleArgumentToLocale duplicates LocaleCompareArgumentToLocale;
// extract both into a shared Intl locale parsing utility.
Result := '';
if (AArg is TGocciaUndefinedLiteralValue) or (AArg = nil) then
Exit;

if AArg is TGocciaStringLiteralValue then
Tag := TGocciaStringLiteralValue(AArg).Value
else if AArg is TGocciaArrayValue then
begin
if TGocciaArrayValue(AArg).GetLength = 0 then
Exit;
Element := TGocciaArrayValue(AArg).GetElement(0);
if Element is TGocciaStringLiteralValue then
Tag := TGocciaStringLiteralValue(Element).Value
else if Element is TGocciaObjectValue then
Tag := Element.ToStringLiteral.Value
else
ThrowTypeError('locales array elements must be strings or objects');
end
else if AArg is TGocciaObjectValue then
Tag := AArg.ToStringLiteral.Value
else
ThrowTypeError('locales argument must be a string, object, array, or undefined');

Canonical := CanonicalizeUnicodeLocaleId(Tag);
if Canonical = '' then
begin
LowerTag := LowerCase(Tag);
FirstUnicodeExtension := Pos('-u-', LowerTag);
if FirstUnicodeExtension <> 0 then
begin
Tail := Copy(LowerTag, FirstUnicodeExtension + 3, MaxInt);
SecondUnicodeExtension := Pos('-u-', Tail);
if SecondUnicodeExtension <> 0 then
begin
SecondUnicodeExtension := FirstUnicodeExtension + 3 + SecondUnicodeExtension - 1;
Canonical := CanonicalizeUnicodeLocaleId(Copy(Tag, 1, SecondUnicodeExtension - 1));
end;
end;
if Canonical = '' then
ThrowRangeError(Format('invalid language tag: %s', [Tag]));
end;
Result := Tag;
end;

function TGocciaIntlBuiltin.CollatorConstructorFn(const AArgs: TGocciaArgumentsCollection;
const AThisValue: TGocciaValue): TGocciaValue;
var
Expand All @@ -719,10 +771,10 @@ function TGocciaIntlBuiltin.CollatorConstructorFn(const AArgs: TGocciaArgumentsC
begin
Locale := '';
if AArgs.Length >= 1 then
Locale := AArgs.GetElement(0).ToStringLiteral.Value;
Locale := CollatorLocaleArgumentToLocale(AArgs.GetElement(0));
Options := nil;
if (AArgs.Length >= 2) and (AArgs.GetElement(1) is TGocciaObjectValue) then
Options := TGocciaObjectValue(AArgs.GetElement(1));
if (AArgs.Length >= 2) and not (AArgs.GetElement(1) is TGocciaUndefinedLiteralValue) then
Options := ToObject(AArgs.GetElement(1));
Result := TGocciaIntlCollatorValue.Create(Locale, Options);
end;

Expand Down
148 changes: 124 additions & 24 deletions source/units/Goccia.Values.ArrayValue.pas
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,15 @@ function DefaultCompare(constref A, B: TGocciaValue): Integer;
var
StrA, StrB: string;
begin
if A is TGocciaUndefinedLiteralValue then
begin
if B is TGocciaUndefinedLiteralValue then
Exit(0);
Exit(1);
end;
if B is TGocciaUndefinedLiteralValue then
Exit(-1);

StrA := A.ToStringLiteral.Value;
StrB := B.ToStringLiteral.Value;
if StrA < StrB then
Expand Down Expand Up @@ -801,6 +810,15 @@ function CallCompareFunc(const ACompareFunc: TGocciaFunctionBase; const ACallArg
PreviousContinuation: TGocciaGeneratorContinuation;
CompareRoot, AValueRoot, BValueRoot, ThisRoot: TGocciaTempRoot;
begin
if A is TGocciaUndefinedLiteralValue then
begin
if B is TGocciaUndefinedLiteralValue then
Exit(0);
Exit(1);
end;
if B is TGocciaUndefinedLiteralValue then
Exit(-1);

ACallArgs.SetElement(0, A);
ACallArgs.SetElement(1, B);
InitializeTempRoot(CompareRoot);
Expand Down Expand Up @@ -835,37 +853,119 @@ function CallCompareFunc(const ACompareFunc: TGocciaFunctionBase; const ACallArg
Result := CompResult.Value;
end;

procedure QuickSortElements(const AElements: TGocciaValueList; const ACompareFunc: TGocciaFunctionBase;
const ACallArgs: TGocciaArgumentsCollection; const AThisValue: TGocciaValue; const ALo, AHi: Integer);
procedure StableSortElements(const AElements: TGocciaValueList; const ACompareFunc: TGocciaFunctionBase;
const ACallArgs: TGocciaArgumentsCollection; const AThisValue: TGocciaValue);
var
I, J: Integer;
Pivot: TGocciaValue;
Buffer: array of TGocciaValue;

procedure SortRange(const ALo, AHi: Integer);
var
Mid, I, J, K: Integer;
begin
if AHi - ALo < 2 then
Exit;

Mid := ALo + ((AHi - ALo) div 2);
SortRange(ALo, Mid);
SortRange(Mid, AHi);

I := ALo;
J := Mid;
K := ALo;
while (I < Mid) and (J < AHi) do
begin
if CallCompareFunc(ACompareFunc, ACallArgs, AElements[I], AElements[J], AThisValue) <= 0 then
begin
Buffer[K] := AElements[I];
Inc(I);
end
else
begin
Buffer[K] := AElements[J];
Inc(J);
end;
Inc(K);
end;
while I < Mid do
begin
Buffer[K] := AElements[I];
Inc(I);
Inc(K);
end;
while J < AHi do
begin
Buffer[K] := AElements[J];
Inc(J);
Inc(K);
end;

for K := ALo to AHi - 1 do
AElements[K] := Buffer[K];
end;

begin
if ALo >= AHi then Exit;
if AElements.Count < 2 then
Exit;

Pivot := AElements[(ALo + AHi) div 2];
I := ALo;
J := AHi;
SetLength(Buffer, AElements.Count);
SortRange(0, AElements.Count);
end;

procedure StableSortElementsDefault(const AElements: TGocciaValueList);
var
Buffer: array of TGocciaValue;

while I <= J do
procedure SortRange(const ALo, AHi: Integer);
var
Mid, I, J, K: Integer;
begin
while CallCompareFunc(ACompareFunc, ACallArgs, AElements[I], Pivot, AThisValue) < 0 do
Inc(I);
while CallCompareFunc(ACompareFunc, ACallArgs, AElements[J], Pivot, AThisValue) > 0 do
Dec(J);
if AHi - ALo < 2 then
Exit;

Mid := ALo + ((AHi - ALo) div 2);
SortRange(ALo, Mid);
SortRange(Mid, AHi);

if I <= J then
I := ALo;
J := Mid;
K := ALo;
while (I < Mid) and (J < AHi) do
begin
if DefaultCompare(AElements[I], AElements[J]) <= 0 then
begin
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Buffer[K] := AElements[I];
Inc(I);
end
else
begin
Buffer[K] := AElements[J];
Inc(J);
end;
Inc(K);
end;
while I < Mid do
begin
AElements.Exchange(I, J);
Buffer[K] := AElements[I];
Inc(I);
Dec(J);
Inc(K);
end;
while J < AHi do
begin
Buffer[K] := AElements[J];
Inc(J);
Inc(K);
end;

for K := ALo to AHi - 1 do
AElements[K] := Buffer[K];
end;

if ALo < J then
QuickSortElements(AElements, ACompareFunc, ACallArgs, AThisValue, ALo, J);
if I < AHi then
QuickSortElements(AElements, ACompareFunc, ACallArgs, AThisValue, I, AHi);
begin
if AElements.Count < 2 then
Exit;

SetLength(Buffer, AElements.Count);
SortRange(0, AElements.Count);
end;

constructor TGocciaArrayValue.Create(const AClass: TGocciaClassValue = nil;
Expand Down Expand Up @@ -2757,12 +2857,12 @@ function TGocciaArrayValue.ArrayToSorted(const AArgs: TGocciaArgumentsCollection
begin
CallArgs := TGocciaArgumentsCollection.Create([nil, nil]);
try
QuickSortElements(ResultArray.Elements, TGocciaFunctionBase(CustomSortFunction), CallArgs, AThisValue, 0, ResultArray.Elements.Count - 1);
StableSortElements(ResultArray.Elements, TGocciaFunctionBase(CustomSortFunction), CallArgs, AThisValue);
finally
CallArgs.Free;
end;
end else
ResultArray.Elements.Sort(TComparer<TGocciaValue>.Construct(DefaultCompare));
StableSortElementsDefault(ResultArray.Elements);

// Step 7: Return A
Result := ResultArray;
Expand Down Expand Up @@ -3269,12 +3369,12 @@ function TGocciaArrayValue.ArraySort(const AArgs: TGocciaArgumentsCollection; co
CallArgs := TGocciaArgumentsCollection.Create([nil, nil]);
try
if TempArr.Elements.Count > 1 then
QuickSortElements(TempArr.Elements, TGocciaFunctionBase(CustomSortFunction), CallArgs, AThisValue, 0, TempArr.Elements.Count - 1);
StableSortElements(TempArr.Elements, TGocciaFunctionBase(CustomSortFunction), CallArgs, AThisValue);
finally
CallArgs.Free;
end;
end else if TempArr.Elements.Count > 1 then
TempArr.Elements.Sort(TComparer<TGocciaValue>.Construct(DefaultCompare));
StableSortElementsDefault(TempArr.Elements);

// Write sorted elements back to front indices
for I := 0 to TempArr.Elements.Count - 1 do
Expand Down
Loading
Loading