Optional types and null propagation

This commit is contained in:
Michael Schimmel
2025-12-21 15:20:59 +01:00
parent e7fdbc3312
commit ac96a105a5
10 changed files with 840 additions and 190 deletions
+46 -6
View File
@@ -639,10 +639,9 @@ begin
else if SameText(head.Token.Text, '?') then
begin
// (? test1 branch1 test2 branch2 ... else)
// Requires odd number of tailNodes (pairs + 1 else)
var count := Length(tailNodes);
if (count < 1) or ((count mod 2) = 0) then
Error('Syntax Error: ''?'' (cond) requires an odd number of arguments (pairs of condition/branch and one final else).');
Error('Syntax Error: ''?'' (cond) requires an odd number of arguments.');
var pairs: TArray<TCondPair>;
SetLength(pairs, count div 2);
@@ -667,7 +666,7 @@ begin
Error('Syntax Error: ''defmacro'' requires a name, a parameter list, and a body.');
var macroName := tailNodes[0].AsIdentifier;
var macroParams := elements[2].Params;
var macroParams := elements[2].Params; // Parameter list parsing happens in ParseExpression for brackets
if tailNodes[2].Kind <> akQuasiquote then
Error('Syntax Error: Expected a quasiquote as macro body.');
@@ -677,25 +676,68 @@ begin
else if SameText(head.Token.Text, 'assign') then
begin
if Length(tailNodes) <> 2 then
Error('Syntax Error: ''assign'' requires exactly 2 arguments (target and value).');
Error('Syntax Error: ''assign'' requires exactly 2 arguments.');
Result := TAst.Assign(tailNodes[0], tailNodes[1], startLoc);
end
else if SameText(head.Token.Text, 'fn') then
begin
if Length(tailNodes) <> 2 then
Error('Syntax Error: ''fn'' requires a parameter list and a body.');
// elements[1] is the parameter list node which contains the parsed Params array
Result := TAst.LambdaExpr(elements[1].Params, tailNodes[1], startLoc);
end
else if SameText(head.Token.Text, 'do') then
begin
Result := TAst.Block(tailNodes, startLoc)
end
else if SameText(head.Token.Text, 'recur') then
begin
Result := TAst.Recur(tailNodes, startLoc)
end
else if SameText(head.Token.Text, 'get') then
begin
if Length(tailNodes) <> 2 then
Error('Syntax Error: ''get'' requires exactly 2 arguments (base and index).');
Result := TAst.Indexer(tailNodes[0], tailNodes[1], startLoc)
end
// --- NEW: Support for new-series ---
else if SameText(head.Token.Text, 'new-series') then
begin
if Length(tailNodes) <> 1 then
Error('Syntax Error: ''new-series'' requires exactly 1 argument (definition string).');
// Extract definition string from constant node
if (tailNodes[0].Kind = akConstant) and (tailNodes[0].AsConstant.Value.Kind = vkText) then
Result := TAst.CreateSeries(tailNodes[0].AsConstant.Value.AsText, startLoc)
else
Error('Syntax Error: ''new-series'' argument must be a string literal.');
end
// --- NEW: Support for add-item ---
else if SameText(head.Token.Text, 'add-item') then
begin
if not (Length(tailNodes) in [2, 3]) then
Error('Syntax Error: ''add-item'' requires 2 or 3 arguments (series, value, [lookback]).');
var lookback: IAstNode := nil;
if Length(tailNodes) = 3 then
lookback := tailNodes[2];
if tailNodes[0].Kind <> akIdentifier then
Error('Syntax Error: ''add-item'' first argument must be a series identifier.');
Result := TAst.AddSeriesItem(tailNodes[0].AsIdentifier, tailNodes[1], lookback, startLoc);
end
// --- NEW: Support for count (SeriesLength) ---
else if SameText(head.Token.Text, 'count') then
begin
if Length(tailNodes) <> 1 then
Error('Syntax Error: ''count'' requires exactly 1 argument (series).');
if tailNodes[0].Kind <> akIdentifier then
Error('Syntax Error: ''count'' argument must be a series identifier.');
Result := TAst.SeriesLength(tailNodes[0].AsIdentifier, startLoc);
end
else if (Length(head.Token.Text) > 1) and (head.Token.Text.StartsWith('.')) then
begin
if Length(tailNodes) <> 1 then
@@ -1136,8 +1178,6 @@ class function TAstScript.Parse(const ASource: string): IAstNode;
var
p: TParser;
begin
if ASource.Trim.IsEmpty then
exit(nil);
p := TParser.Create(ASource);
try
Result := p.Parse;