From 36fe827b0010d4229de4411754391b9e9c4142a0 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Sun, 21 Sep 2025 11:09:55 +0200 Subject: [PATCH] Scripting refinement --- ASTPlayground/MainForm.pas | 6 ++- Src/AST/Myc.Ast.Script.pas | 94 +++++++++++++++++++++++++++--------- Src/Data/Myc.Data.Scalar.pas | 4 +- 3 files changed, 79 insertions(+), 25 deletions(-) diff --git a/ASTPlayground/MainForm.pas b/ASTPlayground/MainForm.pas index 0cc3bfa..05aa447 100644 --- a/ASTPlayground/MainForm.pas +++ b/ASTPlayground/MainForm.pas @@ -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); diff --git a/Src/AST/Myc.Ast.Script.pas b/Src/AST/Myc.Ast.Script.pas index b9cd5b4..a155bb3 100644 --- a/Src/AST/Myc.Ast.Script.pas +++ b/Src/AST/Myc.Ast.Script.pas @@ -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; 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; head: TExpr; - headIdent: IIdentifierNode; tailTokens: TArray; tailNodes: TArray; begin @@ -295,7 +301,11 @@ begin elements := TList.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 := ''; + tkNumber: Result := ''; + tkString: Result := ''; + tkEOF: Result := ''; + else + Result := ''; + end; +end; + end. diff --git a/Src/Data/Myc.Data.Scalar.pas b/Src/Data/Myc.Data.Scalar.pas index 5843d9b..90e2438 100644 --- a/Src/Data/Myc.Data.Scalar.pas +++ b/Src/Data/Myc.Data.Scalar.pas @@ -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 := '<=';