Skip to content

Commit

Permalink
Inlined missing snippets
Browse files Browse the repository at this point in the history
  • Loading branch information
oskardudycz committed Feb 15, 2023
1 parent 6302ff8 commit f14db75
Show file tree
Hide file tree
Showing 7 changed files with 6 additions and 96 deletions.
18 changes: 3 additions & 15 deletions docs/guide/compilation/assembly-generator.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,16 @@ the `LamarCompiler.AssemblyGenerator` class in the LamarCompiler library.

Let's say that you have a simple interface in your system like this:

<!-- snippet: sample_IOperation -->
<a id='snippet-sample_ioperation'></a>
```cs
public interface IOperation
{
int Calculate(int one, int two);
}
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Codegen.cs#L9-L14' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_ioperation' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Next, let's use `AssemblyGenerator` to compile code with a custom implementation of `IOperation` that we've generated
in code:

<!-- snippet: sample_using-AssemblyGenerator -->
<a id='snippet-sample_using-assemblygenerator'></a>
```cs
var generator = new AssemblyGenerator();

Expand Down Expand Up @@ -64,22 +58,18 @@ var operation = (IOperation)Activator.CreateInstance(type);
// Use our new type
var result = operation.Calculate(1, 2);
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Codegen.cs#L29-L66' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_using-assemblygenerator' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

There's only a couple things going on in the code above:

1. I added an assembly reference for the .Net assembly that holds the `IOperation` interface
1. I passed a string to the `GenerateCode()` method, which successfully compiles my code and hands me back a .Net [Assembly](https://msdn.microsoft.com/en-us/library/system.reflection.assembly(v=vs.110).aspx) object
1. Load the newly generated type from the new Assembly
1. Use the new `IOperation`
2. I passed a string to the `GenerateCode()` method, which successfully compiles my code and hands me back a .Net [Assembly](https://msdn.microsoft.com/en-us/library/system.reflection.assembly(v=vs.110).aspx) object
3. Load the newly generated type from the new Assembly
4. Use the new `IOperation`

If you're not perfectly keen on doing brute force string manipulation to generate your code, you can
also use Lamar's built in [ISourceWriter](/guide/compilation/source-writer) to generate some of the code for you with
all its code generation utilities:

<!-- snippet: sample_using-AssemblyGenerator-with-source-writer -->
<a id='snippet-sample_using-assemblygenerator-with-source-writer'></a>
```cs
var generator = new AssemblyGenerator();

Expand Down Expand Up @@ -108,5 +98,3 @@ var operation = (IOperation)Activator.CreateInstance(type);

var result = operation.Calculate(1, 2);
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Codegen.cs#L73-L103' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_using-assemblygenerator-with-source-writer' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
8 changes: 0 additions & 8 deletions docs/guide/compilation/frames/extension-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ determine how a type should be written *in code* with these extension methods in

The functionality of `NameInCode()` is demonstrated below with some of its xUnit tests:

<!-- snippet: sample_get-the-type-name-in-code -->
<a id='snippet-sample_get-the-type-name-in-code'></a>
```cs
[Theory]
[InlineData(typeof(void), "void")]
Expand All @@ -29,13 +27,9 @@ public void alias_name_of_task(Type type, string name)
type.NameInCode().ShouldBe(name);
}
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Codegen/ReflectionExtensionsTests.cs#L70-L87' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_get-the-type-name-in-code' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Likewise, `FullNameInCode()` is shown below:

<!-- snippet: sample_get-the-full-type-name-in-code -->
<a id='snippet-sample_get-the-full-type-name-in-code'></a>
```cs
[Theory]
[InlineData(typeof(void), "void")]
Expand All @@ -53,5 +47,3 @@ public void alias_full_name_of_task(Type type, string name)
type.FullNameInCode().ShouldBe(name);
}
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Codegen/ReflectionExtensionsTests.cs#L89-L105' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_get-the-full-type-name-in-code' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
4 changes: 0 additions & 4 deletions docs/guide/compilation/frames/frame.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,6 @@ To build a custom frame, you first need to create a new class that subclasses `F
The one thing you absolutely have to do when you create a new `Frame` class is to override the `GenerateCode()` method. Take this example
from Lamar itself for a frame that just injects a comment into the generated code:

<!-- snippet: sample_CommentFrame -->
<a id='snippet-sample_commentframe'></a>
```cs
public class CommentFrame : SyncFrame
{
Expand All @@ -35,8 +33,6 @@ public class CommentFrame : SyncFrame
}
}
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCodeGeneration/Frames/Frame.cs#L16-L35' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_commentframe' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

A couple things to note about the `GenerateCode()` method:

Expand Down
26 changes: 3 additions & 23 deletions docs/guide/compilation/frames/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,15 @@ The various types represent:

Alright then, let's make this concrete. Let's say that we want to generate and use dynamic instances of this interface:

<!-- snippet: sample_ISaySomething -->
<a id='snippet-sample_isaysomething'></a>
```cs
public interface ISaySomething
{
void Speak();
}
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Frames.cs#L67-L72' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_isaysomething' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Moreover, I want a version of `ISaySomething` that will call the following method and write the current time to the console:

<!-- snippet: sample_NowSpeaker -->
<a id='snippet-sample_nowspeaker'></a>
```cs
public static class NowSpeaker
{
Expand All @@ -43,15 +37,11 @@ public static class NowSpeaker
}
}
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Frames.cs#L57-L65' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_nowspeaker' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Our dynamic class for ISaySomething will need to pass the current time to the now parameter of that method. To help out here, there's some built in helpers in Lamar specifically to write in the right code to get the current time to a variable of DateTime or DateTimeOffset that is named "now."

To skip ahead a little bit, let's generate a new class and object with the following code:

<!-- snippet: sample_write-new-method -->
<a id='snippet-sample_write-new-method'></a>
```cs
// Configures the code generation rules
// and policies
Expand Down Expand Up @@ -79,8 +69,6 @@ method.Frames.Add(@call);
// Compile the new code!
assembly.CompileAll();
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Frames.cs#L23-L49' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_write-new-method' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

After all that, if we interrogate the source code for the generated type above (type.SourceCode), we'd see this ugly generated code:

Expand All @@ -106,8 +94,6 @@ Some notes about the generated code:

So now let's look at how Lamar was able to add the code to pass along DateTime.UtcNow. First off, let's look at the code that just writes out the date variable:

<!-- snippet: sample_NowFetchFrame -->
<a id='snippet-sample_nowfetchframe'></a>
```cs
public class NowFetchFrame : SyncFrame
{
Expand All @@ -127,22 +113,18 @@ public class NowFetchFrame : SyncFrame
}
}
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCodeGeneration/Model/NowTimeVariableSource.cs#L31-L49' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_nowfetchframe' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

In the frame above, you'll see that the `GenerateCode()` method writes its code into the source, then immediately turns around and tells the next Frame - if there is one - to generated its code. As the last step to write out the new source code, Lamar:

1. Goes through an effort to find any missing frames and variables
1. Sorts them with a topological sort (what frames depend on what other frames or variables, what variables are used or created by what frames)
1. Organizes the frames into a single linked list
1. Calls `GenerateCode()` on the first frame
2. Sorts them with a topological sort (what frames depend on what other frames or variables, what variables are used or created by what frames)
3. Organizes the frames into a single linked list
4. Calls `GenerateCode()` on the first frame

In the generated method up above, the call to `NowSpeaker.Speak(now)` depends on the now variable which is in turn created by the `NowFetchFrame`, and that's enough information for Lamar to order things and generate the final code.

Lastly, we had to use a custom `IVariableSource` to teach Lamar how to resolve the now variable. That code looks like this:

<!-- snippet: sample_NowTimeVariableSource -->
<a id='snippet-sample_nowtimevariablesource'></a>
```cs
public class NowTimeVariableSource : IVariableSource
{
Expand All @@ -167,8 +149,6 @@ public class NowTimeVariableSource : IVariableSource
}
}
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCodeGeneration/Model/NowTimeVariableSource.cs#L6-L29' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_nowtimevariablesource' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

