Scripting refinement

This commit is contained in:
Michael Schimmel
2025-09-21 11:09:55 +02:00
parent 00f5861148
commit 36fe827b00
3 changed files with 79 additions and 25 deletions
+5 -1
View File
@@ -968,8 +968,12 @@ begin
if FScriptUpdate then
exit;
Memo1.Lines.Clear;
try
FLastAst := TAstScript.Parse(ScriptMemo.Lines.Text)
FLastAst := TAstScript.Parse(ScriptMemo.Lines.Text);
FWorkspace.DeleteChildren;
ShowVizualization(14, 14);
except
on E: Exception do
Memo1.Lines.Add(E.Message);
+72 -22
View File
@@ -41,6 +41,10 @@ type
tkError
);
TTokenKindHelper = record helper for TTokenKind
function ToString: String;
end;
TToken = record
Kind: TTokenKind;
Text: string;
@@ -63,6 +67,7 @@ type
TExpr = record
Token: TToken;
Node: IAstNode;
Params: TArray<IIdentifierNode>;
end;
TParser = class
@@ -246,7 +251,7 @@ end;
procedure TParser.Consume(AExpectedKind: TTokenKind);
begin
if FCurrentToken.Kind <> AExpectedKind then
raise Exception.CreateFmt('Syntax Error: Expected token %d, but found %d', [Ord(AExpectedKind), Ord(FCurrentToken.Kind)]);
raise Exception.CreateFmt('Syntax Error: Expected token %s, but found %s', [AExpectedKind.ToString, FCurrentToken.Kind.ToString]);
NextToken;
end;
@@ -259,6 +264,8 @@ begin
try
while FCurrentToken.Kind <> tkRightBracket do
begin
if FCurrentToken.Kind = tkEOF then
raise Exception.Create('Syntax Error: Unexpected end of file.');
if FCurrentToken.Kind <> tkIdentifier then
raise Exception.Create('Syntax Error: Expected identifier in parameter list.');
params.Add(TAst.Identifier(FCurrentToken.Text));
@@ -284,7 +291,6 @@ function TParser.ParseList: IAstNode;
var
elements: TList<TExpr>;
head: TExpr;
headIdent: IIdentifierNode;
tailTokens: TArray<TToken>;
tailNodes: TArray<IAstNode>;
begin
@@ -295,7 +301,11 @@ begin
elements := TList<TExpr>.Create;
try
while FCurrentToken.Kind <> tkRightParen do
begin
if FCurrentToken.Kind = tkEOF then
raise Exception.Create('Syntax Error: Unexpected end of file.');
elements.Add(ParseExpression);
end;
head := elements[0];
@@ -311,37 +321,59 @@ begin
if head.Token.Kind = tkIdentifier then
begin
// Handle special forms
if SameText(headIdent.Name, 'if') then
if SameText(head.Token.Text, 'if') then
Result := TAst.IfExpr(tailNodes[0], tailNodes[1], IfThen(Length(tailNodes) > 2, tailNodes[2], nil))
else if SameText(headIdent.Name, 'def') then
else if SameText(head.Token.Text, 'def') then
begin
var identNode: IIdentifierNode;
if tailTokens[0].Kind <> tkIdentifier then
raise Exception.Create('Syntax Error: Expected an identifier for def statement.');
Result := TAst.VarDecl(identNode, IfThen(Length(tailNodes) > 1, tailNodes[1], nil));
Result := TAst.VarDecl(IIdentifierNode(tailNodes[0]), IfThen(Length(tailNodes) > 1, tailNodes[1], nil));
end
else if SameText(headIdent.Name, 'assign') then
else if SameText(head.Token.Text, 'assign') then
begin
var identNode: IIdentifierNode;
if tailTokens[0].Kind <> tkIdentifier then
raise Exception.Create('Syntax Error: Expected an identifier for assignment.');
Result := TAst.Assign(identNode, tailNodes[1]);
Result := TAst.Assign(IIdentifierNode(tailNodes[0]), tailNodes[1]);
end
else if SameText(headIdent.Name, 'fn') then
Result := TAst.LambdaExpr(ParseParameterList, tailNodes[0]) // Special parsing for params
else if SameText(headIdent.Name, 'do') then
else if SameText(head.Token.Text, 'fn') then
Result := TAst.LambdaExpr(elements[High(tailNodes)].Params, tailNodes[1])
else if SameText(head.Token.Text, 'do') then
Result := TAst.Block(tailNodes)
else if SameText(headIdent.Name, 'recur') then
else if SameText(head.Token.Text, 'recur') then
Result := TAst.Recur(tailNodes)
else if SameText(headIdent.Name, 'get') then
else if SameText(head.Token.Text, 'get') then
Result := TAst.Indexer(tailNodes[0], tailNodes[1])
else if (Length(headIdent.Name) > 1) and (headIdent.Name.StartsWith('.')) then
Result := TAst.MemberAccess(tailNodes[0], TAst.Identifier(headIdent.Name.Substring(1)))
else if (Length(head.Token.Text) > 1) and (head.Token.Text.StartsWith('.')) then
Result := TAst.MemberAccess(tailNodes[0], TAst.Identifier(head.Token.Text.Substring(1)))
else
Result := TAst.FunctionCall(head.Node, tailNodes); // Default is a function call
end
else
Result := TAst.FunctionCall(head.Node, tailNodes); // Callee is a complex expression, e.g. ((fn [x] x) 1)
begin
if Length(tailNodes) = 1 then
begin
for var op := Low(TScalar.TUnaryOp) to High(TScalar.TUnaryOp) do
begin
if head.Token.Text = op.ToString then
begin
Result := TAst.UnaryExpr(op, tailNodes[0]);
break
end;
end;
end
else if Length(tailNodes) = 2 then
begin
for var op := Low(TScalar.TBinaryOp) to High(TScalar.TBinaryOp) do
begin
if head.Token.Text = op.ToString then
begin
Result := TAst.BinaryExpr(tailNodes[0], op, tailNodes[1]);
break
end;
end;
end;
end;
end;
if Result = nil then
Result := TAst.FunctionCall(head.Node, tailNodes);
finally
elements.Free;
@@ -378,15 +410,17 @@ begin
NextToken;
end;
tkLeftParen: Result.Node := ParseList;
tkLeftBracket: Result.Params := ParseParameterList;
tkEOF: {nop};
else
raise Exception.CreateFmt('Syntax Error: Unexpected token %d', [Ord(FCurrentToken.Kind)]);
raise Exception.CreateFmt('Syntax Error: Unexpected token %s', [FCurrentToken.Kind.ToString]);
end;
end;
function TParser.Parse: IAstNode;
begin
var expr := ParseExpression;
if expr.Token.Kind <> tkEOF then
if FCurrentToken.Kind <> tkEOF then
raise Exception.Create('Syntax Error: Unexpected characters after end of expression.');
Result := expr.Node;
end;
@@ -696,4 +730,20 @@ begin
end;
end;
function TTokenKindHelper.ToString: String;
begin
case Self of
tkLeftParen: Result := '(';
tkRightParen: Result := ')';
tkLeftBracket: Result := '[';
tkRightBracket: Result := ']';
tkIdentifier: Result := '<identifier>';
tkNumber: Result := '<number>';
tkString: Result := '<string>';
tkEOF: Result := '<EOF>';
else
Result := '<error>';
end;
end;
end.
+2 -2
View File
@@ -658,8 +658,8 @@ begin
TScalar.TBinaryOp.Subtract: Result := '-';
TScalar.TBinaryOp.Multiply: Result := '*';
TScalar.TBinaryOp.Divide: Result := '/';
TScalar.TBinaryOp.Equal: Result := '==';
TScalar.TBinaryOp.NotEqual: Result := '!=';
TScalar.TBinaryOp.Equal: Result := '=';
TScalar.TBinaryOp.NotEqual: Result := '<>';
TScalar.TBinaryOp.Less: Result := '<';
TScalar.TBinaryOp.Greater: Result := '>';
TScalar.TBinaryOp.LessOrEqual: Result := '<=';