-
Notifications
You must be signed in to change notification settings - Fork 172
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Adding local functions #59
Changes from all commits
ef963cb
1680761
eb77f70
af50517
6c9402f
9c1b460
2898a64
bb2550b
e8c0233
f0fce89
c87e741
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Immutable; | ||
using System.Linq; | ||
using System.Diagnostics; | ||
using Minsk.CodeAnalysis.Lowering; | ||
using Minsk.CodeAnalysis.Symbols; | ||
using Minsk.CodeAnalysis.Syntax; | ||
|
@@ -12,16 +12,20 @@ namespace Minsk.CodeAnalysis.Binding | |
internal sealed class Binder | ||
{ | ||
private readonly DiagnosticBag _diagnostics = new DiagnosticBag(); | ||
private readonly List<(FunctionSymbol function, BoundBlockStatement body)> _functionBodies = new List<(FunctionSymbol function, BoundBlockStatement body)>(); | ||
private readonly FunctionSymbol _function; | ||
private readonly CompilationOptions _options; | ||
|
||
private Stack<(BoundLabel BreakLabel, BoundLabel ContinueLabel)> _loopStack = new Stack<(BoundLabel BreakLabel, BoundLabel ContinueLabel)>(); | ||
private int _labelCounter; | ||
private BoundScope _scope; | ||
|
||
public Binder(BoundScope parent, FunctionSymbol function) | ||
public Binder(BoundScope parent, FunctionSymbol function, CompilationOptions options) | ||
{ | ||
_scope = new BoundScope(parent); | ||
_function = function; | ||
_options = options; | ||
|
||
_scope = new BoundScope(parent); | ||
|
||
if (function != null) | ||
{ | ||
|
@@ -30,62 +34,38 @@ public Binder(BoundScope parent, FunctionSymbol function) | |
} | ||
} | ||
|
||
public static BoundGlobalScope BindGlobalScope(BoundGlobalScope previous, CompilationUnitSyntax syntax) | ||
public static BoundGlobalScope BindGlobalScope(BoundGlobalScope previous, CompilationUnitSyntax syntax, CompilationOptions options) | ||
{ | ||
var parentScope = CreateParentScope(previous); | ||
var binder = new Binder(parentScope, function: null); | ||
|
||
foreach (var function in syntax.Members.OfType<FunctionDeclarationSyntax>()) | ||
binder.BindFunctionDeclaration(function); | ||
|
||
var statements = ImmutableArray.CreateBuilder<BoundStatement>(); | ||
var parentScope = previous == null ? CreateRootScope() : previous.Scope; | ||
var binder = new Binder(parentScope, function: null, options); | ||
|
||
foreach (var globalStatement in syntax.Members.OfType<GlobalStatementSyntax>()) | ||
{ | ||
var statement = binder.BindStatement(globalStatement.Statement); | ||
statements.Add(statement); | ||
} | ||
var statements = binder.BindStatements(syntax.Statements); | ||
|
||
var functions = binder._scope.GetDeclaredFunctions(); | ||
var variables = binder._scope.GetDeclaredVariables(); | ||
var diagnostics = binder.Diagnostics.ToImmutableArray(); | ||
var functionBodies = binder._functionBodies.ToImmutableArray(); | ||
|
||
if (previous != null) | ||
{ | ||
diagnostics = diagnostics.InsertRange(0, previous.Diagnostics); | ||
functionBodies = functionBodies.InsertRange(0, previous.FunctionBodies); | ||
} | ||
|
||
return new BoundGlobalScope(previous, diagnostics, functions, variables, statements.ToImmutable()); | ||
return new BoundGlobalScope(previous, binder._scope, functionBodies, diagnostics, statements); | ||
} | ||
|
||
public static BoundProgram BindProgram(BoundGlobalScope globalScope) | ||
public static LoweredProgram LowerProgram(BoundGlobalScope globalScope) | ||
{ | ||
var parentScope = CreateParentScope(globalScope); | ||
|
||
var functionBodies = ImmutableDictionary.CreateBuilder<FunctionSymbol, BoundBlockStatement>(); | ||
var diagnostics = ImmutableArray.CreateBuilder<Diagnostic>(); | ||
|
||
var scope = globalScope; | ||
var loweredFunctionBodies = ImmutableDictionary.CreateBuilder<FunctionSymbol, BoundBlockStatement>(); | ||
|
||
while (scope != null) | ||
{ | ||
foreach (var function in scope.Functions) | ||
{ | ||
var binder = new Binder(parentScope, function); | ||
var body = binder.BindStatement(function.Declaration.Body); | ||
var loweredBody = Lowerer.Lower(body); | ||
functionBodies.Add(function, loweredBody); | ||
|
||
diagnostics.AddRange(binder.Diagnostics); | ||
} | ||
|
||
scope = scope.Previous; | ||
} | ||
foreach (var (function, body) in globalScope.FunctionBodies) | ||
loweredFunctionBodies.Add(function, Lowerer.Lower(body)); | ||
|
||
var statement = Lowerer.Lower(new BoundBlockStatement(globalScope.Statements)); | ||
var loweredStatement = Lowerer.Lower(new BoundBlockStatement(globalScope.Statements)); | ||
|
||
return new BoundProgram(diagnostics.ToImmutable(), functionBodies.ToImmutable(), statement); | ||
return new LoweredProgram(globalScope.Diagnostics, loweredFunctionBodies.ToImmutable(), loweredStatement); | ||
} | ||
|
||
private void BindFunctionDeclaration(FunctionDeclarationSyntax syntax) | ||
private FunctionSymbol DeclareFunction(FunctionDeclarationSyntax syntax) | ||
{ | ||
var parameters = ImmutableArray.CreateBuilder<ParameterSymbol>(); | ||
|
||
|
@@ -114,34 +94,28 @@ private void BindFunctionDeclaration(FunctionDeclarationSyntax syntax) | |
var function = new FunctionSymbol(syntax.Identifier.Text, parameters.ToImmutable(), type, syntax); | ||
if (!_scope.TryDeclareFunction(function)) | ||
_diagnostics.ReportSymbolAlreadyDeclared(syntax.Identifier.Span, function.Name); | ||
|
||
return function; | ||
} | ||
|
||
private static BoundScope CreateParentScope(BoundGlobalScope previous) | ||
private BoundStatement BindFunctionDeclaration(FunctionDeclarationSyntax syntax) | ||
{ | ||
var stack = new Stack<BoundGlobalScope>(); | ||
while (previous != null) | ||
{ | ||
stack.Push(previous); | ||
previous = previous.Previous; | ||
} | ||
if (_function != null && _options.SourceCodeKind != SourceCodeKind.Script) | ||
_diagnostics.XXX_ReportLocalFunctionsAreOnlySupportedInScriptMode(syntax.Identifier.Span); | ||
|
||
var parent = CreateRootScope(); | ||
var result = _scope.TryLookupFunction(syntax.Identifier.Text, out var function); | ||
Debug.Assert(result); | ||
|
||
while (stack.Count > 0) | ||
{ | ||
previous = stack.Pop(); | ||
var scope = new BoundScope(parent); | ||
var binder = new Binder(_scope, function, _options); | ||
|
||
foreach (var f in previous.Functions) | ||
scope.TryDeclareFunction(f); | ||
var body = binder.BindBlockStatement(function.Declaration.Body); | ||
_functionBodies.Add((function, body)); | ||
|
||
foreach (var v in previous.Variables) | ||
scope.TryDeclareVariable(v); | ||
_diagnostics.AddRange(binder.Diagnostics); | ||
_functionBodies.AddRange(binder._functionBodies); | ||
|
||
parent = scope; | ||
} | ||
|
||
return parent; | ||
// Function declaration is a no-op. | ||
return new BoundBlockStatement(ImmutableArray<BoundStatement>.Empty); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Alternatively, we could create some sort of a no-op bound node. |
||
} | ||
|
||
private static BoundScope CreateRootScope() | ||
|
@@ -165,6 +139,8 @@ private BoundStatement BindStatement(StatementSyntax syntax) | |
{ | ||
switch (syntax.Kind) | ||
{ | ||
case SyntaxKind.FunctionDeclaration: | ||
return BindFunctionDeclaration((FunctionDeclarationSyntax)syntax); | ||
case SyntaxKind.BlockStatement: | ||
return BindBlockStatement((BlockStatementSyntax)syntax); | ||
case SyntaxKind.VariableDeclaration: | ||
|
@@ -188,20 +164,31 @@ private BoundStatement BindStatement(StatementSyntax syntax) | |
} | ||
} | ||
|
||
private BoundStatement BindBlockStatement(BlockStatementSyntax syntax) | ||
private BoundBlockStatement BindBlockStatement(BlockStatementSyntax syntax) | ||
{ | ||
var statements = ImmutableArray.CreateBuilder<BoundStatement>(); | ||
_scope = new BoundScope(_scope); | ||
|
||
foreach (var statementSyntax in syntax.Statements) | ||
var statements = BindStatements(syntax.Statements); | ||
|
||
_scope = _scope.Parent; | ||
|
||
return new BoundBlockStatement(statements); | ||
} | ||
|
||
private ImmutableArray<BoundStatement> BindStatements(ImmutableArray<StatementSyntax> syntax) | ||
{ | ||
var statements = ImmutableArray.CreateBuilder<BoundStatement>(); | ||
|
||
foreach (var functionDeclarationSyntax in syntax.OfType<FunctionDeclarationSyntax>()) | ||
DeclareFunction(functionDeclarationSyntax); | ||
|
||
foreach (var statementSyntax in syntax) | ||
{ | ||
var statement = BindStatement(statementSyntax); | ||
statements.Add(statement); | ||
} | ||
|
||
_scope = _scope.Parent; | ||
|
||
return new BoundBlockStatement(statements.ToImmutable()); | ||
return statements.ToImmutable(); | ||
} | ||
|
||
private BoundStatement BindVariableDeclaration(VariableDeclarationSyntax syntax) | ||
|
@@ -507,9 +494,7 @@ private VariableSymbol BindVariable(SyntaxToken identifier, bool isReadOnly, Typ | |
{ | ||
var name = identifier.Text ?? "?"; | ||
var declare = !identifier.IsMissing; | ||
var variable = _function == null | ||
? (VariableSymbol) new GlobalVariableSymbol(name, isReadOnly, type) | ||
: new LocalVariableSymbol(name, isReadOnly, type); | ||
var variable = new VariableSymbol(name, isReadOnly, type); | ||
|
||
if (declare && !_scope.TryDeclareVariable(variable)) | ||
_diagnostics.ReportSymbolAlreadyDeclared(identifier.Span, name); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,19 +5,19 @@ namespace Minsk.CodeAnalysis.Binding | |
{ | ||
internal sealed class BoundGlobalScope | ||
{ | ||
public BoundGlobalScope(BoundGlobalScope previous, ImmutableArray<Diagnostic> diagnostics, ImmutableArray<FunctionSymbol> functions, ImmutableArray<VariableSymbol> variables, ImmutableArray<BoundStatement> statements) | ||
public BoundGlobalScope(BoundGlobalScope previous, BoundScope scope, ImmutableArray<(FunctionSymbol function, BoundBlockStatement body)> functionBodies, ImmutableArray<Diagnostic> diagnostics, ImmutableArray<BoundStatement> statements) | ||
{ | ||
Previous = previous; | ||
Scope = scope; | ||
FunctionBodies = functionBodies; | ||
Diagnostics = diagnostics; | ||
Functions = functions; | ||
Variables = variables; | ||
Statements = statements; | ||
} | ||
|
||
public BoundGlobalScope Previous { get; } | ||
public BoundScope Scope { get; } | ||
public ImmutableArray<(FunctionSymbol function, BoundBlockStatement body)> FunctionBodies { get; } | ||
public ImmutableArray<Diagnostic> Diagnostics { get; } | ||
public ImmutableArray<FunctionSymbol> Functions { get; } | ||
public ImmutableArray<VariableSymbol> Variables { get; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think this is useful anymore since it's accessible from the scope (and noone actually used these properties anyway after the refactoring I did). |
||
public ImmutableArray<BoundStatement> Statements { get; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,9 +3,9 @@ | |
|
||
namespace Minsk.CodeAnalysis.Binding | ||
{ | ||
internal sealed class BoundProgram | ||
internal sealed class LoweredProgram | ||
{ | ||
public BoundProgram(ImmutableArray<Diagnostic> diagnostics, ImmutableDictionary<FunctionSymbol, BoundBlockStatement> functions, BoundBlockStatement statement) | ||
public LoweredProgram(ImmutableArray<Diagnostic> diagnostics, ImmutableDictionary<FunctionSymbol, BoundBlockStatement> functions, BoundBlockStatement statement) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let the bikeshedding begin. 😄 |
||
{ | ||
Diagnostics = diagnostics; | ||
Functions = functions; | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
namespace Minsk.CodeAnalysis | ||
{ | ||
public sealed class CompilationOptions | ||
{ | ||
public CompilationOptions(SourceCodeKind sourceCodeKind) | ||
{ | ||
SourceCodeKind = sourceCodeKind; | ||
} | ||
|
||
public SourceCodeKind SourceCodeKind { get; } | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I chose a list of tuples instead of a dictionary here because this collection is never actually used for lookup, it's only ever used for enumeration (which a list is more efficient at).