This commit is contained in:
Michael Schimmel
2025-10-30 18:00:58 +01:00
parent dfe1f04333
commit 798aa08f02
12 changed files with 710 additions and 27 deletions
+102 -1
View File
@@ -24,6 +24,7 @@ uses
System.Math,
Myc.Data.Scalar,
Myc.Data.Value,
Myc.Data.Keyword,
Myc.Ast;
type
@@ -35,11 +36,14 @@ type
tkRightParen, // )
tkLeftBracket, // [
tkRightBracket, // ]
tkLeftBrace, // {
tkRightBrace, // }
tkQuote, // '
tkBacktick, // `
tkTilde, // ~
tkAt, // @
tkIdentifier,
tkKeyword,
tkNumber,
tkString,
tkEOF,
@@ -82,6 +86,7 @@ type
procedure Consume(AExpectedKind: TTokenKind);
procedure NextToken;
function ParseList: IAstNode;
function ParseRecordLiteral: IAstNode;
function ParseParameterList: TArray<IIdentifierNode>;
function ParseExpression: TExpr;
public
@@ -108,6 +113,7 @@ type
function Execute(const RootNode: IAstNode): TDataValue;
function VisitConstant(const Node: IConstantNode): TDataValue; override;
function VisitIdentifier(const Node: IIdentifierNode): TDataValue; override;
function VisitKeyword(const Node: IKeywordNode): TDataValue; override;
function VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue; override;
function VisitUnaryExpression(const Node: IUnaryExpressionNode): TDataValue; override;
function VisitIfExpression(const Node: IIfExpressionNode): TDataValue; override;
@@ -124,6 +130,7 @@ type
function VisitAssignment(const Node: IAssignmentNode): TDataValue; override;
function VisitIndexer(const Node: IIndexerNode): TDataValue; override;
function VisitMemberAccess(const Node: IMemberAccessNode): TDataValue; override;
function VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue; override;
function VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue; override;
function VisitAddSeriesItem(const Node: IAddSeriesItemNode): TDataValue; override;
function VisitSeriesLength(const Node: ISeriesLengthNode): TDataValue; override;
@@ -158,7 +165,7 @@ var
begin
startPos := FCurrentPos;
// Reader macro characters are now also delimiters for identifiers.
while (Peek <> #0) and (not (Peek.IsWhiteSpace or CharInSet(Peek, ['(', ')', '[', ']', '''', '`', '~']))) do
while (Peek <> #0) and (not (Peek.IsWhiteSpace or CharInSet(Peek, ['(', ')', '[', ']', '{', '}', '''', '`', '~']))) do
Advance;
Result := Copy(FSource, startPos, FCurrentPos - startPos);
end;
@@ -237,6 +244,16 @@ begin
Result.Kind := tkRightBracket;
Advance;
end;
'{':
begin
Result.Kind := tkLeftBrace;
Advance;
end;
'}':
begin
Result.Kind := tkRightBrace;
Advance;
end;
'''':
begin
Result.Kind := tkQuote;
@@ -262,6 +279,14 @@ begin
Result.Kind := tkString;
Result.Text := ReadString;
end;
':':
begin
Advance; // Skip :
Result.Kind := tkKeyword;
Result.Text := ReadIdentifier;
if Result.Text.IsEmpty then
Result.Kind := tkError;
end;
else
if c.IsDigit or ((c = '-') and (FCurrentPos < Length(FSource)) and FSource[FCurrentPos + 1].IsDigit) then
begin
@@ -326,6 +351,41 @@ begin
Consume(tkRightBracket);
end;
function TParser.ParseRecordLiteral: IAstNode;
var
fields: TList<TRecordFieldLiteral>;
fieldName: string;
fieldValue: IAstNode;
begin
Consume(tkLeftBrace);
fields := TList<TRecordFieldLiteral>.Create;
try
while FCurrentToken.Kind <> tkRightBrace do
begin
if FCurrentToken.Kind = tkEOF then
raise Exception.Create('Syntax Error: Unexpected end of file in record literal.');
// Key must be a keyword
if FCurrentToken.Kind <> tkKeyword then
raise Exception.Create('Syntax Error: Expected keyword (e.g., :key) as field name in record literal.');
fieldName := FCurrentToken.Text;
NextToken;
// Value can be any expression
if FCurrentToken.Kind = tkRightBrace then
raise Exception.Create('Syntax Error: Missing value for key ' + fieldName + ' in record literal.');
fieldValue := ParseExpression.Node;
fields.Add(TRecordFieldLiteral.Create(fieldName, fieldValue));
end;
Result := TAst.RecordLiteral(fields.ToArray);
finally
fields.Free;
end;
Consume(tkRightBrace);
end;
function TParser.ParseList: IAstNode;
var
@@ -504,6 +564,11 @@ begin
Result.Node := TAst.Constant(FCurrentToken.Text);
NextToken;
end;
tkKeyword:
begin
Result.Node := TAst.Keyword(FCurrentToken.Text);
NextToken;
end;
tkIdentifier:
begin
Result.Node := TAst.Identifier(FCurrentToken.Text);
@@ -511,6 +576,7 @@ begin
end;
tkLeftParen: Result.Node := ParseList;
tkLeftBracket: Result.Params := ParseParameterList;
tkLeftBrace: Result.Node := ParseRecordLiteral;
tkEOF: {nop};
else
raise Exception.CreateFmt('Syntax Error: Unexpected token %s', [FCurrentToken.Kind.ToString]);
@@ -590,6 +656,12 @@ begin
Result := TDataValue.Void;
end;
function TPrettyPrintVisitor.VisitKeyword(const Node: IKeywordNode): TDataValue;
begin
Append(':' + Node.Value.Name);
Result := TDataValue.Void;
end;
function TPrettyPrintVisitor.VisitBinaryExpression(const Node: IBinaryExpressionNode): TDataValue;
begin
Append('(' + Node.Operator.ToString);
@@ -829,6 +901,31 @@ begin
Result := TDataValue.Void;
end;
function TPrettyPrintVisitor.VisitRecordLiteral(const Node: IRecordLiteralNode): TDataValue;
var
field: TRecordFieldLiteral;
begin
if Length(Node.Fields) = 0 then
begin
Append('{}');
exit(TDataValue.Void);
end;
Append('{');
Indent;
for field in Node.Fields do
begin
NewLine;
Append(':' + field.Name);
Append(' ');
field.Value.Accept(Self);
end;
Unindent;
NewLine;
Append('}');
Result := TDataValue.Void;
end;
function TPrettyPrintVisitor.VisitCreateSeries(const Node: ICreateSeriesNode): TDataValue;
begin
Append(Format('(new-series "%s")', [Node.Definition]));
@@ -896,10 +993,14 @@ begin
tkRightParen: Result := ')';
tkLeftBracket: Result := '[';
tkRightBracket: Result := ']';
tkLeftBrace: Result := '{';
tkRightBrace: Result := '}';
tkQuote: Result := '''';
tkBacktick: Result := '`';
tkTilde: Result := '~';
tkAt: Result := '@';
tkIdentifier: Result := '<identifier>';
tkKeyword: Result := '<keyword>';
tkNumber: Result := '<number>';
tkString: Result := '<string>';
tkEOF: Result := '<EOF>';