Skip to content

Commit

Permalink
Add support for null
Browse files Browse the repository at this point in the history
  • Loading branch information
kthompson committed Jul 12, 2023
1 parent efc89fc commit c1b0b38
Show file tree
Hide file tree
Showing 20 changed files with 155 additions and 4 deletions.
6 changes: 6 additions & 0 deletions src/Panther.SyntaxGen/Syntax.xml
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,12 @@
<Field Name="ThisToken" Type="SyntaxToken" />
</Node>

<Node Name="NullExpressionSyntax" Base="ExpressionSyntax">
<Kind Name="NullExpression"/>

<Field Name="NullToken" Type="SyntaxToken" />
</Node>

<Node Name="WhileExpressionSyntax" Base="ExpressionSyntax">
<Kind Name="WhileExpression"/>

Expand Down
4 changes: 4 additions & 0 deletions src/Panther.SyntaxGen/Typed.xml
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@
<Field Name="Variable" Type="Symbol" />
</Node>

<Node Name="TypedNullExpression" Base="TypedExpression">
<Kind Name="NullExpression"/>
</Node>

<!-- <AbstractNode Name="TypedName" Base="TypedExpression" />-->

<!-- <Node Name="TypedIdentifierName" Base="TypedName">-->
Expand Down
15 changes: 14 additions & 1 deletion src/Panther/CodeAnalysis/Emit/Emitter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -669,6 +669,10 @@ private void EmitExpression(ILProcessor ilProcessor, TypedExpression expression)
EmitNewExpression(ilProcessor, newExpression);
break;

case TypedNullExpression nullExpression:
EmitNullExpression(ilProcessor, nullExpression);
break;

case TypedIndexExpression indexExpression:
EmitIndexExpression(ilProcessor, indexExpression);
break;
Expand Down Expand Up @@ -826,6 +830,11 @@ TypedConstant constant
}
}

private void EmitNullExpression(ILProcessor ilProcessor, TypedNullExpression nullExpression)
{
ilProcessor.Emit(OpCodes.Ldnull);
}

private void EmitBinaryExpression(
ILProcessor ilProcessor,
TypedBinaryExpression binaryExpression
Expand Down Expand Up @@ -1052,6 +1061,10 @@ TypedConversionExpression conversionExpression
ilProcessor.Emit(OpCodes.Call, _convertObjectToString);
return;
}

// no conversion needed
if (fromType == Type.Null)
return;
}
else if (toType == Type.Any)
{
Expand All @@ -1073,7 +1086,7 @@ TypedConversionExpression conversionExpression
return;
}

if (fromType == Type.String)
if (fromType == Type.String || fromType == Type.Null)
{
// no conversion required
return;
Expand Down
1 change: 1 addition & 0 deletions src/Panther/CodeAnalysis/Symbols/Type.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ protected Type(Symbol symbol)
public static readonly Type Any = new TypeConstructor("any", TypeSymbol.Any);

public static readonly Type Unit = new TypeConstructor("unit", TypeSymbol.Unit);
public static readonly Type Null = new TypeConstructor("null", TypeSymbol.Unit);

public static readonly Type Bool = new TypeConstructor("bool", TypeSymbol.Bool);
public static readonly Type Int = new TypeConstructor("int", TypeSymbol.Int);
Expand Down
7 changes: 7 additions & 0 deletions src/Panther/CodeAnalysis/Syntax/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ public Parser(SourceFile sourceFile)
_prefixParseFunctions[SyntaxKind.IfKeyword] = ParseIfExpression;
_prefixParseFunctions[SyntaxKind.NumberToken] = ParseLiteralExpression;
_prefixParseFunctions[SyntaxKind.NewKeyword] = ParseNewExpression;
_prefixParseFunctions[SyntaxKind.NullKeyword] = ParseNull;
_prefixParseFunctions[SyntaxKind.OpenBraceToken] = ParseBlockExpression;
_prefixParseFunctions[SyntaxKind.OpenParenToken] = ParseGroupOrUnitExpression;
_prefixParseFunctions[SyntaxKind.StringToken] = ParseLiteralExpression;
Expand Down Expand Up @@ -737,6 +738,12 @@ private ThisExpressionSyntax ParseThis()
return new ThisExpressionSyntax(_sourceFile, ident);
}

