Skip to content

Commit

Permalink
Merge pull request #120 from sempare/allow_value_separator_customisation
Browse files Browse the repository at this point in the history
Allow ValueSeparator to be overridden
  • Loading branch information
Sempare Limited authored Mar 28, 2023
2 parents ab7a7cf + 4064c49 commit a1c8f55
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 12 deletions.
23 changes: 16 additions & 7 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ Copyright (c) 2019-2023 [Sempare Limited](http://www.sempare.ltd)
- [Dynamic Template Resolution](#Dynamic_Template_Resolution)
- [Ignoring Whitespace With Multi-Line Statements](#Ignoring_Whitespace_With_Multi_Line_Statements)
- [Options](#Options)
- [Localisation of numbers](#Localisation_of_numbers)
- [Decimal Separators](#Decimal_Separators)
- [Value Separators](#Value_Separators)

# Overview

Expand Down Expand Up @@ -161,21 +162,29 @@ The template engine allows for the following options:
- eoPrettyPrint
- use to review the parsed structure. output is to the console.

### Localisation of numbers
### Decimal Separators

Numbers are commonly formatted using comma and decimal point. e.g. 123.45

However, in some regions, such as Germany, it the coma may be preferred. e.g. 123,45

In order to accomodate this, the context configuration has a ValueSeperator and DecimalSeparator. These default based on locale.
In order to accomodate this, the context configuration has a DecimalSeparator. These default based on locale.

The DecimalSeparator may be set to '.' or ','. As a comma is commonly used to separate parameters, if the DecimalSeparator is ',', then the ValueSeparator becomes ';'.
The DecimalSeparator may be set to '.' or ','.

The motivation for the behaviour is say we have:
### Value Separators

The ValueSeparator may be set to ',' or ';'. It must be explicity set.

The motivation for the behaviour is to avoid any confusion with decimal separators.
```
<% Add(1.23, 4.56) %>
<% Add(1.23 , 4.56) %>
```
When the DecimalSeparator is ',', then the ValueSeparator becomes ';' as illustrated:
```
<% Add(1,23; 4,56) %>
<% Add(1,23 ; 4,56) %>
```
However, the following does work:
```
<% Add(1,23 , 4,56) %>
```
17 changes: 12 additions & 5 deletions src/Sempare.Template.Context.pas
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ interface
procedure SetScriptEndStripToken(const Value: string);
procedure SetScriptStartStripToken(const Value: string);

procedure SetValueSeparator(const ASeparator: char);
function GetValueSeparator: char;
function GetDecimalSeparator: char;
procedure SetDecimalSeparator(const ASeparator: char);
Expand All @@ -158,7 +159,7 @@ interface
property StartStripToken: string read GetScriptStartStripToken write SetScriptStartStripToken;
property EndStripToken: string read GetScriptEndStripToken write SetScriptEndStripToken;

property ValueSeparator: char read GetValueSeparator;
property ValueSeparator: char read GetValueSeparator write SetValueSeparator;
property DecimalSeparator: char read GetDecimalSeparator write SetDecimalSeparator;
property FormatSettings: TFormatSettings read GetFormatSettings;
property DebugErrorFormat: string read GetDebugErrorFormat write SetDebugErrorFormat;
Expand Down Expand Up @@ -286,6 +287,7 @@ TTemplateContext = class(TInterfacedObject, ITemplateContext, ITemplateContext

function GetFormatSettings: TFormatSettings;

procedure SetValueSeparator(const ASeparator: char);
procedure SetDecimalSeparator(const ASeparator: char);

function GetDebugErrorFormat: string;
Expand Down Expand Up @@ -479,10 +481,6 @@ procedure TTemplateContext.SetDecimalSeparator(const ASeparator: char);
if not(FFormatSettings.DecimalSeparator in ['.', ',']) then
raise ETemplate.CreateRes(@SDecimalSeparatorMustBeACommaOrFullStop);
{$WARN WIDECHAR_REDUCED ON}
if FFormatSettings.DecimalSeparator = '.' then
FValueSeparator := ','
else
FValueSeparator := ';';
end;

procedure TTemplateContext.SetEncoding(const AEncoding: TEncoding);
Expand Down Expand Up @@ -512,6 +510,15 @@ procedure TTemplateContext.SetOptions(const AOptions: TTemplateEvaluationOptions
FOptions := AOptions;
end;

procedure TTemplateContext.SetValueSeparator(const ASeparator: char);
begin
{$WARN WIDECHAR_REDUCED OFF}
if not(ASeparator in [',', ';']) then
raise ETemplate.CreateRes(@SDecimalSeparatorMustBeACommaOrFullStop);
{$WARN WIDECHAR_REDUCED ON}
FValueSeparator := ASeparator;
end;

procedure TTemplateContext.SetVariable(const AName: string; const AValue: TValue);
begin
FLock.Enter;
Expand Down
52 changes: 52 additions & 0 deletions tests/Sempare.Template.Test.pas
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ TTestTemplate = class
[Test]
procedure TestVariableNotFound;
[Test]
procedure TestVariableNotFoundException;
[Test]
procedure TestArray;
[Test, Ignore]
// This is ignored because this is a potential future feature that is not currently supported.
Expand Down Expand Up @@ -107,8 +109,15 @@ TTestTemplate = class

[Test]
procedure TestDecimalEncodingErrorWithLists;

[Test]
procedure TestDecimalEncodingErrorWithParameters;

[Test]
procedure TestValueSeparatorSameAsDecimalSeparator;

[Test]
procedure TestDecimalEncodingErrorWithListsDefaultValueSeparator;
end;

type
Expand Down Expand Up @@ -400,13 +409,15 @@ procedure TTestTemplate.TestDecimalEncodingErrorWithLists;
begin
ctx := Template.Context;
ctx.DecimalSeparator := ',';
ctx.ValueSeparator := ';';
Assert.AreEqual('', Template.Eval(ctx, '<% a := ["a"; "b"] %>'));
Assert.WillRaise(
procedure
begin // expecting ;
Assert.AreEqual('', Template.Eval(ctx, '<% a := ["a", "b"] %>'));
end);
ctx.DecimalSeparator := '.';
ctx.ValueSeparator := ',';
Assert.AreEqual('', Template.Eval(ctx, '<% a := ["a", "b"] %>'));
Assert.WillRaise(
procedure
Expand All @@ -415,19 +426,32 @@ procedure TTestTemplate.TestDecimalEncodingErrorWithLists;
end);
end;

procedure TTestTemplate.TestDecimalEncodingErrorWithListsDefaultValueSeparator;
var
ctx: ITemplateContext;
begin
ctx := Template.Context;
ctx.DecimalSeparator := ',';
Assert.AreEqual('', Template.Eval(ctx, '<% a := ["a", "b"] %>'));
ctx.DecimalSeparator := '.';
Assert.AreEqual('', Template.Eval(ctx, '<% a := ["a", "b"] %>'));
end;

procedure TTestTemplate.TestDecimalEncodingErrorWithParameters;
var
ctx: ITemplateContext;
begin
ctx := Template.Context;
ctx.DecimalSeparator := ',';
ctx.ValueSeparator := ';';
Assert.AreEqual('a b', Template.Eval(ctx, '<% fmt("%s %s"; "a"; "b") %>'));
Assert.WillRaise(
procedure
begin // expecting ;
Assert.AreEqual('', Template.Eval(ctx, '<% fmt("%s %s", "a", "b") %>'));
end);
ctx.DecimalSeparator := '.';
ctx.ValueSeparator := ',';
Assert.AreEqual('a b', Template.Eval(ctx, '<% fmt("%s %s", "a", "b") %>'));
Assert.WillRaise(
procedure
Expand Down Expand Up @@ -509,11 +533,39 @@ procedure TTestTemplate.TestUnderscoreIn;
end;
end;

procedure TTestTemplate.TestValueSeparatorSameAsDecimalSeparator;
var
ctx: ITemplateContext;
begin
ctx := Template.Context;
ctx.DecimalSeparator := ',';
ctx.ValueSeparator := ',';
Assert.AreEqual('1,12 4,56 ', Template.Eval(ctx, '<% a := [ 1,12 , 4,56 ] %><% for i of a %><% i %> <% end %>'));
Assert.WillRaise(
procedure
begin // expects ,
Assert.AreEqual('1,12 4,56 ', Template.Eval(ctx, '<% a := [ 1,12 ; 4,56 ] %><% for i of a %><% i %> <% end %>'));
end);
end;

procedure TTestTemplate.TestVariableNotFound;
begin
Assert.AreEqual('', Template.Eval('<% abc %>'));
end;

procedure TTestTemplate.TestVariableNotFoundException;
var
LCtx: ITemplateContext;
begin
LCtx := Template.Context();
LCtx.Options := LCtx.Options + [eoRaiseErrorWhenVariableNotFound];
Assert.WillRaise(
procedure
begin // expects abc
Assert.AreEqual('', Template.Eval(LCtx, '<% abc %>'));
end);
end;

initialization

TDUnitX.RegisterTestFixture(TTestTemplate);
Expand Down
1 change: 1 addition & 0 deletions tests/Sempare.Template.TestLexer.pas
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ procedure TTestTemplateLexer.TestDouble;
begin
LContext := Template.Context();
LContext.DecimalSeparator := ',';
LContext.ValueSeparator := ';';
Assert.AreEqual('543,21', Template.Eval(LContext, '<% x:= 543,21 %><% x %>'));
Assert.AreEqual('5,1234', Template.Eval(LContext, '<% x:= 5,1234 %><% x %>'));
Assert.AreEqual('12,34 43,21 ', Template.Eval(LContext, '<% vals := [ 12,34 ; 43,21] %><% for x in vals %><% vals[x] %> <% end %>'));
Expand Down

0 comments on commit a1c8f55

Please sign in to comment.