diff --git a/LLVMSharp.slnx b/LLVMSharp.slnx
index 474ffa4a..0767f61d 100644
--- a/LLVMSharp.slnx
+++ b/LLVMSharp.slnx
@@ -47,6 +47,17 @@
+
+
+
+
+
+
+
+
+
+
+
@@ -89,5 +100,6 @@
+
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Chapter3.csproj b/samples/KaleidoscopeTutorial/Chapter3/Chapter3.csproj
new file mode 100644
index 00000000..84fb8de9
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter3/Chapter3.csproj
@@ -0,0 +1,8 @@
+
+
+ Exe
+
+
+
+
+
diff --git a/samples/KaleidoscopeTutorial/Chapter3/CodeGenVisitor.cs b/samples/KaleidoscopeTutorial/Chapter3/CodeGenVisitor.cs
new file mode 100644
index 00000000..4266f400
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter3/CodeGenVisitor.cs
@@ -0,0 +1,153 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+using LLVMSharp.Interop;
+
+namespace Kaleidoscope.Chapter3;
+
+///
+/// Chapter 3 — "Code generation to LLVM IR". Lowers the core expression language (numbers, variables,
+/// the built-in + - * < operators, calls) and function definitions to LLVM IR. Later chapters
+/// inherit this class and override only the nodes they add.
+///
+public class CodeGenVisitor : CodeGenVisitorBase
+{
+ protected override LLVMValueRef CodegenNumber(NumberExprAST node)
+ {
+ return LLVMValueRef.CreateConstReal(Module.Context.DoubleType, node.Value);
+ }
+
+ protected override LLVMValueRef CodegenVariable(VariableExprAST node)
+ {
+ if (NamedValues.TryGetValue(node.Name, out LLVMValueRef value))
+ {
+ return value;
+ }
+
+ throw new InvalidOperationException($"Unknown variable name '{node.Name}'");
+ }
+
+ protected override LLVMValueRef CodegenBinary(BinaryExprAST node)
+ {
+ LLVMValueRef left = Codegen(node.Lhs);
+ LLVMValueRef right = Codegen(node.Rhs);
+
+ switch (node.Op)
+ {
+ case '+':
+ {
+ return Builder.BuildFAdd(left, right, "addtmp");
+ }
+
+ case '-':
+ {
+ return Builder.BuildFSub(left, right, "subtmp");
+ }
+
+ case '*':
+ {
+ return Builder.BuildFMul(left, right, "multmp");
+ }
+
+ case '<':
+ {
+ LLVMValueRef comparison = Builder.BuildFCmp(LLVMRealPredicate.LLVMRealULT, left, right, "cmptmp");
+
+ // Convert the i1 result to a double (0.0 or 1.0), Kaleidoscope's only value type.
+ return Builder.BuildUIToFP(comparison, Module.Context.DoubleType, "booltmp");
+ }
+
+ default:
+ {
+ throw new InvalidOperationException($"invalid binary operator '{node.Op}'");
+ }
+ }
+ }
+
+ protected override LLVMValueRef CodegenCall(CallExprAST node)
+ {
+ LLVMValueRef callee = GetFunction(node.Callee);
+ if (callee.Handle == IntPtr.Zero)
+ {
+ throw new InvalidOperationException($"Unknown function '{node.Callee}' referenced");
+ }
+
+ if (callee.ParamsCount != node.Arguments.Count)
+ {
+ throw new InvalidOperationException("Incorrect # arguments passed");
+ }
+
+ var arguments = new LLVMValueRef[node.Arguments.Count];
+ for (int i = 0; i < arguments.Length; i++)
+ {
+ arguments[i] = Codegen(node.Arguments[i]);
+ }
+
+ // Recover the callee's function type from the value itself; BuildCall2 needs it explicitly.
+ LLVMTypeRef functionType = GetFunctionType(callee);
+ return Builder.BuildCall2(functionType, callee, arguments, "calltmp");
+ }
+
+ public override LLVMValueRef CodegenPrototype(PrototypeAST node)
+ {
+ LLVMTypeRef doubleType = Module.Context.DoubleType;
+
+ var parameterTypes = new LLVMTypeRef[node.Arguments.Count];
+ Array.Fill(parameterTypes, doubleType);
+
+ LLVMTypeRef functionType = LLVMTypeRef.CreateFunction(doubleType, parameterTypes);
+ LLVMValueRef function = Module.AddFunction(node.Name, functionType);
+
+ for (int i = 0; i < node.Arguments.Count; i++)
+ {
+ function.GetParam((uint)i).Name = node.Arguments[i];
+ }
+
+ return function;
+ }
+
+ public override LLVMValueRef CodegenFunction(FunctionAST node)
+ {
+ // Remember the prototype (so recursive references resolve) then get-or-declare the function.
+ RegisterPrototype(node.Proto);
+ LLVMValueRef function = GetFunction(node.Proto.Name);
+
+ if (function.BasicBlocksCount != 0)
+ {
+ throw new InvalidOperationException($"Function '{node.Proto.Name}' cannot be redefined");
+ }
+
+ LLVMBasicBlockRef entry = function.AppendBasicBlock("entry");
+ Builder.PositionAtEnd(entry);
+
+ CreateParameterBindings(function, node.Proto);
+
+ try
+ {
+ LLVMValueRef body = Codegen(node.Body);
+ Builder.BuildRet(body);
+ function.VerifyFunction(LLVMVerifierFailureAction.LLVMPrintMessageAction);
+ return function;
+ }
+ catch
+ {
+ // Remove the half-built function so the REPL can keep going after an error.
+ function.DeleteFunction();
+ throw;
+ }
+ }
+
+ ///
+ /// Binds the function's parameters into before the body
+ /// is generated. Chapter 3 binds the SSA parameter values directly; chapter 7 overrides this to give
+ /// each parameter a stack slot so it can be reassigned.
+ ///
+ protected virtual void CreateParameterBindings(LLVMValueRef function, PrototypeAST proto)
+ {
+ NamedValues.Clear();
+ for (int i = 0; i < proto.Arguments.Count; i++)
+ {
+ NamedValues[proto.Arguments[i]] = function.GetParam((uint)i);
+ }
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Chapter3/DumpDriver.cs b/samples/KaleidoscopeTutorial/Chapter3/DumpDriver.cs
new file mode 100644
index 00000000..b20594bb
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter3/DumpDriver.cs
@@ -0,0 +1,44 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+using LLVMSharp.Interop;
+
+namespace Kaleidoscope.Chapter3;
+
+///
+/// Chapter 3's driver: it generates IR into one module and prints it. There is no execution yet — the
+/// point of this chapter is just to see the generated LLVM IR. Chapter 4 keeps the same code generator
+/// but adds a JIT so top-level expressions actually run.
+///
+public sealed class DumpDriver : ReplDriver
+{
+ private readonly CodeGenVisitor _visitor;
+
+ public DumpDriver(Lexer lexer, Parser parser, CodeGenVisitor visitor, LLVMModuleRef module)
+ : base(lexer, parser)
+ {
+ _visitor = visitor;
+ _visitor.SetModule(module);
+ }
+
+ protected override void OnDefinition(FunctionAST function)
+ {
+ LLVMValueRef ir = _visitor.CodegenFunction(function);
+ Console.WriteLine("Read function definition:");
+ Console.WriteLine(ir);
+ }
+
+ protected override void OnExtern(PrototypeAST prototype)
+ {
+ LLVMValueRef ir = _visitor.CodegenExtern(prototype);
+ Console.WriteLine("Read extern:");
+ Console.WriteLine(ir);
+ }
+
+ protected override void OnTopLevelExpression(FunctionAST function)
+ {
+ LLVMValueRef ir = _visitor.CodegenFunction(function);
+ Console.WriteLine("Read top-level expression:");
+ Console.WriteLine(ir);
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope.sln b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope.sln
deleted file mode 100644
index eae9808a..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope.sln
+++ /dev/null
@@ -1,37 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.31101.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kaleidoscope", "Kaleidoscope\Kaleidoscope.csproj", "{CD23CB2E-951B-4FAD-A4CD-4048731DBA31}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KaleidoscopeLLVM", "KaleidoscopeLLVM\KaleidoscopeLLVM.csproj", "{C1147A13-4174-410A-A891-335D858703B7}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
- Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Debug|x64.ActiveCfg = Debug|x64
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Debug|x64.Build.0 = Debug|x64
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Release|Any CPU.Build.0 = Release|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Release|x64.ActiveCfg = Release|x64
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Release|x64.Build.0 = Release|x64
- {C1147A13-4174-410A-A891-335D858703B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Debug|x64.ActiveCfg = Debug|x64
- {C1147A13-4174-410A-A891-335D858703B7}.Debug|x64.Build.0 = Debug|x64
- {C1147A13-4174-410A-A891-335D858703B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Release|Any CPU.Build.0 = Release|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Release|x64.ActiveCfg = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/BinaryExprAST.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/BinaryExprAST.cs
deleted file mode 100644
index 1143f724..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/BinaryExprAST.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-namespace Kaleidoscope.AST
-{
- using System;
-
- public sealed class BinaryExprAST : ExprAST
- {
- public BinaryExprAST(char op, ExprAST lhs, ExprAST rhs)
- {
- switch (op)
- {
- case '+':
- this.NodeType = ExprType.AddExpr;
- break;
- case '-':
- this.NodeType = ExprType.SubtractExpr;
- break;
- case '*':
- this.NodeType = ExprType.MultiplyExpr;
- break;
- case '<':
- this.NodeType = ExprType.LessThanExpr;
- break;
- default:
- throw new ArgumentException("op " + op + " is not a valid operator");
- }
-
- this.Lhs = lhs;
- this.Rhs = rhs;
- }
-
- public ExprAST Lhs { get; private set; }
-
- public ExprAST Rhs { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitBinaryExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/CallExprAST.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/CallExprAST.cs
deleted file mode 100644
index 1a722b27..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/CallExprAST.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace Kaleidoscope.AST
-{
- using System.Collections.Generic;
-
- public sealed class CallExprAST : ExprAST
- {
- public CallExprAST(string callee, List args)
- {
- this.Callee = callee;
- this.Arguments = args;
- this.NodeType = ExprType.CallExpr;
- }
-
- public string Callee { get; private set; }
-
- public List Arguments { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitCallExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/ExprAST.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/ExprAST.cs
deleted file mode 100644
index 6841cbec..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/ExprAST.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public abstract class ExprAST
- {
- public abstract ExprType NodeType { get; protected set; }
-
- protected internal virtual ExprAST VisitChildren(ExprVisitor visitor)
- {
- return visitor.Visit(this);
- }
-
- protected internal virtual ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitExtension(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/ExprType.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/ExprType.cs
deleted file mode 100644
index e890f346..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/ExprType.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public enum ExprType
- {
- AddExpr,
- SubtractExpr,
- MultiplyExpr,
- LessThanExpr,
- CallExpr,
- VariableExpr,
- PrototypeExpr,
- FunctionExpr,
- NumberExpr
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/ExprVisitor.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/ExprVisitor.cs
deleted file mode 100644
index a51f80cc..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/ExprVisitor.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public abstract class ExprVisitor
- {
- protected ExprVisitor()
- {
- }
-
- public virtual ExprAST Visit(ExprAST node)
- {
- if (node != null)
- {
- return node.Accept(this);
- }
-
- return null;
- }
-
- protected internal virtual ExprAST VisitExtension(ExprAST node)
- {
- return node.VisitChildren(this);
- }
-
- protected internal virtual ExprAST VisitBinaryExprAST(BinaryExprAST node)
- {
- this.Visit(node.Lhs);
- this.Visit(node.Rhs);
-
- return node;
- }
-
- protected internal virtual ExprAST VisitCallExprAST(CallExprAST node)
- {
- foreach (var argument in node.Arguments)
- {
- this.Visit(argument);
- }
-
- return node;
- }
-
- protected internal virtual ExprAST VisitFunctionAST(FunctionAST node)
- {
- this.Visit(node.Proto);
- this.Visit(node.Body);
-
- return node;
- }
-
- protected internal virtual ExprAST VisitVariableExprAST(VariableExprAST node)
- {
- return node;
- }
-
- protected internal virtual ExprAST VisitPrototypeAST(PrototypeAST node)
- {
- return node;
- }
-
- protected internal virtual ExprAST VisitNumberExprAST(NumberExprAST node)
- {
- return node;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/FunctionAST.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/FunctionAST.cs
deleted file mode 100644
index a5e90405..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/FunctionAST.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class FunctionAST : ExprAST
- {
- public FunctionAST(PrototypeAST proto, ExprAST body)
- {
- this.Proto = proto;
- this.Body = body;
- this.NodeType = ExprType.FunctionExpr;
- }
-
- public PrototypeAST Proto { get; private set; }
-
- public ExprAST Body { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitFunctionAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/NumberExprAST.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/NumberExprAST.cs
deleted file mode 100644
index bd823cd3..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/NumberExprAST.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class NumberExprAST : ExprAST
- {
- public NumberExprAST(double value)
- {
- this.Value = value;
- this.NodeType = ExprType.NumberExpr;
- }
-
- public double Value { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitNumberExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/PrototypeAST.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/PrototypeAST.cs
deleted file mode 100644
index 3d0f6b4a..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/PrototypeAST.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace Kaleidoscope.AST
-{
- using System.Collections.Generic;
-
- public sealed class PrototypeAST : ExprAST
- {
- public PrototypeAST(string name, List args)
- {
- this.Name = name;
- this.Arguments = args;
- this.NodeType = ExprType.PrototypeExpr;
- }
-
- public string Name { get; private set; }
-
- public List Arguments { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitPrototypeAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/VariableExprAST.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/VariableExprAST.cs
deleted file mode 100644
index 41aeb6c2..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/AST/VariableExprAST.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class VariableExprAST : ExprAST
- {
- public VariableExprAST(string name)
- {
- this.Name = name;
- this.NodeType = ExprType.VariableExpr;
- }
-
- public string Name { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitVariableExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/BaseParserListener.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/BaseParserListener.cs
deleted file mode 100644
index 9eb96731..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/BaseParserListener.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-namespace Kaleidoscope
-{
- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using AST;
-
- internal sealed class BaseParserListener
- {
- private static readonly Type IParserListenerType = typeof(IParserListener);
-
- private readonly Stack descentStack = new Stack();
-
- private readonly Stack ascentStack = new Stack();
-
- private readonly IParserListener listener;
-
- public BaseParserListener(IParserListener listener)
- {
- this.listener = listener;
- }
-
- public void EnterRule(string ruleName)
- {
- this.descentStack.Push(ruleName);
- }
-
- public void ExitRule(ExprAST argument)
- {
- string ruleName = this.descentStack.Pop();
- this.ascentStack.Push(new ASTContext(IParserListenerType.GetMethod("Exit" + ruleName), this.listener, argument));
- this.ascentStack.Push(new ASTContext(IParserListenerType.GetMethod("Enter" + ruleName), this.listener, argument));
- }
-
- public void Listen()
- {
- if (this.listener != null)
- {
- while (this.ascentStack.Count != 0)
- {
- var context = this.ascentStack.Pop();
- context.MethodInfo.Invoke(context.Instance, new object[] { context.Argument });
- }
- }
- }
-
- private sealed class ASTContext
- {
- public ASTContext(MethodInfo methodInfo, object instance, ExprAST argument)
- {
- this.MethodInfo = methodInfo;
- this.Instance = instance;
- this.Argument = argument;
- }
-
- public MethodInfo MethodInfo { get; private set; }
-
- public ExprAST Argument { get; set; }
-
- public object Instance { get; private set; }
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/ILexer.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/ILexer.cs
deleted file mode 100644
index aca5d18f..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/ILexer.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Kaleidoscope
-{
- public interface ILexer
- {
- int CurrentToken { get; }
-
- string GetLastIdentifier();
-
- double GetLastNumber();
-
- int GetTokPrecedence();
-
- int GetNextToken();
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/IParser.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/IParser.cs
deleted file mode 100644
index adf84705..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/IParser.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Kaleidoscope
-{
- public interface IParser
- {
- void HandleDefinition();
-
- void HandleExtern();
-
- void HandleTopLevelExpression();
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/IParserListener.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/IParserListener.cs
deleted file mode 100644
index fbc91f82..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/IParserListener.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Kaleidoscope
-{
- using AST;
-
- public interface IParserListener
- {
- void EnterHandleDefinition(FunctionAST data);
-
- void ExitHandleDefinition(FunctionAST data);
-
- void EnterHandleExtern(PrototypeAST data);
-
- void ExitHandleExtern(PrototypeAST data);
-
- void EnterHandleTopLevelExpression(FunctionAST data);
-
- void ExitHandleTopLevelExpression(FunctionAST data);
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Kaleidoscope.csproj b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Kaleidoscope.csproj
deleted file mode 100644
index 09321765..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Kaleidoscope.csproj
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- net10.0
-
-
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Lexer.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Lexer.cs
deleted file mode 100644
index fc321d68..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Lexer.cs
+++ /dev/null
@@ -1,134 +0,0 @@
-namespace Kaleidoscope
-{
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Text;
-
- public sealed class Lexer : ILexer
- {
- private const int EOF = -1;
-
- private readonly TextReader reader;
-
- private readonly StringBuilder identifierBuilder = new StringBuilder();
-
- private readonly StringBuilder numberBuilder = new StringBuilder();
-
- private readonly Dictionary binopPrecedence;
-
- private int c = ' ';
-
- private string identifier;
-
- private double numVal;
-
- public Lexer(TextReader reader, Dictionary binOpPrecedence)
- {
- this.reader = reader;
- this.binopPrecedence = binOpPrecedence;
- }
-
- public int CurrentToken { get; private set; }
-
- public string GetLastIdentifier()
- {
- return this.identifier;
- }
-
- public double GetLastNumber()
- {
- return this.numVal;
- }
-
- public int GetTokPrecedence()
- {
- // Make sure it's a declared binop.
- int tokPrec;
- if (this.binopPrecedence.TryGetValue((char)this.CurrentToken, out tokPrec))
- {
- return tokPrec;
- }
-
- return -1;
- }
-
- public int GetNextToken()
- {
- // Skip any whitespace.
- while (char.IsWhiteSpace((char)c))
- {
- c = this.reader.Read();
- }
-
- if (char.IsLetter((char)c)) // identifier: [a-zA-Z][a-zA-Z0-9]*
- {
- this.identifierBuilder.Append((char)c);
- while (char.IsLetterOrDigit((char)(c = this.reader.Read())))
- {
- this.identifierBuilder.Append((char)c);
- }
-
- this.identifier = this.identifierBuilder.ToString();
- this.identifierBuilder.Clear();
-
- if (string.Equals(identifier, "def", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.DEF;
- }
- else if (string.Equals(identifier, "extern", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.EXTERN;
- }
- else
- {
- this.CurrentToken = (int)Token.IDENTIFIER;
- }
-
- return this.CurrentToken;
- }
-
- // Number: [0-9.]+
- if (char.IsDigit((char)c) || c == '.')
- {
- do
- {
- this.numberBuilder.Append((char)c);
- c = this.reader.Read();
- } while (char.IsDigit((char)c) || c == '.');
-
- this.numVal = double.Parse(this.numberBuilder.ToString());
- this.numberBuilder.Clear();
- this.CurrentToken = (int)Token.NUMBER;
-
- return this.CurrentToken;
- }
-
- if (c == '#')
- {
- // Comment until end of line.
- do
- {
- c = this.reader.Read();
- } while (c != EOF && c != '\n' && c != '\r');
-
- if (c != EOF)
- {
- return this.GetNextToken();
- }
- }
-
- // Check for end of file. Don't eat the EOF.
- if (c == EOF)
- {
- this.CurrentToken = c;
- return (int)Token.EOF;
- }
-
- this.CurrentToken = c;
- c = this.reader.Read();
- return this.c;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Parser.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Parser.cs
deleted file mode 100644
index 15954149..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Parser.cs
+++ /dev/null
@@ -1,313 +0,0 @@
-namespace Kaleidoscope
-{
- using System;
- using System.Collections.Generic;
- using AST;
-
- public sealed class Parser : IParser
- {
- private readonly Lexer scanner;
-
- private readonly BaseParserListener baseListener;
-
- public Parser(Lexer scanner, IParserListener listener)
- {
- this.scanner = scanner;
- this.baseListener = new BaseParserListener(listener);
- }
-
- public void HandleDefinition()
- {
- this.baseListener.EnterRule("HandleDefinition");
-
- var functionAST = this.ParseDefinition();
-
- baseListener.ExitRule(functionAST);
-
- if (functionAST != null)
- {
- this.baseListener.Listen();
- }
- else
- {
- // Skip token for error recovery.
- this.scanner.GetNextToken();
- }
- }
-
- public void HandleExtern()
- {
- this.baseListener.EnterRule("HandleExtern");
-
- var prototypeAST = this.ParseExtern();
-
- this.baseListener.ExitRule(prototypeAST);
-
- if (prototypeAST != null)
- {
- this.baseListener.Listen();
- }
- else
- {
- // Skip token for error recovery.
- this.scanner.GetNextToken();
- }
- }
-
- public void HandleTopLevelExpression()
- {
- // Evaluate a top-level expression into an anonymous function.
- this.baseListener.EnterRule("HandleTopLevelExpression");
-
- var functionAST = this.ParseTopLevelExpr();
-
- this.baseListener.ExitRule(functionAST);
-
- if (functionAST != null)
- {
- this.baseListener.Listen();
- }
- else
- {
- // Skip token for error recovery.
- this.scanner.GetNextToken();
- }
- }
-
- // identifierexpr
- // ::= identifier
- // ::= identifier '(' expression* ')'
- private ExprAST ParseIdentifierExpr()
- {
- string idName = this.scanner.GetLastIdentifier();
-
- this.scanner.GetNextToken(); // eat identifier.
-
- if (this.scanner.CurrentToken != '(') // Simple variable ref.
- {
- return new VariableExprAST(idName);
- }
-
- // Call.
- this.scanner.GetNextToken(); // eat (
- List args = new List();
-
- if (this.scanner.CurrentToken != ')')
- {
- while (true)
- {
- ExprAST arg = this.ParseExpression();
- if (arg == null)
- {
- return null;
- }
-
- args.Add(arg);
-
- if (this.scanner.CurrentToken == ')')
- {
- break;
- }
-
- if (this.scanner.CurrentToken != ',')
- {
- Console.WriteLine("Expected ')' or ',' in argument list");
- return null;
- }
-
- this.scanner.GetNextToken();
- }
- }
-
- // Eat the ')'.
- this.scanner.GetNextToken();
-
- return new CallExprAST(idName, args);
- }
-
- // numberexpr ::= number
- private ExprAST ParseNumberExpr()
- {
- ExprAST result = new NumberExprAST(this.scanner.GetLastNumber());
- this.scanner.GetNextToken();
- return result;
- }
-
- // parenexpr ::= '(' expression ')'
- private ExprAST ParseParenExpr()
- {
- this.scanner.GetNextToken(); // eat (.
- ExprAST v = this.ParseExpression();
- if (v == null)
- {
- return null;
- }
-
- if (this.scanner.CurrentToken != ')')
- {
- Console.WriteLine("expected ')'");
- return null;
- }
-
- this.scanner.GetNextToken(); // eat ).
-
- return v;
- }
-
- // primary
- // ::= identifierexpr
- // ::= numberexpr
- // ::= parenexpr
- private ExprAST ParsePrimary()
- {
- switch (this.scanner.CurrentToken)
- {
- case (int)Token.IDENTIFIER:
- return this.ParseIdentifierExpr();
- case (int)Token.NUMBER:
- return this.ParseNumberExpr();
- case '(':
- return this.ParseParenExpr();
- default:
- Console.WriteLine("unknown token when expecting an expression");
- return null;
- }
- }
-
- // binoprhs
- // ::= ('+' primary)*
- private ExprAST ParseBinOpRHS(int exprPrec, ExprAST lhs)
- {
- // If this is a binop, find its precedence.
- while (true)
- {
- int tokPrec = this.scanner.GetTokPrecedence();
-
- // If this is a binop that binds at least as tightly as the current binop,
- // consume it, otherwise we are done.
- if (tokPrec < exprPrec)
- {
- return lhs;
- }
-
- // Okay, we know this is a binop.
- int binOp = this.scanner.CurrentToken;
- this.scanner.GetNextToken(); // eat binop
-
- // Parse the primary expression after the binary operator.
- ExprAST rhs = this.ParsePrimary();
- if (rhs == null)
- {
- return null;
- }
-
- // If BinOp binds less tightly with RHS than the operator after RHS, let
- // the pending operator take RHS as its LHS.
- int nextPrec = this.scanner.GetTokPrecedence();
- if (tokPrec < nextPrec)
- {
- rhs = this.ParseBinOpRHS(tokPrec + 1, rhs);
- if (rhs == null)
- {
- return null;
- }
- }
-
- // Merge LHS/RHS.
- lhs = new BinaryExprAST((char)binOp, lhs, rhs);
- }
- }
-
- // expression
- // ::= primary binoprhs
- //
- private ExprAST ParseExpression()
- {
- ExprAST lhs = this.ParsePrimary();
- if (lhs == null)
- {
- return null;
- }
-
- return this.ParseBinOpRHS(0, lhs);
- }
-
- // prototype
- // ::= id '(' id* ')'
- private PrototypeAST ParsePrototype()
- {
- if (this.scanner.CurrentToken != (int)Token.IDENTIFIER)
- {
- Console.WriteLine("Expected function name in prototype");
- return null;
- }
-
- string fnName = this.scanner.GetLastIdentifier();
-
- this.scanner.GetNextToken();
-
- if (this.scanner.CurrentToken != '(')
- {
- Console.WriteLine("Expected '(' in prototype");
- return null;
- }
-
- List argNames = new List();
- while (this.scanner.GetNextToken() == (int)Token.IDENTIFIER)
- {
- argNames.Add(this.scanner.GetLastIdentifier());
- }
-
- if (this.scanner.CurrentToken != ')')
- {
- Console.WriteLine("Expected ')' in prototype");
- return null;
- }
-
- this.scanner.GetNextToken(); // eat ')'.
-
- return new PrototypeAST(fnName, argNames);
- }
-
- // definition ::= 'def' prototype expression
- private FunctionAST ParseDefinition()
- {
- this.scanner.GetNextToken(); // eat def.
- PrototypeAST proto = this.ParsePrototype();
-
- if (proto == null)
- {
- return null;
- }
-
- ExprAST body = this.ParseExpression();
- if (body == null)
- {
- return null;
- }
-
- return new FunctionAST(proto, body);
- }
-
- /// toplevelexpr ::= expression
- private FunctionAST ParseTopLevelExpr()
- {
- ExprAST e = this.ParseExpression();
- if (e == null)
- {
- return null;
- }
-
- // Make an anonymous proto.
- PrototypeAST proto = new PrototypeAST(string.Empty, new List());
- return new FunctionAST(proto, e);
- }
-
- /// external ::= 'extern' prototype
- private PrototypeAST ParseExtern()
- {
- this.scanner.GetNextToken(); // eat extern.
- return this.ParsePrototype();
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Token.cs b/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Token.cs
deleted file mode 100644
index a4f84c23..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/Kaleidoscope/Token.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Kaleidoscope
-{
- public enum Token
- {
- EOF = -1,
- DEF = -2,
- EXTERN = -3,
- IDENTIFIER = -4,
- NUMBER = -5,
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/CodeGenParserListener.cs b/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/CodeGenParserListener.cs
deleted file mode 100644
index ed23a774..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/CodeGenParserListener.cs
+++ /dev/null
@@ -1,47 +0,0 @@
-using LLVMSharp;
-
-namespace KaleidoscopeLLVM
-{
- using Kaleidoscope;
- using Kaleidoscope.AST;
-
- internal sealed class CodeGenParserListener : IParserListener
- {
- private readonly CodeGenVisitor visitor;
-
- public CodeGenParserListener(CodeGenVisitor visitor)
- {
- this.visitor = visitor;
- }
-
- public void EnterHandleDefinition(FunctionAST data)
- {
- }
-
- public void ExitHandleDefinition(FunctionAST data)
- {
- this.visitor.Visit(data);
- LLVM.DumpValue(this.visitor.ResultStack.Pop());
- }
-
- public void EnterHandleExtern(PrototypeAST data)
- {
- }
-
- public void ExitHandleExtern(PrototypeAST data)
- {
- this.visitor.Visit(data);
- LLVM.DumpValue(this.visitor.ResultStack.Pop());
- }
-
- public void EnterHandleTopLevelExpression(FunctionAST data)
- {
- }
-
- public void ExitHandleTopLevelExpression(FunctionAST data)
- {
- this.visitor.Visit(data);
- LLVM.DumpValue(this.visitor.ResultStack.Pop());
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/CodeGenVisitor.cs b/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/CodeGenVisitor.cs
deleted file mode 100644
index f5aa974a..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/CodeGenVisitor.cs
+++ /dev/null
@@ -1,198 +0,0 @@
-using System;
-using System.Collections.Generic;
-using Kaleidoscope.AST;
-using LLVMSharp;
-
-namespace KaleidoscopeLLVM
-{
- internal sealed class CodeGenVisitor : ExprVisitor
- {
- private static readonly LLVMBool LLVMBoolFalse = new LLVMBool(0);
-
- private static readonly LLVMValueRef NullValue = new LLVMValueRef(IntPtr.Zero);
-
- private readonly LLVMModuleRef module;
-
- private readonly LLVMBuilderRef builder;
-
- private readonly Dictionary namedValues = new Dictionary();
-
- private readonly Stack valueStack = new Stack();
-
- public CodeGenVisitor(LLVMModuleRef module, LLVMBuilderRef builder)
- {
- this.module = module;
- this.builder = builder;
- }
-
- public Stack ResultStack { get { return valueStack; } }
-
- public void ClearResultStack()
- {
- this.valueStack.Clear();
- }
-
- protected override ExprAST VisitNumberExprAST(NumberExprAST node)
- {
- this.valueStack.Push(LLVM.ConstReal(LLVM.DoubleType(), node.Value));
- return node;
- }
-
- protected override ExprAST VisitVariableExprAST(VariableExprAST node)
- {
- LLVMValueRef value;
-
- // Look this variable up in the function.
- if (this.namedValues.TryGetValue(node.Name, out value))
- {
- this.valueStack.Push(value);
- }
- else
- {
- throw new Exception("Unknown variable name");
- }
-
- return node;
- }
-
- protected override ExprAST VisitBinaryExprAST(BinaryExprAST node)
- {
- this.Visit(node.Lhs);
- this.Visit(node.Rhs);
-
- LLVMValueRef r = this.valueStack.Pop();
- LLVMValueRef l = this.valueStack.Pop();
-
- LLVMValueRef n;
-
- switch (node.NodeType)
- {
- case ExprType.AddExpr:
- n = LLVM.BuildFAdd(this.builder, l, r, "addtmp");
- break;
- case ExprType.SubtractExpr:
- n = LLVM.BuildFSub(this.builder, l, r, "subtmp");
- break;
- case ExprType.MultiplyExpr:
- n = LLVM.BuildFMul(this.builder, l, r, "multmp");
- break;
- case ExprType.LessThanExpr:
- // Convert bool 0/1 to double 0.0 or 1.0
- n = LLVM.BuildUIToFP(this.builder, LLVM.BuildFCmp(this.builder, LLVMRealPredicate.LLVMRealULT, l, r, "cmptmp"), LLVM.DoubleType(), "booltmp");
- break;
- default:
- throw new Exception("invalid binary operator");
- }
-
- this.valueStack.Push(n);
- return node;
- }
-
- protected override ExprAST VisitCallExprAST(CallExprAST node)
- {
- var calleeF = LLVM.GetNamedFunction(this.module, node.Callee);
- if (calleeF.Pointer == IntPtr.Zero)
- {
- throw new Exception("Unknown function referenced");
- }
-
- if (LLVM.CountParams(calleeF) != node.Arguments.Count)
- {
- throw new Exception("Incorrect # arguments passed");
- }
-
- var argumentCount = (uint)node.Arguments.Count;
- var argsV = new LLVMValueRef[Math.Max(argumentCount, 1)];
- for (int i = 0; i < argumentCount; ++i)
- {
- this.Visit(node.Arguments[i]);
- argsV[i] = this.valueStack.Pop();
- }
-
- valueStack.Push(LLVM.BuildCall(this.builder, calleeF, argsV, "calltmp"));
-
- return node;
- }
-
- protected override ExprAST VisitPrototypeAST(PrototypeAST node)
- {
- // Make the function type: double(double,double) etc.
- var argumentCount = (uint)node.Arguments.Count;
- var arguments = new LLVMTypeRef[Math.Max(argumentCount, 1)];
-
- var function = LLVM.GetNamedFunction(this.module, node.Name);
-
- // If F conflicted, there was already something named 'Name'. If it has a
- // body, don't allow redefinition or reextern.
- if (function.Pointer != IntPtr.Zero)
- {
- // If F already has a body, reject this.
- if (LLVM.CountBasicBlocks(function) != 0)
- {
- throw new Exception("redefinition of function.");
- }
-
- // If F took a different number of args, reject.
- if (LLVM.CountParams(function) != argumentCount)
- {
- throw new Exception("redefinition of function with different # args");
- }
- }
- else
- {
- for (int i = 0; i < argumentCount; ++i)
- {
- arguments[i] = LLVM.DoubleType();
- }
-
- function = LLVM.AddFunction(this.module, node.Name, LLVM.FunctionType(LLVM.DoubleType(), arguments, LLVMBoolFalse));
- LLVM.SetLinkage(function, LLVMLinkage.LLVMExternalLinkage);
- }
-
- for (int i = 0; i < argumentCount; ++i)
- {
- string argumentName = node.Arguments[i];
-
- LLVMValueRef param = LLVM.GetParam(function, (uint)i);
- LLVM.SetValueName(param, argumentName);
-
- this.namedValues[argumentName] = param;
- }
-
- this.valueStack.Push(function);
- return node;
- }
-
- protected override ExprAST VisitFunctionAST(FunctionAST node)
- {
- this.namedValues.Clear();
-
- this.Visit(node.Proto);
-
- LLVMValueRef function = this.valueStack.Pop();
-
- // Create a new basic block to start insertion into.
- LLVM.PositionBuilderAtEnd(this.builder, LLVM.AppendBasicBlock(function, "entry"));
-
- try
- {
- this.Visit(node.Body);
- }
- catch (Exception)
- {
- LLVM.DeleteFunction(function);
- throw;
- }
-
- // Finish off the function.
- LLVM.BuildRet(this.builder, this.valueStack.Pop());
-
- // Validate the generated code, checking for consistency.
- LLVM.VerifyFunction(function, LLVMVerifierFailureAction.LLVMPrintMessageAction);
-
- this.valueStack.Push(function);
-
- return node;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/KaleidoscopeLLVM.csproj b/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/KaleidoscopeLLVM.csproj
deleted file mode 100644
index 96ff9d4b..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/KaleidoscopeLLVM.csproj
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- net10.0
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/Program.cs b/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/Program.cs
deleted file mode 100644
index 91acc031..00000000
--- a/samples/KaleidoscopeTutorial/Chapter3/KaleidoscopeLLVM/Program.cs
+++ /dev/null
@@ -1,68 +0,0 @@
-namespace KaleidoscopeLLVM
-{
- using System;
- using System.Collections.Generic;
- using Kaleidoscope;
- using LLVMSharp;
-
- public sealed class Program
- {
- private static void Main(string[] args)
- {
- // Make the module, which holds all the code.
- LLVMModuleRef module = LLVM.ModuleCreateWithName("my cool jit");
- LLVMBuilderRef builder = LLVM.CreateBuilder();
- var codeGenlistener = new CodeGenParserListener(new CodeGenVisitor(module, builder));
-
- // Install standard binary operators.
- // 1 is lowest precedence.
- var binopPrecedence = new Dictionary
- {
- ['<'] = 10,
- ['+'] = 20,
- ['-'] = 20,
- ['*'] = 40
- };
- // highest.
-
- var scanner = new Lexer(Console.In, binopPrecedence);
- var parser = new Parser(scanner, codeGenlistener);
-
- // Prime the first token.
- Console.Write("ready> ");
- scanner.GetNextToken();
-
- // Run the main "interpreter loop" now.
- MainLoop(scanner, parser);
-
- // Print out all of the generated code.
- LLVM.DumpModule(module);
- }
-
- private static void MainLoop(ILexer lexer, IParser parser)
- {
- // top ::= definition | external | expression | ';'
- while (true)
- {
- Console.Write("ready> ");
- switch (lexer.CurrentToken)
- {
- case (int)Token.EOF:
- return;
- case ';':
- lexer.GetNextToken();
- break;
- case (int)Token.DEF:
- parser.HandleDefinition();
- break;
- case (int)Token.EXTERN:
- parser.HandleExtern();
- break;
- default:
- parser.HandleTopLevelExpression();
- break;
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter3/Program.cs b/samples/KaleidoscopeTutorial/Chapter3/Program.cs
new file mode 100644
index 00000000..566fdd67
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter3/Program.cs
@@ -0,0 +1,31 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope;
+using Kaleidoscope.Chapter3;
+using LLVMSharp.Interop;
+
+// The built-in operator precedences (higher binds tighter). The lexer and parser share this
+// dictionary; from chapter 6 the parser adds user-defined operators to it at run time.
+var binaryOpPrecedence = new Dictionary
+{
+ ['<'] = 10,
+ ['+'] = 20,
+ ['-'] = 20,
+ ['*'] = 40,
+};
+
+using TextReader reader = args.Length > 0 ? new StreamReader(args[0]) : Console.In;
+
+var lexer = new Lexer(reader, binaryOpPrecedence);
+var parser = new Parser(lexer);
+var visitor = new CodeGenVisitor();
+
+LLVMContextRef context = LLVMContextRef.Create();
+LLVMModuleRef module = context.CreateModuleWithName("KaleidoscopeModule");
+
+var driver = new DumpDriver(lexer, parser, visitor, module);
+driver.Run();
+
+Console.WriteLine();
+Console.WriteLine("=== Full module IR ===");
+Console.Write(module.PrintToString());
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Chapter4.csproj b/samples/KaleidoscopeTutorial/Chapter4/Chapter4.csproj
new file mode 100644
index 00000000..678424d9
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter4/Chapter4.csproj
@@ -0,0 +1,8 @@
+
+
+ Exe
+
+
+
+
+
diff --git a/samples/KaleidoscopeTutorial/Chapter4/JitReplDriver.cs b/samples/KaleidoscopeTutorial/Chapter4/JitReplDriver.cs
new file mode 100644
index 00000000..29f19819
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter4/JitReplDriver.cs
@@ -0,0 +1,101 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+using LLVMSharp.Interop;
+
+namespace Kaleidoscope.Chapter4;
+
+///
+/// Chapter 4 — "Adding JIT and optimizer support". Reuses chapter 3's code generator unchanged, but
+/// instead of just printing IR it optimizes each top-level item and feeds it to an ORC LLJIT so
+/// top-level expressions actually run. Each item is compiled into its own module; the anonymous
+/// expression is added under a resource tracker and removed after it runs, so the REPL can evaluate
+/// any number of expressions in a row (upstream issue #1, which the old MCJIT sample could not do).
+///
+/// This driver is deliberately independent of the code generator's chapter — later chapters reuse it
+/// as-is and only pass in their own .
+///
+public sealed unsafe class JitReplDriver : ReplDriver, IDisposable
+{
+ // Function-level cleanup/optimization pipeline, parsed by the new pass manager. mem2reg only does
+ // work once chapter 7 introduces allocas; it is harmless before then, so one pipeline serves all.
+ private const string PassPipeline = "function(mem2reg,instcombine,reassociate,gvn,simplifycfg)";
+
+ private readonly CodeGenVisitorBase _visitor;
+ private readonly KaleidoscopeJit _jit;
+ private readonly LLVMTargetMachineRef _targetMachine;
+
+ public JitReplDriver(Lexer lexer, Parser parser, CodeGenVisitorBase visitor)
+ : base(lexer, parser)
+ {
+ _visitor = visitor;
+ _jit = new KaleidoscopeJit();
+ _targetMachine = LlvmSupport.CreateHostTargetMachine();
+
+ // Make putchard/printd available to any 'extern' that references them.
+ HostFunctions.DefineAll(_jit);
+ }
+
+ protected override void OnDefinition(FunctionAST function)
+ {
+ LLVMModuleRef module = CreateModule();
+ _visitor.SetModule(module);
+
+ LLVMValueRef ir = _visitor.CodegenFunction(function);
+ Console.WriteLine("Read function definition:");
+ Console.Write(ir);
+
+ Optimize(module);
+ _jit.AddModule(module); // keep the definition around so later expressions can call it.
+ }
+
+ protected override void OnExtern(PrototypeAST prototype)
+ {
+ // A declaration needs no module of its own — remember it so it is emitted into whichever
+ // module first references it (see CodeGenVisitorBase.GetFunction).
+ _visitor.RegisterPrototype(prototype);
+ Console.WriteLine($"Read extern: {prototype.Name}");
+ }
+
+ protected override void OnTopLevelExpression(FunctionAST function)
+ {
+ LLVMModuleRef module = CreateModule();
+ _visitor.SetModule(module);
+ _visitor.CodegenFunction(function);
+ Optimize(module);
+
+ // Add under a resource tracker so we can drop this anonymous module once we've called it,
+ // freeing the __anon_expr name for the next top-level expression.
+ nint tracker = _jit.AddModuleRemovable(module);
+ try
+ {
+ ulong address = _jit.Lookup(Parser.AnonymousExpressionName);
+ var evaluate = (delegate* unmanaged[Cdecl])address;
+ Console.WriteLine($"Evaluated to {evaluate()}");
+ }
+ finally
+ {
+ _jit.RemoveModule(tracker);
+ }
+ }
+
+ private LLVMModuleRef CreateModule()
+ {
+ // Each module is built in its own context; the JIT takes ownership of that context when the
+ // module is added. Stamp it with the JIT's data layout/triple so codegen matches the target.
+ LLVMContextRef context = LLVMContextRef.Create();
+ LLVMModuleRef module = context.CreateModuleWithName("KaleidoscopeModule");
+ _jit.ConfigureModule(module);
+ return module;
+ }
+
+ private void Optimize(LLVMModuleRef module)
+ {
+ using LLVMPassBuilderOptionsRef options = LLVMPassBuilderOptionsRef.Create();
+ using var passes = new MarshaledString(PassPipeline);
+ LLVMOpaqueError* error = LLVM.RunPasses(module, passes, _targetMachine, options);
+ LlvmSupport.ThrowIfError(error, "Optimization failed");
+ }
+
+ public void Dispose() => _jit.Dispose();
+}
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope.sln b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope.sln
deleted file mode 100644
index 1bb9c05b..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope.sln
+++ /dev/null
@@ -1,31 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.31101.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kaleidoscope", "Kaleidoscope\Kaleidoscope.csproj", "{CD23CB2E-951B-4FAD-A4CD-4048731DBA31}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KaleidoscopeLLVM", "KaleidoscopeLLVM\KaleidoscopeLLVM.csproj", "{C1147A13-4174-410A-A891-335D858703B7}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
- Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Release|Any CPU.Build.0 = Release|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Release|Any CPU.Build.0 = Release|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Release|x64.ActiveCfg = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/BinaryExprAST.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/BinaryExprAST.cs
deleted file mode 100644
index 1143f724..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/BinaryExprAST.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-namespace Kaleidoscope.AST
-{
- using System;
-
- public sealed class BinaryExprAST : ExprAST
- {
- public BinaryExprAST(char op, ExprAST lhs, ExprAST rhs)
- {
- switch (op)
- {
- case '+':
- this.NodeType = ExprType.AddExpr;
- break;
- case '-':
- this.NodeType = ExprType.SubtractExpr;
- break;
- case '*':
- this.NodeType = ExprType.MultiplyExpr;
- break;
- case '<':
- this.NodeType = ExprType.LessThanExpr;
- break;
- default:
- throw new ArgumentException("op " + op + " is not a valid operator");
- }
-
- this.Lhs = lhs;
- this.Rhs = rhs;
- }
-
- public ExprAST Lhs { get; private set; }
-
- public ExprAST Rhs { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitBinaryExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/CallExprAST.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/CallExprAST.cs
deleted file mode 100644
index 1a722b27..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/CallExprAST.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace Kaleidoscope.AST
-{
- using System.Collections.Generic;
-
- public sealed class CallExprAST : ExprAST
- {
- public CallExprAST(string callee, List args)
- {
- this.Callee = callee;
- this.Arguments = args;
- this.NodeType = ExprType.CallExpr;
- }
-
- public string Callee { get; private set; }
-
- public List Arguments { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitCallExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/ExprAST.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/ExprAST.cs
deleted file mode 100644
index 6841cbec..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/ExprAST.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public abstract class ExprAST
- {
- public abstract ExprType NodeType { get; protected set; }
-
- protected internal virtual ExprAST VisitChildren(ExprVisitor visitor)
- {
- return visitor.Visit(this);
- }
-
- protected internal virtual ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitExtension(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/ExprType.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/ExprType.cs
deleted file mode 100644
index e890f346..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/ExprType.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public enum ExprType
- {
- AddExpr,
- SubtractExpr,
- MultiplyExpr,
- LessThanExpr,
- CallExpr,
- VariableExpr,
- PrototypeExpr,
- FunctionExpr,
- NumberExpr
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/ExprVisitor.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/ExprVisitor.cs
deleted file mode 100644
index a357e1d4..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/ExprVisitor.cs
+++ /dev/null
@@ -1,60 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public abstract class ExprVisitor
- {
- protected ExprVisitor()
- {
- }
-
- public virtual ExprAST Visit(ExprAST node)
- {
- return node?.Accept(this);
- }
-
- protected internal virtual ExprAST VisitExtension(ExprAST node)
- {
- return node.VisitChildren(this);
- }
-
- protected internal virtual ExprAST VisitBinaryExprAST(BinaryExprAST node)
- {
- this.Visit(node.Lhs);
- this.Visit(node.Rhs);
-
- return node;
- }
-
- protected internal virtual ExprAST VisitCallExprAST(CallExprAST node)
- {
- foreach (var argument in node.Arguments)
- {
- this.Visit(argument);
- }
-
- return node;
- }
-
- protected internal virtual ExprAST VisitFunctionAST(FunctionAST node)
- {
- this.Visit(node.Proto);
- this.Visit(node.Body);
-
- return node;
- }
-
- protected internal virtual ExprAST VisitVariableExprAST(VariableExprAST node)
- {
- return node;
- }
-
- protected internal virtual ExprAST VisitPrototypeAST(PrototypeAST node)
- {
- return node;
- }
-
- protected internal virtual ExprAST VisitNumberExprAST(NumberExprAST node)
- {
- return node;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/FunctionAST.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/FunctionAST.cs
deleted file mode 100644
index a5e90405..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/FunctionAST.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class FunctionAST : ExprAST
- {
- public FunctionAST(PrototypeAST proto, ExprAST body)
- {
- this.Proto = proto;
- this.Body = body;
- this.NodeType = ExprType.FunctionExpr;
- }
-
- public PrototypeAST Proto { get; private set; }
-
- public ExprAST Body { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitFunctionAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/NumberExprAST.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/NumberExprAST.cs
deleted file mode 100644
index bd823cd3..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/NumberExprAST.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class NumberExprAST : ExprAST
- {
- public NumberExprAST(double value)
- {
- this.Value = value;
- this.NodeType = ExprType.NumberExpr;
- }
-
- public double Value { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitNumberExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/PrototypeAST.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/PrototypeAST.cs
deleted file mode 100644
index 3d0f6b4a..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/PrototypeAST.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace Kaleidoscope.AST
-{
- using System.Collections.Generic;
-
- public sealed class PrototypeAST : ExprAST
- {
- public PrototypeAST(string name, List args)
- {
- this.Name = name;
- this.Arguments = args;
- this.NodeType = ExprType.PrototypeExpr;
- }
-
- public string Name { get; private set; }
-
- public List Arguments { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitPrototypeAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/VariableExprAST.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/VariableExprAST.cs
deleted file mode 100644
index 41aeb6c2..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/AST/VariableExprAST.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class VariableExprAST : ExprAST
- {
- public VariableExprAST(string name)
- {
- this.Name = name;
- this.NodeType = ExprType.VariableExpr;
- }
-
- public string Name { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitVariableExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/BaseParserListener.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/BaseParserListener.cs
deleted file mode 100644
index 6fb50254..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/BaseParserListener.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-namespace Kaleidoscope
-{
- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using AST;
-
- internal sealed class BaseParserListener
- {
- private static readonly Type IParserListenerType = typeof(IParserListener);
-
- private readonly Stack descentStack = new Stack();
-
- private readonly Stack ascentStack = new Stack();
-
- private readonly IParserListener listener;
-
- public BaseParserListener(IParserListener listener)
- {
- this.listener = listener;
- }
-
- public void EnterRule(string ruleName)
- {
- this.descentStack.Push(ruleName);
- }
-
- public void ExitRule(ExprAST argument)
- {
- string ruleName = this.descentStack.Pop();
- this.ascentStack.Push(new ASTContext(IParserListenerType.GetMethod("Exit" + ruleName), this.listener,
- argument));
- this.ascentStack.Push(new ASTContext(IParserListenerType.GetMethod("Enter" + ruleName), this.listener,
- argument));
- }
-
- public void Listen()
- {
- if (this.listener != null)
- {
- while (this.ascentStack.Count != 0)
- {
- var context = this.ascentStack.Pop();
- context.MethodInfo.Invoke(context.Instance, new object[] {context.Argument});
- }
- }
- }
-
- private sealed class ASTContext
- {
- public ASTContext(MethodInfo methodInfo, object instance, ExprAST argument)
- {
- this.MethodInfo = methodInfo;
- this.Instance = instance;
- this.Argument = argument;
- }
-
- public MethodInfo MethodInfo { get; private set; }
-
- public ExprAST Argument { get; set; }
-
- public object Instance { get; private set; }
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/ILexer.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/ILexer.cs
deleted file mode 100644
index aca5d18f..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/ILexer.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Kaleidoscope
-{
- public interface ILexer
- {
- int CurrentToken { get; }
-
- string GetLastIdentifier();
-
- double GetLastNumber();
-
- int GetTokPrecedence();
-
- int GetNextToken();
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/IParser.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/IParser.cs
deleted file mode 100644
index adf84705..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/IParser.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Kaleidoscope
-{
- public interface IParser
- {
- void HandleDefinition();
-
- void HandleExtern();
-
- void HandleTopLevelExpression();
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/IParserListener.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/IParserListener.cs
deleted file mode 100644
index fbc91f82..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/IParserListener.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Kaleidoscope
-{
- using AST;
-
- public interface IParserListener
- {
- void EnterHandleDefinition(FunctionAST data);
-
- void ExitHandleDefinition(FunctionAST data);
-
- void EnterHandleExtern(PrototypeAST data);
-
- void ExitHandleExtern(PrototypeAST data);
-
- void EnterHandleTopLevelExpression(FunctionAST data);
-
- void ExitHandleTopLevelExpression(FunctionAST data);
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Kaleidoscope.csproj b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Kaleidoscope.csproj
deleted file mode 100644
index 09321765..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Kaleidoscope.csproj
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- net10.0
-
-
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Lexer.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Lexer.cs
deleted file mode 100644
index f73d0612..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Lexer.cs
+++ /dev/null
@@ -1,133 +0,0 @@
-namespace Kaleidoscope
-{
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Text;
-
- public sealed class Lexer : ILexer
- {
- private const int EOF = -1;
-
- private readonly TextReader reader;
-
- private readonly StringBuilder identifierBuilder = new StringBuilder();
-
- private readonly StringBuilder numberBuilder = new StringBuilder();
-
- private readonly Dictionary binopPrecedence;
-
- private int c = ' ';
-
- private string identifier;
-
- private double numVal;
-
- public Lexer(TextReader reader, Dictionary binOpPrecedence)
- {
- this.reader = reader;
- this.binopPrecedence = binOpPrecedence;
- }
-
- public int CurrentToken { get; private set; }
-
- public string GetLastIdentifier()
- {
- return this.identifier;
- }
-
- public double GetLastNumber()
- {
- return this.numVal;
- }
-
- public int GetTokPrecedence()
- {
- // Make sure it's a declared binop.
- if (this.binopPrecedence.TryGetValue((char)this.CurrentToken, out var tokPrec))
- {
- return tokPrec;
- }
-
- return -1;
- }
-
- public int GetNextToken()
- {
- // Skip any whitespace.
- while (char.IsWhiteSpace((char)c))
- {
- c = this.reader.Read();
- }
-
- if (char.IsLetter((char)c)) // identifier: [a-zA-Z][a-zA-Z0-9]*
- {
- this.identifierBuilder.Append((char)c);
- while (char.IsLetterOrDigit((char)(c = this.reader.Read())))
- {
- this.identifierBuilder.Append((char)c);
- }
-
- this.identifier = this.identifierBuilder.ToString();
- this.identifierBuilder.Clear();
-
- if (string.Equals(identifier, "def", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.DEF;
- }
- else if (string.Equals(identifier, "extern", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.EXTERN;
- }
- else
- {
- this.CurrentToken = (int)Token.IDENTIFIER;
- }
-
- return this.CurrentToken;
- }
-
- // Number: [0-9.]+
- if (char.IsDigit((char)c) || c == '.')
- {
- do
- {
- this.numberBuilder.Append((char)c);
- c = this.reader.Read();
- } while (char.IsDigit((char)c) || c == '.');
-
- this.numVal = double.Parse(this.numberBuilder.ToString());
- this.numberBuilder.Clear();
- this.CurrentToken = (int)Token.NUMBER;
-
- return this.CurrentToken;
- }
-
- if (c == '#')
- {
- // Comment until end of line.
- do
- {
- c = this.reader.Read();
- } while (c != EOF && c != '\n' && c != '\r');
-
- if (c != EOF)
- {
- return this.GetNextToken();
- }
- }
-
- // Check for end of file. Don't eat the EOF.
- if (c == EOF)
- {
- this.CurrentToken = c;
- return (int)Token.EOF;
- }
-
- this.CurrentToken = c;
- c = this.reader.Read();
- return this.c;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Parser.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Parser.cs
deleted file mode 100644
index bd17834f..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Parser.cs
+++ /dev/null
@@ -1,313 +0,0 @@
-namespace Kaleidoscope
-{
- using System;
- using System.Collections.Generic;
- using AST;
-
- public sealed class Parser : IParser
- {
- private readonly Lexer scanner;
-
- private readonly BaseParserListener baseListener;
-
- public Parser(Lexer scanner, IParserListener listener)
- {
- this.scanner = scanner;
- this.baseListener = new BaseParserListener(listener);
- }
-
- public void HandleDefinition()
- {
- this.baseListener.EnterRule("HandleDefinition");
-
- var functionAST = this.ParseDefinition();
-
- baseListener.ExitRule(functionAST);
-
- if (functionAST != null)
- {
- this.baseListener.Listen();
- }
- else
- {
- // Skip token for error recovery.
- this.scanner.GetNextToken();
- }
- }
-
- public void HandleExtern()
- {
- this.baseListener.EnterRule("HandleExtern");
-
- var prototypeAST = this.ParseExtern();
-
- this.baseListener.ExitRule(prototypeAST);
-
- if (prototypeAST != null)
- {
- this.baseListener.Listen();
- }
- else
- {
- // Skip token for error recovery.
- this.scanner.GetNextToken();
- }
- }
-
- public void HandleTopLevelExpression()
- {
- // Evaluate a top-level expression into an anonymous function.
- this.baseListener.EnterRule("HandleTopLevelExpression");
-
- var functionAST = this.ParseTopLevelExpr();
-
- this.baseListener.ExitRule(functionAST);
-
- if (functionAST != null)
- {
- this.baseListener.Listen();
- }
- else
- {
- // Skip token for error recovery.
- this.scanner.GetNextToken();
- }
- }
-
- // identifierexpr
- // ::= identifier
- // ::= identifier '(' expression* ')'
- private ExprAST ParseIdentifierExpr()
- {
- string idName = this.scanner.GetLastIdentifier();
-
- this.scanner.GetNextToken(); // eat identifier.
-
- if (this.scanner.CurrentToken != '(') // Simple variable ref.
- {
- return new VariableExprAST(idName);
- }
-
- // Call.
- this.scanner.GetNextToken(); // eat (
- List args = new List();
-
- if (this.scanner.CurrentToken != ')')
- {
- while (true)
- {
- ExprAST arg = this.ParseExpression();
- if (arg == null)
- {
- return null;
- }
-
- args.Add(arg);
-
- if (this.scanner.CurrentToken == ')')
- {
- break;
- }
-
- if (this.scanner.CurrentToken != ',')
- {
- Console.WriteLine("Expected ')' or ',' in argument list");
- return null;
- }
-
- this.scanner.GetNextToken();
- }
- }
-
- // Eat the ')'.
- this.scanner.GetNextToken();
-
- return new CallExprAST(idName, args);
- }
-
- // numberexpr ::= number
- private ExprAST ParseNumberExpr()
- {
- ExprAST result = new NumberExprAST(this.scanner.GetLastNumber());
- this.scanner.GetNextToken();
- return result;
- }
-
- // parenexpr ::= '(' expression ')'
- private ExprAST ParseParenExpr()
- {
- this.scanner.GetNextToken(); // eat (.
- ExprAST v = this.ParseExpression();
- if (v == null)
- {
- return null;
- }
-
- if (this.scanner.CurrentToken != ')')
- {
- Console.WriteLine("expected ')'");
- return null;
- }
-
- this.scanner.GetNextToken(); // eat ).
-
- return v;
- }
-
- // primary
- // ::= identifierexpr
- // ::= numberexpr
- // ::= parenexpr
- private ExprAST ParsePrimary()
- {
- switch (this.scanner.CurrentToken)
- {
- case (int) Token.IDENTIFIER:
- return this.ParseIdentifierExpr();
- case (int) Token.NUMBER:
- return this.ParseNumberExpr();
- case '(':
- return this.ParseParenExpr();
- default:
- Console.WriteLine("unknown token when expecting an expression");
- return null;
- }
- }
-
- // binoprhs
- // ::= ('+' primary)*
- private ExprAST ParseBinOpRHS(int exprPrec, ExprAST lhs)
- {
- // If this is a binop, find its precedence.
- while (true)
- {
- int tokPrec = this.scanner.GetTokPrecedence();
-
- // If this is a binop that binds at least as tightly as the current binop,
- // consume it, otherwise we are done.
- if (tokPrec < exprPrec)
- {
- return lhs;
- }
-
- // Okay, we know this is a binop.
- int binOp = this.scanner.CurrentToken;
- this.scanner.GetNextToken(); // eat binop
-
- // Parse the primary expression after the binary operator.
- ExprAST rhs = this.ParsePrimary();
- if (rhs == null)
- {
- return null;
- }
-
- // If BinOp binds less tightly with RHS than the operator after RHS, let
- // the pending operator take RHS as its LHS.
- int nextPrec = this.scanner.GetTokPrecedence();
- if (tokPrec < nextPrec)
- {
- rhs = this.ParseBinOpRHS(tokPrec + 1, rhs);
- if (rhs == null)
- {
- return null;
- }
- }
-
- // Merge LHS/RHS.
- lhs = new BinaryExprAST((char) binOp, lhs, rhs);
- }
- }
-
- // expression
- // ::= primary binoprhs
- //
- private ExprAST ParseExpression()
- {
- ExprAST lhs = this.ParsePrimary();
- if (lhs == null)
- {
- return null;
- }
-
- return this.ParseBinOpRHS(0, lhs);
- }
-
- // prototype
- // ::= id '(' id* ')'
- private PrototypeAST ParsePrototype()
- {
- if (this.scanner.CurrentToken != (int) Token.IDENTIFIER)
- {
- Console.WriteLine("Expected function name in prototype");
- return null;
- }
-
- string fnName = this.scanner.GetLastIdentifier();
-
- this.scanner.GetNextToken();
-
- if (this.scanner.CurrentToken != '(')
- {
- Console.WriteLine("Expected '(' in prototype");
- return null;
- }
-
- List argNames = new List();
- while (this.scanner.GetNextToken() == (int) Token.IDENTIFIER)
- {
- argNames.Add(this.scanner.GetLastIdentifier());
- }
-
- if (this.scanner.CurrentToken != ')')
- {
- Console.WriteLine("Expected ')' in prototype");
- return null;
- }
-
- this.scanner.GetNextToken(); // eat ')'.
-
- return new PrototypeAST(fnName, argNames);
- }
-
- // definition ::= 'def' prototype expression
- private FunctionAST ParseDefinition()
- {
- this.scanner.GetNextToken(); // eat def.
- PrototypeAST proto = this.ParsePrototype();
-
- if (proto == null)
- {
- return null;
- }
-
- ExprAST body = this.ParseExpression();
- if (body == null)
- {
- return null;
- }
-
- return new FunctionAST(proto, body);
- }
-
- /// toplevelexpr ::= expression
- private FunctionAST ParseTopLevelExpr()
- {
- ExprAST e = this.ParseExpression();
- if (e == null)
- {
- return null;
- }
-
- // Make an anonymous proto.
- PrototypeAST proto = new PrototypeAST(string.Empty, new List());
- return new FunctionAST(proto, e);
- }
-
- /// external ::= 'extern' prototype
- private PrototypeAST ParseExtern()
- {
- this.scanner.GetNextToken(); // eat extern.
- return this.ParsePrototype();
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Token.cs b/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Token.cs
deleted file mode 100644
index a4f84c23..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/Kaleidoscope/Token.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Kaleidoscope
-{
- public enum Token
- {
- EOF = -1,
- DEF = -2,
- EXTERN = -3,
- IDENTIFIER = -4,
- NUMBER = -5,
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/CodeGenParserListener.cs b/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/CodeGenParserListener.cs
deleted file mode 100644
index d1cd661f..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/CodeGenParserListener.cs
+++ /dev/null
@@ -1,66 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using LLVMSharp;
-
-namespace KaleidoscopeLLVM
-{
- using Kaleidoscope;
- using Kaleidoscope.AST;
-
- internal sealed class CodeGenParserListener : IParserListener
- {
- private readonly CodeGenVisitor visitor;
-
- private readonly LLVMExecutionEngineRef ee;
-
- private readonly LLVMPassManagerRef passManager;
-
- public CodeGenParserListener(LLVMExecutionEngineRef ee, LLVMPassManagerRef passManager, CodeGenVisitor visitor)
- {
- this.visitor = visitor;
- this.ee = ee;
- this.passManager = passManager;
- }
-
- public void EnterHandleDefinition(FunctionAST data)
- {
- }
-
- public void ExitHandleDefinition(FunctionAST data)
- {
- this.visitor.Visit(data);
- var function = this.visitor.ResultStack.Pop();
- // LLVM.DumpValue(function);
-
- LLVM.RunFunctionPassManager(this.passManager, function);
- // LLVM.DumpValue(function); // Dump the function for exposition purposes.
- }
-
- public void EnterHandleExtern(PrototypeAST data)
- {
- }
-
- public void ExitHandleExtern(PrototypeAST data)
- {
- this.visitor.Visit(data);
- // LLVM.DumpValue(this.visitor.ResultStack.Pop());
- }
-
- public void EnterHandleTopLevelExpression(FunctionAST data)
- {
- }
-
- public void ExitHandleTopLevelExpression(FunctionAST data)
- {
- this.visitor.Visit(data);
- var anonymousFunction = this.visitor.ResultStack.Pop();
-// LLVM.DumpValue(anonymousFunction); // Dump the function for exposition purposes.
- var dFunc = (Program.D) Marshal.GetDelegateForFunctionPointer(
- LLVM.GetPointerToGlobal(this.ee, anonymousFunction), typeof(Program.D));
- LLVM.RunFunctionPassManager(this.passManager, anonymousFunction);
-
-// LLVM.DumpValue(anonymousFunction); // Dump the function for exposition purposes.
- Console.WriteLine("Evaluated to " + dFunc());
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/CodeGenVisitor.cs b/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/CodeGenVisitor.cs
deleted file mode 100644
index 275b1796..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/CodeGenVisitor.cs
+++ /dev/null
@@ -1,199 +0,0 @@
-namespace KaleidoscopeLLVM
-{
- using System;
- using System.Collections.Generic;
- using Kaleidoscope.AST;
- using LLVMSharp;
-
- internal sealed class CodeGenVisitor : ExprVisitor
- {
- private static readonly LLVMBool LLVMBoolFalse = new LLVMBool(0);
-
- private static readonly LLVMValueRef NullValue = new LLVMValueRef(IntPtr.Zero);
-
- private readonly LLVMModuleRef module;
-
- private readonly LLVMBuilderRef builder;
-
- private readonly Dictionary namedValues = new Dictionary();
-
- private readonly Stack valueStack = new Stack();
-
- public CodeGenVisitor(LLVMModuleRef module, LLVMBuilderRef builder)
- {
- this.module = module;
- this.builder = builder;
- }
-
- public Stack ResultStack => valueStack;
-
- public void ClearResultStack()
- {
- this.valueStack.Clear();
- }
-
- protected override ExprAST VisitNumberExprAST(NumberExprAST node)
- {
- this.valueStack.Push(LLVM.ConstReal(LLVM.DoubleType(), node.Value));
- return node;
- }
-
- protected override ExprAST VisitVariableExprAST(VariableExprAST node)
- {
- // Look this variable up in the function.
- if (this.namedValues.TryGetValue(node.Name, out var value))
- {
- this.valueStack.Push(value);
- }
- else
- {
- throw new Exception($"Unknown variable name {node.Name}");
- }
-
- return node;
- }
-
- protected override ExprAST VisitBinaryExprAST(BinaryExprAST node)
- {
- this.Visit(node.Lhs);
- this.Visit(node.Rhs);
-
- LLVMValueRef r = this.valueStack.Pop();
- LLVMValueRef l = this.valueStack.Pop();
-
- LLVMValueRef n;
-
- switch (node.NodeType)
- {
- case ExprType.AddExpr:
- n = LLVM.BuildFAdd(this.builder, l, r, "addtmp");
- break;
- case ExprType.SubtractExpr:
- n = LLVM.BuildFSub(this.builder, l, r, "subtmp");
- break;
- case ExprType.MultiplyExpr:
- n = LLVM.BuildFMul(this.builder, l, r, "multmp");
- break;
- case ExprType.LessThanExpr:
- // Convert bool 0/1 to double 0.0 or 1.0
- n = LLVM.BuildUIToFP(this.builder,
- LLVM.BuildFCmp(this.builder, LLVMRealPredicate.LLVMRealULT, l, r, "cmptmp"), LLVM.DoubleType(),
- "booltmp");
- break;
- default:
- throw new Exception("invalid binary operator");
- }
-
- this.valueStack.Push(n);
- return node;
- }
-
- protected override ExprAST VisitCallExprAST(CallExprAST node)
- {
- var calleeF = LLVM.GetNamedFunction(this.module, node.Callee);
- if (calleeF.Pointer == IntPtr.Zero)
- {
- throw new Exception($"Unknown function referenced {node.Callee}");
- }
-
- if (LLVM.CountParams(calleeF) != node.Arguments.Count)
- {
- throw new Exception("Incorrect # arguments passed");
- }
-
- var argumentCount = (uint) node.Arguments.Count;
- var argsV = new LLVMValueRef[Math.Max(argumentCount, 1)];
- for (int i = 0; i < argumentCount; ++i)
- {
- this.Visit(node.Arguments[i]);
- argsV[i] = this.valueStack.Pop();
- }
-
- this.valueStack.Push(LLVM.BuildCall(this.builder, calleeF, argsV, "calltmp"));
-
- return node;
- }
-
- protected override ExprAST VisitPrototypeAST(PrototypeAST node)
- {
- // Make the function type: double(double,double) etc.
- var argumentCount = (uint) node.Arguments.Count;
- var arguments = new LLVMTypeRef[Math.Max(argumentCount, 0)];
-
- var function = LLVM.GetNamedFunction(this.module, node.Name);
-
- // If F conflicted, there was already something named 'Name'. If it has a
- // body, don't allow redefinition or reextern.
- if (function.Pointer != IntPtr.Zero)
- {
- // If F already has a body, reject this.
- if (LLVM.CountBasicBlocks(function) != 0)
- {
- throw new Exception("redefinition of function.");
- }
-
- // If F took a different number of args, reject.
- if (LLVM.CountParams(function) != argumentCount)
- {
- throw new Exception("redefinition of function with different # args");
- }
- }
- else
- {
- for (int i = 0; i < argumentCount; ++i)
- {
- arguments[i] = LLVM.DoubleType();
- }
-
- function = LLVM.AddFunction(this.module, node.Name,
- LLVM.FunctionType(LLVM.DoubleType(), arguments, LLVMBoolFalse));
- LLVM.SetLinkage(function, LLVMLinkage.LLVMExternalLinkage);
- }
-
- for (int i = 0; i < argumentCount; ++i)
- {
- var argumentName = node.Arguments[i];
-
- LLVMValueRef param = LLVM.GetParam(function, (uint) i);
- LLVM.SetValueName(param, argumentName);
-
- this.namedValues[argumentName] = param;
- }
-
- this.valueStack.Push(function);
- return node;
- }
-
- protected override ExprAST VisitFunctionAST(FunctionAST node)
- {
- namedValues.Clear();
-
- Visit(node.Proto);
-
- LLVMValueRef function = valueStack.Pop();
-
- // Create a new basic block to start insertion into.
- LLVM.PositionBuilderAtEnd(builder, LLVM.AppendBasicBlock(function, "entry"));
-
- try
- {
- Visit(node.Body);
- }
- catch (Exception)
- {
- LLVM.DeleteFunction(function);
- throw;
- }
-
- // Finish off the function.
- LLVM.BuildRet(builder, valueStack.Pop());
-
- // Validate the generated code, checking for consistency.
- LLVM.VerifyFunction(function, LLVMVerifierFailureAction.LLVMPrintMessageAction);
-
- valueStack.Push(function);
-
- return node;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/KaleidoscopeLLVM.csproj b/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/KaleidoscopeLLVM.csproj
deleted file mode 100644
index 96ff9d4b..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/KaleidoscopeLLVM.csproj
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- net10.0
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/Program.cs b/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/Program.cs
deleted file mode 100644
index edfc659c..00000000
--- a/samples/KaleidoscopeTutorial/Chapter4/KaleidoscopeLLVM/Program.cs
+++ /dev/null
@@ -1,111 +0,0 @@
-namespace KaleidoscopeLLVM
-{
- using System;
- using System.Collections.Generic;
- using Kaleidoscope;
- using LLVMSharp;
-
- public sealed class Program
- {
- public delegate double D();
-
- public static void Main(string[] args)
- {
- // Make the module, which holds all the code.
- LLVMModuleRef module = LLVM.ModuleCreateWithName("my cool jit");
- LLVMBuilderRef builder = LLVM.CreateBuilder();
- LLVM.LinkInMCJIT();
- LLVM.InitializeX86TargetMC();
- LLVM.InitializeX86Target();
- LLVM.InitializeX86TargetInfo();
- LLVM.InitializeX86AsmParser();
- LLVM.InitializeX86AsmPrinter();
-
- if (LLVM.CreateExecutionEngineForModule(out var engine, module, out var errorMessage).Value == 1)
- {
- Console.WriteLine(errorMessage);
- // LLVM.DisposeMessage(errorMessage);
- return;
- }
-
- // Create a function pass manager for this engine
- LLVMPassManagerRef passManager = LLVM.CreateFunctionPassManagerForModule(module);
-
- // Set up the optimizer pipeline. Start with registering info about how the
- // target lays out data structures.
- // LLVM.DisposeTargetData(LLVM.GetExecutionEngineTargetData(engine));
-
- // Provide basic AliasAnalysis support for GVN.
- LLVM.AddBasicAliasAnalysisPass(passManager);
-
- // Promote allocas to registers.
- LLVM.AddPromoteMemoryToRegisterPass(passManager);
-
- // Do simple "peephole" optimizations and bit-twiddling optzns.
- LLVM.AddInstructionCombiningPass(passManager);
-
- // Reassociate expressions.
- LLVM.AddReassociatePass(passManager);
-
- // Eliminate Common SubExpressions.
- LLVM.AddGVNPass(passManager);
-
- // Simplify the control flow graph (deleting unreachable blocks, etc).
- LLVM.AddCFGSimplificationPass(passManager);
-
- LLVM.InitializeFunctionPassManager(passManager);
-
- var codeGenlistener = new CodeGenParserListener(engine, passManager, new CodeGenVisitor(module, builder));
-
- // Install standard binary operators.
- // 1 is lowest precedence.
- var binopPrecedence = new Dictionary
- {
- ['<'] = 10,
- ['+'] = 20,
- ['-'] = 20,
- ['*'] = 40
- };
- // highest.
-
- var scanner = new Lexer(Console.In, binopPrecedence);
- var parser = new Parser(scanner, codeGenlistener);
-
- // Prime the first token.
- Console.Write("ready> ");
- scanner.GetNextToken();
-
- // Run the main "interpreter loop" now.
- MainLoop(scanner, parser);
-
- // Print out all of the generated code.
- LLVM.DumpModule(module);
- }
-
- private static void MainLoop(ILexer lexer, IParser parser)
- {
- // top ::= definition | external | expression | ';'
- while (true)
- {
- Console.Write("ready> ");
- switch (lexer.CurrentToken)
- {
- case (int) Token.EOF:
- return;
- case ';':
- lexer.GetNextToken();
- break;
- case (int) Token.DEF:
- parser.HandleDefinition();
- break;
- case (int) Token.EXTERN:
- parser.HandleExtern();
- break;
- default:
- parser.HandleTopLevelExpression();
- break;
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter4/Program.cs b/samples/KaleidoscopeTutorial/Chapter4/Program.cs
new file mode 100644
index 00000000..eb7b54e8
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter4/Program.cs
@@ -0,0 +1,23 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope;
+using Kaleidoscope.Chapter4;
+
+// Chapter 4 keeps chapter 3's frontend and code generator verbatim and only swaps the dump driver for
+// a JIT driver, so a top-level expression is compiled and executed instead of just printed.
+var binaryOpPrecedence = new Dictionary
+{
+ ['<'] = 10,
+ ['+'] = 20,
+ ['-'] = 20,
+ ['*'] = 40,
+};
+
+using TextReader reader = args.Length > 0 ? new StreamReader(args[0]) : Console.In;
+
+var lexer = new Lexer(reader, binaryOpPrecedence);
+var parser = new Parser(lexer);
+var visitor = new Kaleidoscope.Chapter3.CodeGenVisitor();
+
+using var driver = new JitReplDriver(lexer, parser, visitor);
+driver.Run();
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Chapter5.csproj b/samples/KaleidoscopeTutorial/Chapter5/Chapter5.csproj
new file mode 100644
index 00000000..e5d166ab
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter5/Chapter5.csproj
@@ -0,0 +1,8 @@
+
+
+ Exe
+
+
+
+
+
diff --git a/samples/KaleidoscopeTutorial/Chapter5/CodeGenVisitor.cs b/samples/KaleidoscopeTutorial/Chapter5/CodeGenVisitor.cs
new file mode 100644
index 00000000..ba4e95f5
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter5/CodeGenVisitor.cs
@@ -0,0 +1,103 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+using LLVMSharp.Interop;
+
+namespace Kaleidoscope.Chapter5;
+
+///
+/// Chapter 5 code generation delta — lowers if/then/else and for/in to control flow.
+/// The loop variable here is an SSA PHI node; chapter 7 revisits it with stack allocations so it can
+/// be mutated. Everything else is inherited from chapter 3.
+///
+public class CodeGenVisitor : Chapter3.CodeGenVisitor
+{
+ protected override LLVMValueRef CodegenIf(IfExprAST node)
+ {
+ LLVMValueRef condition = Codegen(node.Condition);
+
+ // Convert the condition to a bool by comparing it to 0.0.
+ LLVMValueRef zero = LLVMValueRef.CreateConstReal(Module.Context.DoubleType, 0.0);
+ condition = Builder.BuildFCmp(LLVMRealPredicate.LLVMRealONE, condition, zero, "ifcond");
+
+ LLVMValueRef function = Builder.InsertBlock.Parent;
+ LLVMBasicBlockRef thenBlock = function.AppendBasicBlock("then");
+ LLVMBasicBlockRef elseBlock = function.AppendBasicBlock("else");
+ LLVMBasicBlockRef mergeBlock = function.AppendBasicBlock("ifcont");
+
+ Builder.BuildCondBr(condition, thenBlock, elseBlock);
+
+ // Emit the 'then' value.
+ Builder.PositionAtEnd(thenBlock);
+ LLVMValueRef thenValue = Codegen(node.Then);
+ Builder.BuildBr(mergeBlock);
+ thenBlock = Builder.InsertBlock; // codegen of 'then' may have changed the current block.
+
+ // Emit the 'else' value.
+ Builder.PositionAtEnd(elseBlock);
+ LLVMValueRef elseValue = Codegen(node.Else);
+ Builder.BuildBr(mergeBlock);
+ elseBlock = Builder.InsertBlock; // codegen of 'else' may have changed the current block.
+
+ // Emit the merge block with a PHI selecting the value from whichever branch ran.
+ Builder.PositionAtEnd(mergeBlock);
+ LLVMValueRef phi = Builder.BuildPhi(Module.Context.DoubleType, "iftmp");
+ phi.AddIncoming([thenValue, elseValue], [thenBlock, elseBlock], 2);
+ return phi;
+ }
+
+ protected override LLVMValueRef CodegenFor(ForExprAST node)
+ {
+ // Emit the start value first, in the current (preheader) block.
+ LLVMValueRef startValue = Codegen(node.Start);
+
+ LLVMValueRef function = Builder.InsertBlock.Parent;
+ LLVMBasicBlockRef preheaderBlock = Builder.InsertBlock;
+ LLVMBasicBlockRef loopBlock = function.AppendBasicBlock("loop");
+ Builder.BuildBr(loopBlock);
+
+ // Start the loop body; the loop variable is a PHI merging the start and stepped values.
+ Builder.PositionAtEnd(loopBlock);
+ LLVMValueRef variable = Builder.BuildPhi(Module.Context.DoubleType, node.VarName);
+ variable.AddIncoming([startValue], [preheaderBlock], 1);
+
+ // Bring the loop variable into scope, shadowing any outer binding of the same name.
+ bool hadOldValue = NamedValues.TryGetValue(node.VarName, out LLVMValueRef oldValue);
+ NamedValues[node.VarName] = variable;
+
+ // Emit the body; its value is discarded.
+ Codegen(node.Body);
+
+ // Compute the next value of the loop variable.
+ LLVMValueRef stepValue = node.Step is not null
+ ? Codegen(node.Step)
+ : LLVMValueRef.CreateConstReal(Module.Context.DoubleType, 1.0);
+ LLVMValueRef nextVariable = Builder.BuildFAdd(variable, stepValue, "nextvar");
+
+ // Evaluate the end condition (!= 0.0 continues the loop).
+ LLVMValueRef endCondition = Codegen(node.End);
+ LLVMValueRef zero = LLVMValueRef.CreateConstReal(Module.Context.DoubleType, 0.0);
+ endCondition = Builder.BuildFCmp(LLVMRealPredicate.LLVMRealONE, endCondition, zero, "loopcond");
+
+ LLVMBasicBlockRef loopEndBlock = Builder.InsertBlock;
+ LLVMBasicBlockRef afterBlock = function.AppendBasicBlock("afterloop");
+ Builder.BuildCondBr(endCondition, loopBlock, afterBlock);
+ Builder.PositionAtEnd(afterBlock);
+
+ // Wire the stepped value back into the PHI now that we know the loop's back-edge block.
+ variable.AddIncoming([nextVariable], [loopEndBlock], 1);
+
+ // Restore the shadowed variable.
+ if (hadOldValue)
+ {
+ NamedValues[node.VarName] = oldValue;
+ }
+ else
+ {
+ NamedValues.Remove(node.VarName);
+ }
+
+ // A 'for' expression always evaluates to 0.0.
+ return LLVMValueRef.CreateConstReal(Module.Context.DoubleType, 0.0);
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope.sln b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope.sln
deleted file mode 100644
index eae9808a..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope.sln
+++ /dev/null
@@ -1,37 +0,0 @@
-
-Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2013
-VisualStudioVersion = 12.0.31101.0
-MinimumVisualStudioVersion = 10.0.40219.1
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Kaleidoscope", "Kaleidoscope\Kaleidoscope.csproj", "{CD23CB2E-951B-4FAD-A4CD-4048731DBA31}"
-EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KaleidoscopeLLVM", "KaleidoscopeLLVM\KaleidoscopeLLVM.csproj", "{C1147A13-4174-410A-A891-335D858703B7}"
-EndProject
-Global
- GlobalSection(SolutionConfigurationPlatforms) = preSolution
- Debug|Any CPU = Debug|Any CPU
- Debug|x64 = Debug|x64
- Release|Any CPU = Release|Any CPU
- Release|x64 = Release|x64
- EndGlobalSection
- GlobalSection(ProjectConfigurationPlatforms) = postSolution
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Debug|x64.ActiveCfg = Debug|x64
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Debug|x64.Build.0 = Debug|x64
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Release|Any CPU.Build.0 = Release|Any CPU
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Release|x64.ActiveCfg = Release|x64
- {CD23CB2E-951B-4FAD-A4CD-4048731DBA31}.Release|x64.Build.0 = Release|x64
- {C1147A13-4174-410A-A891-335D858703B7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Debug|x64.ActiveCfg = Debug|x64
- {C1147A13-4174-410A-A891-335D858703B7}.Debug|x64.Build.0 = Debug|x64
- {C1147A13-4174-410A-A891-335D858703B7}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Release|Any CPU.Build.0 = Release|Any CPU
- {C1147A13-4174-410A-A891-335D858703B7}.Release|x64.ActiveCfg = Release|Any CPU
- EndGlobalSection
- GlobalSection(SolutionProperties) = preSolution
- HideSolutionNode = FALSE
- EndGlobalSection
-EndGlobal
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/BinaryExprAST.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/BinaryExprAST.cs
deleted file mode 100644
index 1143f724..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/BinaryExprAST.cs
+++ /dev/null
@@ -1,42 +0,0 @@
-namespace Kaleidoscope.AST
-{
- using System;
-
- public sealed class BinaryExprAST : ExprAST
- {
- public BinaryExprAST(char op, ExprAST lhs, ExprAST rhs)
- {
- switch (op)
- {
- case '+':
- this.NodeType = ExprType.AddExpr;
- break;
- case '-':
- this.NodeType = ExprType.SubtractExpr;
- break;
- case '*':
- this.NodeType = ExprType.MultiplyExpr;
- break;
- case '<':
- this.NodeType = ExprType.LessThanExpr;
- break;
- default:
- throw new ArgumentException("op " + op + " is not a valid operator");
- }
-
- this.Lhs = lhs;
- this.Rhs = rhs;
- }
-
- public ExprAST Lhs { get; private set; }
-
- public ExprAST Rhs { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitBinaryExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/CallExprAST.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/CallExprAST.cs
deleted file mode 100644
index 1a722b27..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/CallExprAST.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace Kaleidoscope.AST
-{
- using System.Collections.Generic;
-
- public sealed class CallExprAST : ExprAST
- {
- public CallExprAST(string callee, List args)
- {
- this.Callee = callee;
- this.Arguments = args;
- this.NodeType = ExprType.CallExpr;
- }
-
- public string Callee { get; private set; }
-
- public List Arguments { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitCallExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ExprAST.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ExprAST.cs
deleted file mode 100644
index 6841cbec..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ExprAST.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public abstract class ExprAST
- {
- public abstract ExprType NodeType { get; protected set; }
-
- protected internal virtual ExprAST VisitChildren(ExprVisitor visitor)
- {
- return visitor.Visit(this);
- }
-
- protected internal virtual ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitExtension(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ExprType.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ExprType.cs
deleted file mode 100644
index 5e89158a..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ExprType.cs
+++ /dev/null
@@ -1,17 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public enum ExprType
- {
- AddExpr,
- SubtractExpr,
- MultiplyExpr,
- LessThanExpr,
- CallExpr,
- VariableExpr,
- PrototypeExpr,
- FunctionExpr,
- NumberExpr,
- IfExpr,
- ForExpr,
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ExprVisitor.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ExprVisitor.cs
deleted file mode 100644
index 43b36369..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ExprVisitor.cs
+++ /dev/null
@@ -1,84 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public abstract class ExprVisitor
- {
- protected ExprVisitor()
- {
- }
-
- public virtual ExprAST Visit(ExprAST node)
- {
- if (node != null)
- {
- return node.Accept(this);
- }
-
- return null;
- }
-
- protected internal virtual ExprAST VisitExtension(ExprAST node)
- {
- return node.VisitChildren(this);
- }
-
- protected internal virtual ExprAST VisitBinaryExprAST(BinaryExprAST node)
- {
- this.Visit(node.Lhs);
- this.Visit(node.Rhs);
-
- return node;
- }
-
- protected internal virtual ExprAST VisitCallExprAST(CallExprAST node)
- {
- foreach (var argument in node.Arguments)
- {
- this.Visit(argument);
- }
-
- return node;
- }
-
- protected internal virtual ExprAST VisitFunctionAST(FunctionAST node)
- {
- this.Visit(node.Proto);
- this.Visit(node.Body);
-
- return node;
- }
-
- protected internal virtual ExprAST VisitVariableExprAST(VariableExprAST node)
- {
- return node;
- }
-
- protected internal virtual ExprAST VisitPrototypeAST(PrototypeAST node)
- {
- return node;
- }
-
- protected internal virtual ExprAST VisitNumberExprAST(NumberExprAST node)
- {
- return node;
- }
-
- protected internal virtual ExprAST VisitIfExprAST(IfExpAST node)
- {
- this.Visit(node.Condition);
- this.Visit(node.Then);
- this.Visit(node.Else);
-
- return node;
- }
-
- protected internal virtual ExprAST VisitForExprAST(ForExprAST node)
- {
- this.Visit(node.Start);
- this.Visit(node.End);
- this.Visit(node.Step);
- this.Visit(node.Body);
-
- return node;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ForExprAST.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ForExprAST.cs
deleted file mode 100644
index 1316c221..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/ForExprAST.cs
+++ /dev/null
@@ -1,32 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class ForExprAST : ExprAST
- {
- public ForExprAST(string varName, ExprAST start, ExprAST end, ExprAST step, ExprAST body)
- {
- this.VarName = varName;
- this.Start = start;
- this.End = end;
- this.Step = step;
- this.Body = body;
- this.NodeType = ExprType.ForExpr;
- }
-
- public string VarName { get; private set; }
-
- public ExprAST Start { get; private set; }
-
- public ExprAST End { get; private set; }
-
- public ExprAST Step { get; private set; }
-
- public ExprAST Body { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitForExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/FunctionAST.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/FunctionAST.cs
deleted file mode 100644
index a5e90405..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/FunctionAST.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class FunctionAST : ExprAST
- {
- public FunctionAST(PrototypeAST proto, ExprAST body)
- {
- this.Proto = proto;
- this.Body = body;
- this.NodeType = ExprType.FunctionExpr;
- }
-
- public PrototypeAST Proto { get; private set; }
-
- public ExprAST Body { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitFunctionAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/IfExpAST.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/IfExpAST.cs
deleted file mode 100644
index 12b3acb0..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/IfExpAST.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class IfExpAST : ExprAST
- {
- public IfExpAST(ExprAST condition, ExprAST then, ExprAST @else)
- {
- this.Condition = condition;
- this.Then = then;
- this.Else = @else;
- this.NodeType = ExprType.IfExpr;
- }
-
- public ExprAST Condition { get; private set; }
-
- public ExprAST Then { get; private set; }
-
- public ExprAST Else { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitIfExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/NumberExprAST.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/NumberExprAST.cs
deleted file mode 100644
index bd823cd3..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/NumberExprAST.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class NumberExprAST : ExprAST
- {
- public NumberExprAST(double value)
- {
- this.Value = value;
- this.NodeType = ExprType.NumberExpr;
- }
-
- public double Value { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitNumberExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/PrototypeAST.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/PrototypeAST.cs
deleted file mode 100644
index 3d0f6b4a..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/PrototypeAST.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-namespace Kaleidoscope.AST
-{
- using System.Collections.Generic;
-
- public sealed class PrototypeAST : ExprAST
- {
- public PrototypeAST(string name, List args)
- {
- this.Name = name;
- this.Arguments = args;
- this.NodeType = ExprType.PrototypeExpr;
- }
-
- public string Name { get; private set; }
-
- public List Arguments { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitPrototypeAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/VariableExprAST.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/VariableExprAST.cs
deleted file mode 100644
index 41aeb6c2..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/AST/VariableExprAST.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-namespace Kaleidoscope.AST
-{
- public sealed class VariableExprAST : ExprAST
- {
- public VariableExprAST(string name)
- {
- this.Name = name;
- this.NodeType = ExprType.VariableExpr;
- }
-
- public string Name { get; private set; }
-
- public override ExprType NodeType { get; protected set; }
-
- protected internal override ExprAST Accept(ExprVisitor visitor)
- {
- return visitor.VisitVariableExprAST(this);
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/BaseParserListener.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/BaseParserListener.cs
deleted file mode 100644
index 9eb96731..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/BaseParserListener.cs
+++ /dev/null
@@ -1,63 +0,0 @@
-namespace Kaleidoscope
-{
- using System;
- using System.Collections.Generic;
- using System.Reflection;
- using AST;
-
- internal sealed class BaseParserListener
- {
- private static readonly Type IParserListenerType = typeof(IParserListener);
-
- private readonly Stack descentStack = new Stack();
-
- private readonly Stack ascentStack = new Stack();
-
- private readonly IParserListener listener;
-
- public BaseParserListener(IParserListener listener)
- {
- this.listener = listener;
- }
-
- public void EnterRule(string ruleName)
- {
- this.descentStack.Push(ruleName);
- }
-
- public void ExitRule(ExprAST argument)
- {
- string ruleName = this.descentStack.Pop();
- this.ascentStack.Push(new ASTContext(IParserListenerType.GetMethod("Exit" + ruleName), this.listener, argument));
- this.ascentStack.Push(new ASTContext(IParserListenerType.GetMethod("Enter" + ruleName), this.listener, argument));
- }
-
- public void Listen()
- {
- if (this.listener != null)
- {
- while (this.ascentStack.Count != 0)
- {
- var context = this.ascentStack.Pop();
- context.MethodInfo.Invoke(context.Instance, new object[] { context.Argument });
- }
- }
- }
-
- private sealed class ASTContext
- {
- public ASTContext(MethodInfo methodInfo, object instance, ExprAST argument)
- {
- this.MethodInfo = methodInfo;
- this.Instance = instance;
- this.Argument = argument;
- }
-
- public MethodInfo MethodInfo { get; private set; }
-
- public ExprAST Argument { get; set; }
-
- public object Instance { get; private set; }
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/ILexer.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/ILexer.cs
deleted file mode 100644
index aca5d18f..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/ILexer.cs
+++ /dev/null
@@ -1,15 +0,0 @@
-namespace Kaleidoscope
-{
- public interface ILexer
- {
- int CurrentToken { get; }
-
- string GetLastIdentifier();
-
- double GetLastNumber();
-
- int GetTokPrecedence();
-
- int GetNextToken();
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/IParser.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/IParser.cs
deleted file mode 100644
index adf84705..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/IParser.cs
+++ /dev/null
@@ -1,11 +0,0 @@
-namespace Kaleidoscope
-{
- public interface IParser
- {
- void HandleDefinition();
-
- void HandleExtern();
-
- void HandleTopLevelExpression();
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/IParserListener.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/IParserListener.cs
deleted file mode 100644
index fbc91f82..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/IParserListener.cs
+++ /dev/null
@@ -1,19 +0,0 @@
-namespace Kaleidoscope
-{
- using AST;
-
- public interface IParserListener
- {
- void EnterHandleDefinition(FunctionAST data);
-
- void ExitHandleDefinition(FunctionAST data);
-
- void EnterHandleExtern(PrototypeAST data);
-
- void ExitHandleExtern(PrototypeAST data);
-
- void EnterHandleTopLevelExpression(FunctionAST data);
-
- void ExitHandleTopLevelExpression(FunctionAST data);
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Kaleidoscope.csproj b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Kaleidoscope.csproj
deleted file mode 100644
index 09321765..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Kaleidoscope.csproj
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
- net10.0
-
-
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Lexer.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Lexer.cs
deleted file mode 100644
index 820d53de..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Lexer.cs
+++ /dev/null
@@ -1,154 +0,0 @@
-namespace Kaleidoscope
-{
- using System;
- using System.Collections.Generic;
- using System.Globalization;
- using System.IO;
- using System.Text;
-
- public sealed class Lexer : ILexer
- {
- private const int EOF = -1;
-
- private readonly TextReader reader;
-
- private readonly StringBuilder identifierBuilder = new StringBuilder();
-
- private readonly StringBuilder numberBuilder = new StringBuilder();
-
- private readonly Dictionary binopPrecedence;
-
- private int c = ' ';
-
- private string identifier;
-
- private double numVal;
-
- public Lexer(TextReader reader, Dictionary binOpPrecedence)
- {
- this.reader = reader;
- this.binopPrecedence = binOpPrecedence;
- }
-
- public int CurrentToken { get; private set; }
-
- public string GetLastIdentifier()
- {
- return this.identifier;
- }
-
- public double GetLastNumber()
- {
- return this.numVal;
- }
-
- public int GetTokPrecedence()
- {
- // Make sure it's a declared binop.
- int tokPrec;
- if (this.binopPrecedence.TryGetValue((char)this.CurrentToken, out tokPrec))
- {
- return tokPrec;
- }
-
- return -1;
- }
-
- public int GetNextToken()
- {
- // Skip any whitespace.
- while (char.IsWhiteSpace((char)c))
- {
- c = this.reader.Read();
- }
-
- if (char.IsLetter((char)c)) // identifier: [a-zA-Z][a-zA-Z0-9]*
- {
- this.identifierBuilder.Append((char)c);
- while (char.IsLetterOrDigit((char)(c = this.reader.Read())))
- {
- this.identifierBuilder.Append((char)c);
- }
-
- this.identifier = this.identifierBuilder.ToString();
- this.identifierBuilder.Clear();
-
- if (string.Equals(identifier, "def", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.DEF;
- }
- else if (string.Equals(identifier, "extern", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.EXTERN;
- }
- else if (string.Equals(identifier, "if", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.IF;
- }
- else if (string.Equals(identifier, "then", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.THEN;
- }
- else if (string.Equals(identifier, "else", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.ELSE;
- }
- else if (string.Equals(identifier, "for", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.FOR;
- }
- else if (string.Equals(identifier, "in", StringComparison.Ordinal))
- {
- this.CurrentToken = (int)Token.IN;
- }
- else
- {
- this.CurrentToken = (int)Token.IDENTIFIER;
- }
-
- return this.CurrentToken;
- }
-
- // Number: [0-9.]+
- if (char.IsDigit((char)c) || c == '.')
- {
- do
- {
- this.numberBuilder.Append((char)c);
- c = this.reader.Read();
- } while (char.IsDigit((char)c) || c == '.');
-
- this.numVal = double.Parse(this.numberBuilder.ToString());
- this.numberBuilder.Clear();
- this.CurrentToken = (int)Token.NUMBER;
-
- return this.CurrentToken;
- }
-
- if (c == '#')
- {
- // Comment until end of line.
- do
- {
- c = this.reader.Read();
- } while (c != EOF && c != '\n' && c != '\r');
-
- if (c != EOF)
- {
- return this.GetNextToken();
- }
- }
-
- // Check for end of file. Don't eat the EOF.
- if (c == EOF)
- {
- this.CurrentToken = c;
- return (int)Token.EOF;
- }
-
- this.CurrentToken = c;
- c = this.reader.Read();
- return this.c;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Parser.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Parser.cs
deleted file mode 100644
index f7900a62..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Parser.cs
+++ /dev/null
@@ -1,431 +0,0 @@
-using System.Diagnostics;
-
-namespace Kaleidoscope
-{
- using System;
- using System.Collections.Generic;
- using AST;
-
- public sealed class Parser : IParser
- {
- private readonly Lexer scanner;
-
- private readonly BaseParserListener baseListener;
-
- public Parser(Lexer scanner, IParserListener listener)
- {
- this.scanner = scanner;
- this.baseListener = new BaseParserListener(listener);
- }
-
- public void HandleDefinition()
- {
- this.baseListener.EnterRule("HandleDefinition");
-
- var functionAST = this.ParseDefinition();
-
- baseListener.ExitRule(functionAST);
-
- if (functionAST != null)
- {
- this.baseListener.Listen();
- }
- else
- {
- // Skip token for error recovery.
- this.scanner.GetNextToken();
- }
- }
-
- public void HandleExtern()
- {
- this.baseListener.EnterRule("HandleExtern");
-
- var prototypeAST = this.ParseExtern();
-
- this.baseListener.ExitRule(prototypeAST);
-
- if (prototypeAST != null)
- {
- this.baseListener.Listen();
- }
- else
- {
- // Skip token for error recovery.
- this.scanner.GetNextToken();
- }
- }
-
- public void HandleTopLevelExpression()
- {
- // Evaluate a top-level expression into an anonymous function.
- this.baseListener.EnterRule("HandleTopLevelExpression");
-
- var functionAST = this.ParseTopLevelExpr();
-
- this.baseListener.ExitRule(functionAST);
-
- if (functionAST != null)
- {
- this.baseListener.Listen();
- }
- else
- {
- // Skip token for error recovery.
- this.scanner.GetNextToken();
- }
- }
-
- // identifierexpr
- // ::= identifier
- // ::= identifier '(' expression* ')'
- private ExprAST ParseIdentifierExpr()
- {
- string idName = this.scanner.GetLastIdentifier();
-
- this.scanner.GetNextToken(); // eat identifier.
-
- if (this.scanner.CurrentToken != '(') // Simple variable ref.
- {
- return new VariableExprAST(idName);
- }
-
- // Call.
- this.scanner.GetNextToken(); // eat (
- List args = new List();
-
- if (this.scanner.CurrentToken != ')')
- {
- while (true)
- {
- ExprAST arg = this.ParseExpression();
- if (arg == null)
- {
- return null;
- }
-
- args.Add(arg);
-
- if (this.scanner.CurrentToken == ')')
- {
- break;
- }
-
- if (this.scanner.CurrentToken != ',')
- {
- Console.WriteLine("Expected ')' or ',' in argument list");
- return null;
- }
-
- this.scanner.GetNextToken();
- }
- }
-
- // Eat the ')'.
- this.scanner.GetNextToken();
-
- return new CallExprAST(idName, args);
- }
-
- // numberexpr ::= number
- private ExprAST ParseNumberExpr()
- {
- ExprAST result = new NumberExprAST(this.scanner.GetLastNumber());
- this.scanner.GetNextToken();
- return result;
- }
-
- // parenexpr ::= '(' expression ')'
- private ExprAST ParseParenExpr()
- {
- this.scanner.GetNextToken(); // eat (.
- ExprAST v = this.ParseExpression();
- if (v == null)
- {
- return null;
- }
-
- if (this.scanner.CurrentToken != ')')
- {
- Console.WriteLine("expected ')'");
- return null;
- }
-
- this.scanner.GetNextToken(); // eat ).
-
- return v;
- }
-
- // ifexpr ::= 'if' expression 'then' expression 'else' expression
- public ExprAST ParseIfExpr()
- {
- this.scanner.GetNextToken(); // eat the if.
-
- // condition
- ExprAST cond = this.ParseExpression();
- if (cond == null)
- {
- return null;
- }
-
- if (this.scanner.CurrentToken != (int)Token.THEN)
- {
- Console.WriteLine("expected then");
- }
-
- this.scanner.GetNextToken(); // eat the then
-
- ExprAST then = this.ParseExpression();
- if (then == null)
- {
- return null;
- }
-
- if (this.scanner.CurrentToken != (int)Token.ELSE)
- {
- Console.WriteLine("expected else");
- return null;
- }
-
- this.scanner.GetNextToken();
-
- ExprAST @else = this.ParseExpression();
- if (@else == null)
- {
- return null;
- }
-
- return new IfExpAST(cond, then, @else);
- }
-
- // forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expression
- public ExprAST ParseForExpr()
- {
- this.scanner.GetNextToken(); // eat the for.
-
- if (this.scanner.CurrentToken != (int)Token.IDENTIFIER)
- {
- Console.WriteLine("expected identifier after for");
- return null;
- }
-
- string idName = this.scanner.GetLastIdentifier();
- this.scanner.GetNextToken(); // eat identifier.
-
- if (this.scanner.CurrentToken != '=')
- {
- Console.WriteLine("expected '=' after for");
- return null;
- }
-
- this.scanner.GetNextToken(); // eat '='.
-
- ExprAST start = this.ParseExpression();
- if (start == null)
- {
- return null;
- }
-
- if (this.scanner.CurrentToken != ',')
- {
- Console.WriteLine("expected ',' after for start value");
- return null;
- }
-
- this.scanner.GetNextToken();
-
- ExprAST end = this.ParseExpression();
- if (end == null)
- {
- return null;
- }
-
- // The step value is optional;
- ExprAST step = null;
- if (this.scanner.CurrentToken == ',')
- {
- this.scanner.GetNextToken();
- step = this.ParseExpression();
- if (step == null)
- {
- return null;
- }
- }
-
- if (this.scanner.CurrentToken != (int)Token.IN)
- {
- Console.WriteLine("expected 'in' after for");
- return null;
- }
-
- this.scanner.GetNextToken();
- ExprAST body = this.ParseExpression();
- if (body == null)
- {
- return null;
- }
-
- return new ForExprAST(idName, start, end, step, body);
- }
-
- // primary
- // ::= identifierexpr
- // ::= numberexpr
- // ::= parenexpr
- private ExprAST ParsePrimary()
- {
- switch (this.scanner.CurrentToken)
- {
- case (int)Token.IDENTIFIER:
- return this.ParseIdentifierExpr();
- case (int)Token.NUMBER:
- return this.ParseNumberExpr();
- case '(':
- return this.ParseParenExpr();
- case (int)Token.IF:
- return this.ParseIfExpr();
- case (int)Token.FOR:
- return this.ParseForExpr();
- default:
- Console.WriteLine("unknown token when expecting an expression");
- return null;
- }
- }
-
- // binoprhs
- // ::= ('+' primary)*
- private ExprAST ParseBinOpRHS(int exprPrec, ExprAST lhs)
- {
- // If this is a binop, find its precedence.
- while (true)
- {
- int tokPrec = this.scanner.GetTokPrecedence();
-
- // If this is a binop that binds at least as tightly as the current binop,
- // consume it, otherwise we are done.
- if (tokPrec < exprPrec)
- {
- return lhs;
- }
-
- // Okay, we know this is a binop.
- int binOp = this.scanner.CurrentToken;
- this.scanner.GetNextToken(); // eat binop
-
- // Parse the primary expression after the binary operator.
- ExprAST rhs = this.ParsePrimary();
- if (rhs == null)
- {
- return null;
- }
-
- // If BinOp binds less tightly with RHS than the operator after RHS, let
- // the pending operator take RHS as its LHS.
- int nextPrec = this.scanner.GetTokPrecedence();
- if (tokPrec < nextPrec)
- {
- rhs = this.ParseBinOpRHS(tokPrec + 1, rhs);
- if (rhs == null)
- {
- return null;
- }
- }
-
- // Merge LHS/RHS.
- lhs = new BinaryExprAST((char)binOp, lhs, rhs);
- }
- }
-
- // expression
- // ::= primary binoprhs
- //
- private ExprAST ParseExpression()
- {
- ExprAST lhs = this.ParsePrimary();
- if (lhs == null)
- {
- return null;
- }
-
- return this.ParseBinOpRHS(0, lhs);
- }
-
- // prototype
- // ::= id '(' id* ')'
- private PrototypeAST ParsePrototype()
- {
- if (this.scanner.CurrentToken != (int)Token.IDENTIFIER)
- {
- Console.WriteLine("Expected function name in prototype");
- return null;
- }
-
- string fnName = this.scanner.GetLastIdentifier();
-
- this.scanner.GetNextToken();
-
- if (this.scanner.CurrentToken != '(')
- {
- Console.WriteLine("Expected '(' in prototype");
- return null;
- }
-
- List argNames = new List();
- while (this.scanner.GetNextToken() == (int)Token.IDENTIFIER)
- {
- argNames.Add(this.scanner.GetLastIdentifier());
- }
-
- if (this.scanner.CurrentToken != ')')
- {
- Console.WriteLine("Expected ')' in prototype");
- return null;
- }
-
- this.scanner.GetNextToken(); // eat ')'.
-
- return new PrototypeAST(fnName, argNames);
- }
-
- // definition ::= 'def' prototype expression
- private FunctionAST ParseDefinition()
- {
- this.scanner.GetNextToken(); // eat def.
- PrototypeAST proto = this.ParsePrototype();
-
- if (proto == null)
- {
- return null;
- }
-
- ExprAST body = this.ParseExpression();
- if (body == null)
- {
- return null;
- }
-
- return new FunctionAST(proto, body);
- }
-
- /// toplevelexpr ::= expression
- private FunctionAST ParseTopLevelExpr()
- {
- ExprAST e = this.ParseExpression();
- if (e == null)
- {
- return null;
- }
-
- // Make an anonymous proto.
- PrototypeAST proto = new PrototypeAST(string.Empty, new List());
- return new FunctionAST(proto, e);
- }
-
- /// external ::= 'extern' prototype
- private PrototypeAST ParseExtern()
- {
- this.scanner.GetNextToken(); // eat extern.
- return this.ParsePrototype();
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Token.cs b/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Token.cs
deleted file mode 100644
index 9d9443ed..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/Kaleidoscope/Token.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-namespace Kaleidoscope
-{
- public enum Token
- {
- EOF = -1,
- DEF = -2,
- EXTERN = -3,
- IDENTIFIER = -4,
- NUMBER = -5,
- IF = -6,
- THEN = -7,
- ELSE = -8,
- FOR = -9,
- IN = -10
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/CodeGenParserListener.cs b/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/CodeGenParserListener.cs
deleted file mode 100644
index 8648340c..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/CodeGenParserListener.cs
+++ /dev/null
@@ -1,65 +0,0 @@
-using System;
-using System.Runtime.InteropServices;
-using LLVMSharp;
-
-namespace KaleidoscopeLLVM
-{
- using Kaleidoscope;
- using Kaleidoscope.AST;
-
- internal sealed class CodeGenParserListener : IParserListener
- {
- private readonly CodeGenVisitor visitor;
-
- private readonly LLVMExecutionEngineRef ee;
-
- private readonly LLVMPassManagerRef passManager;
-
- public CodeGenParserListener(LLVMExecutionEngineRef ee, LLVMPassManagerRef passManager, CodeGenVisitor visitor)
- {
- this.visitor = visitor;
- this.ee = ee;
- this.passManager = passManager;
- }
-
- public void EnterHandleDefinition(FunctionAST data)
- {
- }
-
- public void ExitHandleDefinition(FunctionAST data)
- {
- this.visitor.Visit(data);
- var function = this.visitor.ResultStack.Pop();
- LLVM.DumpValue(function);
-
- LLVM.RunFunctionPassManager(this.passManager, function);
- LLVM.DumpValue(function); // Dump the function for exposition purposes.
- }
-
- public void EnterHandleExtern(PrototypeAST data)
- {
- }
-
- public void ExitHandleExtern(PrototypeAST data)
- {
- this.visitor.Visit(data);
- LLVM.DumpValue(this.visitor.ResultStack.Pop());
- }
-
- public void EnterHandleTopLevelExpression(FunctionAST data)
- {
- }
-
- public void ExitHandleTopLevelExpression(FunctionAST data)
- {
- this.visitor.Visit(data);
- var anonymousFunction = this.visitor.ResultStack.Pop();
- LLVM.DumpValue(anonymousFunction); // Dump the function for exposition purposes.
- var dFunc = (Program.D)Marshal.GetDelegateForFunctionPointer(LLVM.GetPointerToGlobal(this.ee, anonymousFunction), typeof(Program.D));
- LLVM.RunFunctionPassManager(this.passManager, anonymousFunction);
-
- LLVM.DumpValue(anonymousFunction); // Dump the function for exposition purposes.
- Console.WriteLine("Evaluated to " + dFunc());
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/CodeGenVisitor.cs b/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/CodeGenVisitor.cs
deleted file mode 100644
index 46dea6e0..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/CodeGenVisitor.cs
+++ /dev/null
@@ -1,350 +0,0 @@
-namespace KaleidoscopeLLVM
-{
- using System;
- using System.Collections.Generic;
- using Kaleidoscope.AST;
- using LLVMSharp;
-
- internal sealed class CodeGenVisitor : ExprVisitor
- {
- private static readonly LLVMBool LLVMBoolFalse = new LLVMBool(0);
-
- private static readonly LLVMValueRef NullValue = new LLVMValueRef(IntPtr.Zero);
-
- private readonly LLVMModuleRef module;
-
- private readonly LLVMBuilderRef builder;
-
- private readonly Dictionary namedValues = new Dictionary();
-
- private readonly Stack valueStack = new Stack();
-
- public CodeGenVisitor(LLVMModuleRef module, LLVMBuilderRef builder)
- {
- this.module = module;
- this.builder = builder;
- }
-
- public Stack ResultStack { get { return valueStack; } }
-
- public void ClearResultStack()
- {
- this.valueStack.Clear();
- }
-
- protected override ExprAST VisitNumberExprAST(NumberExprAST node)
- {
- this.valueStack.Push(LLVM.ConstReal(LLVM.DoubleType(), node.Value));
- return node;
- }
-
- protected override ExprAST VisitVariableExprAST(VariableExprAST node)
- {
- LLVMValueRef value;
-
- // Look this variable up in the function.
- if (this.namedValues.TryGetValue(node.Name, out value))
- {
- this.valueStack.Push(value);
- }
- else
- {
- throw new Exception("Unknown variable name");
- }
-
- return node;
- }
-
- protected override ExprAST VisitBinaryExprAST(BinaryExprAST node)
- {
- this.Visit(node.Lhs);
- this.Visit(node.Rhs);
-
- LLVMValueRef r = this.valueStack.Pop();
- LLVMValueRef l = this.valueStack.Pop();
-
- LLVMValueRef n;
-
- switch (node.NodeType)
- {
- case ExprType.AddExpr:
- n = LLVM.BuildFAdd(this.builder, l, r, "addtmp");
- break;
- case ExprType.SubtractExpr:
- n = LLVM.BuildFSub(this.builder, l, r, "subtmp");
- break;
- case ExprType.MultiplyExpr:
- n = LLVM.BuildFMul(this.builder, l, r, "multmp");
- break;
- case ExprType.LessThanExpr:
- // Convert bool 0/1 to double 0.0 or 1.0
- n = LLVM.BuildUIToFP(this.builder, LLVM.BuildFCmp(this.builder, LLVMRealPredicate.LLVMRealULT, l, r, "cmptmp"), LLVM.DoubleType(), "booltmp");
- break;
- default:
- throw new Exception("invalid binary operator");
- }
-
- this.valueStack.Push(n);
- return node;
- }
-
- protected override ExprAST VisitCallExprAST(CallExprAST node)
- {
- var calleeF = LLVM.GetNamedFunction(this.module, node.Callee);
- if (calleeF.Pointer == IntPtr.Zero)
- {
- throw new Exception("Unknown function referenced");
- }
-
- if (LLVM.CountParams(calleeF) != node.Arguments.Count)
- {
- throw new Exception("Incorrect # arguments passed");
- }
-
- var argumentCount = (uint)node.Arguments.Count;
- var argsV = new LLVMValueRef[Math.Max(argumentCount, 1)];
- for (int i = 0; i < argumentCount; ++i)
- {
- this.Visit(node.Arguments[i]);
- argsV[i] = this.valueStack.Pop();
- }
-
- valueStack.Push(LLVM.BuildCall(this.builder, calleeF, argsV, "calltmp"));
-
- return node;
- }
-
- protected override ExprAST VisitPrototypeAST(PrototypeAST node)
- {
- // Make the function type: double(double,double) etc.
- var argumentCount = (uint)node.Arguments.Count;
- var arguments = new LLVMTypeRef[Math.Max(argumentCount, 1)];
-
- var function = LLVM.GetNamedFunction(this.module, node.Name);
-
- // If F conflicted, there was already something named 'Name'. If it has a
- // body, don't allow redefinition or reextern.
- if (function.Pointer != IntPtr.Zero)
- {
- // If F already has a body, reject this.
- if (LLVM.CountBasicBlocks(function) != 0)
- {
- throw new Exception("redefinition of function.");
- }
-
- // If F took a different number of args, reject.
- if (LLVM.CountParams(function) != argumentCount)
- {
- throw new Exception("redefinition of function with different # args");
- }
- }
- else
- {
- for (int i = 0; i < argumentCount; ++i)
- {
- arguments[i] = LLVM.DoubleType();
- }
-
- function = LLVM.AddFunction(this.module, node.Name, LLVM.FunctionType(LLVM.DoubleType(), arguments, LLVMBoolFalse));
- LLVM.SetLinkage(function, LLVMLinkage.LLVMExternalLinkage);
- }
-
- for (int i = 0; i < argumentCount; ++i)
- {
- string argumentName = node.Arguments[i];
-
- LLVMValueRef param = LLVM.GetParam(function, (uint)i);
- LLVM.SetValueName(param, argumentName);
-
- this.namedValues[argumentName] = param;
- }
-
- this.valueStack.Push(function);
- return node;
- }
-
- protected override ExprAST VisitFunctionAST(FunctionAST node)
- {
- this.namedValues.Clear();
-
- this.Visit(node.Proto);
-
- LLVMValueRef function = this.valueStack.Pop();
-
- // Create a new basic block to start insertion into.
- LLVM.PositionBuilderAtEnd(this.builder, LLVM.AppendBasicBlock(function, "entry"));
-
- try
- {
- this.Visit(node.Body);
- }
- catch (Exception)
- {
- LLVM.DeleteFunction(function);
- throw;
- }
-
- // Finish off the function.
- LLVM.BuildRet(this.builder, this.valueStack.Pop());
-
- // Validate the generated code, checking for consistency.
- LLVM.VerifyFunction(function, LLVMVerifierFailureAction.LLVMPrintMessageAction);
-
- this.valueStack.Push(function);
-
- return node;
- }
-
- protected override ExprAST VisitIfExprAST(IfExpAST node)
- {
- this.Visit(node.Condition);
- var condv = LLVM.BuildFCmp(this.builder, LLVMRealPredicate.LLVMRealONE, this.valueStack.Pop(), LLVM.ConstReal(LLVM.DoubleType(), 0.0), "ifcond");
-
- LLVMValueRef func = LLVM.GetBasicBlockParent(LLVM.GetInsertBlock(builder));
-
- // Create blocks for the then and else cases. Insert the 'then' block at the
- // end of the function.
- LLVMBasicBlockRef thenBB = LLVM.AppendBasicBlock(func, "then");
- LLVMBasicBlockRef elseBB = LLVM.AppendBasicBlock(func, "else");
- LLVMBasicBlockRef mergeBB = LLVM.AppendBasicBlock(func, "ifcont");
-
- LLVM.BuildCondBr(this.builder, condv, thenBB, elseBB);
-
- // Emit then value.
- LLVM.PositionBuilderAtEnd(this.builder, thenBB);
-
- this.Visit(node.Then);
- var thenV = this.valueStack.Pop();
-
- LLVM.BuildBr(this.builder, mergeBB);
-
- // Codegen of 'Then' can change the current block, update ThenBB for the PHI.
- thenBB = LLVM.GetInsertBlock(this.builder);
-
- // Emit else block.
-
- LLVM.PositionBuilderAtEnd(this.builder, elseBB);
-
- this.Visit(node.Else);
- var elseV = this.valueStack.Pop();
-
- LLVM.BuildBr(this.builder, mergeBB);
-
- // Codegen of 'Else' can change the current block, update ElseBB for the PHI.
- elseBB = LLVM.GetInsertBlock(this.builder);
-
- // Emit merge block.
- LLVM.PositionBuilderAtEnd(this.builder, mergeBB);
- var phi = LLVM.BuildPhi(this.builder, LLVM.DoubleType(), "iftmp");
-
- LLVM.AddIncoming(phi, new []{thenV}, new []{thenBB}, 1);
- LLVM.AddIncoming(phi, new []{elseV}, new []{elseBB}, 1);
-
- this.valueStack.Push(phi);
-
- return node;
- }
-
- protected override ExprAST VisitForExprAST(ForExprAST node)
- {
- // Output this as:
- // ...
- // start = startexpr
- // goto loop
- // loop:
- // variable = phi [start, loopheader], [nextvariable, loopend]
- // ...
- // bodyexpr
- // ...
- // loopend:
- // step = stepexpr
- // nextvariable = variable + step
- // endcond = endexpr
- // br endcond, loop, endloop
- // outloop:
-
- // Emit the start code first, without 'variable' in scope.
- this.Visit(node.Start);
- var startVal = this.valueStack.Pop();
-
- // Make the new basic block for the loop header, inserting after current
- // block.
- var preheaderBB = LLVM.GetInsertBlock(this.builder);
- var function = LLVM.GetBasicBlockParent(preheaderBB);
- var loopBB = LLVM.AppendBasicBlock(function, "loop");
-
- // Insert an explicit fall through from the current block to the LoopBB.
- LLVM.BuildBr(this.builder, loopBB);
-
- // Start insertion in LoopBB.
- LLVM.PositionBuilderAtEnd(this.builder, loopBB);
-
- // Start the PHI node with an entry for Start.
- var variable = LLVM.BuildPhi(builder, LLVM.DoubleType(), node.VarName);
- LLVM.AddIncoming(variable, new []{startVal}, new []{preheaderBB}, 1);
-
- // Within the loop, the variable is defined equal to the PHI node. If it
- // shadows an existing variable, we have to restore it, so save it now.
- LLVMValueRef oldVal;
- if (this.namedValues.TryGetValue(node.VarName, out oldVal))
- {
- this.namedValues[node.VarName] = variable;
- }
- else
- {
- this.namedValues.Add(node.VarName, variable);
- }
-
- // Emit the body of the loop. This, like any other expr, can change the
- // current BB. Note that we ignore the value computed by the body, but don't
- // allow an error.
- Visit(node.Body);
-
- // Emit the step value.
- LLVMValueRef stepVal;
- if (node.Step != null)
- {
- Visit(node.Step);
- stepVal = valueStack.Pop();
- }
- else
- {
- // If not specified, use 1.0.
- stepVal = LLVM.ConstReal(LLVM.DoubleType(), 1.0);
- }
-
- LLVMValueRef nextVar = LLVM.BuildFAdd(builder, variable, stepVal, "nextvar");
-
- // Compute the end condition.
- Visit(node.End);
- LLVMValueRef endCond = LLVM.BuildFCmp(builder, LLVMRealPredicate.LLVMRealONE, valueStack.Pop(), LLVM.ConstReal(LLVM.DoubleType(), 0.0), "loopcond");
-
- // Create the "after loop" block and insert it.
- var loopEndBB = LLVM.GetInsertBlock(builder);
- var afterBB = LLVM.AppendBasicBlock(function, "afterloop");
-
- // Insert the conditional branch into the end of LoopEndBB.
- LLVM.BuildCondBr(builder, endCond, loopBB, afterBB);
-
- // Any new code will be inserted in AfterBB.
- LLVM.PositionBuilderAtEnd(builder, afterBB);
-
- // Add a new entry to the PHI node for the backedge.
- LLVM.AddIncoming(variable, new []{nextVar}, new []{loopEndBB}, 1);
-
- // Restore the unshadowed variable.
- if (oldVal.Pointer != IntPtr.Zero)
- {
- namedValues[node.VarName] = oldVal;
- }
- else
- {
- namedValues.Remove(node.VarName);
- }
-
- valueStack.Push(LLVM.ConstReal(LLVM.DoubleType(), 0.0));
-
- return node;
- }
- }
-}
\ No newline at end of file
diff --git a/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/KaleidoscopeLLVM.csproj b/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/KaleidoscopeLLVM.csproj
deleted file mode 100644
index 96ff9d4b..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/KaleidoscopeLLVM.csproj
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
- net10.0
-
-
-
-
-
-
-
-
-
-
diff --git a/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/Program.cs b/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/Program.cs
deleted file mode 100644
index ea156bfd..00000000
--- a/samples/KaleidoscopeTutorial/Chapter5/KaleidoscopeLLVM/Program.cs
+++ /dev/null
@@ -1,114 +0,0 @@
-namespace KaleidoscopeLLVM
-{
- using System;
- using System.Collections.Generic;
- using System.Runtime.InteropServices;
- using Kaleidoscope;
- using LLVMSharp;
-
- public sealed class Program
- {
- public delegate double D();
-
- private static void Main(string[] args)
- {
- // Make the module, which holds all the code.
- LLVMModuleRef module = LLVM.ModuleCreateWithName("my cool jit");
- LLVMBuilderRef builder = LLVM.CreateBuilder();
-
- LLVM.LinkInMCJIT();
- LLVM.InitializeX86TargetInfo();
- LLVM.InitializeX86Target();
- LLVM.InitializeX86TargetMC();
-
- if (LLVM.CreateExecutionEngineForModule(out var engine, module, out var errorMessage).Value == 1)
- {
- Console.WriteLine(errorMessage);
- // LLVM.DisposeMessage(errorMessage);
- return;
- }
-
- // Create a function pass manager for this engine
- LLVMPassManagerRef passManager = LLVM.CreateFunctionPassManagerForModule(module);
-
- // Set up the optimizer pipeline. Start with registering info about how the
- // target lays out data structures.
- // LLVM.DisposeTargetData(LLVM.GetExecutionEngineTargetData(engine));
-
- // Provide basic AliasAnalysis support for GVN.
- LLVM.AddBasicAliasAnalysisPass(passManager);
-
- // Promote allocas to registers.
- LLVM.AddPromoteMemoryToRegisterPass(passManager);
-
- // Do simple "peephole" optimizations and bit-twiddling optzns.
- LLVM.AddInstructionCombiningPass(passManager);
-
- // Reassociate expressions.
- LLVM.AddReassociatePass(passManager);
-
- // Eliminate Common SubExpressions.
- LLVM.AddGVNPass(passManager);
-
- // Simplify the control flow graph (deleting unreachable blocks, etc).
- LLVM.AddCFGSimplificationPass(passManager);
-
- LLVM.InitializeFunctionPassManager(passManager);
-
- var codeGenlistener = new CodeGenParserListener(engine, passManager, new CodeGenVisitor(module, builder));
-
- // Install standard binary operators.
- // 1 is lowest precedence.
- var binopPrecedence = new Dictionary
- {
- ['<'] = 10,
- ['+'] = 20,
- ['-'] = 20,
- ['*'] = 40
- };
- // highest.
-
- var scanner = new Lexer(Console.In, binopPrecedence);
- var parser = new Parser(scanner, codeGenlistener);
-
- // Prime the first token.
- Console.Write("ready> ");
- scanner.GetNextToken();
-
- // Run the main "interpreter loop" now.
- MainLoop(scanner, parser);
-
- // Print out all of the generated code.
- LLVM.DumpModule(module);
-
- LLVM.DisposeModule(module);
- LLVM.DisposePassManager(passManager);
- }
-
- private static void MainLoop(ILexer lexer, IParser parser)
- {
- // top ::= definition | external | expression | ';'
- while (true)
- {
- Console.Write("ready> ");
- switch (lexer.CurrentToken)
- {
- case (int)Token.EOF:
- return;
- case ';':
- lexer.GetNextToken();
- break;
- case (int)Token.DEF:
- parser.HandleDefinition();
- break;
- case (int)Token.EXTERN:
- parser.HandleExtern();
- break;
- default:
- parser.HandleTopLevelExpression();
- break;
- }
- }
- }
- }
-}
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Parser.cs b/samples/KaleidoscopeTutorial/Chapter5/Parser.cs
new file mode 100644
index 00000000..4ea67877
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter5/Parser.cs
@@ -0,0 +1,133 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+
+namespace Kaleidoscope.Chapter5;
+
+///
+/// Chapter 5 parser delta — adds the if/then/else and for/in primary expressions. Every
+/// other production is inherited from the base .
+///
+public class Parser(Lexer lexer) : Kaleidoscope.Parser(lexer)
+{
+ protected override bool TryParseKeywordPrimary(int token, out ExprAST? result)
+ {
+ switch (token)
+ {
+ case (int)Token.If:
+ {
+ result = ParseIfExpr();
+ return true;
+ }
+
+ case (int)Token.For:
+ {
+ result = ParseForExpr();
+ return true;
+ }
+
+ default:
+ {
+ return base.TryParseKeywordPrimary(token, out result);
+ }
+ }
+ }
+
+ // ifexpr ::= 'if' expression 'then' expression 'else' expression
+ private ExprAST? ParseIfExpr()
+ {
+ Lexer.GetNextToken(); // eat the if.
+
+ ExprAST? condition = ParseExpression();
+ if (condition is null)
+ {
+ return null;
+ }
+
+ if (Lexer.CurrentToken != (int)Token.Then)
+ {
+ return LogError("expected then");
+ }
+
+ Lexer.GetNextToken(); // eat the then.
+
+ ExprAST? then = ParseExpression();
+ if (then is null)
+ {
+ return null;
+ }
+
+ if (Lexer.CurrentToken != (int)Token.Else)
+ {
+ return LogError("expected else");
+ }
+
+ Lexer.GetNextToken(); // eat the else.
+
+ ExprAST? @else = ParseExpression();
+ return @else is null ? null : new IfExprAST(condition, then, @else);
+ }
+
+ // forexpr ::= 'for' identifier '=' expr ',' expr (',' expr)? 'in' expression
+ private ExprAST? ParseForExpr()
+ {
+ Lexer.GetNextToken(); // eat the for.
+
+ if (Lexer.CurrentToken != (int)Token.Identifier)
+ {
+ return LogError("expected identifier after for");
+ }
+
+ string idName = Lexer.LastIdentifier;
+ Lexer.GetNextToken(); // eat the identifier.
+
+ if (Lexer.CurrentToken != '=')
+ {
+ return LogError("expected '=' after for");
+ }
+
+ Lexer.GetNextToken(); // eat the '='.
+
+ ExprAST? start = ParseExpression();
+ if (start is null)
+ {
+ return null;
+ }
+
+ if (Lexer.CurrentToken != ',')
+ {
+ return LogError("expected ',' after for start value");
+ }
+
+ Lexer.GetNextToken(); // eat the ','.
+
+ ExprAST? end = ParseExpression();
+ if (end is null)
+ {
+ return null;
+ }
+
+ // The step is optional.
+ ExprAST? step = null;
+ if (Lexer.CurrentToken == ',')
+ {
+ Lexer.GetNextToken(); // eat the ','.
+
+ step = ParseExpression();
+ if (step is null)
+ {
+ return null;
+ }
+ }
+
+ if (Lexer.CurrentToken != (int)Token.In)
+ {
+ return LogError("expected 'in' after for");
+ }
+
+ Lexer.GetNextToken(); // eat the in.
+
+ ExprAST? body = ParseExpression();
+ return body is null ? null : new ForExprAST(idName, start, end, step, body);
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Chapter5/Program.cs b/samples/KaleidoscopeTutorial/Chapter5/Program.cs
new file mode 100644
index 00000000..3911861a
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter5/Program.cs
@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope;
+using Kaleidoscope.Chapter4;
+using Kaleidoscope.Chapter5;
+
+// Chapter 5 reuses chapter 4's JIT driver and only swaps in a parser and code generator that
+// understand control flow (if/then/else and for/in).
+var binaryOpPrecedence = new Dictionary
+{
+ ['<'] = 10,
+ ['+'] = 20,
+ ['-'] = 20,
+ ['*'] = 40,
+};
+
+using TextReader reader = args.Length > 0 ? new StreamReader(args[0]) : Console.In;
+
+var lexer = new Lexer(reader, binaryOpPrecedence);
+var parser = new Kaleidoscope.Chapter5.Parser(lexer);
+var visitor = new CodeGenVisitor();
+
+using var driver = new JitReplDriver(lexer, parser, visitor);
+driver.Run();
diff --git a/samples/KaleidoscopeTutorial/Chapter6/Chapter6.csproj b/samples/KaleidoscopeTutorial/Chapter6/Chapter6.csproj
new file mode 100644
index 00000000..81229cb5
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter6/Chapter6.csproj
@@ -0,0 +1,8 @@
+
+
+ Exe
+
+
+
+
+
diff --git a/samples/KaleidoscopeTutorial/Chapter6/CodeGenVisitor.cs b/samples/KaleidoscopeTutorial/Chapter6/CodeGenVisitor.cs
new file mode 100644
index 00000000..2528a6ca
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter6/CodeGenVisitor.cs
@@ -0,0 +1,57 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+using LLVMSharp.Interop;
+
+namespace Kaleidoscope.Chapter6;
+
+///
+/// Chapter 6 code generation delta — user-defined operators lower to ordinary function calls. A unary
+/// operator !x calls unary!; a binary operator a op b that isn't one of the
+/// built-ins calls binary<op>. The operator functions themselves are just normal function
+/// definitions, so they reuse the inherited .
+///
+public class CodeGenVisitor : Chapter5.CodeGenVisitor
+{
+ protected override LLVMValueRef CodegenBinary(BinaryExprAST node)
+ {
+ // The built-in operators keep their inlined lowering from chapter 3.
+ switch (node.Op)
+ {
+ case '+':
+ case '-':
+ case '*':
+ case '<':
+ {
+ return base.CodegenBinary(node);
+ }
+ }
+
+ // Anything else is a user-defined operator: emit a call to its binary function.
+ LLVMValueRef left = Codegen(node.Lhs);
+ LLVMValueRef right = Codegen(node.Rhs);
+
+ LLVMValueRef function = GetFunction("binary" + node.Op);
+ if (function.Handle == IntPtr.Zero)
+ {
+ throw new InvalidOperationException($"binary operator '{node.Op}' not found");
+ }
+
+ LLVMTypeRef functionType = GetFunctionType(function);
+ return Builder.BuildCall2(functionType, function, new LLVMValueRef[] { left, right }, "binop");
+ }
+
+ protected override LLVMValueRef CodegenUnary(UnaryExprAST node)
+ {
+ LLVMValueRef operand = Codegen(node.Operand);
+
+ LLVMValueRef function = GetFunction("unary" + node.Opcode);
+ if (function.Handle == IntPtr.Zero)
+ {
+ throw new InvalidOperationException($"unary operator '{node.Opcode}' not found");
+ }
+
+ LLVMTypeRef functionType = GetFunctionType(function);
+ return Builder.BuildCall2(functionType, function, new LLVMValueRef[] { operand }, "unop");
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Chapter6/Parser.cs b/samples/KaleidoscopeTutorial/Chapter6/Parser.cs
new file mode 100644
index 00000000..9b1b64a6
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter6/Parser.cs
@@ -0,0 +1,136 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+
+namespace Kaleidoscope.Chapter6;
+
+///
+/// Chapter 6 parser delta — user-defined operators. It adds a production for
+/// prefix operators and extends to parse unary<op> and
+/// binary<op> [precedence] definitions, registering a new binary operator's precedence into
+/// the shared precedence table so subsequent input parses with the right binding.
+///
+public class Parser(Lexer lexer, IDictionary binaryOpPrecedence) : Chapter5.Parser(lexer)
+{
+ private readonly IDictionary _binaryOpPrecedence = binaryOpPrecedence;
+
+ // unary ::= primary | unary
+ protected override ExprAST? ParseUnary()
+ {
+ // If the current token isn't an operator character, it must be a primary expression. Negative
+ // tokens are keywords/identifiers/numbers; '(' and ',' start or separate primaries.
+ int token = Lexer.CurrentToken;
+ if (token < 0 || token == '(' || token == ',')
+ {
+ return ParsePrimary();
+ }
+
+ // Otherwise it's a prefix unary operator; read the operator and its operand.
+ int opcode = token;
+ Lexer.GetNextToken();
+
+ ExprAST? operand = ParseUnary();
+ return operand is null ? null : new UnaryExprAST((char)opcode, operand);
+ }
+
+ // prototype
+ // ::= id '(' id* ')'
+ // ::= 'unary' '(' id ')'
+ // ::= 'binary' [precedence] '(' id id ')'
+ protected override PrototypeAST? ParsePrototype()
+ {
+ string functionName;
+ int kind; // 0 = identifier, 1 = unary operator, 2 = binary operator.
+ int binaryPrecedence = 30;
+
+ switch (Lexer.CurrentToken)
+ {
+ case (int)Token.Identifier:
+ {
+ functionName = Lexer.LastIdentifier;
+ kind = 0;
+ Lexer.GetNextToken();
+ break;
+ }
+
+ case (int)Token.Unary:
+ {
+ Lexer.GetNextToken();
+ if (Lexer.CurrentToken <= 0)
+ {
+ return LogErrorProto("Expected unary operator");
+ }
+
+ functionName = "unary" + (char)Lexer.CurrentToken;
+ kind = 1;
+ Lexer.GetNextToken();
+ break;
+ }
+
+ case (int)Token.Binary:
+ {
+ Lexer.GetNextToken();
+ if (Lexer.CurrentToken <= 0)
+ {
+ return LogErrorProto("Expected binary operator");
+ }
+
+ functionName = "binary" + (char)Lexer.CurrentToken;
+ kind = 2;
+ Lexer.GetNextToken();
+
+ // Read the optional precedence.
+ if (Lexer.CurrentToken == (int)Token.Number)
+ {
+ if (Lexer.LastNumber is < 1 or > 100)
+ {
+ return LogErrorProto("Invalid precedence: must be 1..100");
+ }
+
+ binaryPrecedence = (int)Lexer.LastNumber;
+ Lexer.GetNextToken();
+ }
+
+ break;
+ }
+
+ default:
+ {
+ return LogErrorProto("Expected function name in prototype");
+ }
+ }
+
+ if (Lexer.CurrentToken != '(')
+ {
+ return LogErrorProto("Expected '(' in prototype");
+ }
+
+ var argNames = new List();
+ while (Lexer.GetNextToken() == (int)Token.Identifier)
+ {
+ argNames.Add(Lexer.LastIdentifier);
+ }
+
+ if (Lexer.CurrentToken != ')')
+ {
+ return LogErrorProto("Expected ')' in prototype");
+ }
+
+ Lexer.GetNextToken(); // eat ')'.
+
+ if (kind != 0 && argNames.Count != kind)
+ {
+ return LogErrorProto("Invalid number of operands for operator");
+ }
+
+ var prototype = new PrototypeAST(functionName, argNames, IsOperator: kind != 0, binaryPrecedence);
+
+ // Make a user-defined binary operator usable in the input that follows its definition.
+ if (prototype.IsBinaryOperator)
+ {
+ _binaryOpPrecedence[prototype.OperatorName] = prototype.Precedence;
+ }
+
+ return prototype;
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Chapter6/Program.cs b/samples/KaleidoscopeTutorial/Chapter6/Program.cs
new file mode 100644
index 00000000..68f86779
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter6/Program.cs
@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope;
+using Kaleidoscope.Chapter4;
+using Kaleidoscope.Chapter6;
+
+// Chapter 6 reuses chapter 4's JIT driver and swaps in a parser/code generator that understand
+// user-defined unary and binary operators.
+var binaryOpPrecedence = new Dictionary
+{
+ ['<'] = 10,
+ ['+'] = 20,
+ ['-'] = 20,
+ ['*'] = 40,
+};
+
+using TextReader reader = args.Length > 0 ? new StreamReader(args[0]) : Console.In;
+
+var lexer = new Lexer(reader, binaryOpPrecedence);
+var parser = new Kaleidoscope.Chapter6.Parser(lexer, binaryOpPrecedence);
+var visitor = new CodeGenVisitor();
+
+using var driver = new JitReplDriver(lexer, parser, visitor);
+driver.Run();
diff --git a/samples/KaleidoscopeTutorial/Chapter7/Chapter7.csproj b/samples/KaleidoscopeTutorial/Chapter7/Chapter7.csproj
new file mode 100644
index 00000000..e80700fa
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter7/Chapter7.csproj
@@ -0,0 +1,8 @@
+
+
+ Exe
+
+
+
+
+
diff --git a/samples/KaleidoscopeTutorial/Chapter7/CodeGenVisitor.cs b/samples/KaleidoscopeTutorial/Chapter7/CodeGenVisitor.cs
new file mode 100644
index 00000000..45b5b3f6
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter7/CodeGenVisitor.cs
@@ -0,0 +1,175 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+using LLVMSharp.Interop;
+
+namespace Kaleidoscope.Chapter7;
+
+///
+/// Chapter 7 code generation delta — mutable variables. Every variable now lives in a stack slot
+/// (an alloca): reads become loads, assignment (=) becomes a store, function parameters
+/// and loop variables get their own slots, and var/in introduces new ones. The optimizer's
+/// mem2reg pass (already in the pipeline) promotes these back to SSA registers, so the emitted
+/// code is as tight as the earlier chapters despite the extra loads/stores.
+///
+public class CodeGenVisitor : Chapter6.CodeGenVisitor
+{
+ protected override LLVMValueRef CodegenVariable(VariableExprAST node)
+ {
+ if (!NamedValues.TryGetValue(node.Name, out LLVMValueRef alloca))
+ {
+ throw new InvalidOperationException($"Unknown variable name '{node.Name}'");
+ }
+
+ // Variables are now stack slots, so a reference is a load from the slot.
+ return Builder.BuildLoad2(Module.Context.DoubleType, alloca, node.Name);
+ }
+
+ protected override LLVMValueRef CodegenBinary(BinaryExprAST node)
+ {
+ // Assignment is special: it does not evaluate its left-hand side as an expression.
+ if (node.Op == '=')
+ {
+ if (node.Lhs is not VariableExprAST target)
+ {
+ throw new InvalidOperationException("destination of '=' must be a variable");
+ }
+
+ LLVMValueRef value = Codegen(node.Rhs);
+ if (!NamedValues.TryGetValue(target.Name, out LLVMValueRef alloca))
+ {
+ throw new InvalidOperationException($"Unknown variable name '{target.Name}'");
+ }
+
+ Builder.BuildStore(value, alloca);
+ return value;
+ }
+
+ return base.CodegenBinary(node);
+ }
+
+ protected override LLVMValueRef CodegenFor(ForExprAST node)
+ {
+ LLVMValueRef function = Builder.InsertBlock.Parent;
+
+ // Give the loop variable a stack slot and initialize it with the start value.
+ LLVMValueRef alloca = CreateEntryBlockAlloca(function, node.VarName);
+ LLVMValueRef startValue = Codegen(node.Start);
+ Builder.BuildStore(startValue, alloca);
+
+ LLVMBasicBlockRef loopBlock = function.AppendBasicBlock("loop");
+ Builder.BuildBr(loopBlock);
+ Builder.PositionAtEnd(loopBlock);
+
+ // Bring the loop variable into scope, shadowing any outer binding.
+ bool hadOldValue = NamedValues.TryGetValue(node.VarName, out LLVMValueRef oldValue);
+ NamedValues[node.VarName] = alloca;
+
+ // Emit the body; its value is discarded.
+ Codegen(node.Body);
+
+ // Compute the step, then reload/increment/store the loop variable.
+ LLVMValueRef stepValue = node.Step is not null
+ ? Codegen(node.Step)
+ : LLVMValueRef.CreateConstReal(Module.Context.DoubleType, 1.0);
+
+ LLVMValueRef endCondition = Codegen(node.End);
+
+ LLVMValueRef currentValue = Builder.BuildLoad2(Module.Context.DoubleType, alloca, node.VarName);
+ LLVMValueRef nextValue = Builder.BuildFAdd(currentValue, stepValue, "nextvar");
+ Builder.BuildStore(nextValue, alloca);
+
+ LLVMValueRef zero = LLVMValueRef.CreateConstReal(Module.Context.DoubleType, 0.0);
+ endCondition = Builder.BuildFCmp(LLVMRealPredicate.LLVMRealONE, endCondition, zero, "loopcond");
+
+ LLVMBasicBlockRef afterBlock = function.AppendBasicBlock("afterloop");
+ Builder.BuildCondBr(endCondition, loopBlock, afterBlock);
+ Builder.PositionAtEnd(afterBlock);
+
+ // Restore the shadowed variable.
+ if (hadOldValue)
+ {
+ NamedValues[node.VarName] = oldValue;
+ }
+ else
+ {
+ NamedValues.Remove(node.VarName);
+ }
+
+ // A 'for' expression always evaluates to 0.0.
+ return LLVMValueRef.CreateConstReal(Module.Context.DoubleType, 0.0);
+ }
+
+ protected override LLVMValueRef CodegenVar(VarExprAST node)
+ {
+ LLVMValueRef function = Builder.InsertBlock.Parent;
+ var oldBindings = new List<(string Name, LLVMValueRef OldValue, bool Existed)>();
+
+ foreach ((string name, ExprAST? init) in node.VarNames)
+ {
+ // Evaluate the initializer before this name enters scope, so 'var a = a' sees the outer a.
+ LLVMValueRef initValue = init is not null
+ ? Codegen(init)
+ : LLVMValueRef.CreateConstReal(Module.Context.DoubleType, 0.0);
+
+ LLVMValueRef alloca = CreateEntryBlockAlloca(function, name);
+ Builder.BuildStore(initValue, alloca);
+
+ bool existed = NamedValues.TryGetValue(name, out LLVMValueRef oldValue);
+ oldBindings.Add((name, oldValue, existed));
+ NamedValues[name] = alloca;
+ }
+
+ LLVMValueRef body = Codegen(node.Body);
+
+ // Restore all shadowed bindings.
+ foreach ((string name, LLVMValueRef oldValue, bool existed) in oldBindings)
+ {
+ if (existed)
+ {
+ NamedValues[name] = oldValue;
+ }
+ else
+ {
+ NamedValues.Remove(name);
+ }
+ }
+
+ return body;
+ }
+
+ protected override void CreateParameterBindings(LLVMValueRef function, PrototypeAST proto)
+ {
+ // Each parameter gets a stack slot seeded with the incoming argument value.
+ NamedValues.Clear();
+ for (int i = 0; i < proto.Arguments.Count; i++)
+ {
+ string name = proto.Arguments[i];
+ LLVMValueRef alloca = CreateEntryBlockAlloca(function, name);
+ Builder.BuildStore(function.GetParam((uint)i), alloca);
+ NamedValues[name] = alloca;
+ }
+ }
+
+ ///
+ /// Creates an alloca at the very start of the function's entry block. Keeping every alloca in
+ /// the entry block is what lets mem2reg promote them to SSA registers.
+ ///
+ protected LLVMValueRef CreateEntryBlockAlloca(LLVMValueRef function, string name)
+ {
+ LLVMBasicBlockRef entry = function.EntryBasicBlock;
+
+ using LLVMBuilderRef temporary = Module.Context.CreateBuilder();
+ LLVMValueRef firstInstruction = entry.FirstInstruction;
+ if (firstInstruction.Handle != IntPtr.Zero)
+ {
+ temporary.PositionBefore(firstInstruction);
+ }
+ else
+ {
+ temporary.PositionAtEnd(entry);
+ }
+
+ return temporary.BuildAlloca(Module.Context.DoubleType, name);
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Chapter7/Parser.cs b/samples/KaleidoscopeTutorial/Chapter7/Parser.cs
new file mode 100644
index 00000000..63e26f20
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter7/Parser.cs
@@ -0,0 +1,81 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+
+namespace Kaleidoscope.Chapter7;
+
+///
+/// Chapter 7 parser delta — adds the var/in primary for introducing mutable local variables.
+/// Assignment (=) needs no new grammar: it is registered as a low-precedence binary operator in
+/// the precedence table, so the inherited binary-operator parsing handles it.
+///
+public class Parser(Lexer lexer, IDictionary binaryOpPrecedence)
+ : Chapter6.Parser(lexer, binaryOpPrecedence)
+{
+ protected override bool TryParseKeywordPrimary(int token, out ExprAST? result)
+ {
+ if (token == (int)Token.Var)
+ {
+ result = ParseVarExpr();
+ return true;
+ }
+
+ return base.TryParseKeywordPrimary(token, out result);
+ }
+
+ // varexpr ::= 'var' identifier ('=' expression)? (',' identifier ('=' expression)?)* 'in' expression
+ private ExprAST? ParseVarExpr()
+ {
+ Lexer.GetNextToken(); // eat the var.
+
+ var varNames = new List<(string Name, ExprAST? Init)>();
+
+ if (Lexer.CurrentToken != (int)Token.Identifier)
+ {
+ return LogError("expected identifier after var");
+ }
+
+ while (true)
+ {
+ string name = Lexer.LastIdentifier;
+ Lexer.GetNextToken(); // eat the identifier.
+
+ // Read the optional initializer.
+ ExprAST? init = null;
+ if (Lexer.CurrentToken == '=')
+ {
+ Lexer.GetNextToken(); // eat the '='.
+
+ init = ParseExpression();
+ if (init is null)
+ {
+ return null;
+ }
+ }
+
+ varNames.Add((name, init));
+
+ if (Lexer.CurrentToken != ',')
+ {
+ break; // end of the var list.
+ }
+
+ Lexer.GetNextToken(); // eat the ','.
+
+ if (Lexer.CurrentToken != (int)Token.Identifier)
+ {
+ return LogError("expected identifier list after var");
+ }
+ }
+
+ if (Lexer.CurrentToken != (int)Token.In)
+ {
+ return LogError("expected 'in' keyword after 'var'");
+ }
+
+ Lexer.GetNextToken(); // eat the in.
+
+ ExprAST? body = ParseExpression();
+ return body is null ? null : new VarExprAST(varNames, body);
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Chapter7/Program.cs b/samples/KaleidoscopeTutorial/Chapter7/Program.cs
new file mode 100644
index 00000000..7bef134e
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter7/Program.cs
@@ -0,0 +1,25 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope;
+using Kaleidoscope.Chapter4;
+using Kaleidoscope.Chapter7;
+
+// Chapter 7 reuses chapter 4's JIT driver and swaps in a parser/code generator that support mutable
+// variables. Assignment is a low-precedence binary operator, so '=' is added to the precedence table.
+var binaryOpPrecedence = new Dictionary
+{
+ ['='] = 2,
+ ['<'] = 10,
+ ['+'] = 20,
+ ['-'] = 20,
+ ['*'] = 40,
+};
+
+using TextReader reader = args.Length > 0 ? new StreamReader(args[0]) : Console.In;
+
+var lexer = new Lexer(reader, binaryOpPrecedence);
+var parser = new Kaleidoscope.Chapter7.Parser(lexer, binaryOpPrecedence);
+var visitor = new CodeGenVisitor();
+
+using var driver = new JitReplDriver(lexer, parser, visitor);
+driver.Run();
diff --git a/samples/KaleidoscopeTutorial/Chapter8/Chapter8.csproj b/samples/KaleidoscopeTutorial/Chapter8/Chapter8.csproj
new file mode 100644
index 00000000..8694967e
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter8/Chapter8.csproj
@@ -0,0 +1,8 @@
+
+
+ Exe
+
+
+
+
+
diff --git a/samples/KaleidoscopeTutorial/Chapter8/ObjectFileDriver.cs b/samples/KaleidoscopeTutorial/Chapter8/ObjectFileDriver.cs
new file mode 100644
index 00000000..2aa57ba1
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter8/ObjectFileDriver.cs
@@ -0,0 +1,29 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+using LLVMSharp.Interop;
+
+namespace Kaleidoscope.Chapter8;
+
+///
+/// Chapter 8's driver: instead of JIT-executing top-level expressions, it generates every definition,
+/// extern, and top-level expression into a single module. The program then hands that module to a
+/// target machine to emit a native object file. There is no execution here — the output is a `.o`.
+///
+public sealed class ObjectFileDriver : ReplDriver
+{
+ private readonly CodeGenVisitorBase _visitor;
+
+ public ObjectFileDriver(Lexer lexer, Parser parser, CodeGenVisitorBase visitor, LLVMModuleRef module)
+ : base(lexer, parser)
+ {
+ _visitor = visitor;
+ _visitor.SetModule(module);
+ }
+
+ protected override void OnDefinition(FunctionAST function) => _visitor.CodegenFunction(function);
+
+ protected override void OnExtern(PrototypeAST prototype) => _visitor.CodegenExtern(prototype);
+
+ protected override void OnTopLevelExpression(FunctionAST function) => _visitor.CodegenFunction(function);
+}
diff --git a/samples/KaleidoscopeTutorial/Chapter8/Program.cs b/samples/KaleidoscopeTutorial/Chapter8/Program.cs
new file mode 100644
index 00000000..63f0bc9d
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Chapter8/Program.cs
@@ -0,0 +1,48 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope;
+using Kaleidoscope.Chapter8;
+using LLVMSharp.Interop;
+
+// Chapter 8 is a batch compiler rather than a REPL: it reads a whole Kaleidoscope program, generates
+// every definition into one module, and emits a native object file that a C driver can link against.
+var binaryOpPrecedence = new Dictionary
+{
+ ['='] = 2,
+ ['<'] = 10,
+ ['+'] = 20,
+ ['-'] = 20,
+ ['*'] = 40,
+};
+
+string outputPath = args.Length > 1 ? args[1] : "output.o";
+
+using TextReader reader = args.Length > 0 ? new StreamReader(args[0]) : Console.In;
+
+var lexer = new Lexer(reader, binaryOpPrecedence);
+var parser = new Kaleidoscope.Chapter7.Parser(lexer, binaryOpPrecedence);
+var visitor = new Kaleidoscope.Chapter7.CodeGenVisitor();
+
+LlvmSupport.EnsureTargetsInitialized();
+LLVMTargetMachineRef targetMachine = LlvmSupport.CreateHostTargetMachine();
+
+LLVMContextRef context = LLVMContextRef.Create();
+LLVMModuleRef module = context.CreateModuleWithName("KaleidoscopeModule");
+LlvmSupport.PrepareModuleForEmit(module, targetMachine);
+
+var driver = new ObjectFileDriver(lexer, parser, visitor, module);
+driver.Run();
+
+Console.WriteLine("=== Module IR ===");
+Console.Write(module.PrintToString());
+Console.WriteLine();
+
+if (targetMachine.TryEmitToFile(module, outputPath, LLVMCodeGenFileType.LLVMObjectFile, out string message))
+{
+ Console.WriteLine($"Wrote object file to '{outputPath}' (target {module.Target}).");
+}
+else
+{
+ Console.Error.WriteLine($"Failed to emit object file: {message}");
+ Environment.Exit(1);
+}
diff --git a/samples/KaleidoscopeTutorial/Directory.Build.props b/samples/KaleidoscopeTutorial/Directory.Build.props
new file mode 100644
index 00000000..f9d11666
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Directory.Build.props
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+ net10.0
+ enable
+ enable
+ true
+ latest
+
+ false
+
+
+
+
+
+
+
+
diff --git a/samples/KaleidoscopeTutorial/Directory.Build.targets b/samples/KaleidoscopeTutorial/Directory.Build.targets
new file mode 100644
index 00000000..5f35364f
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Directory.Build.targets
@@ -0,0 +1,13 @@
+
+
+
+
+
+ $(NETCoreSdkRuntimeIdentifier)
+
+
+
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Ast/ExprAST.cs b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Ast/ExprAST.cs
new file mode 100644
index 00000000..494142aa
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Ast/ExprAST.cs
@@ -0,0 +1,30 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+namespace Kaleidoscope.AST;
+
+/// Base class for all expression nodes.
+public abstract record ExprAST;
+
+/// A numeric literal, e.g. 1.0 (chapter 3).
+public sealed record NumberExprAST(double Value) : ExprAST;
+
+/// A reference to a variable, e.g. a (chapter 3).
+public sealed record VariableExprAST(string Name) : ExprAST;
+
+/// A binary operator expression, e.g. a + b (chapter 3; user operators in chapter 6).
+public sealed record BinaryExprAST(char Op, ExprAST Lhs, ExprAST Rhs) : ExprAST;
+
+/// A unary operator expression, e.g. !a (chapter 6).
+public sealed record UnaryExprAST(char Opcode, ExprAST Operand) : ExprAST;
+
+/// A function call, e.g. foo(a, b) (chapter 3).
+public sealed record CallExprAST(string Callee, IReadOnlyList Arguments) : ExprAST;
+
+/// An if/then/else expression (chapter 5).
+public sealed record IfExprAST(ExprAST Condition, ExprAST Then, ExprAST Else) : ExprAST;
+
+/// A for/in loop expression; is null when omitted (chapter 5).
+public sealed record ForExprAST(string VarName, ExprAST Start, ExprAST End, ExprAST? Step, ExprAST Body) : ExprAST;
+
+/// A var/in expression introducing mutable locals (chapter 7).
+public sealed record VarExprAST(IReadOnlyList<(string Name, ExprAST? Init)> VarNames, ExprAST Body) : ExprAST;
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Ast/PrototypeAST.cs b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Ast/PrototypeAST.cs
new file mode 100644
index 00000000..10b0ff97
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Ast/PrototypeAST.cs
@@ -0,0 +1,24 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+namespace Kaleidoscope.AST;
+
+///
+/// The "prototype" for a function — its name and argument names. From chapter 6 a prototype can
+/// also declare a user-defined operator, in which case it carries the operator precedence.
+///
+public sealed record PrototypeAST(
+ string Name,
+ IReadOnlyList Arguments,
+ bool IsOperator = false,
+ int Precedence = 0)
+{
+ public bool IsUnaryOperator => IsOperator && Arguments.Count == 1;
+
+ public bool IsBinaryOperator => IsOperator && Arguments.Count == 2;
+
+ /// The operator character for an operator prototype (the last char of the name).
+ public char OperatorName => Name[^1];
+}
+
+/// A function definition: a prototype plus the body expression it evaluates to.
+public sealed record FunctionAST(PrototypeAST Proto, ExprAST Body);
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/CodeGenVisitorBase.cs b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/CodeGenVisitorBase.cs
new file mode 100644
index 00000000..8a07b91a
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/CodeGenVisitorBase.cs
@@ -0,0 +1,111 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+using LLVMSharp.Interop;
+
+namespace Kaleidoscope;
+
+///
+/// Base class for the per-chapter code generators. It owns the state shared by every chapter — the
+/// current , its , the in-scope ,
+/// and the map used to re-declare functions across modules — and routes
+/// each to a protected virtual that a chapter overrides. Nodes a chapter
+/// doesn't support fall through to a throwing default.
+///
+public abstract class CodeGenVisitorBase
+{
+ /// Remembered prototypes so a function can be re-declared in a later module.
+ protected Dictionary FunctionProtos { get; } = new();
+
+ /// The symbols currently in scope while generating a function body.
+ protected Dictionary NamedValues { get; } = new();
+
+ /// The module the generator is currently emitting into.
+ protected LLVMModuleRef Module { get; private set; }
+
+ /// A builder positioned within .
+ protected LLVMBuilderRef Builder { get; private set; }
+
+ /// Points the generator at a new module, creating a builder in that module's context.
+ public void SetModule(LLVMModuleRef module)
+ {
+ Module = module;
+ Builder = module.Context.CreateBuilder();
+ }
+
+ /// Generates code for an expression, dispatching to the chapter-specific override.
+ public LLVMValueRef Codegen(ExprAST node) => node switch
+ {
+ NumberExprAST n => CodegenNumber(n),
+ VariableExprAST n => CodegenVariable(n),
+ BinaryExprAST n => CodegenBinary(n),
+ UnaryExprAST n => CodegenUnary(n),
+ CallExprAST n => CodegenCall(n),
+ IfExprAST n => CodegenIf(n),
+ ForExprAST n => CodegenFor(n),
+ VarExprAST n => CodegenVar(n),
+ _ => throw Unsupported(node),
+ };
+
+ /// Emits a declaration for an extern and remembers it for later modules.
+ public LLVMValueRef CodegenExtern(PrototypeAST node)
+ {
+ RegisterPrototype(node);
+ return CodegenPrototype(node);
+ }
+
+ ///
+ /// Remembers a prototype without emitting anything. The JIT driver uses this for extern:
+ /// a declaration isn't a definition, so it needs no module of its own — the declaration is emitted
+ /// lazily by into whichever module first references it.
+ ///
+ public void RegisterPrototype(PrototypeAST node) => FunctionProtos[node.Name] = node;
+
+ /// Declares a function (its prototype) in the current module.
+ public abstract LLVMValueRef CodegenPrototype(PrototypeAST node);
+
+ /// Defines a function (prototype plus body) in the current module.
+ public abstract LLVMValueRef CodegenFunction(FunctionAST node);
+
+ protected virtual LLVMValueRef CodegenNumber(NumberExprAST node) => throw Unsupported(node);
+
+ protected virtual LLVMValueRef CodegenVariable(VariableExprAST node) => throw Unsupported(node);
+
+ protected virtual LLVMValueRef CodegenBinary(BinaryExprAST node) => throw Unsupported(node);
+
+ protected virtual LLVMValueRef CodegenUnary(UnaryExprAST node) => throw Unsupported(node);
+
+ protected virtual LLVMValueRef CodegenCall(CallExprAST node) => throw Unsupported(node);
+
+ protected virtual LLVMValueRef CodegenIf(IfExprAST node) => throw Unsupported(node);
+
+ protected virtual LLVMValueRef CodegenFor(ForExprAST node) => throw Unsupported(node);
+
+ protected virtual LLVMValueRef CodegenVar(VarExprAST node) => throw Unsupported(node);
+
+ ///
+ /// Returns the named function from the current module, declaring it from a remembered prototype
+ /// when it isn't present. This is what lets a call resolve a callee that was defined in an
+ /// earlier, separate module (each top-level item is its own module once the JIT is involved).
+ ///
+ protected LLVMValueRef GetFunction(string name)
+ {
+ LLVMValueRef existing = Module.GetNamedFunction(name);
+ if (existing.Handle != IntPtr.Zero)
+ {
+ return existing;
+ }
+
+ return FunctionProtos.TryGetValue(name, out PrototypeAST? proto) ? CodegenPrototype(proto) : default;
+ }
+
+ ///
+ /// Recovers a function's type from the function value. Using this avoids the friendly
+ /// LLVMValueRef.FunctionType, which depends on the (unshipped) libLLVMSharp native
+ /// helper; works against the stock libLLVM.
+ ///
+ protected static unsafe LLVMTypeRef GetFunctionType(LLVMValueRef function) => LLVM.GlobalGetValueType(function);
+
+ private static NotSupportedException Unsupported(object node) =>
+ new($"This chapter's code generator does not support {node.GetType().Name}.");
+}
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/HostFunctions.cs b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/HostFunctions.cs
new file mode 100644
index 00000000..14c0c583
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/HostFunctions.cs
@@ -0,0 +1,38 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+namespace Kaleidoscope;
+
+///
+/// The host functions the tutorial exposes to JIT'd Kaleidoscope code — putchard (print a
+/// character) and printd (print a value). Declaring them with extern in Kaleidoscope and
+/// having them resolve to these managed methods is how the tutorial does I/O (upstream issues #69 and
+/// #133). They are registered as absolute symbols in .
+///
+public static unsafe class HostFunctions
+{
+ /// putchard(x): prints the character whose code is ; returns 0.
+ [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
+ public static double Putchard(double value)
+ {
+ Console.Write((char)(int)value);
+ return 0.0;
+ }
+
+ /// printd(x): prints followed by a newline; returns 0.
+ [UnmanagedCallersOnly(CallConvs = [typeof(CallConvCdecl)])]
+ public static double Printd(double value)
+ {
+ Console.WriteLine(value);
+ return 0.0;
+ }
+
+ /// Registers every host function with so externs resolve.
+ public static void DefineAll(KaleidoscopeJit jit)
+ {
+ jit.DefineSymbol("putchard", (nint)(delegate* unmanaged[Cdecl])&Putchard);
+ jit.DefineSymbol("printd", (nint)(delegate* unmanaged[Cdecl])&Printd);
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Kaleidoscope.Common.csproj b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Kaleidoscope.Common.csproj
new file mode 100644
index 00000000..80f0ccba
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Kaleidoscope.Common.csproj
@@ -0,0 +1,5 @@
+
+
+ Library
+
+
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/KaleidoscopeJit.cs b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/KaleidoscopeJit.cs
new file mode 100644
index 00000000..96fd4492
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/KaleidoscopeJit.cs
@@ -0,0 +1,129 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using LLVMSharp.Interop;
+
+namespace Kaleidoscope;
+
+///
+/// A minimal ORC "LLJIT" wrapper — the modern replacement for the removed ExecutionEngine/MCJIT
+/// path. It creates an LLJIT instance, wires up a generator so JIT'd code can call symbols already in
+/// the host process (libc sin/cos, …), and lets the driver add a module per top-level
+/// item. Each top-level expression is added under its own resource tracker so it can be removed after
+/// it runs, which is what lets the REPL evaluate more than one expression (upstream issue #1).
+///
+public sealed unsafe class KaleidoscopeJit : IDisposable
+{
+ private LLVMOrcOpaqueLLJIT* _jit;
+ private readonly LLVMOrcOpaqueJITDylib* _mainJD;
+
+ public KaleidoscopeJit()
+ {
+ LlvmSupport.EnsureTargetsInitialized();
+
+ LLVMOrcOpaqueLLJIT* jit;
+ LlvmSupport.ThrowIfError(LLVM.OrcCreateLLJIT(&jit, null), "Failed to create LLJIT");
+ _jit = jit;
+
+ _mainJD = LLVM.OrcLLJITGetMainJITDylib(jit);
+
+ // Resolve symbols already present in the host process (e.g. libc math functions).
+ LLVMOrcOpaqueDefinitionGenerator* generator;
+ LlvmSupport.ThrowIfError(
+ LLVM.OrcCreateDynamicLibrarySearchGeneratorForProcess(&generator, LLVM.OrcLLJITGetGlobalPrefix(jit), null, null),
+ "Failed to create process-symbol generator");
+ LLVM.OrcJITDylibAddGenerator(_mainJD, generator);
+ }
+
+ /// The data layout string of the JIT, so emitted modules can match it.
+ public string DataLayout => new(LLVM.OrcLLJITGetDataLayoutStr(_jit));
+
+ /// The target triple of the JIT.
+ public string Triple => new(LLVM.OrcLLJITGetTripleString(_jit));
+
+ /// Stamps a module with the JIT's data layout and triple before it is added.
+ public void ConfigureModule(LLVMModuleRef module)
+ {
+ using var dataLayout = new MarshaledString(DataLayout);
+ using var triple = new MarshaledString(Triple);
+ LLVM.SetDataLayout(module, dataLayout);
+ LLVM.SetTarget(module, triple);
+ }
+
+ /// Adds a module permanently (used for def so the function stays callable).
+ public void AddModule(LLVMModuleRef module)
+ {
+ LLVMOrcOpaqueThreadSafeModule* threadSafeModule = WrapModule(module);
+ LlvmSupport.ThrowIfError(LLVM.OrcLLJITAddLLVMIRModule(_jit, _mainJD, threadSafeModule), "Failed to add module");
+ }
+
+ ///
+ /// Adds a module under a fresh resource tracker and returns that tracker. Used for the anonymous
+ /// top-level expression so the caller can it after evaluating, freeing
+ /// the __anon_expr name for the next expression.
+ ///
+ public nint AddModuleRemovable(LLVMModuleRef module)
+ {
+ LLVMOrcOpaqueResourceTracker* tracker = LLVM.OrcJITDylibCreateResourceTracker(_mainJD);
+ LLVMOrcOpaqueThreadSafeModule* threadSafeModule = WrapModule(module);
+ LlvmSupport.ThrowIfError(LLVM.OrcLLJITAddLLVMIRModuleWithRT(_jit, tracker, threadSafeModule), "Failed to add module");
+ return (nint)tracker;
+ }
+
+ /// Removes a module previously added via .
+ public void RemoveModule(nint tracker)
+ {
+ var resourceTracker = (LLVMOrcOpaqueResourceTracker*)tracker;
+ LlvmSupport.ThrowIfError(LLVM.OrcResourceTrackerRemove(resourceTracker), "Failed to remove module");
+ LLVM.OrcReleaseResourceTracker(resourceTracker);
+ }
+
+ /// Looks up a symbol's address in the JIT.
+ public ulong Lookup(string name)
+ {
+ ulong address;
+ using var marshaled = new MarshaledString(name);
+ LlvmSupport.ThrowIfError(LLVM.OrcLLJITLookup(_jit, &address, marshaled), $"Symbol '{name}' not found");
+ return address;
+ }
+
+ ///
+ /// Defines an absolute symbol pointing at a host function address. This is how the tutorial exposes
+ /// its own putchard/printd helpers to JIT'd Kaleidoscope code (issues #69 and #133).
+ ///
+ public void DefineSymbol(string name, nint address)
+ {
+ LLVMOrcOpaqueSymbolStringPoolEntry* entry;
+ using (var marshaled = new MarshaledString(name))
+ {
+ entry = LLVM.OrcLLJITMangleAndIntern(_jit, marshaled);
+ }
+
+ LLVMOrcCSymbolMapPair pair;
+ pair.Name = entry;
+ pair.Sym.Address = (ulong)address;
+ pair.Sym.Flags.GenericFlags = (byte)(LLVMJITSymbolGenericFlags.LLVMJITSymbolGenericFlagsExported | LLVMJITSymbolGenericFlags.LLVMJITSymbolGenericFlagsCallable);
+ pair.Sym.Flags.TargetFlags = 0;
+
+ LLVMOrcOpaqueMaterializationUnit* unit = LLVM.OrcAbsoluteSymbols(&pair, 1);
+ LlvmSupport.ThrowIfError(LLVM.OrcJITDylibDefine(_mainJD, unit), $"Failed to define '{name}'");
+ }
+
+ private static LLVMOrcOpaqueThreadSafeModule* WrapModule(LLVMModuleRef module)
+ {
+ // Wrap the module (built in its own LLVMContext) in a thread-safe module the JIT can take. The
+ // LLVM 21 API takes ownership of the context we pass, so we dispose our handle afterwards.
+ LLVMOrcOpaqueThreadSafeContext* threadSafeContext = LLVM.OrcCreateNewThreadSafeContextFromLLVMContext(module.Context);
+ LLVMOrcOpaqueThreadSafeModule* threadSafeModule = LLVM.OrcCreateNewThreadSafeModule(module, threadSafeContext);
+ LLVM.OrcDisposeThreadSafeContext(threadSafeContext);
+ return threadSafeModule;
+ }
+
+ public void Dispose()
+ {
+ if (_jit is not null)
+ {
+ _ = LLVM.OrcDisposeLLJIT(_jit);
+ _jit = null;
+ }
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Lexer.cs b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Lexer.cs
new file mode 100644
index 00000000..a39a8caa
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Lexer.cs
@@ -0,0 +1,134 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using System.Globalization;
+using System.Text;
+
+namespace Kaleidoscope;
+
+///
+/// Turns a character stream into Kaleidoscope tokens (tutorial chapter 1). One lexer serves every
+/// chapter: it always recognizes the full keyword set, and the per-chapter parser decides which of
+/// those keywords it is willing to accept.
+///
+public sealed class Lexer
+{
+ private const int Eof = -1;
+
+ private readonly TextReader _reader;
+ private readonly IReadOnlyDictionary _binaryOpPrecedence;
+ private readonly StringBuilder _builder = new();
+
+ private int _lastChar = ' ';
+
+ public Lexer(TextReader reader, IReadOnlyDictionary binaryOpPrecedence)
+ {
+ _reader = reader;
+ _binaryOpPrecedence = binaryOpPrecedence;
+ }
+
+ /// The most recently scanned token. Positive values are literal characters.
+ public int CurrentToken { get; private set; }
+
+ /// The identifier text for the most recent .
+ public string LastIdentifier { get; private set; } = string.Empty;
+
+ /// The value for the most recent .
+ public double LastNumber { get; private set; }
+
+ /// Precedence of the current token if it is a known binary operator, otherwise -1.
+ public int GetTokenPrecedence()
+ {
+ if (CurrentToken > 0 && _binaryOpPrecedence.TryGetValue((char)CurrentToken, out int precedence))
+ {
+ return precedence;
+ }
+
+ return -1;
+ }
+
+ /// Scans and returns the next token, also exposed via .
+ public int GetNextToken()
+ {
+ CurrentToken = ReadToken();
+ return CurrentToken;
+ }
+
+ private int ReadToken()
+ {
+ // Skip any whitespace.
+ while (char.IsWhiteSpace((char)_lastChar))
+ {
+ _lastChar = _reader.Read();
+ }
+
+ // identifier: [a-zA-Z][a-zA-Z0-9]*
+ if (char.IsLetter((char)_lastChar))
+ {
+ _builder.Clear();
+ do
+ {
+ _builder.Append((char)_lastChar);
+ _lastChar = _reader.Read();
+ }
+ while (char.IsLetterOrDigit((char)_lastChar));
+
+ LastIdentifier = _builder.ToString();
+
+ return LastIdentifier switch
+ {
+ "def" => (int)Token.Def,
+ "extern" => (int)Token.Extern,
+ "if" => (int)Token.If,
+ "then" => (int)Token.Then,
+ "else" => (int)Token.Else,
+ "for" => (int)Token.For,
+ "in" => (int)Token.In,
+ "binary" => (int)Token.Binary,
+ "unary" => (int)Token.Unary,
+ "var" => (int)Token.Var,
+ _ => (int)Token.Identifier,
+ };
+ }
+
+ // number: [0-9.]+
+ if (char.IsDigit((char)_lastChar) || _lastChar == '.')
+ {
+ _builder.Clear();
+ do
+ {
+ _builder.Append((char)_lastChar);
+ _lastChar = _reader.Read();
+ }
+ while (char.IsDigit((char)_lastChar) || _lastChar == '.');
+
+ LastNumber = double.Parse(_builder.ToString(), CultureInfo.InvariantCulture);
+ return (int)Token.Number;
+ }
+
+ // comment until end of line
+ if (_lastChar == '#')
+ {
+ do
+ {
+ _lastChar = _reader.Read();
+ }
+ while (_lastChar != Eof && _lastChar != '\n' && _lastChar != '\r');
+
+ if (_lastChar != Eof)
+ {
+ return ReadToken();
+ }
+ }
+
+ // Don't eat the EOF.
+ if (_lastChar == Eof)
+ {
+ return (int)Token.Eof;
+ }
+
+ // Otherwise, return the character as its ASCII value.
+ int thisChar = _lastChar;
+ _lastChar = _reader.Read();
+ return thisChar;
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/LlvmSupport.cs b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/LlvmSupport.cs
new file mode 100644
index 00000000..ac1d8acd
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/LlvmSupport.cs
@@ -0,0 +1,71 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using LLVMSharp.Interop;
+
+namespace Kaleidoscope;
+
+///
+/// Small helpers over the raw LLVM-C API that every JIT/codegen chapter needs: one-time native
+/// target initialization, a host , and turning an
+/// LLVMErrorRef into a managed exception. Kept here so the chapters read like the tutorial.
+///
+public static unsafe class LlvmSupport
+{
+ private static bool s_targetsInitialized;
+
+ /// Initializes the native targets (idempotent). Required before JIT or object emission.
+ public static void EnsureTargetsInitialized()
+ {
+ if (s_targetsInitialized)
+ {
+ return;
+ }
+
+ LLVM.InitializeAllTargetInfos();
+ LLVM.InitializeAllTargets();
+ LLVM.InitializeAllTargetMCs();
+ LLVM.InitializeAllAsmPrinters();
+ LLVM.InitializeAllAsmParsers();
+
+ s_targetsInitialized = true;
+ }
+
+ /// Creates a target machine for the host, used by the optimizer and object emitter.
+ public static LLVMTargetMachineRef CreateHostTargetMachine()
+ {
+ EnsureTargetsInitialized();
+
+ string triple = LLVMTargetRef.DefaultTriple;
+ LLVMTargetRef target = LLVMTargetRef.GetTargetFromTriple(triple);
+
+ return target.CreateTargetMachine(
+ triple,
+ cpu: "generic",
+ features: "",
+ LLVMCodeGenOptLevel.LLVMCodeGenLevelDefault,
+ LLVMRelocMode.LLVMRelocDefault,
+ LLVMCodeModel.LLVMCodeModelDefault);
+ }
+
+ /// Stamps a module with the target triple and data layout for object-file emission.
+ public static void PrepareModuleForEmit(LLVMModuleRef module, LLVMTargetMachineRef targetMachine)
+ {
+ module.Target = LLVMTargetRef.DefaultTriple;
+ LLVM.SetModuleDataLayout(module, targetMachine.CreateTargetDataLayout());
+ }
+
+ /// Throws if is non-null, consuming and freeing the error message.
+ public static void ThrowIfError(LLVMOpaqueError* error, string context)
+ {
+ if (error is null)
+ {
+ return;
+ }
+
+ sbyte* message = LLVM.GetErrorMessage(error);
+ string text = new(message);
+ LLVM.DisposeErrorMessage(message);
+
+ throw new InvalidOperationException($"{context}: {text}");
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Parser.cs b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Parser.cs
new file mode 100644
index 00000000..8630bd8e
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Parser.cs
@@ -0,0 +1,256 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+
+namespace Kaleidoscope;
+
+///
+/// A recursive-descent parser producing the (tutorial chapter 2). It exposes a
+/// handful of protected virtual seams — ,
+/// , , and —
+/// so later chapters can add new grammar productions without copying the whole parser.
+///
+public class Parser
+{
+ /// The name given to the anonymous function wrapping a top-level expression.
+ public const string AnonymousExpressionName = "__anon_expr";
+
+ public Parser(Lexer lexer) => Lexer = lexer;
+
+ protected Lexer Lexer { get; }
+
+ // definition ::= 'def' prototype expression
+ public FunctionAST? ParseDefinition()
+ {
+ Lexer.GetNextToken(); // eat def.
+
+ PrototypeAST? proto = ParsePrototype();
+ if (proto is null)
+ {
+ return null;
+ }
+
+ ExprAST? body = ParseExpression();
+ return body is null ? null : new FunctionAST(proto, body);
+ }
+
+ // external ::= 'extern' prototype
+ public PrototypeAST? ParseExtern()
+ {
+ Lexer.GetNextToken(); // eat extern.
+ return ParsePrototype();
+ }
+
+ // toplevelexpr ::= expression
+ public FunctionAST? ParseTopLevelExpr()
+ {
+ ExprAST? expr = ParseExpression();
+ if (expr is null)
+ {
+ return null;
+ }
+
+ // Make an anonymous prototype so a top-level expression can be JIT-compiled and called.
+ var proto = new PrototypeAST(AnonymousExpressionName, Array.Empty());
+ return new FunctionAST(proto, expr);
+ }
+
+ protected static ExprAST? LogError(string message)
+ {
+ Console.Error.WriteLine($"Error: {message}");
+ return null;
+ }
+
+ protected static PrototypeAST? LogErrorProto(string message)
+ {
+ Console.Error.WriteLine($"Error: {message}");
+ return null;
+ }
+
+ // expression ::= unary binoprhs
+ protected ExprAST? ParseExpression()
+ {
+ ExprAST? lhs = ParseUnary();
+ return lhs is null ? null : ParseBinOpRHS(0, lhs);
+ }
+
+ // primary
+ // ::= identifierexpr
+ // ::= numberexpr
+ // ::= parenexpr
+ // ::=
+ protected virtual ExprAST? ParsePrimary()
+ {
+ switch (Lexer.CurrentToken)
+ {
+ case (int)Token.Identifier:
+ return ParseIdentifierExpr();
+
+ case (int)Token.Number:
+ return ParseNumberExpr();
+
+ case '(':
+ return ParseParenExpr();
+
+ default:
+ if (TryParseKeywordPrimary(Lexer.CurrentToken, out ExprAST? result))
+ {
+ return result;
+ }
+
+ return LogError("unknown token when expecting an expression");
+ }
+ }
+
+ /// Hook for chapters that add primary forms (if/for in ch5, var in ch7).
+ protected virtual bool TryParseKeywordPrimary(int token, out ExprAST? result)
+ {
+ result = null;
+ return false;
+ }
+
+ // unary ::= primary | unary. Chapter 6 overrides this to parse user-defined unary operators.
+ protected virtual ExprAST? ParseUnary() => ParsePrimary();
+
+ // binoprhs ::= ( unary)*
+ protected ExprAST? ParseBinOpRHS(int expressionPrecedence, ExprAST lhs)
+ {
+ while (true)
+ {
+ int tokenPrecedence = Lexer.GetTokenPrecedence();
+
+ // If this binop binds less tightly than the current one, we are done.
+ if (tokenPrecedence < expressionPrecedence)
+ {
+ return lhs;
+ }
+
+ int binaryOp = Lexer.CurrentToken;
+ Lexer.GetNextToken(); // eat binop.
+
+ ExprAST? rhs = ParseUnary();
+ if (rhs is null)
+ {
+ return null;
+ }
+
+ // If the operator after RHS binds more tightly, let it take RHS as its LHS.
+ int nextPrecedence = Lexer.GetTokenPrecedence();
+ if (tokenPrecedence < nextPrecedence)
+ {
+ rhs = ParseBinOpRHS(tokenPrecedence + 1, rhs);
+ if (rhs is null)
+ {
+ return null;
+ }
+ }
+
+ lhs = new BinaryExprAST((char)binaryOp, lhs, rhs);
+ }
+ }
+
+ // prototype ::= id '(' id* ')'. Chapter 6 overrides this to parse operator prototypes.
+ protected virtual PrototypeAST? ParsePrototype()
+ {
+ if (Lexer.CurrentToken != (int)Token.Identifier)
+ {
+ return LogErrorProto("Expected function name in prototype");
+ }
+
+ string fnName = Lexer.LastIdentifier;
+ Lexer.GetNextToken();
+
+ if (Lexer.CurrentToken != '(')
+ {
+ return LogErrorProto("Expected '(' in prototype");
+ }
+
+ var argNames = new List();
+ while (Lexer.GetNextToken() == (int)Token.Identifier)
+ {
+ argNames.Add(Lexer.LastIdentifier);
+ }
+
+ if (Lexer.CurrentToken != ')')
+ {
+ return LogErrorProto("Expected ')' in prototype");
+ }
+
+ Lexer.GetNextToken(); // eat ')'.
+ return new PrototypeAST(fnName, argNames);
+ }
+
+ // numberexpr ::= number
+ private ExprAST ParseNumberExpr()
+ {
+ var result = new NumberExprAST(Lexer.LastNumber);
+ Lexer.GetNextToken(); // consume the number.
+ return result;
+ }
+
+ // parenexpr ::= '(' expression ')'
+ private ExprAST? ParseParenExpr()
+ {
+ Lexer.GetNextToken(); // eat (.
+
+ ExprAST? v = ParseExpression();
+ if (v is null)
+ {
+ return null;
+ }
+
+ if (Lexer.CurrentToken != ')')
+ {
+ return LogError("expected ')'");
+ }
+
+ Lexer.GetNextToken(); // eat ).
+ return v;
+ }
+
+ // identifierexpr
+ // ::= identifier
+ // ::= identifier '(' expression* ')'
+ private ExprAST? ParseIdentifierExpr()
+ {
+ string idName = Lexer.LastIdentifier;
+ Lexer.GetNextToken(); // eat identifier.
+
+ if (Lexer.CurrentToken != '(')
+ {
+ return new VariableExprAST(idName); // simple variable ref.
+ }
+
+ Lexer.GetNextToken(); // eat (.
+ var args = new List();
+
+ if (Lexer.CurrentToken != ')')
+ {
+ while (true)
+ {
+ ExprAST? arg = ParseExpression();
+ if (arg is null)
+ {
+ return null;
+ }
+
+ args.Add(arg);
+
+ if (Lexer.CurrentToken == ')')
+ {
+ break;
+ }
+
+ if (Lexer.CurrentToken != ',')
+ {
+ return LogError("Expected ')' or ',' in argument list");
+ }
+
+ Lexer.GetNextToken();
+ }
+ }
+
+ Lexer.GetNextToken(); // eat ).
+ return new CallExprAST(idName, args);
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/ReplDriver.cs b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/ReplDriver.cs
new file mode 100644
index 00000000..f296997c
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/ReplDriver.cs
@@ -0,0 +1,122 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using Kaleidoscope.AST;
+
+namespace Kaleidoscope;
+
+///
+/// The top-level "read a token, decide what to parse" loop (tutorial chapter 2). It is unchanged from
+/// chapter to chapter, so it lives here: it drives the lexer, recovers from a parse error by skipping a
+/// token, and hands each parsed construct to a protected abstract that the concrete driver
+/// implements (dump IR, JIT and run, or emit an object file).
+///
+public abstract class ReplDriver
+{
+ protected ReplDriver(Lexer lexer, Parser parser)
+ {
+ Lexer = lexer;
+ Parser = parser;
+ }
+
+ protected Lexer Lexer { get; }
+
+ protected Parser Parser { get; }
+
+ /// Runs the loop until end-of-file.
+ public void Run()
+ {
+ Lexer.GetNextToken(); // prime the first token.
+
+ while (true)
+ {
+ switch (Lexer.CurrentToken)
+ {
+ case (int)Token.Eof:
+ {
+ return;
+ }
+
+ case ';':
+ {
+ Lexer.GetNextToken(); // ignore top-level semicolons.
+ break;
+ }
+
+ case (int)Token.Def:
+ {
+ HandleDefinition();
+ break;
+ }
+
+ case (int)Token.Extern:
+ {
+ HandleExtern();
+ break;
+ }
+
+ default:
+ {
+ HandleTopLevelExpression();
+ break;
+ }
+ }
+ }
+ }
+
+ /// Consumes and code-generates a def.
+ protected abstract void OnDefinition(FunctionAST function);
+
+ /// Consumes and registers an extern.
+ protected abstract void OnExtern(PrototypeAST prototype);
+
+ /// Consumes and evaluates a top-level expression.
+ protected abstract void OnTopLevelExpression(FunctionAST function);
+
+ private void HandleDefinition()
+ {
+ FunctionAST? function = Parser.ParseDefinition();
+ if (function is null)
+ {
+ Lexer.GetNextToken(); // skip token for error recovery.
+ return;
+ }
+
+ Dispatch(() => OnDefinition(function));
+ }
+
+ private void HandleExtern()
+ {
+ PrototypeAST? prototype = Parser.ParseExtern();
+ if (prototype is null)
+ {
+ Lexer.GetNextToken(); // skip token for error recovery.
+ return;
+ }
+
+ Dispatch(() => OnExtern(prototype));
+ }
+
+ private void HandleTopLevelExpression()
+ {
+ FunctionAST? function = Parser.ParseTopLevelExpr();
+ if (function is null)
+ {
+ Lexer.GetNextToken(); // skip token for error recovery.
+ return;
+ }
+
+ Dispatch(() => OnTopLevelExpression(function));
+ }
+
+ private static void Dispatch(Action action)
+ {
+ try
+ {
+ action();
+ }
+ catch (Exception exception)
+ {
+ Console.Error.WriteLine($"Error: {exception.Message}");
+ }
+ }
+}
diff --git a/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Token.cs b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Token.cs
new file mode 100644
index 00000000..3dd228c3
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/Kaleidoscope.Common/Token.cs
@@ -0,0 +1,36 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+namespace Kaleidoscope;
+
+///
+/// The token kinds produced by the . Any positive value returned by the
+/// lexer is a literal character (an operator or punctuation); the negative values below are
+/// the "known" tokens. The full set for every chapter lives here so a single lexer can serve
+/// all of them — early chapters simply never ask the parser to accept the later keywords.
+///
+public enum Token
+{
+ Eof = -1,
+
+ // commands
+ Def = -2,
+ Extern = -3,
+
+ // primary
+ Identifier = -4,
+ Number = -5,
+
+ // control flow (chapter 5)
+ If = -6,
+ Then = -7,
+ Else = -8,
+ For = -9,
+ In = -10,
+
+ // user-defined operators (chapter 6)
+ Binary = -11,
+ Unary = -12,
+
+ // mutable variables (chapter 7)
+ Var = -13,
+}
diff --git a/samples/KaleidoscopeTutorial/KaleidoscopeTutorial.slnx b/samples/KaleidoscopeTutorial/KaleidoscopeTutorial.slnx
new file mode 100644
index 00000000..a1e16ef7
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/KaleidoscopeTutorial.slnx
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/KaleidoscopeTutorial/README.md b/samples/KaleidoscopeTutorial/README.md
new file mode 100644
index 00000000..7100a10b
--- /dev/null
+++ b/samples/KaleidoscopeTutorial/README.md
@@ -0,0 +1,103 @@
+# Kaleidoscope tutorial (LLVMSharp)
+
+A C# port of LLVM's [Kaleidoscope tutorial](https://llvm.org/docs/tutorial/), built on
+[LLVMSharp.Interop](../../sources/LLVMSharp.Interop). Kaleidoscope is a small functional language
+(every value is a `double`) that the tutorial grows one feature at a time while introducing the LLVM
+APIs for building IR, optimizing it, JIT-executing it, and finally emitting native object code.
+
+## Layout
+
+Unlike the upstream C++ tutorial — which copies the whole compiler into each chapter — this port keeps
+the invariant frontend in a shared `Kaleidoscope.Common` library, and each chapter project contains
+**only its delta** over the previous one. That makes the incremental steps easy to see: a chapter's
+`Parser`/`CodeGenVisitor` derive from the previous chapter's and override just the new productions.
+
+```
+Kaleidoscope.Common/ Lexer, Token, the full AST, base Parser + base CodeGenVisitor,
+ the ORC LLJIT wrapper, the REPL loop, and host functions.
+Chapter3/ Emit LLVM IR (no execution) — dumps the module.
+Chapter4/ Add the JIT and the optimizer (new pass manager).
+Chapter5/ Add control flow: if/then/else and for loops.
+Chapter6/ Add user-defined unary and binary operators.
+Chapter7/ Add mutable variables (alloca + mem2reg) and assignment.
+Chapter8/ Compile a whole program to a native object file.
+```
+
+Each chapter references the previous one, so `Chapter7` transitively sees `Chapter3`–`Chapter6` and
+`Kaleidoscope.Common`. Chapters 4 and 8 are purely a new driver over the previous chapter's frontend;
+Chapters 5–7 add grammar and codegen.
+
+### Chapter ↔ tutorial mapping
+
+| Chapter | Tutorial | Adds |
+| --- | --- | --- |
+| 3 | Ch. 2–3 | Lexer, parser, AST, IR generation (dump only) |
+| 4 | Ch. 4 | ORC LLJIT execution + optimization passes |
+| 5 | Ch. 5 | `if`/`then`/`else`, `for` loops |
+| 6 | Ch. 6 | User-defined operators, operator precedence |
+| 7 | Ch. 7 | Mutable variables, `var`/`in`, `=` assignment |
+| 8 | Ch. 8 | Native object-file emission |
+
+The tutorial's Chapter 9 (debug info / DWARF) and Chapter 10 (conclusion) are not ported.
+
+## Running
+
+Requires the .NET 10 preview SDK (resolved via the repo's `global.json`). From this directory:
+
+```
+dotnet build KaleidoscopeTutorial.slnx -c Release
+dotnet run --project Chapter4 -c Release
+```
+
+Chapters 3–7 read Kaleidoscope from stdin (or a file passed as the first argument) as a REPL. Try:
+
+```
+def fib(x) if x < 3 then 1 else fib(x - 1) + fib(x - 2);
+fib(10);
+
+extern sin(x);
+sin(1.0);
+
+# user-defined operators (chapter 6+)
+def unary!(v) if v then 0 else 1;
+!0;
+
+# mutable variables (chapter 7+)
+def binary : 1 (x y) y;
+def fibi(x) var a = 1, b = 1, c in (for i = 3, i < x in c = a + b : a = b : b = c) : b;
+fibi(10);
+```
+
+Chapter 8 is a batch compiler: it reads a whole program and writes an object file (default `output.o`,
+or a second argument):
+
+```
+echo "def average(x y) (x + y) * 0.5;" | dotnet run --project Chapter8 -c Release
+```
+
+The resulting object exports `average` with C ABI (`double average(double, double)`), so it can be
+linked into a C/C++ program.
+
+## Tests
+
+These samples are part of the root `LLVMSharp.slnx`, so they build and are validated in CI. The
+`LLVMSharp.KaleidoscopeTests` project under `tests/` launches each chapter as a subprocess, feeds it a
+Kaleidoscope script, and asserts on the emitted IR / evaluated results / object file. Run them with:
+
+```
+dotnet build -c Release
+dotnet test -c Release --no-build --filter "FullyQualifiedName~KaleidoscopeTests"
+```
+
+## Notes
+
+- **JIT.** Execution uses ORC LLJIT (`Kaleidoscope.Common/KaleidoscopeJit.cs`). Each top-level
+ expression is added as its own module under a resource tracker, executed, then removed, so entering
+ several expressions in a row works correctly.
+- **Extern / host functions.** `extern` declarations resolve against the host process (e.g. libc `sin`,
+ `cos`), and `putchard`/`printd` are injected as absolute symbols (`Kaleidoscope.Common/HostFunctions.cs`)
+ so the tutorial's `printstar`/`printd` examples work.
+- **Optimizer.** Chapters 4+ run the new pass-manager pipeline
+ (`mem2reg,instcombine,reassociate,gvn,simplifycfg`) via `LLVM.RunPasses`.
+- The interop under `../../sources/LLVMSharp.Interop/llvm` is auto-generated; these samples only use the
+ hand-written friendly wrappers and the raw ORC C API.
diff --git a/tests/LLVMSharp.KaleidoscopeTests/ChapterTests.cs b/tests/LLVMSharp.KaleidoscopeTests/ChapterTests.cs
new file mode 100644
index 00000000..ce76d2fe
--- /dev/null
+++ b/tests/LLVMSharp.KaleidoscopeTests/ChapterTests.cs
@@ -0,0 +1,225 @@
+// Copyright (c) .NET Foundation and Contributors. All Rights Reserved. Licensed under the MIT License (MIT). See License.md in the repository root for more information.
+
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using NUnit.Framework;
+
+namespace LLVMSharp.KaleidoscopeTests;
+
+///
+/// Smoke tests that each Kaleidoscope tutorial chapter still builds and produces the expected output.
+/// Every chapter is an executable REPL (or, for chapter 8, a batch compiler), so each test launches the
+/// chapter as a subprocess, feeds it a Kaleidoscope script on stdin, and asserts on what it prints.
+/// Running each chapter in its own process matches how a user runs the samples and keeps the native LLVM
+/// state isolated between chapters.
+///
+public sealed class ChapterTests
+{
+ private const int TimeoutMilliseconds = 120_000;
+
+ [Test]
+ public void Chapter3_EmitsIr()
+ {
+ var output = RunChapter("Chapter3", """
+ extern sin(x);
+ def foo(a b) a + b;
+ foo(2, 3);
+ """);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(output, Does.Contain("declare double @sin(double)"));
+ Assert.That(output, Does.Contain("define double @foo(double %a, double %b)"));
+ });
+ }
+
+ [Test]
+ public void Chapter4_JitsAndOptimizes()
+ {
+ // Two calls to the same function must give distinct results in one session (upstream #1), and
+ // an extern libc function must resolve from the host process (#69).
+ var output = RunChapter("Chapter4", """
+ def foo(a b) a*a + 2*a*b + b*b;
+ foo(2, 3);
+ foo(4, 5);
+ extern cos(x);
+ cos(0);
+ """);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(output, Does.Contain("Evaluated to 25"));
+ Assert.That(output, Does.Contain("Evaluated to 81"));
+ Assert.That(output, Does.Contain("Evaluated to 1"));
+ });
+ }
+
+ [Test]
+ public void Chapter5_ControlFlowAndHostOutput()
+ {
+ // fib exercises if/else; printstar exercises the for loop plus the putchard host function (#133).
+ var output = RunChapter("Chapter5", """
+ def fib(x) if x < 3 then 1 else fib(x - 1) + fib(x - 2);
+ fib(10);
+ extern putchard(x);
+ def printstar(n) for i = 1, i < n in putchard(42);
+ printstar(6);
+ """);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(output, Does.Contain("Evaluated to 55"));
+ Assert.That(output.Count(c => c == '*'), Is.EqualTo(6), "printstar(6) runs the loop while i < 6 starting at i = 1, printing six '*' characters");
+ });
+ }
+
+ [Test]
+ public void Chapter6_UserDefinedOperators()
+ {
+ var output = RunChapter("Chapter6", """
+ def unary!(v) if v then 0 else 1;
+ !0;
+ !1;
+ """);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(output, Does.Contain("Evaluated to 1"));
+ Assert.That(output, Does.Contain("Evaluated to 0"));
+ });
+ }
+
+ [Test]
+ public void Chapter7_MutableVariables()
+ {
+ // test mutates its parameter; fib is the iterative mutable-variable formulation.
+ var output = RunChapter("Chapter7", """
+ def binary : 1 (x y) y;
+ def test(x) (x = x + 1) + x;
+ test(3);
+ def fib(x) var a = 1, b = 1, c in (for i = 3, i < x in c = a + b : a = b : b = c) : b;
+ fib(10);
+ """);
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(output, Does.Contain("Evaluated to 8"));
+ Assert.That(output, Does.Contain("Evaluated to 55"));
+ });
+ }
+
+ [Test]
+ public void Chapter8_EmitsObjectFile()
+ {
+ var workingDirectory = Directory.CreateTempSubdirectory("kaleidoscope-ch8-").FullName;
+
+ try
+ {
+ var output = RunChapter("Chapter8", "def average(x y) (x + y) * 0.5;", workingDirectory);
+ var objectFile = Path.Combine(workingDirectory, "output.o");
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(output, Does.Contain("define double @average"));
+ Assert.That(File.Exists(objectFile), Is.True, "expected output.o to be emitted");
+ Assert.That(new FileInfo(objectFile).Length, Is.GreaterThan(0), "output.o should not be empty");
+ });
+ }
+ finally
+ {
+ Directory.Delete(workingDirectory, recursive: true);
+ }
+ }
+
+ private static string RunChapter(string chapter, string input, string? workingDirectory = null)
+ {
+ var assembly = LocateChapterAssembly(chapter);
+
+ var startInfo = new ProcessStartInfo("dotnet")
+ {
+ RedirectStandardInput = true,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ UseShellExecute = false,
+ WorkingDirectory = workingDirectory ?? Path.GetDirectoryName(assembly)!,
+ };
+ startInfo.ArgumentList.Add("exec");
+ startInfo.ArgumentList.Add(assembly);
+
+ using var process = new Process { StartInfo = startInfo };
+ var standardOutput = new StringBuilder();
+ var standardError = new StringBuilder();
+ process.OutputDataReceived += (_, e) => { if (e.Data is not null) { _ = standardOutput.AppendLine(e.Data); } };
+ process.ErrorDataReceived += (_, e) => { if (e.Data is not null) { _ = standardError.AppendLine(e.Data); } };
+
+ _ = process.Start();
+ process.BeginOutputReadLine();
+ process.BeginErrorReadLine();
+ process.StandardInput.Write(input);
+ process.StandardInput.Close();
+
+ if (!process.WaitForExit(TimeoutMilliseconds))
+ {
+ process.Kill(entireProcessTree: true);
+ Assert.Fail($"{chapter} did not exit within {TimeoutMilliseconds}ms.\nstdout:\n{standardOutput}\nstderr:\n{standardError}");
+ }
+
+ process.WaitForExit(); // flush the async output readers.
+
+ Assert.That(process.ExitCode, Is.Zero, $"{chapter} exited with {process.ExitCode}.\nstdout:\n{standardOutput}\nstderr:\n{standardError}");
+ return standardOutput.ToString();
+ }
+
+ private static string LocateChapterAssembly(string chapter)
+ {
+ var repositoryRoot = FindRepositoryRoot();
+ var configuration = GetBuildConfiguration();
+ var chapterBin = Path.Combine(repositoryRoot, "samples", "KaleidoscopeTutorial", chapter, "bin", configuration, "net10.0");
+
+ if (Directory.Exists(chapterBin))
+ {
+ // The chapter builds to a runtime-identifier subdirectory (e.g. .../net10.0/win-x64/), so
+ // search rather than hard-coding the RID.
+ var assembly = Directory.EnumerateFiles(chapterBin, chapter + ".dll", SearchOption.AllDirectories).FirstOrDefault();
+ if (assembly is not null)
+ {
+ return assembly;
+ }
+ }
+
+ Assert.Fail($"Could not find {chapter}.dll under '{chapterBin}'. Build the '{configuration}' configuration of the solution before running these tests.");
+ return string.Empty; // unreachable; Assert.Fail throws.
+ }
+
+ private static string FindRepositoryRoot()
+ {
+ for (var directory = new DirectoryInfo(AppContext.BaseDirectory); directory is not null; directory = directory.Parent)
+ {
+ if (File.Exists(Path.Combine(directory.FullName, "LLVMSharp.slnx")) &&
+ Directory.Exists(Path.Combine(directory.FullName, "samples")))
+ {
+ return directory.FullName;
+ }
+ }
+
+ Assert.Fail("Could not locate the repository root (no ancestor directory contains LLVMSharp.slnx and samples/).");
+ return string.Empty; // unreachable; Assert.Fail throws.
+ }
+
+ private static string GetBuildConfiguration()
+ {
+ foreach (var metadata in typeof(ChapterTests).Assembly.GetCustomAttributes())
+ {
+ if ((metadata.Key == "BuildConfiguration") && !string.IsNullOrEmpty(metadata.Value))
+ {
+ return metadata.Value;
+ }
+ }
+
+ return "Release";
+ }
+}
diff --git a/tests/LLVMSharp.KaleidoscopeTests/LLVMSharp.KaleidoscopeTests.csproj b/tests/LLVMSharp.KaleidoscopeTests/LLVMSharp.KaleidoscopeTests.csproj
new file mode 100644
index 00000000..4bb50c50
--- /dev/null
+++ b/tests/LLVMSharp.KaleidoscopeTests/LLVMSharp.KaleidoscopeTests.csproj
@@ -0,0 +1,23 @@
+
+
+
+
+ net10.0
+
+
+
+
+
+
+
+
+ $(NoWarn);CA1515;CA1707;CA1711;CA1724;IDE0130
+
+
+
+
+
+
+
+