private ExpressionSyntax ParseNull()
{
var token = Accept();
return new NullExpressionSyntax(_sourceFile, token);
}

private LiteralExpressionSyntax ParseBooleanLiteral()
{
var value = CurrentKind == SyntaxKind.TrueKeyword;
Expand Down
26 changes: 26 additions & 0 deletions src/Panther/CodeAnalysis/Syntax/Syntax.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,32 @@ public override string ToString()
public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor) => visitor.VisitThisExpression(this);
}

public sealed partial record NullExpressionSyntax(SourceFile SourceFile, SyntaxToken NullToken)
: ExpressionSyntax(SourceFile) {
public override SyntaxKind Kind => SyntaxKind.NullExpression;

public override int GetHashCode()
{
return HashCode.Combine(NullToken);
}

public override IEnumerable<SyntaxNode> GetChildren()
{
yield return NullToken;
}

public override string ToString()
{
using var writer = new StringWriter();
this.WriteTo(writer);
return writer.ToString();
}

public override void Accept(SyntaxVisitor visitor) => visitor.VisitNullExpression(this);

public override TResult Accept<TResult>(SyntaxVisitor<TResult> visitor) => visitor.VisitNullExpression(this);
}

public sealed partial record WhileExpressionSyntax(SourceFile SourceFile, SyntaxToken WhileKeyword, SyntaxToken OpenParenToken, ExpressionSyntax ConditionExpression, SyntaxToken CloseParenToken, ExpressionSyntax Body)
: ExpressionSyntax(SourceFile) {
public override SyntaxKind Kind => SyntaxKind.WhileExpression;
Expand Down
2 changes: 2 additions & 0 deletions src/Panther/CodeAnalysis/Syntax/SyntaxFacts.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public static SyntaxKind GetKeywordKind(string span)
"implicit" => SyntaxKind.ImplicitKeyword,
"namespace" => SyntaxKind.NamespaceKeyword,
"new" => SyntaxKind.NewKeyword,
"null" => SyntaxKind.NullKeyword,
"object" => SyntaxKind.ObjectKeyword,
"static" => SyntaxKind.StaticKeyword,
"this" => SyntaxKind.ThisKeyword,
Expand Down Expand Up @@ -131,6 +132,7 @@ public static SyntaxKind GetKeywordKind(string span)
SyntaxKind.LessThanToken => "<",
SyntaxKind.NamespaceKeyword => "namespace",
SyntaxKind.NewKeyword => "new",
SyntaxKind.NullKeyword => "null",
SyntaxKind.ObjectKeyword => "object",
SyntaxKind.OpenBraceToken => "{",
SyntaxKind.OpenBracketToken => "[",
Expand Down
2 changes: 2 additions & 0 deletions src/Panther/CodeAnalysis/Syntax/SyntaxKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public enum SyntaxKind
ImplicitKeyword,
NamespaceKeyword,
NewKeyword,
NullKeyword,
ObjectKeyword,
StaticKeyword,
ThisKeyword,
Expand Down Expand Up @@ -127,6 +128,7 @@ public enum SyntaxKind
LiteralExpression,
MemberAccessExpression,
NewExpression,
NullExpression,
ThisExpression,
UnaryExpression,
UnitExpression,
Expand Down
6 changes: 6 additions & 0 deletions src/Panther/CodeAnalysis/Syntax/SyntaxVisitor.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ public virtual void VisitNewExpression(NewExpressionSyntax node) =>
public virtual void VisitNoOperandInstruction(NoOperandInstructionSyntax node) =>
this.DefaultVisit(node);

public virtual void VisitNullExpression(NullExpressionSyntax node) =>
this.DefaultVisit(node);

public virtual void VisitObjectDeclaration(ObjectDeclarationSyntax node) =>
this.DefaultVisit(node);

Expand Down Expand Up @@ -244,6 +247,9 @@ public virtual TResult VisitNewExpression(NewExpressionSyntax node) =>
public virtual TResult VisitNoOperandInstruction(NoOperandInstructionSyntax node) =>
this.DefaultVisit(node);

public virtual TResult VisitNullExpression(NullExpressionSyntax node) =>
this.DefaultVisit(node);

public virtual TResult VisitObjectDeclaration(ObjectDeclarationSyntax node) =>
this.DefaultVisit(node);

Expand Down
5 changes: 5 additions & 0 deletions src/Panther/CodeAnalysis/Typing/Conversion.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ public static Conversion Classify(Type from, Type to)
if (from == Type.Int && to == Type.Char)
return Explicit;

if (from == Type.Null && !IsValueType(to))
return Implicit;

if ((from == Type.Bool || from == Type.Int || from == Type.Char) && to == Type.String)
return Explicit;

Expand All @@ -53,4 +56,6 @@ from is ArrayType(_, var fromElement)

return None;
}

