Skip to content
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

feat(binder): add new simplified binder #45

Merged
merged 1 commit into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 128 additions & 0 deletions src/Panther/CodeAnalysis/Binder/Binder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using Panther.CodeAnalysis.Syntax;
using Panther.CodeAnalysis.Text;

namespace Panther.CodeAnalysis.Binder;

public class Binder : SyntaxVisitor
{
private readonly DiagnosticBag _diagnostics;
private Symbol _symbolTable;

private Binder(Symbol symbolTable, DiagnosticBag diagnostics)
{
_diagnostics = diagnostics;
_symbolTable = symbolTable;
}

public static (ImmutableArray<Diagnostic> diagnostics, Symbol symbolTable) Bind(
ImmutableArray<SyntaxTree> syntaxTrees
) => Bind(Symbol.NewRoot(), syntaxTrees);

public static (ImmutableArray<Diagnostic> diagnostics, Symbol symbolTable) Bind(
Symbol symbolTable,
ImmutableArray<SyntaxTree> syntaxTrees
)
{
var diagnostics = new DiagnosticBag();

foreach (var tree in syntaxTrees)
{
var binder = new Binder(symbolTable, diagnostics);
binder.Visit(tree.Root);
}

return (diagnostics.ToImmutableArray(), symbolTable);
}

protected override void DefaultVisit(SyntaxNode node)
{
foreach (
var child in node.GetChildren().Where(child => child.Kind > SyntaxKind.ExpressionMarker)
)
Visit(child);
}

private void VisitContainer(SyntaxNode node, SymbolFlags symbolFlags, SyntaxToken identifier)
{
VisitContainer(node, symbolFlags, identifier.Text, identifier.Location);
}

private void VisitContainer(
SyntaxNode node,
SymbolFlags symbolFlags,
string name,
TextLocation location
)
{
var (scope, existing) = _symbolTable.DeclareSymbol(name, symbolFlags, location);
if (existing)
{
_diagnostics.ReportDuplicateSymbol(name, scope.Location, location);
}

var current = _symbolTable;

_symbolTable = scope;

this.DefaultVisit(node);

_symbolTable = current;
}

private void DeclareSymbol(SyntaxToken identifier, SymbolFlags symbolFlags)
{
var name = identifier.Text;
var location = identifier.Location;
var existing = _symbolTable.Lookup(name);
if (existing is not null)
{
_diagnostics.ReportDuplicateSymbol(name, existing.Location, location);
}

_symbolTable.DeclareSymbol(name, symbolFlags, location);
}

public override void VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)
{
VisitContainer(node, SymbolFlags.Namespace, node.Name.ToText(), node.Name.Location);
}

public override void VisitClassDeclaration(ClassDeclarationSyntax node)
{
VisitContainer(node, SymbolFlags.Class, node.Identifier);
}

public override void VisitObjectDeclaration(ObjectDeclarationSyntax node)
{
VisitContainer(node, SymbolFlags.Object, node.Identifier);
}

public override void VisitFunctionDeclaration(FunctionDeclarationSyntax node)
{
VisitContainer(node, SymbolFlags.Method, node.Identifier);
}

public override void VisitParameter(ParameterSyntax node)
{
var flags = _symbolTable.Flags.HasFlag(SymbolFlags.Class)
? SymbolFlags.Field
: SymbolFlags.Parameter;

DeclareSymbol(node.Identifier, flags);
}

public override void VisitVariableDeclarationStatement(VariableDeclarationStatementSyntax node)
{
var flags =
_symbolTable.Flags.HasFlag(SymbolFlags.Class)
|| _symbolTable.Flags.HasFlag(SymbolFlags.Object)
? SymbolFlags.Field
: SymbolFlags.Local;

// TODO: need a way to deal with block scope for variables (or maybe we just don't have block scope for now)
DeclareSymbol(node.IdentifierToken, flags);
}
}
67 changes: 67 additions & 0 deletions src/Panther/CodeAnalysis/Binder/Symbol.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System.Collections;
using System.Collections.Generic;
using Panther.CodeAnalysis.Text;

namespace Panther.CodeAnalysis.Binder;