Out of the box, the Lamar + [Jasper](https://jasperfx.github.io) combination uses variable sources for:

Expand Down
4 changes: 0 additions & 4 deletions docs/guide/compilation/frames/injected-fields.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ As an example, let's take the `WhatTimeIsIt` generated type from the [frames mod
this time generate the class with the assumption that the "now" time is injected into the generated type's constructor
like this:

<!-- snippet: sample_using-injected-field -->
<a id='snippet-sample_using-injected-field'></a>
```cs
var assembly = GeneratedAssembly.Empty();
var type = assembly.AddType("WhatTimeIsIt", typeof(ISaySomething));
Expand All @@ -30,8 +28,6 @@ method.Frames.Add(@call);

assembly.CompileAll();
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/InjectedFieldUsage.cs#L22-L38' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_using-injected-field' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

At runtime as Lamar tries to write the code for a new generated type, it will seek out any or all `InjectedField` variables
used within any of the methods and use those to generate a constructor function. The generated code for the dynamic type
Expand Down
26 changes: 0 additions & 26 deletions docs/guide/compilation/frames/variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ The `Variable` class in LamarCodeGeneration models the CLR type and usage of a v

Here are some samples of creating variable objects with a variable type and name:

<!-- snippet: sample_create-a-variable -->
<a id='snippet-sample_create-a-variable'></a>
```cs
// Create a connection for the type SqlConnection
// with the name "conn"
Expand All @@ -19,21 +17,15 @@ var conn2 = new Variable(typeof(SqlConnection), "conn2");
var conn3 = Variable.For<SqlConnection>();
conn3.Usage.ShouldBe("sqlConnection");
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Variables.cs#L40-L52' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_create-a-variable' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Default Naming

If you do not give a name for a variable, Lamar will derive a default variable name from the type like this:

<!-- snippet: sample_default-variable-name-usage -->
<a id='snippet-sample_default-variable-name-usage'></a>
```cs
var widget = Variable.For<IWidget>();
widget.Usage.ShouldBe("widget");
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Variables.cs#L26-L29' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_default-variable-name-usage' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

The best way to understand the full rules for deriving the default variable names is to just peek at the
[unit tests within the Lamar codebase](https://github.com/JasperFx/lamar/blob/master/src/Lamar.Testing/Codegen/VariableTests.cs).
Expand All @@ -43,8 +35,6 @@ The best way to understand the full rules for deriving the default variable name
If a variable is created by a [Frame](/guide/compilation/frames/frame), you really want to mark that relationship by
either passing the creating frame into the constructor function like this:

<!-- snippet: sample_NowFetchFrame -->
<a id='snippet-sample_nowfetchframe'></a>
```cs
public class NowFetchFrame : SyncFrame
{
Expand All @@ -64,46 +54,30 @@ public class NowFetchFrame : SyncFrame
}
}
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCodeGeneration/Model/NowTimeVariableSource.cs#L31-L49' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_nowfetchframe' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Overriding Variable Usage or Type