private static bool IsValueType(Type type) => type == Type.Bool || type == Type.Int || type == Type.Char;
}
3 changes: 2 additions & 1 deletion src/Panther/CodeAnalysis/Typing/TypedNodeKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ internal enum TypedNodeKind
MethodExpression,
NamespaceExpression,
NewExpression,
NullExpression,
PropertyExpression,
TypeExpression,
UnaryExpression,
Expand All @@ -42,5 +43,5 @@ internal enum TypedNodeKind
// Declarations
ClassDeclaration,
FunctionDeclaration,
ObjectDeclaration
ObjectDeclaration,
}
6 changes: 6 additions & 0 deletions src/Panther/CodeAnalysis/Typing/TypedNodeVisitor.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ public virtual void VisitNewExpression(TypedNewExpression node) =>
public virtual void VisitNopStatement(TypedNopStatement node) =>
this.DefaultVisit(node);

public virtual void VisitNullExpression(TypedNullExpression node) =>
this.DefaultVisit(node);

public virtual void VisitPropertyExpression(TypedPropertyExpression node) =>
this.DefaultVisit(node);

Expand Down Expand Up @@ -178,6 +181,9 @@ public virtual TResult VisitNewExpression(TypedNewExpression node) =>
public virtual TResult VisitNopStatement(TypedNopStatement node) =>
this.DefaultVisit(node);

public virtual TResult VisitNullExpression(TypedNullExpression node) =>
this.DefaultVisit(node);

public virtual TResult VisitPropertyExpression(TypedPropertyExpression node) =>
this.DefaultVisit(node);

Expand Down
6 changes: 6 additions & 0 deletions src/Panther/CodeAnalysis/Typing/TypedNodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,3 +91,9 @@ internal partial record TypedVariableExpression
{
public override Type Type { get; init; } = Variable.Type;
}


internal partial record TypedNullExpression
{
public override Type Type { get; init; } = Type.Null;
}
6 changes: 4 additions & 2 deletions src/Panther/CodeAnalysis/Typing/TypedTreeRewriter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ TypedLiteralExpression boundLiteralExpression
=> RewriteLiteralExpression(boundLiteralExpression),
TypedNewExpression boundLiteralExpression
=> RewriteNewExpression(boundLiteralExpression),

TypedNullExpression boundNullExpression => RewriteNullExpression(boundNullExpression),
TypedPropertyExpression boundPropertyExpression
=> RewritePropertyExpression(boundPropertyExpression),
TypedTypeExpression boundTypeExpression => RewriteTypeExpression(boundTypeExpression),
Expand All @@ -43,7 +43,7 @@ TypedVariableExpression boundVariableExpression
=> RewriteVariableExpression(boundVariableExpression),
TypedWhileExpression boundWhileExpression
=> RewriteWhileExpression(boundWhileExpression),
_ => throw new ArgumentOutOfRangeException(nameof(node))
_ => throw new ArgumentOutOfRangeException(nameof(node), node.GetType().FullName)
};

protected virtual TypedExpression RewriteArrayCreationExpression(
Expand Down Expand Up @@ -86,6 +86,8 @@ TypedArrayCreationExpression node
);
}

protected virtual TypedExpression RewriteNullExpression(TypedNullExpression node) => node;

protected virtual TypedExpression RewriteFieldExpression(TypedFieldExpression node) => node;

