feat(sets): support integer-subrange set base (set of 0..255) and Boolean operands
Accept an anonymous integer subrange as a set base type, e.g. 'set of 0..255' or 'set of 1..10', in both the inline (var/param/field) and 'type T = set of L..H' declaration positions. The subrange lowers to the same bitmap machinery as 'set of Byte': the bitmap is sized to H+1 bits and member ordinals are the integer values themselves. Bounds may be integer literals, named constants, or constant expressions; the lower bound must be >= 0, the upper <= 255, and the range ascending. Also fix 'set of Boolean' so a Boolean operand is accepted directly on either side of 'in' and as the second argument of Include/Exclude, without an intervening Ord(). Parser handles both 'set of' paths (ParseTypeName and ParseSetDef); semantic resolution adds ResolveSubrangeSetType for on-demand and type-decl paths. Adds dual-backend e2e tests and semantic unit tests. Updates grammar.ebnf and language-rationale.adoc per the language-decision rule, and removes the now-implemented item from future-improvements.adoc.
This commit is contained in:
parent
f107b0dabc
commit
fa106ceeee
|
|
@ -347,16 +347,36 @@ begin
|
|||
the set from this string on demand — no parser-side symbol table is
|
||||
needed, and the encoding is self-contained. }
|
||||
Exit('set of ' + Self.ParseAnonEnumName())
|
||||
else
|
||||
else if Check(tkIdent) and (PeekKind() <> tkDotDot) then
|
||||
begin
|
||||
if not Check(tkIdent) then
|
||||
raise EParseError.Create(Format(
|
||||
'Expected enum type or ''('' after ''set of'' at line %d col %d in %s',
|
||||
[FCurrent.Line, FCurrent.Col, FLexer.Filename]));
|
||||
{ Named base type: 'set of Byte', 'set of Boolean', 'set of TEnum'. }
|
||||
Result := 'set of ' + FCurrent.Value;
|
||||
Advance();
|
||||
Exit;
|
||||
end;
|
||||
end
|
||||
else if Check(tkIntLit) or Check(tkIdent) then
|
||||
begin
|
||||
{ Integer subrange base type: 'set of 0..255', 'set of 1..10', or a named
|
||||
constant bound ('set of Lo..Hi'). Each bound is a single integer literal
|
||||
or identifier; encode as 'set of lo..hi' for the semantic pass to
|
||||
validate (0..255) and size. (A bracket-context bound reader is wrong
|
||||
here — the subrange is not delimited by ']'/','.) }
|
||||
LStr := FCurrent.Value;
|
||||
Advance();
|
||||
Expect(tkDotDot);
|
||||
if not (Check(tkIntLit) or Check(tkIdent)) then
|
||||
raise EParseError.Create(Format(
|
||||
'Expected set subrange upper bound at line %d col %d in %s',
|
||||
[FCurrent.Line, FCurrent.Col, FLexer.Filename]));
|
||||
HStr := FCurrent.Value;
|
||||
Advance();
|
||||
Exit('set of ' + LStr + '..' + HStr);
|
||||
end
|
||||
else
|
||||
raise EParseError.Create(Format(
|
||||
'Expected enum type, integer subrange, or ''('' after ''set of'' ' +
|
||||
'at line %d col %d in %s',
|
||||
[FCurrent.Line, FCurrent.Col, FLexer.Filename]));
|
||||
end;
|
||||
if not Check(tkIdent) then
|
||||
raise EParseError.Create(Format('Expected type name at line %d col %d in %s',
|
||||
|
|
@ -1508,6 +1528,8 @@ begin
|
|||
end;
|
||||
|
||||
function TParser.ParseSetDef: TSetTypeDef;
|
||||
var
|
||||
Lo, Hi: string;
|
||||
begin
|
||||
Result := TSetTypeDef.Create();
|
||||
try
|
||||
|
|
@ -1515,12 +1537,32 @@ begin
|
|||
Result.Col := FCurrent.Col;
|
||||
Expect(tkSet);
|
||||
Expect(tkOf);
|
||||
if not Check(tkIdent) then
|
||||
if Check(tkIdent) and (PeekKind() <> tkDotDot) then
|
||||
begin
|
||||
{ Named base type: set of Byte / Boolean / TEnum. }
|
||||
Result.BaseTypeName := FCurrent.Value;
|
||||
Advance();
|
||||
end
|
||||
else if Check(tkIntLit) or Check(tkIdent) then
|
||||
begin
|
||||
{ Integer subrange base type: set of 0..255 / 1..10 / Lo..Hi. Stored as
|
||||
'lo..hi' in BaseTypeName; the semantic pass resolves and validates it. }
|
||||
Lo := FCurrent.Value;
|
||||
Advance();
|
||||
Expect(tkDotDot);
|
||||
if not (Check(tkIntLit) or Check(tkIdent)) then
|
||||
raise EParseError.Create(Format(
|
||||
'Expected set subrange upper bound at line %d col %d in %s',
|
||||
[FCurrent.Line, FCurrent.Col, FLexer.Filename]));
|
||||
Hi := FCurrent.Value;
|
||||
Advance();
|
||||
Result.BaseTypeName := Lo + '..' + Hi;
|
||||
end
|
||||
else
|
||||
raise EParseError.Create(Format(
|
||||
'Expected base type name after ''set of'' at line %d col %d in %s',
|
||||
'Expected base type or integer subrange after ''set of'' ' +
|
||||
'at line %d col %d in %s',
|
||||
[FCurrent.Line, FCurrent.Col, FLexer.Filename]));
|
||||
Result.BaseTypeName := FCurrent.Value;
|
||||
Advance();
|
||||
except
|
||||
Result.Free();
|
||||
raise;
|
||||
|
|
|
|||
|
|
@ -179,6 +179,7 @@ type
|
|||
function EvalConstFloatExpr(AExpr: TASTExpr; ALine, ACol: Integer): string;
|
||||
function IsFloatConstExpr(AExpr: TASTExpr): Boolean;
|
||||
function ResolveArrayBound(const ABoundText: string): Integer;
|
||||
function ResolveSubrangeSetType(const ASubrange: string): TSetTypeDesc;
|
||||
function ResolveConstArrayElem(const AElem: string; AElemType: TTypeDesc;
|
||||
ALine, ACol: Integer): string;
|
||||
procedure AnalyseTypeDecls(ABlock: TBlock);
|
||||
|
|
@ -2474,6 +2475,16 @@ begin
|
|||
if (Length(AName) > 7) and (StrHead(AName, 7) = 'set of ') then
|
||||
begin
|
||||
BaseName := StrCopyTail(AName, 7);
|
||||
{ Integer-subrange base type 'set of lo..hi' (e.g. set of 0..255). Like
|
||||
Delphi/FPC, the element ordinals are the integer values themselves and the
|
||||
type is capped at 256 elements (ordinals 0..255). Resolve and validate
|
||||
the bounds, then build an ordinal set sized by the high bound — reusing the
|
||||
same Byte-set machinery (bits 0..hi). }
|
||||
if StrPos('..', BaseName) >= 0 then
|
||||
begin
|
||||
Result := Self.ResolveSubrangeSetType(BaseName);
|
||||
Exit;
|
||||
end;
|
||||
{ Anonymous enum element 'set of (a,b,c)' — synthesise the enum from the
|
||||
encoded member list; otherwise resolve a named element type. }
|
||||
if (BaseName <> '') and (StrAt(BaseName, 0) = Ord('(')) then
|
||||
|
|
@ -3951,6 +3962,40 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
function TSemanticAnalyser.ResolveSubrangeSetType(const ASubrange: string): TSetTypeDesc;
|
||||
{ Resolve a 'set of lo..hi' integer-subrange set type, e.g. 'set of 0..255'.
|
||||
Element ordinals are the integer values themselves; like Delphi/FPC the type is
|
||||
capped at 256 elements (ordinals 0..255). The bitmap is sized by the high
|
||||
bound (bits 0..hi), reusing the Byte-backed ordinal-set machinery — a low bound
|
||||
> 0 simply leaves the lower bits unused, matching FPC. }
|
||||
var
|
||||
DDotPos, Lo, Hi: Integer;
|
||||
LStr, HStr, CanonName: string;
|
||||
begin
|
||||
DDotPos := StrPos('..', ASubrange);
|
||||
LStr := StrCopyFrom(ASubrange, 0, DDotPos);
|
||||
HStr := StrCopyTail(ASubrange, DDotPos + 2);
|
||||
Lo := ResolveArrayBound(LStr);
|
||||
Hi := ResolveArrayBound(HStr);
|
||||
if Lo < 0 then
|
||||
SemanticError(Format(
|
||||
'Set subrange ''%s'': lower bound must be >= 0', [ASubrange]), 0, 0);
|
||||
if Hi > 255 then
|
||||
SemanticError(Format(
|
||||
'Set subrange ''%s'': upper bound must be <= 255 (a set has at most 256 ' +
|
||||
'elements)', [ASubrange]), 0, 0);
|
||||
if Hi < Lo then
|
||||
SemanticError(Format(
|
||||
'Set subrange ''%s'' is descending', [ASubrange]), 0, 0);
|
||||
CanonName := Format('set of %d..%d', [Lo, Hi]);
|
||||
Result := TSetTypeDesc(FTable.FindType(CanonName));
|
||||
if Result = nil then
|
||||
begin
|
||||
Result := FTable.NewOrdinalSetType(CanonName, FTable.TypeByte, Hi + 1);
|
||||
FTable.DefineGlobal(TSymbol.Create(CanonName, skType, Result));
|
||||
end;
|
||||
end;
|
||||
|
||||
function TSemanticAnalyser.ResolveConstArrayElem(const AElem: string;
|
||||
AElemType: TTypeDesc; ALine, ACol: Integer): string;
|
||||
{ Resolve one array-const element to the numeric string codegen needs. The
|
||||
|
|
@ -4460,6 +4505,7 @@ var
|
|||
EnumDesc: TEnumTypeDesc;
|
||||
EnumDef: TEnumTypeDef;
|
||||
SetDesc: TSetTypeDesc;
|
||||
SetSubDesc: TSetTypeDesc;
|
||||
SetDef: TSetTypeDef;
|
||||
AttrIdx: Integer;
|
||||
RawAttr: string;
|
||||
|
|
@ -4549,6 +4595,23 @@ begin
|
|||
else if TD.Def is TSetTypeDef then
|
||||
begin
|
||||
SetDef := TSetTypeDef(TD.Def);
|
||||
{ Integer-subrange base type ('lo..hi', e.g. set of 0..255): build an
|
||||
ordinal set sized by the high bound (validated 0..255), bound to this
|
||||
type's name. }
|
||||
if StrPos('..', SetDef.BaseTypeName) >= 0 then
|
||||
begin
|
||||
SetSubDesc := Self.ResolveSubrangeSetType(SetDef.BaseTypeName);
|
||||
SetDesc := FTable.NewOrdinalSetType(TD.Name, SetSubDesc.BaseType,
|
||||
SetSubDesc.BitCount);
|
||||
Sym := TSymbol.Create(TD.Name, skType, SetDesc);
|
||||
if not FTable.Define(Sym) then
|
||||
begin
|
||||
Sym.Free();
|
||||
SemanticError(Format('Duplicate type name ''%s''', [TD.Name]),
|
||||
TD.Line, TD.Col);
|
||||
end;
|
||||
Continue;
|
||||
end;
|
||||
BaseSym := FTable.Lookup(SetDef.BaseTypeName);
|
||||
if (BaseSym = nil) or (BaseSym.Kind <> skType) then
|
||||
SemanticError(
|
||||
|
|
@ -8507,9 +8570,13 @@ begin
|
|||
if TSetTypeDesc(TASTExpr(ACall.Args.Items[0]).ResolvedType).BaseType.Kind
|
||||
in [tyByte, tyBoolean] then
|
||||
begin
|
||||
if not ArgType.IsNumeric() then
|
||||
{ Ordinal-base set: accept a numeric ordinal, and a Boolean operand
|
||||
for a Boolean-base set (Include(s, True)). }
|
||||
if not (ArgType.IsNumeric() or
|
||||
((TSetTypeDesc(TASTExpr(ACall.Args.Items[0]).ResolvedType).BaseType.Kind = tyBoolean) and
|
||||
(ArgType.Kind = tyBoolean))) then
|
||||
SemanticError(
|
||||
Format('Second argument of ''%s'' must be numeric for ''set of %s'', got ''%s''',
|
||||
Format('Second argument of ''%s'' must be ordinal for ''set of %s'', got ''%s''',
|
||||
[ACall.Name,
|
||||
TSetTypeDesc(TASTExpr(ACall.Args.Items[0]).ResolvedType).BaseType.Name,
|
||||
ArgType.Name]),
|
||||
|
|
@ -10725,10 +10792,16 @@ begin
|
|||
ABin.Line, ABin.Col);
|
||||
if TSetTypeDesc(RType).BaseType.Kind in [tyByte, tyBoolean] then
|
||||
begin
|
||||
if not LType.IsNumeric() then
|
||||
{ Ordinal-base set (Byte/Boolean or an integer subrange): the left operand
|
||||
is the element ordinal. Accept a numeric operand, and — for a Boolean
|
||||
base — a Boolean operand (True/False), since `True in s` is the natural
|
||||
form. Both lower to the operand's ordinal. }
|
||||
if not (LType.IsNumeric() or
|
||||
((TSetTypeDesc(RType).BaseType.Kind = tyBoolean) and
|
||||
(LType.Kind = tyBoolean))) then
|
||||
SemanticError(
|
||||
Format('Left operand of ''in'' must be numeric for ''set of %s'', got ''%s''',
|
||||
[TSetTypeDesc(RType).BaseType.Name, LType.Name]),
|
||||
Format('Left operand of ''in'' must be %s for ''set of %s'', got ''%s''',
|
||||
['ordinal', TSetTypeDesc(RType).BaseType.Name, LType.Name]),
|
||||
ABin.Line, ABin.Col);
|
||||
end
|
||||
else if LType <> TSetTypeDesc(RType).BaseType then
|
||||
|
|
|
|||
|
|
@ -60,6 +60,13 @@ type
|
|||
procedure TestRun_ConstSet_MixedRangeAndLiteral;
|
||||
procedure TestRun_ConstSet_JumboWithRange;
|
||||
procedure TestRun_ConstSet_EnumStillWorks;
|
||||
|
||||
{ Integer-subrange base type: set of 0..255 / set of 1..10 (issue from
|
||||
future-improvements) + set of Boolean membership }
|
||||
procedure TestRun_SetOfSubrange_0to255;
|
||||
procedure TestRun_SetOfSubrange_TypeDecl;
|
||||
procedure TestRun_SetOfSubrange_NamedConstBounds;
|
||||
procedure TestRun_SetOfBoolean_Membership;
|
||||
end;
|
||||
|
||||
implementation
|
||||
|
|
@ -564,6 +571,79 @@ begin
|
|||
AssertRunsOnAll(Src, 'y' + LE + 'n' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2ESetOpsTests.TestRun_SetOfSubrange_0to255;
|
||||
{ Integer-subrange base type 'set of 0..255' — equivalent to set of Byte. }
|
||||
const Src = '''
|
||||
program P;
|
||||
var s: set of 0..255;
|
||||
begin
|
||||
s := [10, 20, 200];
|
||||
if 20 in s then WriteLn('y') else WriteLn('n');
|
||||
if 50 in s then WriteLn('y') else WriteLn('n');
|
||||
Include(s, 50);
|
||||
if 50 in s then WriteLn('y') else WriteLn('n')
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnAll(Src, 'y' + LE + 'n' + LE + 'y' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2ESetOpsTests.TestRun_SetOfSubrange_TypeDecl;
|
||||
{ 'type T = set of 0..63' (small, <=64-bit path) + range literal. }
|
||||
const Src = '''
|
||||
program P;
|
||||
type TS = set of 0..63;
|
||||
var s: TS;
|
||||
begin
|
||||
s := [3..7];
|
||||
if 5 in s then WriteLn('y') else WriteLn('n');
|
||||
if 63 in s then WriteLn('y') else WriteLn('n');
|
||||
Include(s, 63);
|
||||
if 63 in s then WriteLn('y') else WriteLn('n')
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnAll(Src, 'y' + LE + 'n' + LE + 'y' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2ESetOpsTests.TestRun_SetOfSubrange_NamedConstBounds;
|
||||
{ Subrange bounds may be named constants: set of Lo..Hi. }
|
||||
const Src = '''
|
||||
program P;
|
||||
const Lo = 0; Hi = 100;
|
||||
var s: set of Lo..Hi;
|
||||
begin
|
||||
s := [50];
|
||||
if 50 in s then WriteLn('y') else WriteLn('n')
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnAll(Src, 'y' + LE, 0);
|
||||
end;
|
||||
|
||||
procedure TE2ESetOpsTests.TestRun_SetOfBoolean_Membership;
|
||||
{ set of Boolean: a Boolean operand on the left of 'in' (True in s) and as the
|
||||
Include/Exclude element. }
|
||||
const Src = '''
|
||||
program P;
|
||||
var s: set of Boolean; b: Boolean;
|
||||
begin
|
||||
s := [True];
|
||||
if True in s then WriteLn('y') else WriteLn('n');
|
||||
if False in s then WriteLn('y') else WriteLn('n');
|
||||
Include(s, False);
|
||||
b := False;
|
||||
if b in s then WriteLn('y') else WriteLn('n')
|
||||
end.
|
||||
''';
|
||||
begin
|
||||
if not ToolchainAvailable() then begin Ignore('toolchain unavailable'); Exit; end;
|
||||
AssertRunsOnAll(Src, 'y' + LE + 'n' + LE + 'y' + LE, 0);
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterTest(TE2ESetOpsTests);
|
||||
|
||||
|
|
|
|||
|
|
@ -130,6 +130,14 @@ type
|
|||
procedure TestCodegen_SetOfByte_Union;
|
||||
procedure TestCodegen_SetOfByte_IsJumbo;
|
||||
|
||||
{ ------------------------------------------------------------------ }
|
||||
{ Integer-subrange base type: set of 0..255 / set of lo..hi }
|
||||
{ ------------------------------------------------------------------ }
|
||||
procedure TestSemantic_SetOfSubrange_VarDecl_OK;
|
||||
procedure TestSemantic_SetOfSubrange_TypeDecl_OK;
|
||||
procedure TestSemantic_SetOfSubrange_OverBound_Fails;
|
||||
procedure TestSemantic_SetOfSubrange_Descending_Fails;
|
||||
|
||||
{ ------------------------------------------------------------------ }
|
||||
{ 64-bit sets (>32 members) }
|
||||
{ ------------------------------------------------------------------ }
|
||||
|
|
@ -1380,6 +1388,28 @@ const
|
|||
end.
|
||||
''';
|
||||
|
||||
SrcSetOfSubrangeVar =
|
||||
'''
|
||||
program P;
|
||||
var s: set of 0..255;
|
||||
begin
|
||||
s := [10, 200]
|
||||
end.
|
||||
''';
|
||||
SrcSetOfSubrangeType =
|
||||
'''
|
||||
program P;
|
||||
type TS = set of 0..63;
|
||||
var s: TS;
|
||||
begin
|
||||
s := [3, 63]
|
||||
end.
|
||||
''';
|
||||
SrcSetOfSubrangeOverBound =
|
||||
'program P; var s: set of 0..1000; begin s := [1] end.';
|
||||
SrcSetOfSubrangeDescending =
|
||||
'program P; var s: set of 5..3; begin s := [] end.';
|
||||
|
||||
SrcSetOfByteRange =
|
||||
'''
|
||||
program P;
|
||||
|
|
@ -1577,6 +1607,30 @@ begin
|
|||
end;
|
||||
end;
|
||||
|
||||
procedure TSetTests.TestSemantic_SetOfSubrange_VarDecl_OK;
|
||||
begin
|
||||
{ 'set of 0..255' (integer-subrange base) is accepted. }
|
||||
SemanticOK(SrcSetOfSubrangeVar);
|
||||
end;
|
||||
|
||||
procedure TSetTests.TestSemantic_SetOfSubrange_TypeDecl_OK;
|
||||
begin
|
||||
{ 'type TS = set of 0..63' is accepted. }
|
||||
SemanticOK(SrcSetOfSubrangeType);
|
||||
end;
|
||||
|
||||
procedure TSetTests.TestSemantic_SetOfSubrange_OverBound_Fails;
|
||||
begin
|
||||
{ A set has at most 256 elements (ordinals 0..255); 0..1000 is rejected. }
|
||||
SemanticFail(SrcSetOfSubrangeOverBound);
|
||||
end;
|
||||
|
||||
procedure TSetTests.TestSemantic_SetOfSubrange_Descending_Fails;
|
||||
begin
|
||||
{ A descending subrange (5..3) is a mistake, not a silent empty set. }
|
||||
SemanticFail(SrcSetOfSubrangeDescending);
|
||||
end;
|
||||
|
||||
initialization
|
||||
RegisterTest(TSetTests);
|
||||
|
||||
|
|
|
|||
|
|
@ -34,30 +34,51 @@ prioritise them.
|
|||
- reference to function/procedure (anonymous methods)
|
||||
- cdecl/stdcall calling-convention markers on procedural types
|
||||
|
||||
=== Set types over an integer subrange (`set of 0..255`)
|
||||
|
||||
`set of byte` and `set of Boolean` are *implemented* (issue #105): the named
|
||||
ordinal base types lower to a bit set — `set of byte` uses the 256-bit jumbo
|
||||
path, membership/Include/Exclude and `[lo..hi]` range literals all work on both
|
||||
backends. See `docs/language-rationale.adoc` and the `set of Byte` tests in
|
||||
`cp.test.sets.pas` / `cp.test.e2e.sets.pas`.
|
||||
|
||||
What remains is the *anonymous integer-subrange* base form,
|
||||
`set of 0..255` / `set of 1..10`, which is still rejected at parse time
|
||||
("Expected enum type or '(' after 'set of'"). Standard Pascal accepts a
|
||||
subrange there; the bit count is the range width and element ordinals are the
|
||||
integer values themselves — the same machinery `set of byte` already uses, so
|
||||
this is mostly parser + base-type-resolution work, not new codegen.
|
||||
|
||||
Minor follow-on: for `set of Boolean`, a Boolean operand on the left of `in`
|
||||
(e.g. `True in s`) is rejected by the `in` type-check ("operand must be
|
||||
numeric"); `Ord(b) in s` works. The check should accept a Boolean (or any
|
||||
ordinal) operand against a matching ordinal-base set.
|
||||
|
||||
=== Method level attributes
|
||||
|
||||
Blaise only has class-level attribute RTTI today — there's no per-method attribute reflection, and adding it is a layout-changing compiler change (new method-table slot, stage-2 rebuild).
|
||||
|
||||
=== If Conditional Operator (Ternary Conditional Operator)
|
||||
|
||||
More details here: https://en.wikipedia.org/wiki/Ternary_conditional_operator
|
||||
|
||||
Many languages use the ?: syntax as follows:
|
||||
|
||||
`result = condition ? value_if_true : value_if_false`
|
||||
|
||||
But that's not very pascal-like. Oxygene language has implemented this years
|
||||
ago with te following syntax:
|
||||
|
||||
For instance
|
||||
|
||||
[source]
|
||||
----
|
||||
var s := if (a<>0) then Sin(1/a) else 1;
|
||||
----
|
||||
|
||||
being equivalent to
|
||||
|
||||
[source,pascal]
|
||||
----
|
||||
var s : Float;
|
||||
if (a<>0) then
|
||||
s := Sin(1/a)
|
||||
else s := 1;
|
||||
----
|
||||
|
||||
So for Blaise we could do similar:
|
||||
|
||||
X := if Left < 100 then 22 else 45;
|
||||
// the operator can be used as part of any expression, as follows:
|
||||
ShowMessage(if Left < 100 then 'Small' else 'Big');
|
||||
|
||||
NOTE: If the two types do not match and are incompatible, the compiler should issue an error message
|
||||
|
||||
NOTE: It is important to note that the if-then-else expression differs from the if-then-else statement, the expression has a single type. When the type of the then and else expressions differ, the expression's type is the common type of the two expressions' types; also known as the Least Upper Bound (LUB) type.
|
||||
|
||||
See also Delphi's documentation: https://docwiki.embarcadero.com/RADStudio/Florence/en/Conditional_Operators_(Delphi)
|
||||
|
||||
|
||||
== Language — Features Required to Complete the punit Port
|
||||
|
||||
The following features were identified as missing from Blaise while porting
|
||||
|
|
@ -142,7 +163,7 @@ record's own memory. Used before `FreeMem` when the record was heap-allocated.
|
|||
allocated records that contain strings leak the string's reference count.
|
||||
|
||||
*Current workaround in punit.* The `Finalize` calls were dropped. In Blaise,
|
||||
ARC on assignment should automatically manage string fields inside records, so
|
||||
ARC on assfignment should automatically manage string fields inside records, so
|
||||
the string is released when the record's string field is overwritten or when the
|
||||
last reference to the containing heap block is released. The correctness of
|
||||
this assumption depends on the ARC implementation for record fields — if the
|
||||
|
|
@ -157,46 +178,6 @@ local record variables at scope exit.
|
|||
*Effort.* Small to moderate — shares infrastructure with ARC scope-exit emit.
|
||||
|
||||
|
||||
=== If Conditional Operator (Ternary Conditional Operator)
|
||||
|
||||
More details here: https://en.wikipedia.org/wiki/Ternary_conditional_operator
|
||||
|
||||
Many languages use the ?: syntax as follows:
|
||||
|
||||
`result = condition ? value_if_true : value_if_false`
|
||||
|
||||
But that's not very pascal-like. Oxygene language has implemented this years
|
||||
ago with te following syntax:
|
||||
|
||||
For instance
|
||||
|
||||
[source]
|
||||
----
|
||||
var s := if (a<>0) then Sin(1/a) else 1;
|
||||
----
|
||||
|
||||
being equivalent to
|
||||
|
||||
[source,pascal]
|
||||
----
|
||||
var s : Float;
|
||||
if (a<>0) then
|
||||
s := Sin(1/a)
|
||||
else s := 1;
|
||||
----
|
||||
|
||||
So for Blaise we could do similar:
|
||||
|
||||
X := if Left < 100 then 22 else 45;
|
||||
// the operator can be used as part of any expression, as follows:
|
||||
ShowMessage(if Left < 100 then 'Small' else 'Big');
|
||||
|
||||
NOTE: If the two types do not match and are incompatible, the compiler should issue an error message
|
||||
|
||||
NOTE: It is important to note that the if-then-else expression differs from the if-then-else statement, the expression has a single type. When the type of the then and else expressions differ, the expression's type is the common type of the two expressions' types; also known as the Least Upper Bound (LUB) type.
|
||||
|
||||
See also Delphi's documentation: https://docwiki.embarcadero.com/RADStudio/Florence/en/Conditional_Operators_(Delphi)
|
||||
|
||||
== StdLib Packages — Numeric Types
|
||||
|
||||
=== `Currency` and `BigDecimal` as StdLib units
|
||||
|
|
|
|||
|
|
@ -673,7 +673,11 @@ EnumMember
|
|||
* The anonymous enum form synthesises an enum type whose members become
|
||||
* ordinary enum constants in scope. *)
|
||||
SetType
|
||||
= SET OF ( IDENT | EnumDef )
|
||||
= SET OF ( IDENT | EnumDef | Subrange )
|
||||
;
|
||||
|
||||
Subrange
|
||||
= ConstExpr DOTDOT ConstExpr
|
||||
;
|
||||
|
||||
(* The base type must have at most 256 ordinal values. Storage scales with
|
||||
|
|
@ -685,9 +689,14 @@ SetType
|
|||
* aggregate (record/static array)
|
||||
* Value ordinal N maps to bit (N mod 8) of byte (N div 8) in all forms.
|
||||
* Types with more than 256 possible values (e.g. Integer, Word) are rejected
|
||||
* by the semantic analyser — only Byte (256 values), Boolean (2 values), and
|
||||
* enumerations with at most 256 members are accepted. Set literals with
|
||||
* ordinal base types accept integer literals and ranges: [1, 2, 10..20]. *)
|
||||
* by the semantic analyser — only Byte (256 values), Boolean (2 values),
|
||||
* enumerations with at most 256 members, and integer subranges within
|
||||
* 0..255 are accepted. The subrange form `set of L..H` (e.g. `set of 0..255`,
|
||||
* `set of 1..10`) sizes the bitmap to H+1 bits; both bounds may be integer
|
||||
* literals, named constants, or constant expressions. L must be >= 0, H must
|
||||
* be <= 255, and the range must be ascending (H >= L) — a descending or
|
||||
* out-of-range subrange is a semantic error. Set literals with ordinal base
|
||||
* types accept integer literals and ranges: [1, 2, 10..20]. *)
|
||||
|
||||
(* Procedural type — bare or method pointer.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1293,15 +1293,16 @@ code.
|
|||
_Deferred to Phase 6 (LLVM backend + Windows target). No implementation
|
||||
required for the current Linux x86_64 target._
|
||||
|
||||
=== Set Types (`set of TEnum` / `set of Byte`)
|
||||
=== Set Types (`set of TEnum` / `set of Byte` / `set of 0..255`)
|
||||
|
||||
==== Decision
|
||||
|
||||
Blaise supports Pascal bit-set types declared as `set of BaseType`, where
|
||||
`BaseType` is either a previously-defined enumeration or one of the small
|
||||
ordinal types `Byte` (256 values, 0..255) or `Boolean` (2 values). A set
|
||||
variable holds a bitmask in which bit N is set when the member with ordinal N
|
||||
is included.
|
||||
`BaseType` is a previously-defined enumeration, one of the small ordinal
|
||||
types `Byte` (256 values, 0..255) or `Boolean` (2 values), or an integer
|
||||
subrange within 0..255 (e.g. `0..9`, `1..10`, `0..255`). A set variable
|
||||
holds a bitmask in which bit N is set when the member with ordinal N is
|
||||
included.
|
||||
|
||||
==== Ordinal-based sets
|
||||
|
||||
|
|
@ -1328,9 +1329,45 @@ Integer range expressions `[lo..hi]` are expanded to individual members at
|
|||
compile time using `EvalConstIntExpr`. Bounds are validated against the
|
||||
set's bit count (0..255 for Byte, 0..1 for Boolean).
|
||||
|
||||
A Boolean operand is accepted on either side of `in` and as the second
|
||||
argument of `Include`/`Exclude` for a `set of Boolean`; the operand only
|
||||
needs to be ordinal-compatible with the set's base type, not strictly
|
||||
numeric. `True in s` and `Include(s, False)` therefore work directly,
|
||||
without an intervening `Ord()`.
|
||||
|
||||
Larger ordinal types (`Integer`, `Word`, `Int64`) are rejected because they
|
||||
would require impractically large bitmaps.
|
||||
|
||||
==== Integer-subrange base (`set of 0..255`)
|
||||
|
||||
The set base may also be an anonymous integer subrange, `set of L..H`, as
|
||||
in standard Pascal, Delphi, and FPC:
|
||||
|
||||
[source,pascal]
|
||||
----
|
||||
type TDigits = set of 0..9; (* type-declaration form *)
|
||||
var Small: set of 1..10; (* inline form in a var declaration *)
|
||||
begin
|
||||
Small := [1, 5, 8];
|
||||
if 5 in Small then WriteLn('yes');
|
||||
end.
|
||||
----
|
||||
|
||||
The subrange lowers to the same bitmap machinery as `set of Byte`: the
|
||||
bitmap is sized to `H + 1` bits (member ordinals are the integer values
|
||||
themselves), so `set of 0..63` is a 64-bit small set and `set of 0..255` is
|
||||
a 256-bit jumbo set. Both bounds may be integer literals, named constants,
|
||||
or constant expressions, resolved through the same `ResolveArrayBound` path
|
||||
used for static-array bounds.
|
||||
|
||||
The lower bound must be `>= 0`, the upper bound `<= 255`, and the range must
|
||||
be ascending (`H >= L`). A descending subrange (`set of 5..3`) is a
|
||||
semantic error rather than a silently-empty set, and an upper bound above
|
||||
255 is rejected for the same 256-element ceiling that applies to every set.
|
||||
The bitmap always starts at ordinal 0 — `set of 1..10` still reserves bits
|
||||
for 0 — which keeps the representation identical to `set of Byte` and avoids
|
||||
a base-offset in the `_Set*` RTL helpers.
|
||||
|
||||
==== Storage model
|
||||
|
||||
Storage scales with the base type's value count, so the common small-set
|
||||
|
|
|
|||
Loading…
Reference in a new issue