From 6ffa9ea9c27cebf49d9b0ada651643b1fced0671 Mon Sep 17 00:00:00 2001 From: Ian Wold Date: Tue, 16 Sep 2014 17:28:50 -0500 Subject: [PATCH 1/3] Create CommentParser Added CommentParser class to construct custom single and multi-line comment parsers. * Updated AssemblerTests.cs to use new CommentParser class. * Removed EndOfLineComment in Parse.Primitives.cs --- src/Sprache.Tests/Scenarios/AssemblerTests.cs | 13 +- src/Sprache/CommentParser.cs | 114 ++++++++++++++++++ src/Sprache/Parse.Primitives.cs | 13 -- src/Sprache/Sprache.csproj | 1 + 4 files changed, 121 insertions(+), 20 deletions(-) create mode 100644 src/Sprache/CommentParser.cs diff --git a/src/Sprache.Tests/Scenarios/AssemblerTests.cs b/src/Sprache.Tests/Scenarios/AssemblerTests.cs index 8731a1a..d0302cb 100644 --- a/src/Sprache.Tests/Scenarios/AssemblerTests.cs +++ b/src/Sprache.Tests/Scenarios/AssemblerTests.cs @@ -23,8 +23,7 @@ from instructionName in AsmToken(Parse.LetterOrDigit.Many().Text()) from operands in AsmToken(Identifier).XDelimitedBy(Parse.Char(',')) select Tuple.Create(instructionName, operands.ToArray()); - public static Parser Comment = - AsmToken(Parse.EndOfLineComment(";")); + public static CommentParser Comment = new CommentParser() { Single = ";" }; public static Parser LabelId = Parse.Identifier(Parse.Letter.Or(Parse.Chars("._?")), Parse.LetterOrDigit.Or(Parse.Chars("_@#$~.?"))); @@ -37,7 +36,7 @@ from colon in AsmToken(Parse.Char(':')) public static readonly Parser> Assembler = ( from label in Label.Optional() from instruction in Instruction.Optional() - from comment in Comment.Optional() + from comment in AsmToken(Comment.SingleLineComment).Optional() from lineTerminator in Parse.LineTerminator select new AssemblerLine( label.GetOrDefault(), @@ -150,7 +149,7 @@ public override bool Equals(object obj) if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; - return Equals((AssemblerLine) obj); + return Equals((AssemblerLine)obj); } public override int GetHashCode() @@ -158,9 +157,9 @@ public override int GetHashCode() unchecked { var hashCode = (Label != null ? Label.GetHashCode() : 0); - hashCode = (hashCode*397) ^ (InstructionName != null ? InstructionName.GetHashCode() : 0); - hashCode = (hashCode*397) ^ (Operands != null ? Operands.GetHashCode() : 0); - hashCode = (hashCode*397) ^ (Comment != null ? Comment.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (InstructionName != null ? InstructionName.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (Operands != null ? Operands.GetHashCode() : 0); + hashCode = (hashCode * 397) ^ (Comment != null ? Comment.GetHashCode() : 0); return hashCode; } } diff --git a/src/Sprache/CommentParser.cs b/src/Sprache/CommentParser.cs new file mode 100644 index 0000000..4000acf --- /dev/null +++ b/src/Sprache/CommentParser.cs @@ -0,0 +1,114 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Sprache +{ + /// + /// Constructs customizable comment parsers. + /// + public class CommentParser + { + /// + ///Single-line comment header. + /// + public string Single { get; set; } + + /// + ///Newline character preference. + /// + public string NewLine { get; set; } + + /// + ///Multi-line comment opener. + /// + public string MultiOpen { get; set; } + + /// + ///Multi-line comment closer. + /// + public string MultiClose { get; set; } + + /// + /// Initializes a Comment with C-style headers and Windows newlines. + /// + public CommentParser() + { + Single = "//"; + MultiOpen = "/*"; + MultiClose = "*/"; + NewLine = "\r\n"; + } + + /// + /// Initializes a Comment with custom headers newline characters. + /// + /// + /// + /// + /// + public CommentParser(string single, string multiOpen, string multiClose, string newLine = "\r\n") + { + Single = single; + MultiOpen = multiOpen; + MultiClose = multiClose; + newLine = NewLine; + } + + /// + ///Parse a single-line comment. + /// + public Parser SingleLineComment + { + get + { + if (Single == null) + throw new ParseException("Field 'Single' is null; single-line comments not allowed."); + + return from first in Parse.String(Single) + from rest in Parse.CharExcept("\r\n").Many().Text() + select rest; + } + private set { } + } + + /// + ///Parse a multi-line comment. + /// + public Parser MultiLineComment + { + get + { + if (MultiOpen == null) + throw new ParseException("Field 'MultiOpen' is null; multi-line comments not allowed."); + else if (MultiClose == null) + throw new ParseException("Field 'MultiClose' is null; multi-line comments not allowed."); + + return from first in Parse.String(MultiOpen) + from rest in Parse.AnyChar + .Until(Parse.String(MultiClose)).Text() + select rest; + } + private set { } + } + + /// + ///Parse a comment. + /// + public Parser AnyComment + { + get + { + if (Single != null && MultiOpen != null && MultiClose != null) + return SingleLineComment.Or(MultiLineComment); + else if (Single != null && (MultiOpen == null || MultiClose == null)) + return SingleLineComment; + else if (Single == null && (MultiOpen != null && MultiClose != null)) + return MultiLineComment; + else throw new ParseException("Unable to parse comment; check values of fields 'MultiOpen' and 'MultiClose'."); + } + private set { } + } + } +} diff --git a/src/Sprache/Parse.Primitives.cs b/src/Sprache/Parse.Primitives.cs index b48ce70..0b7b631 100644 --- a/src/Sprache/Parse.Primitives.cs +++ b/src/Sprache/Parse.Primitives.cs @@ -20,19 +20,6 @@ from n in Char('\n') .Or(LineEnd) .Named("LineTerminator"); - /// - /// Parser for single line comment. Doesn't contain tail line ending - /// - /// Symbols to start comment. I.e. "//" for C#, "#" for perl, ";" for assembler - /// - public static Parser EndOfLineComment(string commentStart) - { - return - from start in String(commentStart) - from comment in CharExcept("\r\n").Many().Text() - select comment; - } - /// /// Parser for identifier starting with and continuing with /// diff --git a/src/Sprache/Sprache.csproj b/src/Sprache/Sprache.csproj index b4b3416..7cf48bf 100644 --- a/src/Sprache/Sprache.csproj +++ b/src/Sprache/Sprache.csproj @@ -36,6 +36,7 @@ + From 74dbfab7596ee9997d189744b15178242bb64ea4 Mon Sep 17 00:00:00 2001 From: Ian Wold Date: Thu, 9 Oct 2014 17:15:09 -0500 Subject: [PATCH 2/3] Create IComment Creates an interface for CommentParser. --- src/Sprache/CommentParser.cs | 2 +- src/Sprache/IComment.cs | 45 ++++++++++++++++++++++++++++++++++++ src/Sprache/Sprache.csproj | 1 + 3 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/Sprache/IComment.cs diff --git a/src/Sprache/CommentParser.cs b/src/Sprache/CommentParser.cs index 4000acf..fe4c7a5 100644 --- a/src/Sprache/CommentParser.cs +++ b/src/Sprache/CommentParser.cs @@ -8,7 +8,7 @@ namespace Sprache /// /// Constructs customizable comment parsers. /// - public class CommentParser + public class CommentParser : IComment { /// ///Single-line comment header. diff --git a/src/Sprache/IComment.cs b/src/Sprache/IComment.cs new file mode 100644 index 0000000..0929892 --- /dev/null +++ b/src/Sprache/IComment.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Sprache +{ + interface IComment + { + /// + ///Single-line comment header. + /// + string Single { get; set; } + + /// + ///Newline character preference. + /// + string NewLine { get; set; } + + /// + ///Multi-line comment opener. + /// + string MultiOpen { get; set; } + + /// + ///Multi-line comment closer. + /// + string MultiClose { get; set; } + + /// + ///Parse a single-line comment. + /// + Parser SingleLineComment { get; } + + /// + ///Parse a multi-line comment. + /// + Parser MultiLineComment { get; } + + /// + ///Parse a comment. + /// + Parser AnyComment { get; } + } +} diff --git a/src/Sprache/Sprache.csproj b/src/Sprache/Sprache.csproj index 7cf48bf..845e5f8 100644 --- a/src/Sprache/Sprache.csproj +++ b/src/Sprache/Sprache.csproj @@ -37,6 +37,7 @@ + From 2e7c62ac4d51af668ac0b9a7afa549993645ac38 Mon Sep 17 00:00:00 2001 From: Ian Wold Date: Tue, 20 Jan 2015 16:47:02 -0600 Subject: [PATCH 3/3] Integrate CommentParser with XmlExample Adds comments to XmlExample to demonstrate CommentParser class. --- src/Sprache/CommentParser.cs | 25 ++++++++++++++++++++----- src/XmlExample/Program.cs | 16 ++++++++++++---- src/XmlExample/TestFile.xml | 7 +++++++ src/XmlExample/XmlExample.csproj | 5 +++++ 4 files changed, 44 insertions(+), 9 deletions(-) create mode 100644 src/XmlExample/TestFile.xml diff --git a/src/Sprache/CommentParser.cs b/src/Sprache/CommentParser.cs index fe4c7a5..5a53a4c 100644 --- a/src/Sprache/CommentParser.cs +++ b/src/Sprache/CommentParser.cs @@ -38,22 +38,37 @@ public CommentParser() Single = "//"; MultiOpen = "/*"; MultiClose = "*/"; - NewLine = "\r\n"; + NewLine = "\n"; } /// - /// Initializes a Comment with custom headers newline characters. + /// Initializes a Comment with custom multi-line headers and newline characters. + /// Single-line headers are made null, it is assumed they would not be used. + /// + /// + /// + /// + public CommentParser(string multiOpen, string multiClose, string newLine = "\n") + { + Single = null; + MultiOpen = multiOpen; + MultiClose = multiClose; + NewLine = newLine; + } + + /// + /// Initializes a Comment with custom headers and newline characters. /// /// /// /// /// - public CommentParser(string single, string multiOpen, string multiClose, string newLine = "\r\n") + public CommentParser(string single, string multiOpen, string multiClose, string newLine = "\n") { Single = single; MultiOpen = multiOpen; MultiClose = multiClose; - newLine = NewLine; + NewLine = newLine; } /// @@ -67,7 +82,7 @@ public Parser SingleLineComment throw new ParseException("Field 'Single' is null; single-line comments not allowed."); return from first in Parse.String(Single) - from rest in Parse.CharExcept("\r\n").Many().Text() + from rest in Parse.CharExcept(NewLine).Many().Text() select rest; } private set { } diff --git a/src/XmlExample/Program.cs b/src/XmlExample/Program.cs index 6048bda..2a27e14 100644 --- a/src/XmlExample/Program.cs +++ b/src/XmlExample/Program.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Text; using Sprache; +using System.IO; namespace XmlExample { @@ -45,6 +46,8 @@ public override string ToString() public static class XmlParser { + static CommentParser Comment = new CommentParser("", "\r\n"); + static readonly Parser Identifier = from first in Parse.Letter.Once() from rest in Parse.LetterOrDigit.XOr(Parse.Char('-')).XOr(Parse.Char('_')).Many() @@ -81,10 +84,14 @@ from end in EndTag(tag) static readonly Parser ShortNode = Tag(from id in Identifier from slash in Parse.Char('/') select new Node { Name = id }); - + static readonly Parser Node = ShortNode.Or(FullNode); - static readonly Parser Item = Node.Select(n => (Item)n).XOr(Content); + static readonly Parser Item = + from leading in Comment.MultiLineComment.Many() + from item in Node.Select(n => (Item)n).XOr(Content) + from trailing in Comment.MultiLineComment.Many() + select item; public static readonly Parser Document = from leading in Parse.WhiteSpace.Many() @@ -96,8 +103,9 @@ class Program { static void Main() { - const string doc = "

hello,
world!

"; - var parsed = XmlParser.Document.Parse(doc); + StreamReader reader = new StreamReader("TestFile.xml"); + var parsed = XmlParser.Document.Parse(reader.ReadToEnd()); + reader.Close(); Console.WriteLine(parsed); Console.ReadKey(true); } diff --git a/src/XmlExample/TestFile.xml b/src/XmlExample/TestFile.xml new file mode 100644 index 0000000..f6aae14 --- /dev/null +++ b/src/XmlExample/TestFile.xml @@ -0,0 +1,7 @@ + +

+ hello,
world! +

+ \ No newline at end of file diff --git a/src/XmlExample/XmlExample.csproj b/src/XmlExample/XmlExample.csproj index 0d75967..b05958e 100644 --- a/src/XmlExample/XmlExample.csproj +++ b/src/XmlExample/XmlExample.csproj @@ -129,6 +129,11 @@ Sprache + + + Always + +