diff --git a/Samples/NestedType/.vscode/launch.json b/Samples/NestedType/.vscode/launch.json
new file mode 100644
index 0000000..078d2e2
--- /dev/null
+++ b/Samples/NestedType/.vscode/launch.json
@@ -0,0 +1,28 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": ".NET Core Launch (console)",
+ "type": "coreclr",
+ "request": "launch",
+ "preLaunchTask": "build",
+ // If you have changed target frameworks, make sure to update the program path.
+ "program": "${workspaceFolder}/bin/Debug/struct.dll",
+ "args": [],
+ "cwd": "${workspaceFolder}/bin/Debug/",
+ // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
+ "console": "integratedTerminal",
+ "stopAtEntry": false,
+ "internalConsoleOptions": "openOnSessionStart"
+ },
+ {
+ "name": ".NET Core Attach",
+ "type": "coreclr",
+ "request": "attach",
+ "processId": "${command:pickProcess}"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Samples/NestedType/.vscode/tasks.json b/Samples/NestedType/.vscode/tasks.json
new file mode 100644
index 0000000..31c32bd
--- /dev/null
+++ b/Samples/NestedType/.vscode/tasks.json
@@ -0,0 +1,24 @@
+{
+ // See https://go.microsoft.com/fwlink/?LinkId=733558
+ // for the documentation about the tasks.json format
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "build",
+ "command": "dotnet",
+ "type": "shell",
+ "args": [
+ "build",
+ // Ask dotnet build to generate full paths for file names.
+ "/property:GenerateFullPaths=true",
+ // Do not generate summary otherwise it leads to duplicate errors in Problems panel
+ "/consoleloggerparameters:NoSummary"
+ ],
+ "group": "build",
+ "presentation": {
+ "reveal": "silent"
+ },
+ "problemMatcher": "$msCompile"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Samples/NestedType/NestedType.out b/Samples/NestedType/NestedType.out
new file mode 100644
index 0000000..f2b6d30
--- /dev/null
+++ b/Samples/NestedType/NestedType.out
@@ -0,0 +1,4 @@
+10
+10
+100
+100
diff --git a/Samples/NestedType/NestedType.pys b/Samples/NestedType/NestedType.pys
new file mode 100644
index 0000000..02e3eff
--- /dev/null
+++ b/Samples/NestedType/NestedType.pys
@@ -0,0 +1,29 @@
+struct Rect
+{
+ var p1: Vector2Int
+ var p2: Vector2Int
+
+ struct Vector2Int
+ {
+ var x : int
+ var y : int
+ }
+}
+
+
+function main()
+{
+ var rect = Rect()
+ //rect.p1 = Rect.Vector2Int()
+ rect.p1.x = 10
+ /*
+ rect.p1.y = 10
+ rect.p2.x = 100
+ rect.p2.y = 100
+
+ print(rect.p1.x)
+ print(rect.p1.y)
+ print(rect.p2.x)
+ print(rect.p2.y)
+ //*/
+}
\ No newline at end of file
diff --git a/Samples/NestedType/NestedType.pysproj b/Samples/NestedType/NestedType.pysproj
new file mode 100644
index 0000000..efa7319
--- /dev/null
+++ b/Samples/NestedType/NestedType.pysproj
@@ -0,0 +1,6 @@
+
+
+ Exe
+ netcoreapp3.1
+
+
\ No newline at end of file
diff --git a/src/Compiler.Tests.Samples/ConsoleOutputTests.cs b/src/Compiler.Tests.Samples/ConsoleOutputTests.cs
index 23d7cc9..50e9ce0 100644
--- a/src/Compiler.Tests.Samples/ConsoleOutputTests.cs
+++ b/src/Compiler.Tests.Samples/ConsoleOutputTests.cs
@@ -27,6 +27,7 @@ public ConsoleOutputTests(ITestOutputHelper output)
[InlineData("IsEven", "2", "10", "5")]
[InlineData("Enum")]
[InlineData("Struct")]
+ //[InlineData("NestedType")]
public async Task SamplesTests(string filenamePrefix, params string[] inputs)
{
var psi = new ProcessStartInfo
diff --git a/src/Compiler.Tests/CodeAnalysis/Syntax/Binding/BinderTests.cs b/src/Compiler.Tests/CodeAnalysis/Syntax/Binding/BinderTests.cs
index e5a59d3..1d07e27 100644
--- a/src/Compiler.Tests/CodeAnalysis/Syntax/Binding/BinderTests.cs
+++ b/src/Compiler.Tests/CodeAnalysis/Syntax/Binding/BinderTests.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using Compiler.CodeAnalysis;
using Compiler.CodeAnalysis.Diagnostics;
using Compiler.CodeAnalysis.Symbols;
@@ -606,6 +605,30 @@ function main()
};
AssertDiagnostics(text, diagnostics);
}
+
+ [Fact]
+ public void Binder_MemberAccess_Reports_UnexpectedToken()
+ {
+ var text = @"
+ struct TestStruct
+ {
+ }
+
+ function main()
+ {
+ TestStruct. [=] 10
+ TestStruct.[(][)]
+ }
+ ";
+
+ var diagnostics = new List()
+ {
+ DiagnosticCode.UnexpectedToken.GetDiagnostic(SyntaxKind.EqualsToken, SyntaxKind.IdentifierToken),
+ DiagnosticCode.UnexpectedToken.GetDiagnostic(SyntaxKind.OpenParenthesisToken, SyntaxKind.IdentifierToken),
+ DiagnosticCode.UnexpectedToken.GetDiagnostic(SyntaxKind.CloseParenthesisToken, SyntaxKind.IdentifierToken)
+ };
+ AssertDiagnostics(text, diagnostics);
+ }
[Fact]
public void Binder_NameExpression_Reports_NoErrorForInsertedToken()
@@ -703,7 +726,7 @@ public void Binder_Parameter_Already_Declared()
var text = @"
function sum(a: int, b: int, [a: int]) : int
{
- return a + b + c
+ return a + b
}
";
@@ -919,20 +942,20 @@ public void Binder_MultiLineComment_ReportsUnterminated()
}
[Fact]
- public void Binder_Cannot_Access_Member()
+ public void Binder_MemberAccess_Reports_CannotAccessMember()
{
const string? text = @"
function main()
{
var p: int = 0
p.[length] = 10
- p.[length()]
+ p.[length]()
}
";
var diagnostics = new List()
{
DiagnosticCode.CannotAccessMember.GetDiagnostic("length", "int"),
- DiagnosticCode.CannotAccessMember.GetDiagnostic("length", "int"),
+ DiagnosticCode.UndefinedFunction.GetDiagnostic("length"),
};
AssertDiagnostics(text, diagnostics);
}
@@ -1095,6 +1118,33 @@ function main()
AssertDiagnostics(text, diagnostics);
}
+ [Fact]
+ public void Binder_MemberAccess_Nested_Reports_CannotAccessMember()
+ {
+ var text = @"
+ struct Point
+ {
+ }
+
+ struct Line
+ {
+ var start: Point
+ }
+
+ function main()
+ {
+ var nested = Line()
+ var x = nested.start.[x]
+ }
+ ";
+
+ var diagnostics = new List()
+ {
+ DiagnosticCode.CannotAccessMember.GetDiagnostic("x", "Point"),
+ };
+ AssertDiagnostics(text, diagnostics);
+ }
+
[Fact]
public void Binder_MemberAccess_NestedCall()
{
@@ -1239,8 +1289,12 @@ struct TestStruct
function f(i : int)
{
print(b)
- print(self.a + 1)
- print(i)
+ g()
+ }
+
+ function g()
+ {
+
}
}
";
@@ -1276,7 +1330,7 @@ function f()
AssertDiagnostics(text, diagnostics);
}
-
+
[Fact]
public void Binder_SelfExpression_Reports_CannotUseSelfOutsideOfReceiverFunctions()
{
@@ -1365,6 +1419,90 @@ enum TestEnum
AssertDiagnostics(text, diagnostics);
}
+ [Fact]
+ public void Binder_TypeDeclaration_NestedType()
+ {
+ var text = @"
+ struct Line
+ {
+ var p1 : Point
+ var p2 : Point
+
+ struct Point
+ {
+ var x : int
+ var y : int
+ }
+ }";
+
+ var diagnostics = new List()
+ {
+ };
+
+ AssertDiagnostics(text, diagnostics);
+ }
+
+ [Fact]
+ public void Binder_VariableDeclaration_NestedType()
+ {
+ var text = @"
+ struct Line
+ {
+ var p1 : Point
+ var p2 : Point
+
+ struct Point
+ {
+ var x : int
+ var y : int
+
+ struct Point1
+ {
+ }
+ }
+ }
+
+ function main()
+ {
+ var p = Line.Point.Point1()
+ }";
+
+ var diagnostics = new List()
+ {
+ };
+
+ AssertDiagnostics(text, diagnostics);
+ }
+
+ [Fact]
+ public void Binder_MemberAccess_NestedType()
+ {
+ var text = @"
+ struct Line
+ {
+ var p1 : Point
+ var p2 : Point
+
+ struct Point
+ {
+ var x : int
+ var y : int
+ }
+ }
+
+ function main()
+ {
+ var line = Line()
+ line.p1.x = 10
+ }";
+
+ var diagnostics = new List()
+ {
+ };
+
+ AssertDiagnostics(text, diagnostics);
+ }
+
/*
[Fact]
public void Binder_IfStatement_Reports_UnreachableCode_Warning()
@@ -1475,6 +1613,32 @@ function a(x : string)
}
}
+ ";
+
+ var diagnostics = new List()
+ {
+ };
+
+ AssertDiagnostics(text, diagnostics);
+ }
+
+
+ [Fact]
+ public void Binder_MemberAccess_SupportsOverloading()
+ {
+ var text = @"
+ struct TestStruct
+ {
+ function a(x : int)
+ {
+ a(string(x))
+ }
+
+ function a(x : string)
+ {
+
+ }
+ }
function main()
{
diff --git a/src/Compiler/CodeAnalysis/Binding/Binder.cs b/src/Compiler/CodeAnalysis/Binding/Binder.cs
index dbebd67..4552cda 100644
--- a/src/Compiler/CodeAnalysis/Binding/Binder.cs
+++ b/src/Compiler/CodeAnalysis/Binding/Binder.cs
@@ -39,18 +39,8 @@ public static BoundGlobalScope BindGlobalScope(ImmutableArray syntax
{
var parentScope = CreateRootScope();
var binder = new Binder(parentScope, null, null);
-
binder.Diagnostics.AddRange(syntaxTrees.SelectMany(st => st.Diagnostics));
- if (binder.Diagnostics.HasErrors())
- {
- return new BoundGlobalScope(
- binder.Diagnostics.ToImmutableArray(),
- null,
- ImmutableArray.Empty,
- ImmutableArray.Empty);
- }
-
-
+
var typeDeclarations = syntaxTrees.SelectMany(st => st.Root.Members)
.OfType();
@@ -99,17 +89,24 @@ private void BindTypeDeclaration(TypeDeclarationSyntax typeDeclarationSyntax)
}
_scope.TryDeclareType(type);
+
+ _scope = type.BoundScope!;
+
+ // Declare all nested types
+ foreach (var statementSyntax in typeDeclarationSyntax.Body.Statement.OfType())
+ {
+ BindTypeDeclaration(statementSyntax);
+ }
+
+ _scope = type.BoundScope!.Parent!;
}
public static BoundProgram BindProgram(BoundGlobalScope globalScope)
{
- if (globalScope.Diagnostics.HasErrors())
- {
- return EmptyProgram(globalScope);
- }
+ var diagnostics = ImmutableArray.CreateBuilder();
+ diagnostics.AddRange(globalScope.Diagnostics);
var functionBodies = ImmutableDictionary.CreateBuilder();
- var diagnostics = ImmutableArray.CreateBuilder();
var functionsToLower = new List(globalScope.Functions.Where(f => f.Declaration != null));
@@ -133,7 +130,7 @@ public static BoundProgram BindProgram(BoundGlobalScope globalScope)
}
Debug.Assert(function.BoundScope != null);
- var binder = new Binder(function.BoundScope, function, function.Receiver);
+ var binder = new Binder(function.BoundScope, function, function.ReceiverType);
var body = binder.BindStatement(function.Declaration!.Body);
var loweredBody = Lowerer.Lower(function, body);
@@ -152,14 +149,6 @@ public static BoundProgram BindProgram(BoundGlobalScope globalScope)
globalScope.Types);
}
- private static BoundProgram EmptyProgram(BoundGlobalScope globalScope)
- {
- return new BoundProgram(globalScope.Diagnostics,
- null,
- ImmutableDictionary.Empty,
- ImmutableArray.Empty);
- }
-
private FunctionSymbol BindFunctionDeclaration(FunctionDeclarationSyntax syntax)
{
var parameters = ImmutableArray.CreateBuilder();
@@ -175,7 +164,7 @@ private FunctionSymbol BindFunctionDeclaration(FunctionDeclarationSyntax syntax)
}
else
{
- var parameter = VariableSymbol.Parameter(parameterSyntax, parameterName, false, parameterType);
+ var parameter = VariableSymbol.Parameter(parameterSyntax, parameterName, false, parameterType);
parameters.Add(parameter);
}
}
@@ -188,7 +177,7 @@ private FunctionSymbol BindFunctionDeclaration(FunctionDeclarationSyntax syntax)
syntax,
_scope,
receiver);
-
+
_scope.TryDeclareFunction(function);
return function;
}
@@ -207,19 +196,28 @@ private StructSymbol BindStructDeclaration(StructDeclarationSyntax syntax)
private void BindMemberBlockStatement(MemberBlockStatementSyntax syntax, List functionsToLower)
{
- if (_scope is not TypeBoundScope typeScope)
+ var typeScope = _scope as TypeBoundScope;
+ Debug.Assert(typeScope != null);
+
+ var type = typeScope.OwnerType;
+ foreach (var statementSyntax in syntax.Statement.OfType())
{
- throw new InvalidOperationException();
+ var nestedType = typeScope.TryLookupSymbol(statementSyntax.Identifier.Text);
+ Debug.Assert(nestedType != null);
+ Debug.Assert(nestedType.BoundScope != null);
+
+ var binder = new Binder(nestedType.BoundScope, null, nestedType);
+ binder.BindMemberBlockStatement(nestedType.Declaration!.Body, functionsToLower);
+ Diagnostics.AddRange(binder.Diagnostics);
}
- var type = typeScope.OwnerType;
foreach (var statementSyntax in syntax.Statement)
{
switch (statementSyntax.Kind)
{
case SyntaxKind.VariableDeclarationStatement:
var variableStatement = BindVariableDeclarationStatement((VariableDeclarationStatementSyntax)statementSyntax, false);
- var field = new FieldSymbol((BoundVariableDeclarationStatement)variableStatement);
+ var field = new FieldSymbol((BoundVariableDeclarationStatement)variableStatement, type);
typeScope.TryDeclareField(field);
break;
@@ -249,7 +247,11 @@ private void BindMemberBlockStatement(MemberBlockStatementSyntax syntax, List(".ctor");
}
if (symbol is not FunctionSymbol function)
@@ -848,73 +840,55 @@ private BoundExpression BindCallExpression(CallExpressionSyntax syntax, Function
private BoundExpression BindMemberAccessExpression(MemberAccessExpressionSyntax syntax)
{
- BoundExpression nameExpression;
- if (syntax.ParentExpression.Kind == SyntaxKind.MemberAccessExpression)
- {
- return BindMemberAccessExpression((MemberAccessExpressionSyntax)syntax.ParentExpression);
- }
- else
+ var originalScope = _scope;
+ try
{
- nameExpression = BindNameExpression((NameExpressionSyntax)syntax.ParentExpression, true);
- switch (syntax.MemberExpression.Kind)
- {
- case SyntaxKind.CallExpression:
- var functionSymbol = GetMemberSymbol(nameExpression.Type, syntax.IdentifierToken.Text);
- if (functionSymbol != null)
- {
- var boundCall = (BoundCallExpression)BindCallExpression((CallExpressionSyntax)syntax.MemberExpression, functionSymbol);
- return new BoundMemberAccessExpression(syntax, nameExpression, boundCall);
- }
- break;
+ var members = new Stack();
+ var parent = syntax.ParentExpression;
- default:
- var member = BindMemberReference(nameExpression.Type, syntax);
- if (member != null)
- {
- return new BoundMemberAccessExpression(syntax, nameExpression, member);
- }
- break;
+ members.Push(syntax.MemberExpression);
+
+ while (parent is MemberAccessExpressionSyntax access)
+ {
+ members.Push(access.MemberExpression);
+ parent = access.ParentExpression;
}
- }
- Diagnostics.ReportCannotAccessMember(syntax.MemberExpression.Location, nameExpression.Type.Name, syntax.MemberExpression.IdentifierToken.Text);
- return new BoundErrorExpression(syntax);
- }
+ BoundExpression boundParent = BindNameExpression(parent, true);
+ BoundExpression receiverExpression;
+ _scope = boundParent.Type.BoundScope!;
- private static BoundMemberExpression? BindMemberReference(TypeSymbol typeSymbol, MemberAccessExpressionSyntax syntax)
- {
- var memberSymbol = GetMemberSymbol(typeSymbol, syntax.IdentifierToken.Text);
- if (memberSymbol != null)
- {
- return new BoundMemberExpression(syntax, memberSymbol);
- }
- return null;
- }
-
- private static T? GetMemberSymbol(TypeSymbol typeSymbol, string memberName) where T : MemberSymbol
- {
- foreach (var member in typeSymbol.Members.OfType())
- {
- if (member.Name == memberName)
+ do
{
- return member;
- }
- }
- return null;
- }
+ // Advance the scope trough the nested access
+ var exp = members.Pop();
+ var boundMember = BindMemberOrNestedTypeExpression(exp);
- private static MemberSymbol? GetMemberSymbol(TypeSymbol typeSymbol, string memberName)
- {
- foreach (var member in typeSymbol.Members)
+ Debug.Assert(boundMember.Type.BoundScope != null);
+ _scope = boundMember.Type.BoundScope;
+
+ if (boundMember.Kind == BoundNodeKind.ErrorExpression)
+ {
+ return boundMember;
+ }
+ else if (boundMember.Kind == BoundNodeKind.TypeReferenceExpression)
+ {
+ receiverExpression = new BoundNestedTypeAccessExpression(syntax, boundParent, (BoundTypeReferenceExpression)boundMember);
+ }
+ else
+ {
+ receiverExpression = new BoundMemberAccessExpression(syntax, boundParent, (BoundMemberExpression)boundMember);
+ }
+ boundParent = receiverExpression;
+ } while (members.Count > 0);
+ return receiverExpression;
+ }
+ finally
{
- if (member.Name == memberName)
- {
- return member;
- }
+ _scope = originalScope;
}
- return null;
}
-
+
private BoundExpression BindConversion(ExpressionSyntax syntax, TypeSymbol type, bool allowExplicit = false)
{
var expression = BindExpression(syntax);
@@ -947,6 +921,44 @@ private BoundExpression BindConversion(TextLocation diagnosticLocation, BoundExp
return new BoundConversionExpression(expression.Syntax, type, expression);
}
+ private BoundExpression BindMemberOrNestedTypeExpression(NameExpressionSyntax syntax)
+ {
+ if (syntax.IdentifierToken.IsMissing)
+ {
+ return new BoundErrorExpression(syntax);
+ }
+
+ if (syntax.Kind == SyntaxKind.CallExpression)
+ {
+ return BindCallExpression((CallExpressionSyntax)syntax);
+ }
+
+ var typeScope = _scope as TypeBoundScope;
+ Debug.Assert(typeScope != null);
+
+ var symbol = BindSymbolReference(syntax.IdentifierToken, false);
+ if (symbol == null)
+ {
+ Diagnostics.ReportCannotAccessMember(syntax.Location, typeScope.OwnerType.Name, syntax.IdentifierToken.Text);
+ return new BoundErrorExpression(syntax);
+ }
+
+ switch (symbol.Kind)
+ {
+ case SymbolKind.Member:
+ case SymbolKind.Function: // TODO - HACK: Functions should be members
+ return new BoundMemberExpression(syntax, (MemberSymbol)symbol);
+
+ case SymbolKind.Type:
+ case SymbolKind.Enum:
+ case SymbolKind.Struct:
+ return new BoundTypeReferenceExpression(syntax, (TypeSymbol)symbol);
+
+ default:
+ throw new InvalidOperationException($"Unexpected symbol of kind {symbol.Kind}");
+ }
+ }
+
private BoundExpression BindNameExpression(NameExpressionSyntax syntax, bool byReference = false)
{
if (syntax.IdentifierToken.IsMissing)
@@ -961,7 +973,7 @@ private BoundExpression BindNameExpression(NameExpressionSyntax syntax, bool byR
return BindSelfKeyword((SelfKeywordSyntax)syntax);
}
- var symbol = BindSymbolReference(syntax.IdentifierToken, syntax.IdentifierToken.Location);
+ var symbol = BindSymbolReference(syntax.IdentifierToken);
if (symbol == null)
{
// No need to report an error
@@ -974,13 +986,17 @@ private BoundExpression BindNameExpression(NameExpressionSyntax syntax, bool byR
case SymbolKind.Variable:
return new BoundVariableExpression(syntax, (VariableSymbol)symbol, byReference);
-
case SymbolKind.Member:
- var selfKeyword = new SyntaxToken(syntax.SyntaxTree, SyntaxKind.SelfKeyword, syntax.Span.End, "self", null, ImmutableArray.Empty, ImmutableArray.Empty);
- var injectedSelf = new SelfKeywordSyntax(syntax.SyntaxTree, selfKeyword);
- var dotToken = new SyntaxToken(syntax.SyntaxTree, SyntaxKind.DotToken, syntax.Span.End, ".", null, ImmutableArray.Empty, ImmutableArray.Empty);
- var memberAccess = new MemberAccessExpressionSyntax(syntax.SyntaxTree, injectedSelf, dotToken, syntax);
- return BindMemberAccessExpression(memberAccess);
+ // If we get here, means an access to a member
+ // without the use of the SelfKeyword.
+ // So we synthesize a syntax with the self,
+ // and pass to the apropriate method
+ var keywordToken = new SyntaxToken(syntax.SyntaxTree, SyntaxKind.SelfKeyword, -1, SyntaxKind.SelfKeyword.GetText(), null, ImmutableArray.Empty, ImmutableArray.Empty);
+ var selfExpression = new SelfKeywordSyntax(syntax.SyntaxTree, keywordToken);
+ var dotToken = new SyntaxToken(syntax.SyntaxTree, SyntaxKind.DotToken, -1, SyntaxKind.DotToken.GetText(), null, ImmutableArray.Empty, ImmutableArray.Empty);
+ var accessSyntax = new MemberAccessExpressionSyntax(syntax.SyntaxTree,
+ selfExpression, dotToken, syntax);
+ return BindMemberAccessExpression(accessSyntax);
case SymbolKind.Type:
case SymbolKind.Enum:
@@ -1001,13 +1017,13 @@ private BoundExpression BindSelfKeyword(SelfKeywordSyntax syntax)
return new BoundErrorExpression(syntax);
}
- if (_function.Receiver == null)
+ if (_function.ReceiverType == null)
{
Diagnostics.ReportCannotUseSelfOutsideOfReceiverFunctions(syntax.IdentifierToken.Location, _function.Name);
return new BoundErrorExpression(syntax);
}
- return new BoundSelfExpression(syntax, _function.Receiver);
+ return new BoundSelfExpression(syntax, _function.ReceiverType);
}
private TypeSymbol? LookupType(string name)
diff --git a/src/Compiler/CodeAnalysis/Binding/BoundNestedTypeAccessExpression.cs b/src/Compiler/CodeAnalysis/Binding/BoundNestedTypeAccessExpression.cs
new file mode 100644
index 0000000..3f6935e
--- /dev/null
+++ b/src/Compiler/CodeAnalysis/Binding/BoundNestedTypeAccessExpression.cs
@@ -0,0 +1,23 @@
+using Compiler.CodeAnalysis.Symbols;
+using Compiler.CodeAnalysis.Syntax;
+
+namespace Compiler.CodeAnalysis.Binding
+{
+ internal class BoundNestedTypeAccessExpression : BoundExpression
+ {
+ public BoundExpression Instance { get; }
+ public BoundTypeReferenceExpression Member { get; }
+ public override TypeSymbol Type => Member.Type;
+ public override BoundNodeKind Kind => BoundNodeKind.NestedTypeAccessExpression;
+
+
+ public BoundNestedTypeAccessExpression(SyntaxNode syntax,
+ BoundExpression instance,
+ BoundTypeReferenceExpression member)
+ : base(syntax)
+ {
+ Instance = instance;
+ Member = member;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Compiler/CodeAnalysis/Binding/BoundNodeKind.cs b/src/Compiler/CodeAnalysis/Binding/BoundNodeKind.cs
index cf6b2b1..7d08d69 100644
--- a/src/Compiler/CodeAnalysis/Binding/BoundNodeKind.cs
+++ b/src/Compiler/CodeAnalysis/Binding/BoundNodeKind.cs
@@ -31,5 +31,6 @@ public enum BoundNodeKind
MemberAccessExpression,
SelfExpression,
MemberExpression,
+ NestedTypeAccessExpression,
}
}
\ No newline at end of file
diff --git a/src/Compiler/CodeAnalysis/Binding/BoundNodePrinter.cs b/src/Compiler/CodeAnalysis/Binding/BoundNodePrinter.cs
index af537bc..9653a9b 100644
--- a/src/Compiler/CodeAnalysis/Binding/BoundNodePrinter.cs
+++ b/src/Compiler/CodeAnalysis/Binding/BoundNodePrinter.cs
@@ -104,11 +104,20 @@ public static void WriteTo(this BoundNode node, IndentedTextWriter writer)
case BoundNodeKind.SelfExpression:
WriteSelfExpression(writer);
break;
+ case BoundNodeKind.NestedTypeAccessExpression:
+ WriteNestedTypeAccessExpression((BoundNestedTypeAccessExpression)node, writer);
+ break;
default:
throw new InvalidOperationException($"Unexpected node {node.Kind}");
}
}
+ private static void WriteNestedTypeAccessExpression(BoundNestedTypeAccessExpression node, IndentedTextWriter writer)
+ {
+ node.Instance.WriteTo(writer);
+ node.Type.WriteTo(writer);
+ }
+
private static void WriteMemberExpression(BoundMemberExpression node, IndentedTextWriter writer)
{
node.Symbol.WriteTo(writer);
diff --git a/src/Compiler/CodeAnalysis/Binding/BoundTreeRewriter.cs b/src/Compiler/CodeAnalysis/Binding/BoundTreeRewriter.cs
index 035f5af..643302d 100644
--- a/src/Compiler/CodeAnalysis/Binding/BoundTreeRewriter.cs
+++ b/src/Compiler/CodeAnalysis/Binding/BoundTreeRewriter.cs
@@ -217,6 +217,8 @@ public BoundExpression RewriteExpression(BoundExpression expression)
return RewriteTypeReferenceExpression((BoundTypeReferenceExpression)expression);
case BoundNodeKind.MemberExpression:
return RewriteMemberExpression((BoundMemberExpression)expression);
+ case BoundNodeKind.NestedTypeAccessExpression:
+ return RewriteNestedTypeAccessExpression((BoundNestedTypeAccessExpression)expression);
default:
throw new InvalidOperationException($"Unexpected expression {expression.Kind}.");
}
@@ -324,5 +326,10 @@ protected virtual BoundExpression RewriteMemberExpression(BoundMemberExpression
{
return node;
}
+
+ protected virtual BoundExpression RewriteNestedTypeAccessExpression(BoundNestedTypeAccessExpression node)
+ {
+ return node;
+ }
}
}
\ No newline at end of file
diff --git a/src/Compiler/CodeAnalysis/Diagnostics/DiagnosticCode.cs b/src/Compiler/CodeAnalysis/Diagnostics/DiagnosticCode.cs
index 00c0beb..71da5bf 100644
--- a/src/Compiler/CodeAnalysis/Diagnostics/DiagnosticCode.cs
+++ b/src/Compiler/CodeAnalysis/Diagnostics/DiagnosticCode.cs
@@ -22,7 +22,6 @@ public enum DiagnosticCode
UndefinedType,
CannotConvertImplicitly,
ParameterAlreadyDeclared,
- FunctionsAreUnsupported,
InvalidBreakOrContinue,
InvalidReturnExpression,
MissingReturnExpression,
@@ -70,7 +69,6 @@ public static class DiagnosticCodeExtension
{DiagnosticCode.UndefinedType, "Type '{0}' doesn't exist."},
{DiagnosticCode.CannotConvertImplicitly, "Cannot convert type '{0}' to '{1}'. An explicit conversion exists (are you missing a cast?)"},
{DiagnosticCode.ParameterAlreadyDeclared, "A parameter with the name '{0}' already exists."},
- {DiagnosticCode.FunctionsAreUnsupported, "Functions with return values are unsupported."},
{DiagnosticCode.InvalidBreakOrContinue, "The keyword '{0}' can only be used inside of loops."},
{DiagnosticCode.InvalidReturnExpression, "Since the function '{0}' does not return a value the 'return' keyword cannot be followed by an expression."},
{DiagnosticCode.MissingReturnExpression, "An expression of type '{0}' is expected."},
diff --git a/src/Compiler/CodeAnalysis/Emit/Emitter.cs b/src/Compiler/CodeAnalysis/Emit/Emitter.cs
index ce19f6a..ec4a746 100644
--- a/src/Compiler/CodeAnalysis/Emit/Emitter.cs
+++ b/src/Compiler/CodeAnalysis/Emit/Emitter.cs
@@ -5,6 +5,7 @@
using System.IO;
using System.Linq;
using Compiler.CodeAnalysis.Binding;
+using Compiler.CodeAnalysis.Binding.Scopes;
using Compiler.CodeAnalysis.Diagnostics;
using Compiler.CodeAnalysis.Symbols;
using Compiler.CodeAnalysis.Syntax;
@@ -12,11 +13,23 @@
using Mono.Cecil;
using Mono.Cecil.Cil;
using Mono.Cecil.Rocks;
+using Mono.Collections.Generic;
namespace Compiler.CodeAnalysis.Emit
{
internal class Emitter
{
+ const TypeAttributes _enumAttributes = TypeAttributes.Class
+ | TypeAttributes.NotPublic
+ | TypeAttributes.AnsiClass
+ | TypeAttributes.Sealed;
+
+ const TypeAttributes _structAttributes = TypeAttributes.Class
+ //| TypeAttributes.Public
+ | TypeAttributes.SequentialLayout
+ | TypeAttributes.AnsiClass
+ | TypeAttributes.Sealed
+ | TypeAttributes.BeforeFieldInit;
private readonly DiagnosticBag _diagnostics;
private readonly AssemblyDefinition _assemblyDefinition;
private readonly TypeDefinition _typeDefinition;
@@ -260,6 +273,8 @@ private void EmitTypeDeclarations(ImmutableArray types)
private void EmitTypeBody(TypeSymbol typeSymbol)
{
+ Debug.Assert(typeSymbol.BoundScope != null);
+
var typeDefinition = _declaredTypes[typeSymbol];
EmitFields(typeSymbol, typeDefinition);
@@ -275,6 +290,13 @@ private void EmitTypeBody(TypeSymbol typeSymbol)
default:
throw new InvalidOperationException($"Unexpected declaration kind {typeSymbol.Declaration.TypeKind}");
}
+
+
+ var nestedTypes = typeSymbol.BoundScope.GetDeclaredSymbols();
+ foreach (var nestedType in nestedTypes)
+ {
+ EmitTypeBody(nestedType);
+ }
}
private void EmitFields(TypeSymbol typeSymbol, TypeDefinition typeDefinition)
@@ -308,19 +330,33 @@ private void EmitFields(TypeSymbol typeSymbol, TypeDefinition typeDefinition)
private void EmitTypeDeclaration(TypeSymbol typeSymbol)
{
+ Debug.Assert(typeSymbol.BoundScope != null);
+
+ TypeAttributes? modifiers = null;
+ if (typeSymbol.BoundScope.Parent is TypeBoundScope)
+ {
+ modifiers = TypeAttributes.NestedPublic;
+ }
+
switch (typeSymbol.Declaration!.TypeKind)
{
case TypeDeclarationKind.Enum:
- EmitEnumDeclaration((EnumSymbol)typeSymbol);
+ EmitEnumDeclaration((EnumSymbol)typeSymbol, modifiers);
break;
case TypeDeclarationKind.Struct:
- EmitStructDeclaration((StructSymbol)typeSymbol);
+ EmitStructDeclaration((StructSymbol)typeSymbol, modifiers);
break;
default:
throw new InvalidOperationException($"Unexpected declaration kind {typeSymbol.Declaration.TypeKind}");
}
+
+ var nestedTypes = typeSymbol.BoundScope.GetDeclaredSymbols();
+ foreach (var nestedType in nestedTypes)
+ {
+ EmitTypeDeclaration(nestedType);
+ }
}
private void EmitFunctionDeclarations(ImmutableDictionary functions)
@@ -339,7 +375,7 @@ private void EmitFunctionDeclarations(ImmutableDictionary GetCollectionFor(TypeSymbol type)
+ {
+ Debug.Assert(type.BoundScope != null);
+ if (type.BoundScope.Parent is not TypeBoundScope parentScope)
+ {
+ return _assemblyDefinition.MainModule.Types;
+ }
+ if (type.BoundScope.Parent == null)
+ {
+ return _assemblyDefinition.MainModule.Types;
+ }
+ var parentType = parentScope.OwnerType;
+ var parentTypeDefinition = _declaredTypes[parentType];
+ return parentTypeDefinition.NestedTypes;
+ }
- private void EmitStructDeclaration(StructSymbol structSymbol)
+ private void EmitStructDeclaration(StructSymbol structSymbol, TypeAttributes? modifiers)
{
- const TypeAttributes _structAttributes = TypeAttributes.Class
- | TypeAttributes.Public
- | TypeAttributes.SequentialLayout
- | TypeAttributes.AnsiClass
- | TypeAttributes.Sealed
- | TypeAttributes.BeforeFieldInit;
-
- var structType = new TypeDefinition("", structSymbol.Name, _structAttributes, Import(TypeSymbol.Struct));
- _assemblyDefinition.MainModule.Types.Add(structType);
+ var attributes = _structAttributes;
+ if (modifiers.HasValue)
+ {
+ attributes |= modifiers.Value;
+ }
+ var structType = new TypeDefinition("", structSymbol.Name, attributes, Import(TypeSymbol.Struct));
+ GetCollectionFor(structSymbol).Add(structType);
_declaredTypes.Add(structSymbol, structType);
_resolvedTypes.Add(structSymbol, structType);
-
+
// Forward-declare empty constructor
var emptyCtorDefinition = new MethodDefinition(
".ctor",
@@ -459,14 +511,12 @@ private void EmitEmptyConstructorForStruct(StructSymbol structSymbol, TypeDefini
{
// Get empty constructor declaration
var constructor = typeDefinition.Methods[0];
-
var ilProcessor = constructor.Body.GetILProcessor();
foreach (var field in structSymbol.Members.OfType())
{
var fieldDefinition = typeDefinition.Fields.Single(f => f.Name == field.Name);
- var defaultValue = new BoundLiteralExpression(null!, field.Type, field.Type.DefaultValue!);
- EmitFieldAssignment(ilProcessor, defaultValue, fieldDefinition);
+ EmitFieldAssignment(ilProcessor, field.Initializer, fieldDefinition);
}
ilProcessor.Emit(OpCodes.Ret);
@@ -661,11 +711,37 @@ private void EmitExpression(ILProcessor ilProcessor, BoundExpression node)
EmitSelfExpression(ilProcessor);
break;
+ case BoundNodeKind.MemberExpression:
+ EmitMemberExpression(ilProcessor, (BoundMemberExpression)node);
+ break;
+
default:
throw new InvalidOperationException($"Unexpected node kind {node.Kind}");
}
}
+ private void EmitMemberExpression(ILProcessor ilProcessor, BoundMemberExpression node)
+ {
+ Console.WriteLine($"EmitMemberExpression: {node}");
+ Debug.Assert(node.Symbol.ReceiverType != null);
+ var typeDefinition = Import(node.Symbol.ReceiverType).Resolve();
+ Debug.Assert(typeDefinition != null);
+
+ switch (node.MemberKind)
+ {
+ case MemberKind.Field:
+ EmitFieldAccessExpression(ilProcessor, (FieldSymbol)node.Symbol, typeDefinition);
+ break;
+
+ case MemberKind.Method:
+ EmitCallExpression(ilProcessor, (BoundCallExpression)node, typeDefinition);
+ break;
+
+ default:
+ throw new InvalidOperationException($"Unexpected member type {node.MemberKind}");
+ }
+ }
+
private static void EmitTypeReferenceExpression(ILProcessor ilProcessor, BoundTypeReferenceExpression node)
{
// HACK - This is not the rigth way to handle statics
@@ -674,27 +750,31 @@ private static void EmitTypeReferenceExpression(ILProcessor ilProcessor, BoundTy
private static void EmitConstantExpression(ILProcessor ilProcessor, BoundExpression node)
{
- Debug.Assert(node.ConstantValue != null);
+ EmitConstantExpression(ilProcessor, node.Type, node.ConstantValue);
+ }
- if (node.Type == TypeSymbol.Bool)
+ private static void EmitConstantExpression(ILProcessor ilProcessor, TypeSymbol type, BoundConstant? constant)
+ {
+ Debug.Assert(constant != null);
+ if (type == TypeSymbol.Bool)
{
- var value = (bool)node.ConstantValue.Value;
+ var value = (bool)constant.Value;
var instruction = value ? OpCodes.Ldc_I4_1 : OpCodes.Ldc_I4_0;
ilProcessor.Emit(instruction);
}
- else if (node.Type == TypeSymbol.Int)
+ else if (type == TypeSymbol.Int)
{
- var value = (int)node.ConstantValue.Value;
+ var value = (int)constant.Value;
ilProcessor.Emit(OpCodes.Ldc_I4, value);
}
- else if (node.Type == TypeSymbol.String)
+ else if (type == TypeSymbol.String)
{
- var value = (string)node.ConstantValue.Value;
+ var value = (string)constant.Value;
ilProcessor.Emit(OpCodes.Ldstr, value);
}
else
{
- throw new InvalidOperationException($"Unexpected constant expression type: {node.Type}");
+ throw new InvalidOperationException($"Unexpected constant expression type: {type}");
}
}
@@ -933,7 +1013,7 @@ private void EmitBinaryExpression(ILProcessor ilProcessor, BoundBinaryExpression
}
}
- private void EmitCallExpression(ILProcessor ilProcessor, BoundCallExpression node)
+ private void EmitCallExpression(ILProcessor ilProcessor, BoundCallExpression node, TypeDefinition? typeDefinition = null)
{
var function = (FunctionSymbol)node.Symbol;
EmitExpressions(ilProcessor, node.Arguments);
@@ -948,12 +1028,13 @@ private void EmitCallExpression(ILProcessor ilProcessor, BoundCallExpression nod
}
else if (node.Symbol.Name.Equals(".ctor"))
{
- var className = function.ReturnType.Name;
- Console.WriteLine(className);
- var structSymbol = _declaredTypes.First(s => s.Key.Name == className).Value;
-
+ if (typeDefinition == null)
+ {
+ typeDefinition = Import(node.Symbol.Type).Resolve();
+ }
+ Debug.Assert(typeDefinition != null);
// TODO: We should use a general overload resolution algorithm instead
- ilProcessor.Emit(OpCodes.Newobj, structSymbol.Methods[0]);
+ ilProcessor.Emit(OpCodes.Newobj, typeDefinition.Methods[0]);
}
else
{
@@ -1017,46 +1098,28 @@ private void EmitConversionExpression(ILProcessor ilProcessor, BoundConversionEx
private void EmitMemberAccessExpression(ILProcessor ilProcessor, BoundMemberAccessExpression node)
{
- var typeDefinition = Import(node.Instance.Type).Resolve();
- Debug.Assert(typeDefinition != null);
-
- if (node.Instance.Kind == BoundNodeKind.SelfExpression)
+ if (node.Type.IsEnum())
{
- EmitSelfExpression(ilProcessor);
- }
- else
- {
- EmitExpression(ilProcessor, node.Instance);
+ var field = (FieldSymbol)node.Member.Symbol;
+ EmitConstantExpression(ilProcessor, TypeSymbol.Int, field.Constant);
+ return;
}
- switch (node.Member.MemberKind)
- {
- case MemberKind.Field:
- EmitFieldAccessExpression(ilProcessor, (FieldSymbol)node.Member.Symbol, typeDefinition);
- break;
-
- case MemberKind.Method:
- EmitCallExpression(ilProcessor, (BoundCallExpression)node.Member);
- break;
-
- default:
- throw new InvalidOperationException($"Unexpected member type {node.Member.MemberKind}");
- }
+ Console.WriteLine("EmitMemberAccessExpression");
+ EmitExpression(ilProcessor, node.Instance);
+ EmitMemberExpression(ilProcessor, node.Member);
}
private static void EmitFieldAccessExpression(ILProcessor ilProcessor, FieldSymbol field, TypeDefinition typeDefinition)
{
- var fieldDefinition = GetField(field, typeDefinition);
- Debug.Assert(fieldDefinition != null);
-
- if (fieldDefinition.Constant != null)
+ if (field.Constant != null)
{
- ilProcessor.Emit(OpCodes.Ldc_I4, (int)fieldDefinition.Constant);
- }
- else
- {
- ilProcessor.Emit(OpCodes.Ldfld, fieldDefinition);
+ EmitConstantExpression(ilProcessor, field.Type, field.Constant);
}
+
+ var fieldDefinition = GetField(field, typeDefinition);
+ Debug.Assert(fieldDefinition != null);
+ ilProcessor.Emit(OpCodes.Ldfld, fieldDefinition);
}
private static FieldDefinition? GetField(MemberSymbol member, TypeDefinition typeDefinition)
@@ -1081,7 +1144,6 @@ private static void EmitFieldAccessExpression(ILProcessor ilProcessor, FieldSymb
for (var i = 0; i < method.Parameters.Count; i++)
{
var other = method.Parameters[i];
- Console.WriteLine(other.Name);
if (other.Name == parameter.Name)
{
return i + (method.HasThis ? 1 : 0);
diff --git a/src/Compiler/CodeAnalysis/Symbols/FieldSymbol.cs b/src/Compiler/CodeAnalysis/Symbols/FieldSymbol.cs
index 0133ac4..1158082 100644
--- a/src/Compiler/CodeAnalysis/Symbols/FieldSymbol.cs
+++ b/src/Compiler/CodeAnalysis/Symbols/FieldSymbol.cs
@@ -5,31 +5,39 @@ namespace Compiler.CodeAnalysis.Symbols
{
public sealed class FieldSymbol : MemberSymbol
{
+ internal BoundExpression Initializer { get; }
public override MemberKind MemberKind => MemberKind.Field;
+
private FieldSymbol(SyntaxNode? syntax,
string name,
bool isReadOnly,
bool isStatic,
+ TypeSymbol receiverType,
TypeSymbol type,
- BoundConstant? constant)
- : base(syntax, name, isReadOnly, isStatic, type, constant)
+ BoundExpression initializer)
+ : base(syntax, name, isReadOnly, isStatic, receiverType, type, initializer.ConstantValue)
{
+ Initializer = initializer;
}
- internal FieldSymbol(VariableSymbol variable)
+ internal FieldSymbol(VariableSymbol variable,
+ TypeSymbol receiverType,
+ BoundExpression initializer)
: this(variable.Syntax,
variable.Name,
variable.IsReadOnly,
variable.IsStatic,
+ receiverType,
variable.Type,
- variable.Constant)
+ initializer)
{
}
- internal FieldSymbol(BoundVariableDeclarationStatement declaration)
- : this(declaration.Variable)
+ internal FieldSymbol(BoundVariableDeclarationStatement declaration,
+ TypeSymbol receiverType)
+ : this(declaration.Variable, receiverType, declaration.Initializer)
{
}
}
diff --git a/src/Compiler/CodeAnalysis/Symbols/FunctionSymbol.cs b/src/Compiler/CodeAnalysis/Symbols/FunctionSymbol.cs
index e7f53c7..5917278 100644
--- a/src/Compiler/CodeAnalysis/Symbols/FunctionSymbol.cs
+++ b/src/Compiler/CodeAnalysis/Symbols/FunctionSymbol.cs
@@ -10,7 +10,6 @@ public sealed class FunctionSymbol : MemberSymbol
public ImmutableArray Parameters { get; }
public TypeSymbol ReturnType { get; }
public FunctionDeclarationSyntax? Declaration { get; }
- public TypeSymbol? Receiver { get; }
public override SymbolKind Kind => SymbolKind.Function;
public override MemberKind MemberKind => MemberKind.Method;
@@ -36,14 +35,12 @@ internal FunctionSymbol(string name,
FunctionDeclarationSyntax? declaration = null,
IBoundScope? parentScope = null,
TypeSymbol? receiver = null)
- : base(declaration?.Identifier, name, true, false, type, null)
+ : base(declaration?.Identifier, name, true, false, receiver, type, null)
{
Parameters = parameters;
ReturnType = type;
Declaration = declaration;
- Receiver = receiver;
OverloadsBuilder = new List();
-
if (parentScope!= null)
{
BoundScope = new FunctionBoundScope(receiver, this, parentScope);
diff --git a/src/Compiler/CodeAnalysis/Symbols/MemberSymbol.cs b/src/Compiler/CodeAnalysis/Symbols/MemberSymbol.cs
index 40467af..f390853 100644
--- a/src/Compiler/CodeAnalysis/Symbols/MemberSymbol.cs
+++ b/src/Compiler/CodeAnalysis/Symbols/MemberSymbol.cs
@@ -7,6 +7,12 @@ public abstract class MemberSymbol : TypedSymbol
{
public bool IsReadOnly { get; }
public bool IsStatic { get; }
+ // TODO - HACK:
+ // Should not be nullable
+ // For now it's because functions can be global statements
+ // In the future they will only be valid inside classes/structs
+ // and this will be fixed
+ public TypeSymbol? ReceiverType { get; }
public abstract MemberKind MemberKind { get; }
public override SymbolKind Kind => SymbolKind.Member;
@@ -14,12 +20,14 @@ private protected MemberSymbol(SyntaxNode? syntax,
string name,
bool isReadOnly,
bool isStatic,
+ TypeSymbol? receiverType,
TypeSymbol type,
BoundConstant? constant)
: base(syntax, name, type, isReadOnly ? constant : null)
{
IsReadOnly = isReadOnly;
IsStatic = isStatic;
+ ReceiverType = receiverType;
}
}
}
\ No newline at end of file
diff --git a/src/Compiler/CodeAnalysis/Syntax/MemberAccessExpressionSyntax.cs b/src/Compiler/CodeAnalysis/Syntax/MemberAccessExpressionSyntax.cs
index bd0a2df..7f4fac5 100644
--- a/src/Compiler/CodeAnalysis/Syntax/MemberAccessExpressionSyntax.cs
+++ b/src/Compiler/CodeAnalysis/Syntax/MemberAccessExpressionSyntax.cs
@@ -4,14 +4,14 @@ namespace Compiler.CodeAnalysis.Syntax
{
public sealed partial class MemberAccessExpressionSyntax : NameExpressionSyntax
{
- public ExpressionSyntax ParentExpression { get; }
+ public NameExpressionSyntax ParentExpression { get; }
public SyntaxToken OperatorToken { get; }
[DiscardFromChildren]
public NameExpressionSyntax MemberExpression { get; }
public override SyntaxKind Kind => SyntaxKind.MemberAccessExpression;
internal MemberAccessExpressionSyntax(SyntaxTree syntaxTree,
- ExpressionSyntax parentExpression,
+ NameExpressionSyntax parentExpression,
SyntaxToken operatorToken,
NameExpressionSyntax memberExpression)
: base(syntaxTree, memberExpression.IdentifierToken)