Skip to content

Commit 837f7cc

Browse files
authored
Kaleidoscope tutorial chapter 4 updates (#55)
1 parent a09c484 commit 837f7cc

File tree

12 files changed

+393
-89
lines changed

12 files changed

+393
-89
lines changed

Samples/Kaleidoscope/Chapter3/CodeGenerator.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ public override Value VisitVariableExpression( [NotNull] VariableExpressionConte
8383

8484
public override Value VisitFunctionCallExpression( [NotNull] FunctionCallExpressionContext context )
8585
{
86-
var function = GetFunction( context.CaleeName );
86+
var function = FindCallTarget( context.CaleeName );
8787
if( function == null )
8888
{
8989
throw new CodeGeneratorException( $"function '{context.CaleeName}' is unknown" );
@@ -185,7 +185,7 @@ private Value EmitBinaryOperator( Value lhs, BinaryopContext op, IParseTree righ
185185
}
186186
// </EmitBinaryOperator>
187187

188-
private Function GetFunction( string name )
188+
private Function FindCallTarget( string name )
189189
{
190190
return Module.GetFunction( name );
191191
}

Samples/Kaleidoscope/Chapter3/Program.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ public static void Main( string[ ] args )
4747
var replLoop = new ReplLoop<Value>( generator, parser );
4848
replLoop.ReadyStateChanged += ( s, e ) => Console.Write( e.PartialParse ? ">" : "Ready>" );
4949
replLoop.GeneratedResultAvailable += OnGeneratedResultAvailable;
50+
replLoop.CodeGenerationError += OnGeneratorError;
5051

5152
replLoop.Run( );
5253
Console.WriteLine( generator.GeneratedModule.WriteToString( ) );
@@ -55,6 +56,22 @@ public static void Main( string[ ] args )
5556
}
5657
}
5758

59+
// <ErrorHandling>
60+
private static void OnGeneratorError( object sender, CodeGenerationExceptionArgs e )
61+
{
62+
var color = Console.ForegroundColor;
63+
Console.ForegroundColor = ConsoleColor.Red;
64+
try
65+
{
66+
Console.Error.WriteLine( e.Exception.Message );
67+
}
68+
finally
69+
{
70+
Console.ForegroundColor = color;
71+
}
72+
}
73+
// </ErrorHandling>
74+
5875
// <ResultProcessing>
5976
private static void OnGeneratedResultAvailable( object sender, GeneratedResultAvailableArgs<Value> e )
6077
{

Samples/Kaleidoscope/Chapter4/CodeGenerator.cs

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ public CodeGenerator( DynamicRuntimeState globalState )
3434
{
3535
RuntimeState = globalState;
3636
Context = new Context( );
37+
JIT = new KaleidoscopeJIT( );
3738
InitializeModuleAndPassManager( );
3839
InstructionBuilder = new InstructionBuilder( Context );
39-
JIT = new KaleidoscopeJIT( );
4040
FunctionPrototypes = new PrototypeCollection( );
4141
FunctionModuleMap = new Dictionary<string, IJitModuleHandle>( );
4242
NamedValues = new Dictionary<string, Value>( );
@@ -84,7 +84,7 @@ public override Value VisitVariableExpression( [NotNull] VariableExpressionConte
8484

8585
public override Value VisitFunctionCallExpression( [NotNull] FunctionCallExpressionContext context )
8686
{
87-
var function = GetFunction( context.CaleeName );
87+
var function = FindCallTarget( context.CaleeName );
8888
if( function == null )
8989
{
9090
throw new CodeGeneratorException( $"function '{context.CaleeName}' is unknown" );
@@ -104,13 +104,16 @@ public override Value VisitFunctionPrototype( [NotNull] FunctionPrototypeContext
104104
return GetOrDeclareFunction( new Prototype( context ) );
105105
}
106106

107+
// <VisitFunctionDefinition>
107108
public override Value VisitFunctionDefinition( [NotNull] FunctionDefinitionContext context )
108109
{
109110
return DefineFunction( ( Function )context.Signature.Accept( this )
110111
, context.BodyExpression
111112
).Function;
112113
}
114+
// </VisitFunctionDefinition>
113115

116+
// <VisitTopLevelExpression>
114117
public override Value VisitTopLevelExpression( [NotNull] TopLevelExpressionContext context )
115118
{
116119
var proto = new Prototype( $"anon_expr_{AnonNameIndex++}" );
@@ -120,9 +123,11 @@ public override Value VisitTopLevelExpression( [NotNull] TopLevelExpressionConte
120123

121124
var nativeFunc = JIT.GetDelegateForFunction<AnonExpressionFunc>( proto.Identifier.Name );
122125
var retVal = Context.CreateConstant( nativeFunc( ) );
126+
FunctionModuleMap.Remove( function.Name );
123127
JIT.RemoveModule( jitHandle );
124128
return retVal;
125129
}
130+
// </VisitTopLevelExpression>
126131

127132
public override Value VisitExpression( [NotNull] ExpressionContext context )
128133
{
@@ -181,19 +186,25 @@ private Value EmitBinaryOperator( Value lhs, BinaryopContext op, IParseTree righ
181186
}
182187
}
183188

189+
// <InitializeModuleAndPassManager>
184190
private void InitializeModuleAndPassManager( )
185191
{
186192
Module = Context.CreateBitcodeModule( );
193+
Module.Layout = JIT.TargetMachine.TargetData;
187194
FunctionPassManager = new FunctionPassManager( Module );
188195
FunctionPassManager.AddInstructionCombiningPass( )
189196
.AddReassociatePass( )
190197
.AddGVNPass( )
191198
.AddCFGSimplificationPass( )
192199
.Initialize( );
193200
}
201+
// </InitializeModuleAndPassManager>
194202

195-
private Function GetFunction( string name )
203+
// <FindCallTarget>
204+
private Function FindCallTarget( string name )
196205
{
206+
// lookup the prototype for the function to get the signature
207+
// and create a declaration in this module
197208
if( FunctionPrototypes.TryGetValue( name, out var signature ) )
198209
{
199210
return GetOrDeclareFunction( signature );
@@ -207,7 +218,9 @@ private Function GetFunction( string name )
207218

208219
return null;
209220
}
221+
// </FindCallTarget>
210222

223+
// <GetOrDeclareFunction>
211224
private Function GetOrDeclareFunction( Prototype prototype, bool isAnonymous = false )
212225
{
213226
var function = Module.GetFunction( prototype.Identifier.Name );
@@ -235,7 +248,9 @@ private Function GetOrDeclareFunction( Prototype prototype, bool isAnonymous = f
235248

236249
return retVal;
237250
}
251+
// </GetOrDeclareFunction>
238252

253+
// <DefineFunction>
239254
private (Function Function, IJitModuleHandle JitHandle) DefineFunction( Function function, ExpressionContext body )
240255
{
241256
if( !function.IsDeclaration )
@@ -283,6 +298,7 @@ private Function GetOrDeclareFunction( Prototype prototype, bool isAnonymous = f
283298
InitializeModuleAndPassManager( );
284299
return (function, jitHandle);
285300
}
301+
// </DefineFunction>
286302

287303
// <PrivateMembers>
288304
private readonly DynamicRuntimeState RuntimeState;

Samples/Kaleidoscope/Chapter4/Program.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414

1515
[assembly: SuppressMessage( "StyleCop.CSharp.DocumentationRules", "SA1652:Enable XML documentation output", Justification = "Sample application" )]
1616

17+
#pragma warning disable SA1512, SA1513, SA1515 // single line comments used to tag regions for extraction into docs
18+
1719
namespace Kaleidoscope
1820
{
1921
public static class Program
@@ -35,6 +37,8 @@ public static void Main( string[ ] args )
3537
using( InitializeLLVM( ) )
3638
{
3739
RegisterNative( );
40+
41+
// <generatorloop>
3842
var parser = new ReplParserStack( LanguageLevel.SimpleExpressions );
3943
using( var generator = new CodeGenerator( parser.GlobalState ) )
4044
{
@@ -43,12 +47,31 @@ public static void Main( string[ ] args )
4347
var replLoop = new ReplLoop<Value>( generator, parser );
4448
replLoop.ReadyStateChanged += ( s, e ) => Console.Write( e.PartialParse ? ">" : "Ready>" );
4549
replLoop.GeneratedResultAvailable += OnGeneratedResultAvailable;
50+
replLoop.CodeGenerationError += OnGeneratorError;
4651

4752
replLoop.Run( );
4853
}
54+
// </generatorloop>
55+
}
56+
}
57+
58+
// <ErrorHandling>
59+
private static void OnGeneratorError( object sender, CodeGenerationExceptionArgs e )
60+
{
61+
var color = Console.ForegroundColor;
62+
Console.ForegroundColor = ConsoleColor.Red;
63+
try
64+
{
65+
Console.Error.WriteLine( e.Exception.Message );
66+
}
67+
finally
68+
{
69+
Console.ForegroundColor = color;
4970
}
5071
}
72+
// </ErrorHandling>
5173

74+
// <ResultProcessing>
5275
private static void OnGeneratedResultAvailable( object sender, GeneratedResultAvailableArgs<Value> e )
5376
{
5477
var source = ( ReplLoop<Value> )sender;
@@ -69,5 +92,6 @@ private static void OnGeneratedResultAvailable( object sender, GeneratedResultAv
6992
break;
7093
}
7194
}
95+
// </ResultProcessing>
7296
}
7397
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// <copyright file="ReplLoop.cs" company=".NET Foundation">
2+
// Copyright (c) .NET Foundation. All rights reserved.
3+
// </copyright>
4+
5+
using System;
6+
7+
namespace Kaleidoscope.Runtime
8+
{
9+
public class CodeGenerationExceptionArgs
10+
: EventArgs
11+
{
12+
public CodeGenerationExceptionArgs( CodeGeneratorException exception )
13+
{
14+
Exception = exception;
15+
}
16+
17+
public CodeGeneratorException Exception { get; }
18+
}
19+
}

Samples/Kaleidoscope/Kaleidoscope.Runtime/ReplLoop.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ public ReplLoop( IKaleidoscopeCodeGenerator<TResult> generator
4242

4343
public event EventHandler<ReadyStateChangedArgs> ReadyStateChanged = (s,e)=> { };
4444

45+
public event EventHandler<CodeGenerationExceptionArgs> CodeGenerationError = (s,e) => { };
46+
4547
public bool Ready { get; private set; } = true;
4648

4749
public DiagnosticRepresentations AdditionalDiagnostics { get; }
@@ -63,8 +65,15 @@ public void Run( )
6365
if( !IsPartial )
6466
{
6567
var (parseTree, recognizer) = Parser.Parse( Txt, AdditionalDiagnostics );
66-
TResult value = Generator.Generate( recognizer, parseTree, AdditionalDiagnostics );
67-
GeneratedResultAvailable( this, new GeneratedResultAvailableArgs<TResult>( value, recognizer, parseTree ) );
68+
try
69+
{
70+
TResult value = Generator.Generate( recognizer, parseTree, AdditionalDiagnostics );
71+
GeneratedResultAvailable( this, new GeneratedResultAvailableArgs<TResult>( value, recognizer, parseTree ) );
72+
}
73+
catch(CodeGeneratorException ex)
74+
{
75+
CodeGenerationError( this, new CodeGenerationExceptionArgs( ex ) );
76+
}
6877
}
6978

7079
/*

docfx/articles/Samples/Kaleidoscope-ch2.md

Lines changed: 78 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
1-
# Implementing the parser
1+
# 2. Kaleidoscope: Implementing the parser
22
The Lllvm.NET version of Kaleidoscope leverages ANTLR4 to parse the language into a parse tree.
33
The chapter 2 sample doesn't actually generate any code. Instead it focuses on the general structure
44
of the samples and parsing of the language. The sample for this chapter enables all language features
55
to allow exploring the language and how it is parsed to help better understand the rest of the chapters
6-
better. It is hoped that users of this library find this helpful as understanding the language grammar
6+
better. It is hoped that users of this library find this helpful. Understanding the language grammar
77
from reading the LVM tutorials and source was a difficult task since it isn't formally defined in one
88
place. (There are some EBNF like comments in the code but it is spread around without much real discussion
99
of the language the tutorials guide you to implement)
1010

11-
1211
## Formal Grammar
1312
### Lexer symbols
1413

@@ -78,10 +77,10 @@ for the language. Subsequent chapters will introduce the meaning and use of each
7877

7978
#### Language Feature Defined Keywords
8079
Chapters 5-7 each introduce new language features that introduce new keywords into the language. In order to
81-
maintain a single grammar for all chapters the lexer uses a technique of ANTLR4 called semantic predicates.
82-
These are basically boolean expressions that determine if a given rule should be applied. These are applied
83-
to the rules for the feature specific keywords. Thus, at runtime, if a given feature is disabled then the
84-
keyword is not recognized.
80+
maintain a single grammar for all chapters the lexer uses a technique of ANTLR4 called [Semantic Predicates](https://github.com/antlr/antlr4/blob/master/doc/predicates.md).
81+
These are basically boolean expressions that determine if a given rule should be applied while parsing the
82+
input language. These are applied to the rules for the feature specific keywords. Thus, at runtime, if a given
83+
feature is disabled then the keyword is not recognized.
8584

8685
```antlr
8786
IF: {FeatureControlFlow}? 'if';
@@ -266,9 +265,78 @@ for the call `foo(1, 2, 3);` is:
266265

267266
![Parse Tree](parsetree-func-call.svg)
268267

269-
###### Other expressions
270-
The other expressions are either simple tokens like `Identifier` and `Number` or more complex expressions that are
271-
covered in detail in later chapters.
268+
###### VarInExpression
269+
```antlr
270+
VAR initializer (COMMA initializer)* IN expression[0]
271+
```
272+
The VarInExpression rule provides variable declaration, with optional initialization. The scope of the
273+
variables is that of the expression on the right of the `in` keyword. The `var ... in ...` expression is
274+
in many ways like a declaration of an inline function. The variables declared are scoped to the internal
275+
implementation of the function. Once the function produces the return value the variables no longer exist.
276+
277+
###### ConditionalExpression
278+
```antlr
279+
IF expression[0] THEN expression[0] ELSE expression[0]
280+
```
281+
Conditional expressions use the very common and familiar if-then-else syntax and semantics with one noteable
282+
unique quality. In Kaleidoscope every language construct is an expression, there are no statements. Expressions
283+
all produce a value. So the result of the conditional expression is the result of the sub expression selected
284+
based on the condition. The condition value is computed and if the result == 0.0 (false) the `else` expression
285+
is used to produce the final result. Otherwise, the `then` expression is executed to produce the result. Thus,
286+
the actual semantics are more like the ternary operator found C and other languages:
287+
```C
288+
condition ? thenExpression : elseExpression`
289+
```
290+
291+
Example:
292+
```Kaleidoscope
293+
def fib(x)
294+
if x < 3 then
295+
1
296+
else
297+
fib(x-1)+fib(x-2);
298+
```
299+
##### ForInExpression
300+
The ForInExpression provides support for classic for loop constructs. In particular it provides a variable scope for a loop
301+
value, a condition to test when to exit the loop and an optional step value for incrementing the loop value (default is 1.0).
302+
303+
```Kaleidoscope
304+
extern putchard(char);
305+
def printstar(n)
306+
for i = 1, i < n, 1.0 in
307+
putchard(42); # ascii 42 = '*'
308+
309+
# print 100 '*' characters
310+
printstar(100);
311+
```
312+
Note: That there are no statements in Kaleidoscope, everything is an expression and has a value. putchard() implicitly returns a
313+
value as does printstar(). (e.g. there is no void return ALL functions implictly return a floating point value, even if it is always 0.0)
314+
For loops with mutable values support in the language may provide a result that isn't always 0.0, for example:
315+
316+
```Kaleidoscope
317+
# Define ':' for sequencing: as a low-precedence operator that ignores operands
318+
# and just returns the RHS.
319+
def binary : 1 (x y) y;
320+
321+
# Recursive fib, we could do this before.
322+
def fib(x)
323+
if (x < 3) then
324+
1
325+
else
326+
fib(x-1)+fib(x-2);
327+
328+
# Iterative fib.
329+
def fibi(x)
330+
var a = 1, b = 1, c in
331+
(for i = 3, i < x in
332+
c = a + b :
333+
a = b :
334+
b = c) :
335+
b;
336+
337+
# Call it.
338+
fibi(10);
339+
```
272340

273341
#### Parse Tree
274342
The Llvm.NET implementation of Kaleidoscope doesn't use an AST per se. Instead it use the parse tree generated

0 commit comments

Comments
 (0)