added macro quoting nodes

This commit is contained in:
Michael Schimmel
2025-10-03 14:17:34 +02:00
parent fd97799b7b
commit bb0ecda6be
4 changed files with 273 additions and 19 deletions
+93 -16
View File
@@ -34,6 +34,9 @@ type
tkRightParen, // )
tkLeftBracket, // [
tkRightBracket, // ]
tkQuote, // '
tkBacktick, // `
tkTilde, // ~
tkIdentifier,
tkNumber,
tkString,
@@ -109,6 +112,9 @@ type
function VisitTernaryExpression(const Node: ITernaryExpressionNode): TDataValue;
function VisitLambdaExpression(const Node: ILambdaExpressionNode): TDataValue;
function VisitMacroDefinition(const Node: IMacroDefinitionNode): TDataValue;
function VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
function VisitUnquote(const Node: IUnquoteNode): TDataValue;
function VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
function VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
function VisitBlockExpression(const Node: IBlockExpressionNode): TDataValue;
function VisitVariableDeclaration(const Node: IVariableDeclarationNode): TDataValue;
@@ -148,8 +154,8 @@ var
startPos: Integer;
begin
startPos := FCurrentPos;
// Corrected W1050: Use CharInSet for robust unicode support.
while (Peek <> #0) and (not (Peek.IsWhiteSpace or CharInSet(Peek, ['(', ')', '[', ']']))) do
// Reader macro characters are now also delimiters for identifiers.
while (Peek <> #0) and (not (Peek.IsWhiteSpace or CharInSet(Peek, ['(', ')', '[', ']', '''', '`', '~']))) do
Advance;
Result := Copy(FSource, startPos, FCurrentPos - startPos);
end;
@@ -228,6 +234,21 @@ begin
Result.Kind := tkRightBracket;
Advance;
end;
'''':
begin
Result.Kind := tkQuote;
Advance;
end;
'`':
begin
Result.Kind := tkBacktick;
Advance;
end;
'~':
begin
Result.Kind := tkTilde;
Advance;
end;
'"':
begin
Result.Kind := tkString;
@@ -382,7 +403,6 @@ begin
end
else if SameText(head.Token.Text, 'fn') then
begin
// Corrected handling for lambda expressions
if Length(tailNodes) <> 2 then
raise Exception.Create('Syntax Error: ''fn'' requires a parameter list and a body.');
if elements[1].Node <> nil then
@@ -438,19 +458,43 @@ function TParser.ParseExpression: TExpr;
var
i64: Int64;
dbl: Double;
expr: TExpr;
begin
// TODO: Implement reader macros for quasiquoting here.
// The current lexer will tokenize `, ~, and ~@ as identifiers.
// Check for them here and wrap the subsequent expression accordingly.
// Example:
// if FCurrentToken.Text = '`' then
// begin
// NextToken;
// var exprToQuote := ParseExpression;
// Result.Node := TAst.Quasiquote(exprToQuote.Node);
// exit;
// end;
// (This requires IQuasiquoteNode and TAst.Quasiquote to be defined first).
// Handle reader macros.
case FCurrentToken.Kind of
tkBacktick:
begin
NextToken;
expr := ParseExpression;
Result.Node := TAst.Quasiquote(expr.Node);
exit;
end;
tkTilde:
begin
NextToken;
// Check for unquote-splicing ('~@')
if (FCurrentToken.Kind = tkIdentifier) and (FCurrentToken.Text = '@') then
begin
NextToken;
expr := ParseExpression;
Result.Node := TAst.UnquoteSplicing(expr.Node);
end
else // It's a regular unquote ('~')
begin
expr := ParseExpression;
Result.Node := TAst.Unquote(expr.Node);
end;
exit;
end;
tkQuote:
begin
NextToken;
expr := ParseExpression;
// A regular quote '(...) can be desugared into (quote (...))
Result.Node := TAst.FunctionCall(TAst.Identifier('quote'), [expr.Node]);
exit;
end;
end;
Result.Token := FCurrentToken;
case FCurrentToken.Kind of
@@ -639,7 +683,6 @@ var
param: IIdentifierNode;
sb: TStringBuilder;
begin
// Added visitor for pretty printing macro definitions.
sb := TStringBuilder.Create;
try
for param in Node.Parameters do
@@ -661,10 +704,41 @@ begin
Result := TDataValue.Void;
end;
function TPrettyPrintVisitor.VisitQuasiquote(const Node: IQuasiquoteNode): TDataValue;
begin
Append('`');
Node.Expression.Accept(Self);
Result := TDataValue.Void;
end;
function TPrettyPrintVisitor.VisitUnquote(const Node: IUnquoteNode): TDataValue;
begin
Append('~');
Node.Expression.Accept(Self);
Result := TDataValue.Void;
end;
function TPrettyPrintVisitor.VisitUnquoteSplicing(const Node: IUnquoteSplicingNode): TDataValue;
begin
// To handle '~@', we need to check if the expression is an identifier '@'.
// However, the parser logic now handles this, so we just print '~@'.
Append('~@');
Node.Expression.Accept(Self);
Result := TDataValue.Void;
end;
function TPrettyPrintVisitor.VisitFunctionCall(const Node: IFunctionCallNode): TDataValue;
var
arg: IAstNode;
begin
// Special case for (quote ...) to print it as '...
if (Node.Callee is TIdentifierNode) and (TIdentifierNode(Node.Callee).Name = 'quote') and (Length(Node.Arguments) = 1) then
begin
Append('''');
Node.Arguments[0].Accept(Self);
exit(TDataValue.Void);
end;
Append('(');
Node.Callee.Accept(Self);
@@ -828,6 +902,9 @@ begin
tkRightParen: Result := ')';
tkLeftBracket: Result := '[';
tkRightBracket: Result := ']';
tkQuote: Result := '''';
tkBacktick: Result := '`';
tkTilde: Result := '~';
tkIdentifier: Result := '<identifier>';
tkNumber: Result := '<number>';
tkString: Result := '<string>';