public class Symbol(string name, SymbolFlags flags, TextLocation location, Symbol? parent)
: IEnumerable<Symbol>
{
private Dictionary<string, Symbol>? _symbols;
private List<Symbol>? _symbolList;

public static Symbol NewRoot() => new("", SymbolFlags.None, TextLocation.None, null);

public string Name => name;
public SymbolFlags Flags => flags;
public TextLocation Location => location;
public Symbol? Parent => parent;

public string FullName
{
get
{
var parentName = Parent?.FullName;
return string.IsNullOrEmpty(parentName) ? Name : $"{parentName}.{Name}";
}
}


public (Symbol, bool existing) DeclareClass(string name, TextLocation location) =>
DeclareSymbol(name, SymbolFlags.Class, location);

public (Symbol, bool existing) DeclareField(string name, TextLocation location) =>
DeclareSymbol(name, SymbolFlags.Field, location);

public (Symbol, bool existing) DeclareMethod(string name, TextLocation location) =>
DeclareSymbol(name, SymbolFlags.Method, location);

public (Symbol, bool existing) DeclareSymbol(string name, SymbolFlags flags, TextLocation location)
{
_symbols ??= new();
_symbolList ??= new();
var symbol = new Symbol(name, flags, location, this);
var existing = !_symbols.TryAdd(name, symbol);

if(!existing) _symbolList.Add(symbol);

return (existing ? _symbols[name] : symbol, existing);
}

public Symbol? Lookup(string name, bool includeParents = true) =>
_symbols?.GetValueOrDefault(name) ?? this.Parent?.Lookup(name, includeParents);

public IEnumerator<Symbol> GetEnumerator()
{
if(_symbolList == null)
yield break;

foreach (var symbol in _symbolList)
yield return symbol;
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
}
24 changes: 24 additions & 0 deletions src/Panther/CodeAnalysis/Binder/SymbolFlags.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System;

namespace Panther.CodeAnalysis.Binder;

[Flags]
public enum SymbolFlags
{
None = 0,

// Symbol type
Namespace = 1,
Object = 1 << 1,
Class = 1 << 2,
Method = 1 << 3,
Field = 1 << 4,
Property = 1 << 5,
Parameter = 1 << 6,
Local = 1 << 7,

// Symbol Access
Private = None, // may not use this but should eventually be the default access level
Protected = 1 << 8,
Public = 1 << 9,
}
18 changes: 9 additions & 9 deletions src/Panther/CodeAnalysis/Compilation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,23 +98,23 @@ public static Compilation CreateScript(
params SyntaxTree[] syntaxTrees
) => new Compilation(references, isScript: true, previous, syntaxTrees);

public IEnumerable<Symbol> GetSymbols()
public IEnumerable<Binder.Symbol> GetSymbols()
{
var compilation = this;
var symbolNames = new HashSet<string>();

while (compilation != null)
{
foreach (
var type in compilation.RootSymbol.Types.Where(type => symbolNames.Add(type.Name))
)
var (_, symbolTable) = Binder.Binder.Bind(compilation.SyntaxTrees);

foreach (var type in symbolTable.Where(type => symbolNames.Add(type.FullName)))
{
yield return type;

foreach (var member in type.Members)
{
yield return member;
}
//
// foreach (var member in type.Members)
// {
// yield return member;
// }
}

compilation = compilation.Previous;
Expand Down
10 changes: 10 additions & 0 deletions src/Panther/CodeAnalysis/DiagnosticBag.cs
Original file line number Diff line number Diff line change
Expand Up @@ -289,4 +289,14 @@ public void ReportGenericTypeNotSupported(TextLocation location, string typeName

public void ReportNoThisInScope(TextLocation location, string scopeName) =>
Report(location, $"`this` keyword not valid in {scopeName} scope");

public void ReportDuplicateSymbol(
string name,
TextLocation existing,
TextLocation newIdentifier
)
{
Report(newIdentifier, $"A symbol with the name '{name}' already exists in this scope");
Report(existing, $"A symbol with the name '{name}' already exists in this scope");
}
}
38 changes: 38 additions & 0 deletions src/Panther/CodeAnalysis/Symbols/SymbolPrinter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,44 @@ public static void WriteTo(this Type symbol, TextWriter writer)
}
}

public static void WriteTo(this Binder.Symbol symbol, TextWriter writer)
{
if (symbol.Flags.HasFlag(Binder.SymbolFlags.Method))
{
writer.WriteKeyword("def ");
writer.WriteIdentifier(symbol.Name);
writer.WritePunctuation("(");
using var enumerator = symbol.GetEnumerator();
if (enumerator.MoveNext())
{
enumerator.Current.WriteTo(writer);

while (enumerator.MoveNext())
{
writer.WritePunctuation(", ");
enumerator.Current.WriteTo(writer);
}
}
writer.WritePunctuation(")");
}
else if (
symbol.Flags.HasFlag(Binder.SymbolFlags.Field)
|| symbol.Flags.HasFlag(Binder.SymbolFlags.Local)
)
{
writer.WriteKeyword("val ");
writer.WriteIdentifier(symbol.Name);
}
else if (symbol.Flags.HasFlag(Binder.SymbolFlags.Parameter))
{
writer.WriteIdentifier(symbol.Name);
}
else
{
throw new NotImplementedException();
}
}

public static void WriteTo(this Symbol symbol, TextWriter writer)
{
switch (symbol)
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 @@ -116,6 +116,8 @@ public enum SyntaxKind
CloseBracketToken,

// Expressions
ExpressionMarker, // marker for SyntaxKinds that are more complicated than tokens and keywords

ArrayCreationExpression,
AssignmentExpression,
BinaryExpression,
Expand Down
14 changes: 10 additions & 4 deletions src/Panther/Repl/PantherRepl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
using Mono.Cecil;
using Panther.CodeAnalysis;
using Panther.CodeAnalysis.Authoring;
using Panther.CodeAnalysis.Symbols;
using Panther.CodeAnalysis.Syntax;
using Panther.CodeAnalysis.Text;
using Panther.IO;
using SymbolFlags = Panther.CodeAnalysis.Binder.SymbolFlags;

namespace Panther.Repl;

Expand Down Expand Up @@ -150,7 +152,11 @@ private void MetaDumpSymbols()
if (_previous == null)
return;

foreach (var symbol in _previous.GetSymbols())
foreach (
var symbol in _previous
.GetSymbols()
.Where(sym => !sym.Flags.HasFlag(SymbolFlags.Parameter))
)
{
symbol.WriteTo(Console.Out);
Console.WriteLine();
Expand All @@ -166,16 +172,16 @@ private void MetaDumpFunction(string functionName)

var function = _previous
.GetSymbols()
.Where(m => m.IsMethod)
.FirstOrDefault(func => func.Name == functionName);
.Where(m => m.Flags.HasFlag(SymbolFlags.Method))
.FirstOrDefault(func => func.FullName == functionName);

if (function == null)
{
WriteError($"Function {functionName} not found.");
return;
}

_previous.EmitTree(function, Console.Out);
// _previous.EmitTree(function, Console.Out);
}

protected override bool IsCompleteSubmission(string text)
Expand Down
Loading
Loading