Skip to content

Commit

Permalink
Implement user defined unary operators
Browse files Browse the repository at this point in the history
  • Loading branch information
coetaur0 committed Jul 12, 2024
1 parent d062103 commit b66208a
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 7 deletions.
4 changes: 4 additions & 0 deletions Kaleidoscope.Tests/InterpreterTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ public void InterpretCode()
(_, result) = interpreter.Run("3 + 2 $ 4");
Assert.Equal(11.0, result);

interpreter.Run("def unary-(x) 0 - x");
(_, result) = interpreter.Run("-42");
Assert.Equal(-42.0, result);

var exception = Assert.Throws<CodegenException>(
() => interpreter.Run("def fact(x) x")
);
Expand Down
19 changes: 19 additions & 0 deletions Kaleidoscope.Tests/ParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ public void ParseBinaryOp()
Assert.Equal("Syntax error at 1:14..1:17: invalid number of operands for operator.", exception.Message);
}

[Fact]
public void ParseUnaryOp()
{
var function = _parser.ParseItem("def unary-(v) 0 - v");
Assert.Equal("def unary-(v) (0 - v)", function.ToString());

var exception = Assert.Throws<ParseException>(
() => _parser.ParseItem("def unary£(x, y) y")
);
Assert.Equal("Syntax error at 1:11..1:17: invalid number of operands for operator.", exception.Message);
}

[Fact]
public void ParseExtern()
{
Expand Down Expand Up @@ -110,6 +122,13 @@ public void ParseBinaryExpr()
Assert.Equal("Syntax error at 1:8..1:8: expect an expression.", exception.Message);
}

[Fact]
public void ParseUnaryExpr()
{
var expr = _parser.ParseItem("!!0");
Assert.Equal("def __anon_expr() !!0", expr.ToString());
}

[Fact]
public void ParseCallExpr()
{
Expand Down
13 changes: 13 additions & 0 deletions Kaleidoscope/Codegen/LLVMCodegen.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,19 @@ public LLVMValueRef Visit(BinaryExpr expr)
}
}

public LLVMValueRef Visit(UnaryExpr expr)
{
var operandValue = expr.Operand.Accept(this);
var function = Module.GetNamedFunction($"unary{expr.Op}");
if (function.Handle == IntPtr.Zero)
{
throw Exception("unknown operator referenced", expr.Range);
}

var functionType = LLVMTypeRef.CreateFunction(LLVMTypeRef.Double, [LLVMTypeRef.Double]);
return _builder.BuildCall2(functionType, function, new[] { operandValue }, "unop");
}

public LLVMValueRef Visit(CallExpr expr)
{
var function = Module.GetNamedFunction(expr.Callee);
Expand Down
36 changes: 29 additions & 7 deletions Kaleidoscope/Parser/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,25 @@ private Prototype ParsePrototype()

case TokenKind.Binary:
Advance();
var op = Consume(TokenKind.Op, "expect a binary operator").Range;
name = $"binary{_source[op]}";
var binOp = Consume(TokenKind.Op, "expect a binary operator").Range;
name = $"binary{_source[binOp]}";
kind = 2;
if (_nextToken.Kind == TokenKind.Number)
{
precedence = Convert.ToInt32(_source[Advance().Range]);
}

_precedence[_source[op]] = precedence;
_precedence[_source[binOp]] = precedence;

break;

case TokenKind.Unary:
Advance();
var unOp = Consume(TokenKind.Op, "expect a unary operator").Range;
name = $"unary{_source[unOp]}";
kind = 1;
break;

default:
throw Exception("expect a function or operator declaration", _nextToken.Range);
}
Expand All @@ -123,7 +130,7 @@ private Prototype ParsePrototype()
var parameters = ParseList(ParseParameter, TokenKind.RightParen);
var end = Consume(TokenKind.RightParen, "expect a ')'").Range.End;

if (kind == 2 && parameters.Count != 2)
if (kind != 0 && parameters.Count != kind)
{
throw Exception("invalid number of operands for operator", new Syntax.Range(paramStart, end));
}
Expand All @@ -144,7 +151,7 @@ private string ParseParameter()
/// </summary>
private IExpr ParseExpr()
{
return ParseBinary(ParsePrimary(), 0);
return ParseBinary(ParseUnary(), 0);
}

/// <summary>
Expand All @@ -162,7 +169,7 @@ private IExpr ParseBinary(IExpr lhs, int precedence)
}

var op = _source[Advance().Range];
var rhs = ParsePrimary();
var rhs = ParseUnary();

var nextPrecedence = _precedence.GetValueOrDefault(_source[_nextToken.Range], -1);
if (opPrecedence < nextPrecedence)
Expand All @@ -174,10 +181,25 @@ private IExpr ParseBinary(IExpr lhs, int precedence)
}
}

/// <summary>
/// Parses a unary expression.
/// </summary>
private IExpr ParseUnary()
{
if (_nextToken.Kind != TokenKind.Op)
{
return ParsePrimary();
}

var start = Advance().Range;
var op = _source[start];
var operand = ParseUnary();
return new UnaryExpr(op, operand, operand.Range with { Start = start.Start });
}

/// <summary>
/// Parses a primary expression.
/// </summary>
/// <returns></returns>
private IExpr ParsePrimary()
{
switch (_nextToken.Kind)
Expand Down
5 changes: 5 additions & 0 deletions Kaleidoscope/Syntax/IExprVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@ public interface IExprVisitor<out T>
/// </summary>
T Visit(BinaryExpr expr);

/// <summary>
/// Visits a unary expression node.
/// </summary>
T Visit(UnaryExpr expr);

/// <summary>
/// Visits a call expression node.
/// </summary>
Expand Down
17 changes: 17 additions & 0 deletions Kaleidoscope/Syntax/UnaryExpr.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace Kaleidoscope.Syntax;

/// <summary>
/// A unary expression.
/// </summary>
/// <param name="Op">The expression's operator.</param>
/// <param name="Operand">The expression's operand.</param>
/// <param name="Range">The expression's source range.</param>
public sealed record UnaryExpr(string Op, IExpr Operand, Range Range) : IExpr
{
public T Accept<T>(IExprVisitor<T> visitor)
{
return visitor.Visit(this);
}

public override string ToString() => $"{Op}{Operand}";
}

0 comments on commit b66208a

Please sign in to comment.