Do this sparingly, but you can override the name or usage and type of a previously built variable like this:

<!-- snippet: sample_override-variable-usage-and-type -->
<a id='snippet-sample_override-variable-usage-and-type'></a>
```cs
var service = new Variable(typeof(IService), "service");
service.OverrideName("myService");
service.OverrideType(typeof(WhateverService));
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Variables.cs#L33-L37' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_override-variable-usage-and-type' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Derived Variables

Variables don't have to mean literal C# variables in the generated code. They can be derived values like this example:

<!-- snippet: sample_derived-variable -->
<a id='snippet-sample_derived-variable'></a>
```cs
var now = new Variable(typeof(DateTime), $"{typeof(DateTime).FullName}.{nameof(DateTime.Now)}");
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Variables.cs#L22-L24' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_derived-variable' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Dependencies to Other Variables

For the sake of frame ordering, you might need to give Lamar a hint that your variable depends on another variable. Here's
an example of doing that with the `HttpResponse` type from ASP.Net Core:

<!-- snippet: sample_variable-dependencies -->
<a id='snippet-sample_variable-dependencies'></a>
```cs
var context = Variable.For<HttpContext>();
var response = new Variable(typeof(HttpResponse), $"{context.Usage}.{nameof(HttpContext.Response)}");
response.Dependencies.Add(context);
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/Variables.cs#L56-L60' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_variable-dependencies' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->
16 changes: 0 additions & 16 deletions docs/guide/compilation/source-writer.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ maintain legible code indention just like you'd use if you were writing the code

To dip our toes into source generation, let's write a simple method to a string that would just write out "Hello" to the console:

<!-- snippet: sample_simple-usage-of-source-writer -->
<a id='snippet-sample_simple-usage-of-source-writer'></a>
```cs
var writer = new SourceWriter();

Expand All @@ -28,8 +26,6 @@ END

Console.WriteLine(writer.Code());
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/UsingSourceWriter.cs#L20-L30' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_simple-usage-of-source-writer' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

After that code, the value of the `SourceWriter.Code()` method is this text:

Expand All @@ -50,8 +46,6 @@ A few notes on what `SourceWriter.Write()` is doing:

Other basic method usages are shown below:

<!-- snippet: sample_other-sourcewriter-basics -->
<a id='snippet-sample_other-sourcewriter-basics'></a>
```cs
var writer = new SourceWriter();

Expand All @@ -68,8 +62,6 @@ writer.WriteLine("// A comment");
// indention for the following lines of code
writer.FinishBlock();
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/UsingSourceWriter.cs#L37-L52' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_other-sourcewriter-basics' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

## Advanced Usages

Expand All @@ -79,8 +71,6 @@ All the usages in this section are from extension methods in the `Lamar.Compilat

Here are some of the advanced usages of `ISourceWriter`:

<!-- snippet: sample_SourceWriterAdvanced -->
<a id='snippet-sample_sourcewriteradvanced'></a>
```cs
var writer = new SourceWriter();

Expand All @@ -92,24 +82,18 @@ writer.Namespace("GeneratedCode");
// Write new classes and code within the namespace
writer.FinishBlock();


// Helper to write using blocks in C# code
writer.UsingBlock("var conn = new SqlConnection()", w =>
{
w.Write("conn.Open();");
// other statements
});



// Write a comment text into the code at the correct indention
// level
writer.WriteComment("Some message");


// Start the declaration of a new public class named "MyClass"
// that implements the IDisposable interface
writer.StartClass("MyClass", typeof(IDisposable));
```
<sup><a href='https://github.com/JasperFx/lamar/blob/master/src/LamarCompiler.Testing/Samples/UsingSourceWriter.cs#L58-L87' title='Snippet source file'>snippet source</a> | <a href='#snippet-sample_sourcewriteradvanced' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

0 comments on commit f14db75

Please sign in to comment.