diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..5f299a8
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,17 @@
+# These files are text and should be normalized (convert crlf => lf)
+*.cs text diff=csharp
+*.xaml text
+*.csproj text
+*.sln text
+*.tt text
+*.ps1 text
+*.cmd text
+*.msbuild text
+*.md text
+
+# Images should be treated as binary
+# (binary is a macro for -text -diff)
+*.png binary
+*.jepg binary
+
+*.sdf binary
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..daa9e0f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+# Build Folders (you can keep bin if you'd like, to store dlls and pdbs)
+[Bb]in/
+[Oo]bj/
+
+# ApprovalTests (received files)
+*.received.txt
+
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+[Dd]ebug/
+[Rr]elease/
+x64/
+
+# ReSharper is a .NET coding add-in
+_ReSharper*
+
+# NuGet Packages Directory
+packages
+
+# Others
+*.Cache
+[Ss]tyle[Cc]op.*
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
diff --git a/src/.nuget/NuGet.Config b/src/.nuget/NuGet.Config
new file mode 100644
index 0000000..67f8ea0
--- /dev/null
+++ b/src/.nuget/NuGet.Config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/.nuget/NuGet.exe b/src/.nuget/NuGet.exe
new file mode 100644
index 0000000..9cba6ed
Binary files /dev/null and b/src/.nuget/NuGet.exe differ
diff --git a/src/.nuget/NuGet.targets b/src/.nuget/NuGet.targets
new file mode 100644
index 0000000..2c3545b
--- /dev/null
+++ b/src/.nuget/NuGet.targets
@@ -0,0 +1,151 @@
+
+
+
+ $(MSBuildProjectDirectory)\..\
+
+
+ false
+
+
+ false
+
+
+ true
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+ $([System.IO.Path]::Combine($(SolutionDir), ".nuget"))
+
+
+
+
+ $(SolutionDir).nuget
+
+
+
+ packages.$(MSBuildProjectName.Replace(' ', '_')).config
+
+
+
+
+
+ $(PackagesProjectConfig)
+
+
+
+
+ packages.config
+
+
+
+
+
+
+ $(NuGetToolsPath)\NuGet.exe
+ @(PackageSource)
+
+ "$(NuGetExePath)"
+ mono --runtime=v4.0.30319 $(NuGetExePath)
+
+ $(TargetDir.Trim('\\'))
+
+ -RequireConsent
+ -NonInteractive
+
+ "$(SolutionDir) "
+ "$(SolutionDir)"
+
+
+ $(NuGetCommand) install "$(PackagesConfig)" -source "$(PackageSources)" $(NonInteractiveSwitch) $(RequireConsentSwitch) -solutionDir $(PaddedSolutionDir)
+ $(NuGetCommand) pack "$(ProjectPath)" -Properties "Configuration=$(Configuration);Platform=$(Platform)" $(NonInteractiveSwitch) -OutputDirectory "$(PackageOutputDir)" -symbols
+
+
+
+ RestorePackages;
+ $(BuildDependsOn);
+
+
+
+
+ $(BuildDependsOn);
+ BuildPackage;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SemanticReleaseNotes.Tests/Examples.Example_A.approved.txt b/src/SemanticReleaseNotes.Tests/Examples.Example_A.approved.txt
new file mode 100644
index 0000000..40d0fb2
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Examples.Example_A.approved.txt
@@ -0,0 +1,29 @@
+ReleaseNotes
+ [Section
+ (null,null,
+ Some
+ (Summary
+ "Incremental release designed to provide an update to some of the core plugins.
+"),
+ Some
+ (Items
+ [Item
+ (null,
+ Summary
+ "Release Checker: Now gives you a breakdown of exactly what you are missing. +New",
+ null);
+ Item
+ (null,
+ Summary
+ "Structured Layout: An alternative layout engine that allows developers to control layout. +New",
+ null);
+ Item
+ (null,
+ Summary
+ "Timeline: Comes with an additional grid view to show the same data. +Changed",
+ null);
+ Item
+ (null,
+ Summary
+ "Ajax: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix",
+ null)]))]
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Examples.Example_B.approved.txt b/src/SemanticReleaseNotes.Tests/Examples.Example_B.approved.txt
new file mode 100644
index 0000000..51ba810
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Examples.Example_B.approved.txt
@@ -0,0 +1,36 @@
+ReleaseNotes
+ [Section
+ (null,null,
+ Some
+ (Summary
+ "Incremental release designed to provide an update to some of the core plugins.
+"),
+ null);
+ Section
+ (Some (Name " System"),Some (Priority 1),null,
+ Some
+ (Items
+ [Item
+ (null,
+ Summary
+ "*Release Checker*: Now gives you a breakdown of exactly what you are missing. +New",
+ null);
+ Item
+ (null,
+ Summary
+ "*Structured Layout*: An alternative layout engine that allows developers to control layout. +New",
+ null)]));
+ Section
+ (Some (Name " Plugin"),Some (Priority 1),null,
+ Some
+ (Items
+ [Item
+ (null,
+ Summary
+ "*Timeline*: Comes with an additional grid view to show the same data. +Changed",
+ null);
+ Item
+ (null,
+ Summary
+ "*Ajax*: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix",
+ null)]))]
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Examples.Example_C.approved.txt b/src/SemanticReleaseNotes.Tests/Examples.Example_C.approved.txt
new file mode 100644
index 0000000..3e519d5
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Examples.Example_C.approved.txt
@@ -0,0 +1,43 @@
+ReleaseNotes
+ [Section
+ (null,null,
+ Some
+ (Summary
+ "Incremental release designed to provide an update to some of the core plugins."),
+ Some
+ (Items
+ [Item
+ (null,
+ Summary
+ "*Example*: You can have global issues that aren't grouped to a section",
+ null)]));
+ Section
+ (Some (Name " System"),Some (Priority 1),
+ Some (Summary "This description is specific to system section."),
+ Some
+ (Items
+ [Item
+ (null,
+ Summary
+ "*Release Checker*: Now gives you a +new breakdown of exactly what you are missing.",
+ null);
+ Item
+ (null,
+ Summary
+ "*Structured Layout*: A +new alternative layout engine that allows developers to control layout.",
+ null)]));
+ Section
+ (Some (Name " Plugin"),Some (Priority 1),
+ Some (Summary "This description is specific to plugin section."),
+ Some
+ (Items
+ [Item
+ (null,
+ Summary
+ "*Timeline*: Comes with an additional grid view to show the same data. +Changed",
+ null);
+ Item
+ (null,
+ Summary
+ "*Ajax*: +Fix that crashed poll in Chrome and IE due to log/trace statement. [[i1234][http://getglimpse.com]]",
+ null)]))]
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Examples.Example_D.approved.txt b/src/SemanticReleaseNotes.Tests/Examples.Example_D.approved.txt
new file mode 100644
index 0000000..df5e293
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Examples.Example_D.approved.txt
@@ -0,0 +1,46 @@
+ReleaseNotes
+ [Section
+ (null,null,
+ Some
+ (Summary
+ "Incremental release designed to provide an update to some of the core plugins."),
+ Some
+ (Items
+ [Item
+ (Some (Priority 1),
+ Summary
+ "*Example*: You can have global issues that aren't grouped to a section",
+ null)]));
+ Section
+ (Some
+ (Name " System [[icon][http://getglimpse.com/release/icon/core.png]]"),
+ Some (Priority 1),
+ Some (Summary "This description is specific to system section."),
+ Some
+ (Items
+ [Item
+ (Some (Priority 3),
+ Summary
+ "*Release Checker*: Now gives you a breakdown of exactly what you are missing. +New",
+ null);
+ Item
+ (Some (Priority 2),
+ Summary
+ "*Structured Layout*: An alternative layout engine that allows developers to control layout. +New",
+ null)]));
+ Section
+ (Some (Name " Plugin [[icon][http://getglimpse.com/release/icon/mvc.png]]"),
+ Some (Priority 1),
+ Some (Summary "This description is specific to plugin section."),
+ Some
+ (Items
+ [Item
+ (Some (Priority 1),
+ Summary
+ "*Timeline*: Comes with an additional grid view to show the same data. +Changed",
+ null);
+ Item
+ (Some (Priority 1),
+ Summary
+ "*Ajax*: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix [[i1234][http://getglimpse.com]]",
+ null)]))]
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Examples.cs b/src/SemanticReleaseNotes.Tests/Examples.cs
new file mode 100644
index 0000000..4558447
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Examples.cs
@@ -0,0 +1,86 @@
+using System.Linq;
+using ApprovalTests;
+using NUnit.Framework;
+
+namespace SemanticReleaseNotes.Tests
+{
+ [TestFixture]
+ public class Examples
+ {
+ [Test]
+ public void Example_A()
+ {
+ var input = @"
+Incremental release designed to provide an update to some of the core plugins.
+
+ - Release Checker: Now gives you a breakdown of exactly what you are missing. +New
+ - Structured Layout: An alternative layout engine that allows developers to control layout. +New
+ - Timeline: Comes with an additional grid view to show the same data. +Changed
+ - Ajax: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix
+";
+ var result = Parser.ParseAST(input);
+ Approvals.Verify(Parser.PrettyPrint(result));
+ }
+
+ [Test]
+ public void Example_B()
+ {
+ var input = @"
+Incremental release designed to provide an update to some of the core plugins.
+
+# System
+ - *Release Checker*: Now gives you a breakdown of exactly what you are missing. +New
+ - *Structured Layout*: An alternative layout engine that allows developers to control layout. +New
+
+# Plugin
+ - *Timeline*: Comes with an additional grid view to show the same data. +Changed
+ - *Ajax*: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix
+";
+ var result = Parser.ParseAST(input);
+ Approvals.Verify(Parser.PrettyPrint(result));
+ }
+
+ [Test]
+ public void Example_C()
+ {
+ var input = @"
+Incremental release designed to provide an update to some of the core plugins.
+ - *Example*: You can have global issues that aren't grouped to a section
+
+# System
+This description is specific to system section.
+ - *Release Checker*: Now gives you a +new breakdown of exactly what you are missing.
+ - *Structured Layout*: A +new alternative layout engine that allows developers to control layout.
+
+# Plugin
+This description is specific to plugin section.
+ - *Timeline*: Comes with an additional grid view to show the same data. +Changed
+ - *Ajax*: +Fix that crashed poll in Chrome and IE due to log/trace statement. [[i1234][http://getglimpse.com]]
+";
+ var result = Parser.ParseAST(input);
+ Approvals.Verify(Parser.PrettyPrint(result));
+ }
+
+ [Test]
+ public void Example_D()
+ {
+ var input = @"
+Incremental release designed to provide an update to some of the core plugins.
+ 1. *Example*: You can have global issues that aren't grouped to a section
+
+# System [[icon][http://getglimpse.com/release/icon/core.png]]
+This description is specific to system section.
+ 3. *Release Checker*: Now gives you a breakdown of exactly what you are missing. +New
+ 2. *Structured Layout*: An alternative layout engine that allows developers to control layout. +New
+
+# Plugin [[icon][http://getglimpse.com/release/icon/mvc.png]]
+This description is specific to plugin section.
+ 1. *Timeline*: Comes with an additional grid view to show the same data. +Changed
+ 1. *Ajax*: Fix that crashed poll in Chrome and IE due to log/trace statement. +Fix [[i1234][http://getglimpse.com]]
+";
+ var result = Parser.ParseAST(input);
+ Approvals.Verify(Parser.PrettyPrint(result));
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Properties/AssemblyInfo.cs b/src/SemanticReleaseNotes.Tests/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000..ae1cb00
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Properties/AssemblyInfo.cs
@@ -0,0 +1,39 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using ApprovalTests.Reporters;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("SemanticReleaseNotes.Tests")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("SemanticReleaseNotes.Tests")]
+[assembly: AssemblyCopyright("Copyright © 2014")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("eee93377-e39d-4834-83bd-3351d7dd7b99")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+
+[assembly: UseReporter(typeof(DiffReporter))]
diff --git a/src/SemanticReleaseNotes.Tests/SemanticReleaseNotes.Tests.csproj b/src/SemanticReleaseNotes.Tests/SemanticReleaseNotes.Tests.csproj
new file mode 100644
index 0000000..b7c76d0
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/SemanticReleaseNotes.Tests.csproj
@@ -0,0 +1,128 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {E54CFD45-C5C8-456A-99AE-04E810410E23}
+ Library
+ Properties
+ SemanticReleaseNotes.Tests
+ SemanticReleaseNotes.Tests
+ v4.0
+ 512
+ ..\
+ true
+
+
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+ ..\packages\ApprovalTests.3.0.5\lib\net40\ApprovalTests.dll
+
+
+ ..\packages\ApprovalUtilities.3.0.5\lib\net35\ApprovalUtilities.dll
+
+
+ ..\packages\FParsec-Big-Data-Edition.1.0.1\lib\net40-client\FParsec.dll
+
+
+ ..\packages\FParsec-Big-Data-Edition.1.0.1\lib\net40-client\FParsecCS.dll
+
+
+ True
+ ..\packages\FSharp.Core.4.0.0\lib\FSharp.Core.dll
+
+
+ ..\packages\Newtonsoft.Json.6.0.1\lib\net40\Newtonsoft.Json.dll
+
+
+ ..\packages\NUnit.2.6.3\lib\nunit.framework.dll
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}
+ SemanticReleaseNotes
+
+
+
+
+ Examples.cs
+
+
+ Examples.cs
+
+
+ Examples.cs
+
+
+ Examples.cs
+
+
+ Syntax.cs
+
+
+ Syntax.cs
+
+
+ Syntax.cs
+
+
+ Syntax.cs
+
+
+ Syntax.cs
+
+
+ Syntax.cs
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Syntax.ItemsAST.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.ItemsAST.approved.txt
new file mode 100644
index 0000000..e47c0d7
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Syntax.ItemsAST.approved.txt
@@ -0,0 +1,9 @@
+ReleaseNotes
+ [Section
+ (null,null,null,
+ Some
+ (Items
+ [Item (null,Summary "This is the _first_ *list* item.",null);
+ Item (null,Summary "This is the **second** __list__ item.",null);
+ Item (null,Summary "This is the `third` list item.",null);
+ Item (null,Summary "This is the [forth](?) list item.",null)]))]
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Syntax.ItemsJSON.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.ItemsJSON.approved.txt
new file mode 100644
index 0000000..2d6cf20
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Syntax.ItemsJSON.approved.txt
@@ -0,0 +1,16 @@
+{
+ "items": [
+ {
+ "summary": "This is the _first_ *list* item."
+ },
+ {
+ "summary": "This is the **second** __list__ item."
+ },
+ {
+ "summary": "This is the `third` list item."
+ },
+ {
+ "summary": "This is the [forth](?) list item."
+ }
+ ]
+}
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Syntax.PriorityAST.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.PriorityAST.approved.txt
new file mode 100644
index 0000000..a49d27d
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Syntax.PriorityAST.approved.txt
@@ -0,0 +1,26 @@
+ReleaseNotes
+ [Section
+ (null,null,null,
+ Some
+ (Items
+ [Item
+ (Some (Priority 1),Summary "This is a High priority list item.",
+ null);
+ Item
+ (Some (Priority 1),Summary "This is a High priority list item. ",
+ null);
+ Item
+ (Some (Priority 2),Summary "This is a Normal priority list item. ",
+ null);
+ Item
+ (Some (Priority 1),Summary "This is a High priority list item. ",
+ null);
+ Item
+ (Some (Priority 2),Summary "This is a Normal priority list item.",
+ null);
+ Item
+ (Some (Priority 3),Summary "This is a Minor priority list item.",
+ null);
+ Item
+ (Some (Priority 3),Summary "This is a Minor priority list item. ",
+ null)]))]
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Syntax.SectionsAST.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.SectionsAST.approved.txt
new file mode 100644
index 0000000..74df0d2
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Syntax.SectionsAST.approved.txt
@@ -0,0 +1,20 @@
+ReleaseNotes
+ [Section
+ (Some (Name " Section"),Some (Priority 1),
+ Some (Summary "This is the summary for Section."),
+ Some
+ (Items
+ [Item (null,Summary "This is a Section scoped first list item.",null);
+ Item
+ (null,Summary "This is a Section scoped second list item.",null)]));
+ Section
+ (Some (Name " Other Section"),Some (Priority 1),
+ Some (Summary "This is the summary for Other Section."),
+ Some
+ (Items
+ [Item
+ (null,Summary "This is a Other Section scoped first list item.",
+ null);
+ Item
+ (null,Summary "This is a Other Section scoped second list item.",
+ null)]))]
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Syntax.SummaryAST.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.SummaryAST.approved.txt
new file mode 100644
index 0000000..fa4f0be
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Syntax.SummaryAST.approved.txt
@@ -0,0 +1,13 @@
+ReleaseNotes
+ [Section
+ (null,null,
+ Some
+ (Summary
+ "This is a _project_ summary with two paragraphs.
+Lorem ipsum dolor sit amet consectetuer **adipiscing** elit.
+Aliquam hendreritmi posuere lectus.
+
+Vestibulum `enim wisi` viverra nec fringilla in laoreet
+vitae risus. Donec sit amet nisl. Aliquam [semper](?) ipsum
+sit amet velit."),
+ null)]
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Syntax.SummaryJSON.approved.txt b/src/SemanticReleaseNotes.Tests/Syntax.SummaryJSON.approved.txt
new file mode 100644
index 0000000..33a699a
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Syntax.SummaryJSON.approved.txt
@@ -0,0 +1,4 @@
+{
+ "summary": "This is a _project_ summary with two paragraphs. \nLorem ipsum dolor sit amet consectetuer **adipiscing** elit. \nAliquam hendreritmi posuere lectus.\n\nVestibulum `enim wisi` viverra nec fringilla in laoreet\nvitae risus. Donec sit amet nisl. Aliquam [semper](?) ipsum\nsit amet velit.",
+ "items": []
+}
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/Syntax.cs b/src/SemanticReleaseNotes.Tests/Syntax.cs
new file mode 100644
index 0000000..ffc383a
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/Syntax.cs
@@ -0,0 +1,170 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using ApprovalTests;
+using NUnit.Framework;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Serialization;
+using SemanticReleaseNotes.Tests.TestHelpers;
+
+namespace SemanticReleaseNotes.Tests
+{
+ ///
+ /// Syntax examples sourced from: http://semanticreleasenotes.org/#Syntax [Sat.Feb.8.2014]
+ ///
+ [TestFixture]
+ public class Syntax
+ {
+ private static class SyntaxExamples
+ {
+ ///
+ /// A summary is one or more paragraphs of text.
+ ///
+ public static string Summary =
+@"This is a _project_ summary with two paragraphs.
+Lorem ipsum dolor sit amet consectetuer **adipiscing** elit.
+Aliquam hendreritmi posuere lectus.
+
+Vestibulum `enim wisi` viverra nec fringilla in laoreet
+vitae risus. Donec sit amet nisl. Aliquam [semper](?) ipsum
+sit amet velit.";
+
+ ///
+ /// Items are indicated via a standard Markdown
+ /// ordered or unordered list.
+ ///
+ public static string Items = @"
+- This is the _first_ *list* item.
+- This is the **second** __list__ item.
+- This is the `third` list item.
+- This is the [forth](?) list item.";
+
+ }
+
+ ///
+ /// The required to mimic
+ /// the JSON output on http://semanticreleasenotes.org
+ ///
+ private static readonly JsonSerializerSettings semanticReleaseNotesJsonSerializerSettings =
+ new JsonSerializerSettings
+ {
+ ContractResolver = new CamelCasePropertyNamesContractResolver(),
+ Formatting = Formatting.Indented,
+ NullValueHandling = NullValueHandling.Ignore,
+ };
+
+ [Test]
+ public void SummaryAST()
+ {
+ var input = SyntaxExamples.Summary;
+
+ var result = Parser.ParseAST(input);
+
+ //Assert.AreEqual(input.NormalizeLineEndings(),result);
+ Approvals.Verify(Parser.PrettyPrint(result));
+ }
+
+ [Test]
+ public void SummaryJSON()
+ {
+ var input = SyntaxExamples.Summary;
+
+ var result = Parser.Parse(input);
+
+ var json = JsonConvert.SerializeObject(result, semanticReleaseNotesJsonSerializerSettings);
+
+ Approvals.Verify(json);
+ }
+
+ [Test]
+ public void ItemsAST()
+ {
+ var input = SyntaxExamples.Items;
+
+ var result = Parser.ParseAST(input);
+
+ Approvals.Verify(Parser.PrettyPrint(result));
+ }
+
+ [Test]
+ public void ItemsJSON()
+ {
+ var input = SyntaxExamples.Items;
+
+ var result = Parser.Parse(input);
+
+ var json = JsonConvert.SerializeObject(result, semanticReleaseNotesJsonSerializerSettings);
+
+ Approvals.Verify(json);
+ }
+
+ ///
+ /// Priority is indicated via a standard Markdown
+ /// ordered list.
+ ///
+ [Test]
+ public void PriorityAST()
+ {
+ var input = @"
+1. This is a High priority list item.
+1. This is a High priority list item.
+2. This is a Normal priority list item.
+1. This is a High priority list item.
+2. This is a Normal priority list item.
+3. This is a Minor priority list item.
+3. This is a Minor priority list item. ";
+
+ var result = Parser.ParseAST(input);
+
+ Approvals.Verify(Parser.PrettyPrint(result));
+ }
+
+ ///
+ /// A Section can be arbitrary in nature and specific
+ /// to the release notes of the application. Sections
+ /// are indicated via a standard Markdown header.
+ ///
+ [Test]
+ public void SectionsAST()
+ {
+ var input = @"
+# Section
+This is the summary for Section.
+ - This is a Section scoped first list item.
+ - This is a Section scoped second list item.
+
+# Other Section
+This is the summary for Other Section.
+ - This is a Other Section scoped first list item.
+ - This is a Other Section scoped second list item.
+";
+
+ var result = Parser.ParseAST(input);
+
+ Approvals.Verify(Parser.PrettyPrint(result));
+ }
+
+ ///
+ ///
+ /// In some cases we want to have the one document that
+ /// describes many releases. In this case, the syntax
+ /// simply allows you to define a heading which is the
+ /// Version Number. If you use this feature in conjunction
+ /// with sections, Section headers are also altered.
+ ///
+ ///
+ /// For the purposes of interpretation of the version number,
+ /// it is assumed that you are using Semantic Visioning -
+ /// http://semver.org/. Normal Markdown headers are used
+ /// to describe the version number for each release and
+ /// the scope of reach release (which item, etc makes up
+ /// the release).
+ ///
+ ///
+ [Test]
+ public void Release()
+ {
+ Assert.Inconclusive("Examples not yet specified.");
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.Tests/TestHelpers/StringExtensions.cs b/src/SemanticReleaseNotes.Tests/TestHelpers/StringExtensions.cs
new file mode 100644
index 0000000..958cb1a
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/TestHelpers/StringExtensions.cs
@@ -0,0 +1,12 @@
+using System.Linq;
+
+namespace SemanticReleaseNotes.Tests.TestHelpers
+{
+ public static class StringExtensions
+ {
+ public static string NormalizeLineEndings(this string src)
+ {
+ return (src ?? string.Empty).Replace("\r\n", "\n");
+ }
+ }
+}
diff --git a/src/SemanticReleaseNotes.Tests/packages.config b/src/SemanticReleaseNotes.Tests/packages.config
new file mode 100644
index 0000000..813bbac
--- /dev/null
+++ b/src/SemanticReleaseNotes.Tests/packages.config
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes.sln b/src/SemanticReleaseNotes.sln
new file mode 100644
index 0000000..7122501
--- /dev/null
+++ b/src/SemanticReleaseNotes.sln
@@ -0,0 +1,33 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2010
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{D9C61DF4-BEB2-4305-87CA-73FF05D7B64F}"
+ ProjectSection(SolutionItems) = preProject
+ .nuget\NuGet.Config = .nuget\NuGet.Config
+ .nuget\NuGet.exe = .nuget\NuGet.exe
+ .nuget\NuGet.targets = .nuget\NuGet.targets
+ EndProjectSection
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SemanticReleaseNotes.Tests", "SemanticReleaseNotes.Tests\SemanticReleaseNotes.Tests.csproj", "{E54CFD45-C5C8-456A-99AE-04E810410E23}"
+EndProject
+Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "SemanticReleaseNotes", "SemanticReleaseNotes\SemanticReleaseNotes.fsproj", "{E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {E54CFD45-C5C8-456A-99AE-04E810410E23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E54CFD45-C5C8-456A-99AE-04E810410E23}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E54CFD45-C5C8-456A-99AE-04E810410E23}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E54CFD45-C5C8-456A-99AE-04E810410E23}.Release|Any CPU.Build.0 = Release|Any CPU
+ {E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {E8D4FB83-FB86-416B-B2D4-8ED4E5E360E9}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/src/SemanticReleaseNotes/AST.fs b/src/SemanticReleaseNotes/AST.fs
new file mode 100644
index 0000000..63b87a5
--- /dev/null
+++ b/src/SemanticReleaseNotes/AST.fs
@@ -0,0 +1,44 @@
+module AST
+
+// Notes sourced from: http://semanticreleasenotes.org/#Syntax [Sat.Feb.8.2014]
+
+// At its most basic level, SRN should be able to take a paragraph
+// and interpret that as the summary of a given release.
+type Summary = Summary of string
+
+// The release note should be able to let you define the importance
+// feature or item that has been defined.
+type Priority = Priority of int
+
+// The release note should be able to indicate what category an item is.
+// For instance, "+New", "+Change", "+Bug-Fix", "+Developer".
+type Category = Category of string
+
+// A release note should be able to show which "items" are included
+// in a given release. Items typically include features, bug fixes,
+// or improvements.
+type Item = Item of Priority option * Summary * Category option
+type Items = Items of Item list
+
+// Lets the author group items which represent logical sections within
+// the application. These can be arbitrary in nature and specific to
+// the release notes which application is for.
+type Name = Name of string
+type Section =
+ | Section of Name option * Priority option * Summary option * Items option
+
+// TBD: As SRN allows for many releases to be defined within the one
+// block of text, the system needs to provide a means by which an
+// individual release can be identified.
+// type Release = ???
+
+type ReleaseNotes =
+ | ReleaseNotes of Section list
+
+(*
+let ToObjectModel(ReleaseNotes n) =
+ let (Summary summary, Items items) = n
+ let a = new SemanticReleaseNotes.ReleaseNotes()
+ a.Summary <- summary
+ a
+*)
diff --git a/src/SemanticReleaseNotes/ASTToObjectModelVisitor.fs b/src/SemanticReleaseNotes/ASTToObjectModelVisitor.fs
new file mode 100644
index 0000000..4839e02
--- /dev/null
+++ b/src/SemanticReleaseNotes/ASTToObjectModelVisitor.fs
@@ -0,0 +1,33 @@
+namespace SemanticReleaseNotes.Visitors
+
+module ASTToObjectModelVisitor =
+
+ open AST
+ open SemanticReleaseNotes
+
+ let private ItemToObjectModel (item:Item) =
+ let (Item (priority,(Summary summary),category)) = item
+ let result = new ObjectModel.Item()
+ result.Summary <- summary
+ result.Category <- null
+ result
+
+ let Visit (ast:ReleaseNotes) : ObjectModel.ReleaseNotes =
+ let (ReleaseNotes notes) = ast
+ let result = new ObjectModel.ReleaseNotes()
+ match notes with
+ | (section::tail) ->
+ let (Section (head,priority,summary,items)) = section
+ match summary with
+ | Some (Summary s) -> result.Summary <- s
+ | None -> result.Summary <- null
+ match items with
+ | Some (Items n) ->
+ n
+ |> List.map ItemToObjectModel
+ |> List.map (fun x -> result.Items.Add(x))
+ |> ignore
+ | None -> result.Items.Clear()
+ result
+ | [] ->
+ result
diff --git a/src/SemanticReleaseNotes/Parser.fs b/src/SemanticReleaseNotes/Parser.fs
new file mode 100644
index 0000000..be18115
--- /dev/null
+++ b/src/SemanticReleaseNotes/Parser.fs
@@ -0,0 +1,129 @@
+module Parser
+
+open System
+open FParsec
+
+open AST
+open SemanticReleaseNotes.Visitors
+
+// convenience type for locking down generic type inference issues
+// from: http://www.quanttec.com/fparsec/tutorial.html#fs-value-restriction
+type private State = unit
+
+let private ws = spaces
+
+let private ch c = pchar c
+let private ch_ws c = ch c .>> ws
+let private ws_ch_ws c = ws >>. ch c .>> ws
+
+let private str s = pstring s
+let private str_ws s = str s .>> ws
+let private ws_str_ws s = ws >>. str s .>> ws
+
+// make this compiler directive condition true to trace the parsers
+#if xDEBUG
+let () (p: Parser<_,_>) label : Parser<_,_> =
+ fun stream ->
+ printfn "%A: Entering %s" stream.Position label
+ let reply = p stream
+ printfn "%A: Leaving %s (%A)" stream.Position label reply.Status
+ reply
+#else
+let () (p: Parser<_,_>) label : Parser<_,_> =
+ fun stream -> p stream
+#endif
+
+(*
+let private anyStringUntil_ch c = manySatisfy ((<>) c)
+let private between_ch cStart cEnd p = between (ch cStart) (ch cEnd) p
+let private string_between_ch cStart cEnd = between_ch cStart cEnd (anyStringUntil_ch cEnd) // "stringSurroundedBy"
+//let private quotedString = string_between_ch '"' '"'
+let private hexn n = manyMinMaxSatisfy n n isHex
+let private guidFromTuple t =
+ let (((a,b),c),d),e = t
+ let s = sprintf "%s-%s-%s-%s-%s" a b c d e
+ Guid.Parse(s)
+let private guid : Parser = (hexn 8 .>> ch '-' .>>. hexn 4 .>> ch '-' .>>. hexn 4 .>> ch '-' .>>. hexn 4 .>> ch '-' .>>. hexn 12) |>> guidFromTuple // "guid"
+let private guid_between_str sStart sEnd = between (str sStart) (str sEnd) guid
+
+let private emptyIfNone items =
+ match items with
+ | Some x -> x
+ | None -> []
+*)
+
+//let private restOfLineAsText skipNewline = restOfLine skipNewline |>> Text "text"
+
+// -------
+let private newline_as_string = newline |>> fun x -> x.ToString()
+let private eof_as_string = eof >>. preturn ""
+let private eol = newline_as_string <|> eof_as_string
+
+let private priority = ws >>. pint32 .>> skipString ". " |>> Priority "item priority"
+
+let private hn = many1Satisfy (fun c -> match c with '#' -> true | _ -> false) |>> (fun h -> h.Length) |>> Priority "heading priority"
+let private name = restOfLine true "name"
+let private heading = ws >>. hn .>>. name "heading"
+
+let private empty_line = newline >>. preturn "" "empty line"
+let private summaryLine = lookAhead (ws >>. noneOf "-+*#123456789") >>. many1CharsTill (noneOf "\r\n") eol "summary line"
+let private join (s:string) (list:string list) = String.Join(s, list)
+let private summary = many1 (summaryLine <|> empty_line) |>> join "\n" |>> Summary "summary"
+
+let private unorderedItem = tuple3 (preturn None) (ws >>. anyOf "-+*" >>. ws >>. restOfLine true |>> Summary) (preturn None) "unordered item"
+let private orderedItem = tuple3 (priority |>> Some) (restOfLine true |>> Summary) (preturn None) "ordered item"
+let private item = (attempt orderedItem <|> unorderedItem) |>> Item
+let private items = many1 (attempt item) |>> Items "items"
+
+let private resolveSectionTuple (optHeading:(Priority*string) option) (summary:Summary option) (items:Items option) =
+ let (optPriority,optName) = match optHeading with
+ | Some(p,n) -> (Some(p),Some(Name n))
+ | None -> (None,None)
+ (optName, optPriority, summary, items)
+
+// Ensure that a section has at least one of name, summary, or items
+let private ensureSectionResultIsValid (p: Parser) =
+ fun stream ->
+ let reply = p stream
+ if reply.Status = ReplyStatus.Ok then
+ let (name,priority,summary,items) = reply.Result
+ if name.IsSome || summary.IsSome || items.IsSome then
+ Reply(reply.Result)
+ else
+ Reply(Error, expected "Either a heading, a summary paragraph, or a list of items.")
+ else
+ Reply(reply.Status,reply.Error)
+
+let private sectionContent = pipe3 (opt heading) (opt summary) (opt items) resolveSectionTuple
+let private section = ensureSectionResultIsValid sectionContent |>> Section "section"
+
+let private releaseNotes = ws >>. many1 section .>> ws .>> eof |>> ReleaseNotes "release notes"
+
+let private parser = releaseNotes .>> eof
+
+exception ParseError of string * ParserError
+
+type ParseException (message:string, context:ParserError) =
+ inherit ApplicationException(message, null)
+ let Context = context
+
+let ParseAST str =
+ match run parser str with
+ | Success(result, _, _) -> result
+ | Failure(errorMsg, errorContext, _) -> raise (new ParseException(errorMsg, errorContext))
+
+let Parse str = ParseAST str |> ASTToObjectModelVisitor.Visit
+
+let PrettyPrint a = sprintf "%A" a
+
+#if DEBUG
+let Test p str =
+ match run p str with
+ | Success(result, _, _) -> printfn "Success: %A" result
+ | Failure(errorMsg, _, _) -> printfn "Failure: %s" errorMsg
+
+let Run p str =
+ match run p str with
+ | Success(result, _, _) -> result
+ | Failure(errorMsg, errorContext, _) -> raise (new ParseException(errorMsg, errorContext))
+#endif
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes/ReleaseNotes.fs b/src/SemanticReleaseNotes/ReleaseNotes.fs
new file mode 100644
index 0000000..5f0a79f
--- /dev/null
+++ b/src/SemanticReleaseNotes/ReleaseNotes.fs
@@ -0,0 +1,31 @@
+namespace SemanticReleaseNotes.ObjectModel
+
+open System
+open System.Collections.Generic
+
+type Item() =
+ let mutable priority = new Nullable()
+ let mutable summary = ""
+ let mutable category = ""
+
+ member this.Priority
+ with get() = priority
+ and set(value) = priority <- value
+
+ member this.Summary
+ with get() = summary
+ and set(value) = summary <- value
+
+ member this.Category
+ with get() = category
+ and set(value) = category <- value
+
+type ReleaseNotes() =
+ let mutable summary = ""
+ let mutable items = new List- ()
+ member this.Summary
+ with get() = summary
+ and set value = summary <- value
+ member this.Items
+ with get() = items
+ and set value = items <- value
diff --git a/src/SemanticReleaseNotes/SemanticReleaseNotes.fsproj b/src/SemanticReleaseNotes/SemanticReleaseNotes.fsproj
new file mode 100644
index 0000000..c33caaa
--- /dev/null
+++ b/src/SemanticReleaseNotes/SemanticReleaseNotes.fsproj
@@ -0,0 +1,74 @@
+
+
+
+ Debug
+ AnyCPU
+ 8.0.30703
+ 2.0
+ {e8d4fb83-fb86-416b-b2d4-8ed4e5e360e9}
+ Library
+ SemanticReleaseNotes
+ SemanticReleaseNotes
+ v4.0
+ SemanticReleaseNotes
+ ..\
+ true
+
+
+ true
+ full
+ false
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ 3
+ bin\Debug\SemanticReleaseNotes.XML
+
+
+ pdbonly
+ true
+ true
+ bin\Release\
+ TRACE
+ 3
+ bin\Release\SemanticReleaseNotes.XML
+
+
+
+
+
+
+ This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
+
+
+
+
+
+
+
+
+
+
+
+
+ ..\packages\FParsec-Big-Data-Edition.1.0.1\lib\net40-client\FParsec.dll
+ True
+
+
+ ..\packages\FParsec-Big-Data-Edition.1.0.1\lib\net40-client\FParsecCS.dll
+ True
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/SemanticReleaseNotes/packages.config b/src/SemanticReleaseNotes/packages.config
new file mode 100644
index 0000000..2a39bd6
--- /dev/null
+++ b/src/SemanticReleaseNotes/packages.config
@@ -0,0 +1,4 @@
+
+
+
+
\ No newline at end of file