protected virtual TypedExpression RewriteTypeExpression(TypedTypeExpression node) => node;
Expand Down
4 changes: 4 additions & 0 deletions src/Panther/CodeAnalysis/Typing/Typer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -951,6 +951,7 @@ ThisExpressionSyntax thisExpressionSyntax
=> BindThisExpression(thisExpressionSyntax, scope),
NewExpressionSyntax newExpressionSyntax
=> BindNewExpression(newExpressionSyntax, scope),
NullExpressionSyntax nullExpressionSyntax => BindNullExpression(nullExpressionSyntax, scope),
UnaryExpressionSyntax unaryExpressionSyntax
=> BindUnaryExpression(unaryExpressionSyntax, scope),
UnitExpressionSyntax unit => BindUnitExpression(unit),
Expand Down Expand Up @@ -1901,6 +1902,9 @@ string @namespace
return symbol;
}

private TypedExpression BindNullExpression(NullExpressionSyntax expr, TypedScope scope) =>
new TypedNullExpression(expr);

private TypedExpression BindThisExpression(ThisExpressionSyntax syntax, TypedScope scope)
{
var symbol = scope.Symbol;
Expand Down
16 changes: 16 additions & 0 deletions src/Panther/CodeAnalysis/Typing/Typing.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,22 @@ public override string ToString()
public override TResult Accept<TResult>(TypedNodeVisitor<TResult> visitor) => visitor.VisitVariableExpression(this);
}

internal sealed partial record TypedNullExpression(SyntaxNode Syntax)
: TypedExpression(Syntax) {
public override TypedNodeKind Kind => TypedNodeKind.NullExpression;

public override string ToString()
{
using var writer = new StringWriter();
this.WriteTo(writer);
return writer.ToString();
}

public override void Accept(TypedNodeVisitor visitor) => visitor.VisitNullExpression(this);

public override TResult Accept<TResult>(TypedNodeVisitor<TResult> visitor) => visitor.VisitNullExpression(this);
}

internal sealed partial record TypedAssignmentStatement(SyntaxNode Syntax, TypedExpression Left, TypedExpression Right)
: TypedStatement(Syntax) {
public override TypedNodeKind Kind => TypedNodeKind.AssignmentStatement;
Expand Down
38 changes: 38 additions & 0 deletions tests/Panther.Tests/CodeAnalysis/Emit/Nulls/expected.il
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// IL code: Nulls
.class private auto ansi '<Module>'
{
} // end of class <Module>

.class public auto ansi sealed $Program
extends [System.Private.CoreLib]System.Object
{
.field public static string y
.field public static string x

.method public static
void main () cil managed
{
// Method begins at RVA 0x2070
// Code size 19 (0x13)
.maxstack 1
.entrypoint
.locals init (
[0] object
)

IL_0000: ldnull
IL_0001: stsfld string $Program::y
IL_0006: ldstr "test"
IL_000b: stsfld string $Program::x
IL_0010: ldnull
IL_0011: stsfld string $Program::x
IL_0016: ldsfld string $Program::x
IL_001b: stloc.0
IL_001c: ldloc.0
IL_001d: call void [Panther.StdLib]Panther.Predef::println(object)
IL_0022: ret
} // end of method $Program::main

} // end of class $Program


4 changes: 4 additions & 0 deletions tests/Panther.Tests/CodeAnalysis/Emit/Nulls/main.pn
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
val y : string = null
var x : string = "test"
x = null
println(x)
1 change: 1 addition & 0 deletions tests/Panther.Tests/CodeAnalysis/Emit/Nulls/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

1 change: 1 addition & 0 deletions tests/Panther.Tests/CodeAnalysis/Syntax/TokenGenerators.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ public static Arbitrary<NonSeparatorTokenTestData> NonSeparatorTokenTestData() =
new NonSeparatorTokenTestData(SyntaxKind.NumberToken, "1"),
new NonSeparatorTokenTestData(SyntaxKind.NumberToken, "123"),
new NonSeparatorTokenTestData(SyntaxKind.NumberToken, "0"),
new NonSeparatorTokenTestData(SyntaxKind.NullKeyword, "null"),
}.Concat(
Enum.GetValues(typeof(SyntaxKind))
.Cast<SyntaxKind>()
Expand Down

0 comments on commit c1b0b38

Please sign in to comment.