Skip to content

Commit

Permalink
feat: bind literals & do some other shit
Browse files Browse the repository at this point in the history
  • Loading branch information
R-unic committed Dec 12, 2024
1 parent 3a0cf8c commit dbde4d5
Show file tree
Hide file tree
Showing 19 changed files with 184 additions and 48 deletions.
30 changes: 15 additions & 15 deletions Heir.Tests/ParserTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,9 @@ public void Parses_UnaryOperators(string input, SyntaxKind operatorKind)
public void Parses_BinaryOperators(string input, SyntaxKind operatorKind)
{
var (node, _) = Parse(input);
Assert.IsType<BinaryOp>(node);
Assert.IsType<BoundBinaryOp>(node);

var binaryOperation = (BinaryOp)node;
var binaryOperation = (BoundBinaryOp)node;
Assert.IsType<Literal>(binaryOperation.Left);
Assert.IsType<Literal>(binaryOperation.Right);
Assert.Equal(operatorKind, binaryOperation.Operator.Kind);
Expand All @@ -69,15 +69,15 @@ public void Parses_OperatorPrecedence()
{
{
var (node, _) = Parse("3 ^ 2 * 4 - 2");
Assert.IsType<BinaryOp>(node);
Assert.IsType<BoundBinaryOp>(node);

var subtraction = (BinaryOp)node;
Assert.IsType<BinaryOp>(subtraction.Left);
var multiplication = (BinaryOp)subtraction.Left;
var subtraction = (BoundBinaryOp)node;
Assert.IsType<BoundBinaryOp>(subtraction.Left);
var multiplication = (BoundBinaryOp)subtraction.Left;
Assert.IsType<Literal>(subtraction.Right);
var twoLiteral = (Literal)subtraction.Right;
Assert.IsType<BinaryOp>(multiplication.Left);
var exponentation = (BinaryOp)multiplication.Left;
Assert.IsType<BoundBinaryOp>(multiplication.Left);
var exponentation = (BoundBinaryOp)multiplication.Left;
Assert.IsType<Literal>(multiplication.Right);
var fourLiteral = (Literal)multiplication.Right;

Expand All @@ -89,13 +89,13 @@ public void Parses_OperatorPrecedence()
}
{
var (node, _) = Parse("true || false && true");
Assert.IsType<BinaryOp>(node);
Assert.IsType<BoundBinaryOp>(node);

var or = (BinaryOp)node;
var or = (BoundBinaryOp)node;
Assert.IsType<Literal>(or.Left);
var trueLiteral = (Literal)or.Left;
Assert.IsType<BinaryOp>(or.Right);
var and = (BinaryOp)or.Right;
Assert.IsType<BoundBinaryOp>(or.Right);
var and = (BoundBinaryOp)or.Right;
Assert.IsType<Literal>(and.Left);
Assert.IsType<Literal>(and.Right);

Expand All @@ -110,8 +110,8 @@ public void Parses_OperatorPrecedence()
var assignment = (AssignmentOp)node;
Assert.IsType<IdentifierName>(assignment.Left);
var target = (IdentifierName)assignment.Left;
Assert.IsType<BinaryOp>(assignment.Right);
var multiplication = (BinaryOp)assignment.Right;
Assert.IsType<BoundBinaryOp>(assignment.Right);
var multiplication = (BoundBinaryOp)assignment.Right;
Assert.IsType<IdentifierName>(multiplication.Left);
Assert.IsType<IdentifierName>(multiplication.Right);

Expand Down Expand Up @@ -149,7 +149,7 @@ public void Parses_ParenthesizedExpressions()
Assert.IsType<Parenthesized>(node);

var parenthesized = (Parenthesized)node;
Assert.IsType<BinaryOp>(parenthesized.Expression);
Assert.IsType<BoundBinaryOp>(parenthesized.Expression);
}

[Theory]
Expand Down
2 changes: 1 addition & 1 deletion Heir/AST/AssignmentOp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

namespace Heir.AST
{
public class AssignmentOp(SyntaxNode left, Token op, SyntaxNode right) : BinaryOp(left, op, right)
public class AssignmentOp(Expression left, Token op, Expression right) : BoundBinaryOp(left, op, right)

Check failure on line 5 in Heir/AST/AssignmentOp.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'BoundBinaryOp' could not be found (are you missing a using directive or an assembly reference?)
{
public override void Display(int indent)
{
Expand Down
6 changes: 3 additions & 3 deletions Heir/AST/BinaryOp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

namespace Heir.AST
{
public class BinaryOp(SyntaxNode left, Token op, SyntaxNode right) : Expression
public class BinaryOp(Expression left, Token op, Expression right) : Expression
{
public static readonly Dictionary<SyntaxKind, OpCode> StandardOpCodeMap = new()
{
Expand Down Expand Up @@ -37,9 +37,9 @@ public class BinaryOp(SyntaxNode left, Token op, SyntaxNode right) : Expression
{ SyntaxKind.PipePipeEquals, OpCode.OR }
};

public SyntaxNode Left { get; } = left;
public Expression Left { get; } = left;
public Token Operator { get; } = op;
public SyntaxNode Right { get; } = right;
public Expression Right { get; } = right;

public override R Accept<R>(Visitor<R> visitor) => visitor.VisitBinaryOpExpression(this);
public override List<Token> GetTokens() => Left.GetTokens().Append(Operator).Concat(Right.GetTokens()).ToList();
Expand Down
5 changes: 4 additions & 1 deletion Heir/AST/Block.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ public class Block(List<SyntaxNode> statements) : Statement

public override void Display(int indent = 0)
{
Console.WriteLine($"{string.Concat(Enumerable.Repeat(" ", indent))}Block(");
foreach (var statement in Statements)
statement.Display(indent);
statement.Display(indent + 1);

Console.Write($"{string.Concat(Enumerable.Repeat(" ", indent))})");
}

public override List<Token> GetTokens() =>
Expand Down
6 changes: 6 additions & 0 deletions Heir/AST/SyntaxTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,11 @@
public class SyntaxTree(List<SyntaxNode> statements) : Block(statements)
{
public override R Accept<R>(Visitor<R> visitor) => visitor.VisitSyntaxTree(this);

public override void Display(int indent = 0)
{
foreach (var statement in Statements)
statement.Display(indent);
}
}
}
4 changes: 2 additions & 2 deletions Heir/AST/UnaryOp.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@

namespace Heir.AST
{
public class UnaryOp(SyntaxNode operand, Token op) : Expression
public class UnaryOp(Expression operand, Token op) : Expression
{
public SyntaxNode Operand { get; } = operand;
public Expression Operand { get; } = operand;
public Token Operator { get; } = op;

public override R Accept<R>(Visitor<R> visitor) => visitor.VisitUnaryOpExpression(this);
Expand Down
10 changes: 5 additions & 5 deletions Heir/Binder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ enum Context
Parameters
}

public sealed class Binder(SyntaxTree syntaxTree) : Statement.Visitor<BoundStatement>, Expression.Visitor<BoundExpression>
public sealed class Binder(DiagnosticBag diagnostics, SyntaxTree syntaxTree) : Statement.Visitor<BoundStatement>, Expression.Visitor<BoundExpression>

Check failure on line 11 in Heir/Binder.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'BoundStatement' could not be found (are you missing a using directive or an assembly reference?)

Check failure on line 11 in Heir/Binder.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'BoundExpression' could not be found (are you missing a using directive or an assembly reference?)
{
public DiagnosticBag Diagnostics { get; } = diagnostics;

private readonly SyntaxTree _syntaxTree = syntaxTree;
private readonly Dictionary<SyntaxNode, BoundSyntaxNode> _boundNodes = new();
private Context _context = Context.Global;
Expand All @@ -18,6 +20,7 @@ public sealed class Binder(SyntaxTree syntaxTree) : Statement.Visitor<BoundState

public BoundStatement GetBoundNode(Statement statement) => (BoundStatement)_boundNodes[statement];

Check failure on line 21 in Heir/Binder.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'BoundStatement' could not be found (are you missing a using directive or an assembly reference?)
public BoundExpression GetBoundNode(Expression expression) => (BoundExpression)_boundNodes[expression];

Check failure on line 22 in Heir/Binder.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'BoundExpression' could not be found (are you missing a using directive or an assembly reference?)
public BoundSyntaxNode GetBoundNode(SyntaxNode expression) => _boundNodes[expression];

public BoundStatement VisitSyntaxTree(SyntaxTree syntaxTree) => new BoundSyntaxTree(BindStatements(syntaxTree.Statements));

Check failure on line 25 in Heir/Binder.cs

View workflow job for this annotation

GitHub Actions / build

The type or namespace name 'BoundStatement' could not be found (are you missing a using directive or an assembly reference?)

Expand All @@ -41,10 +44,7 @@ public BoundExpression VisitIdentifierNameExpression(IdentifierName identifierNa
throw new NotImplementedException();
}

public BoundExpression VisitLiteralExpression(Literal literal)
{
throw new NotImplementedException();
}
public BoundExpression VisitLiteralExpression(Literal literal) => new BoundLiteral(literal.Token);

public BoundExpression VisitNoOp(NoOp noOp)
{
Expand Down
29 changes: 29 additions & 0 deletions Heir/BoundAST/BoundBinaryOp.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
using Heir.Syntax;
using Heir.Types;

namespace Heir.BoundAST
{
public class BoundBinaryOp(BoundExpression left, Token op, BoundExpression right) : BoundExpression
{
public override BaseType Type => throw new NotImplementedException();
public BoundExpression Left { get; } = left;
public Token Operator { get; } = op;
public BoundExpression Right { get; } = right;

public override R Accept<R>(Visitor<R> visitor) => visitor.VisitBoundBinaryOpExpression(this);
public override List<Token> GetTokens() => Left.GetTokens().Append(Operator).Concat(Right.GetTokens()).ToList();

public override void Display(int indent)
{
Console.WriteLine($"{string.Concat(Enumerable.Repeat(" ", indent))}BinaryOp(");
Console.WriteLine($"{string.Concat(Enumerable.Repeat(" ", indent + 1))}Left ->");
Left.Display(indent + 2);
Console.WriteLine(",");
Console.WriteLine($"{string.Concat(Enumerable.Repeat(" ", indent + 1))}Operator: {Operator.Text},");
Console.WriteLine($"{string.Concat(Enumerable.Repeat(" ", indent + 1))}Right ->");
Right.Display(indent + 2);
Console.WriteLine();
Console.Write($"{string.Concat(Enumerable.Repeat(" ", indent))})");
}
}
}
7 changes: 5 additions & 2 deletions Heir/BoundAST/BoundBlock.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Heir.Syntax;
using Heir.Types;

namespace Heir.AST
namespace Heir.BoundAST
{
public class BoundBlock(List<BoundSyntaxNode> statements) : BoundStatement
{
Expand All @@ -12,8 +12,11 @@ public class BoundBlock(List<BoundSyntaxNode> statements) : BoundStatement

public override void Display(int indent = 0)
{
Console.WriteLine($"{string.Concat(Enumerable.Repeat(" ", indent))}BoundBlock(");
foreach (var statement in Statements)
statement.Display(indent);
statement.Display(indent + 1);

Console.Write($"{string.Concat(Enumerable.Repeat(" ", indent))})");
}

public override List<Token> GetTokens() =>
Expand Down
23 changes: 23 additions & 0 deletions Heir/BoundAST/BoundLiteral.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Heir.Syntax;
using Heir.Types;

namespace Heir.BoundAST
{
public class BoundLiteral(Token token) : BoundExpression
{
public override PrimitiveType Type => PrimitiveType.FromValue(Token.Value);
public Token Token { get; } = token;

public override R Accept<R>(Visitor<R> visitor) => visitor.VisitBoundLiteralExpression(this);
public override List<Token> GetTokens() => [Token];

public override void Display(int indent)
{
var valueText = Token.Value?.ToString() ?? "none";
if (Token.IsKind(SyntaxKind.BoolLiteral))
valueText = valueText.ToLower();

Console.Write($"{string.Concat(Enumerable.Repeat(" ", indent))}BoundLiteral({Token.Kind}, {valueText})");
}
}
}
13 changes: 10 additions & 3 deletions Heir/BoundAST/BoundSyntaxNode.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using Heir.Types;
using Heir.AST;
using Heir.Types;

namespace Heir.AST
namespace Heir.BoundAST
{
public abstract class BoundStatement : BoundSyntaxNode
{
Expand All @@ -23,7 +24,13 @@ public abstract class BoundExpression : BoundSyntaxNode

public interface Visitor<R>
{

//public R VisitBoundIdentifierNameExpression(BoundIdentifierName identifierName);
//public R VisitBoundAssignmentOpExpression(BoundAssignmentOp assignmentOp);
//public R VisitBoundUnaryOpExpression(BoundUnaryOp unaryOp);
public R VisitBoundBinaryOpExpression(BoundBinaryOp binaryOp);
//public R VisitBoundParenthesizedExpression(BoundParenthesized parenthesized);
public R VisitBoundLiteralExpression(BoundLiteral literal);
//public R VisitBoundNoOp(BoundNoOp noOp);
}
}

Expand Down
7 changes: 6 additions & 1 deletion Heir/BoundAST/BoundSyntaxTree.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
namespace Heir.AST
namespace Heir.BoundAST
{
public class BoundSyntaxTree(List<BoundSyntaxNode> statements) : BoundBlock(statements)
{
public override void Display(int indent = 0)
{
foreach (var statement in Statements)
statement.Display(indent);
}
}
}
32 changes: 28 additions & 4 deletions Heir/BytecodeGenerator.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
using Heir.Syntax;
using Heir.AST;
using Heir.CodeGeneration;
using Heir.Types;
using Heir.BoundAST;

namespace Heir
{
public sealed class BytecodeGenerator(SyntaxTree syntaxTree) : Statement.Visitor<List<Instruction>>, Expression.Visitor<List<Instruction>>
public sealed class BytecodeGenerator(Binder binder, SyntaxTree syntaxTree) : Statement.Visitor<List<Instruction>>, Expression.Visitor<List<Instruction>>
{
public DiagnosticBag Diagnostics { get; } = binder.Diagnostics;

private readonly Binder _binder = binder;
private readonly SyntaxTree _syntaxTree = syntaxTree;

public List<Instruction> GenerateBytecode() => GenerateBytecode(_syntaxTree);
Expand All @@ -15,16 +20,35 @@ public sealed class BytecodeGenerator(SyntaxTree syntaxTree) : Statement.Visitor
public List<Instruction> VisitBlock(Block block) => GenerateStatementsBytecode(block.Statements);

public List<Instruction> VisitAssignmentOpExpression(AssignmentOp assignmentOp) => VisitBinaryOpExpression(assignmentOp);
public List<Instruction> VisitBinaryOpExpression(BinaryOp binaryOp)
public List<Instruction> VisitBinaryOpExpression(BoundBinaryOp binaryOp)
{
var leftBoundNode = _binder.GetBoundNode(binaryOp.Left);
var rightBoundNode = _binder.GetBoundNode(binaryOp.Right);

List<Instruction> cannotApply()
{
Diagnostics.Error("H010", $"Cannot apply operator \"{binaryOp.Operator.Text}\" to operands of type \"{leftBoundNode.Type.ToString()}\" and \"{rightBoundNode.Type.ToString()}\"", binaryOp.Operator);
return [];
}
bool isNumber(BoundExpression boundExpression)
{
return boundExpression.Type.IsAssignableTo(new PrimitiveType(PrimitiveTypeKind.Int))
|| boundExpression.Type.IsAssignableTo(new PrimitiveType(PrimitiveTypeKind.Float));
}

if (SyntaxFacts.NumberOperators.Contains(binaryOp.Operator.Kind) && (!isNumber(leftBoundNode) && !isNumber(rightBoundNode)))
return cannotApply();
else if (!leftBoundNode.Type.IsAssignableTo(rightBoundNode.Type))
return cannotApply();

var leftInstructions = GenerateBytecode(binaryOp.Left);
var rightInstructions = GenerateBytecode(binaryOp.Right);
var combined = leftInstructions.Concat(rightInstructions);

if (BinaryOp.StandardOpCodeMap.TryGetValue(binaryOp.Operator.Kind, out var standardOp))
if (BoundBinaryOp.StandardOpCodeMap.TryGetValue(binaryOp.Operator.Kind, out var standardOp))
return combined.Append(new Instruction(binaryOp, standardOp)).ToList();

if (BinaryOp.AssignmentOpCodeMap.TryGetValue(binaryOp.Operator.Kind, out var assignmentOp))
if (BoundBinaryOp.AssignmentOpCodeMap.TryGetValue(binaryOp.Operator.Kind, out var assignmentOp))
return leftInstructions
.Concat(rightInstructions)
.Append(new Instruction(binaryOp, assignmentOp))
Expand Down
Loading

0 comments on commit dbde4d5

Please sign in to comment.