From 6142bace175ec01125a516dfd2872fc214bcb530 Mon Sep 17 00:00:00 2001 From: Aizaz Zaidee Date: Wed, 19 Jun 2019 13:18:18 +0200 Subject: [PATCH] Fixed multiple issues with parsing elements support for # and tag without { % this is a comment @misc{rydell1994bipolar, month=feb # "~1", title={Bipolar electrosurgical scalpel with paired loop electrodes}, author={Rydell, Mark A}, year={1994}, publisher={Google Patents}, note={US Patent 5,282,799} } --- BibTeXLibrary/BibEntry.cs | 111 ++++++++++-------- BibTeXLibrary/BibParser.cs | 166 ++++++++++++++++----------- BibTeXLibrary/Config.cs | 2 + BibTeXLibrary/ParseErrorException.cs | 2 +- BibTeXLibrary/Token.cs | 2 +- 5 files changed, 167 insertions(+), 116 deletions(-) diff --git a/BibTeXLibrary/BibEntry.cs b/BibTeXLibrary/BibEntry.cs index a34ae49..a1d496a 100644 --- a/BibTeXLibrary/BibEntry.cs +++ b/BibTeXLibrary/BibEntry.cs @@ -4,6 +4,9 @@ namespace BibTeXLibrary { + using System.Linq; + using System.Runtime.CompilerServices; + public class BibEntry { #region Private Field @@ -21,134 +24,146 @@ public class BibEntry #region Public Property public string Address { - get { return this["address"]; } - set { this["address"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Annote { - get { return this["annote"]; } - set { this["annote"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Author { - get { return this["author"]; } - set { this["author"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Booktitle { - get { return this["booktitle"]; } - set { this["booktitle"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Chapter { - get { return this["chapter"]; } - set { this["chapter"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Crossref { - get { return this["crossref"]; } - set { this["crossref"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Edition { - get { return this["edition"]; } - set { this["edition"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Editor { - get { return this["editor"]; } - set { this["editor"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Howpublished { - get { return this["howpublished"]; } - set { this["howpublished"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Institution { - get { return this["institution"]; } - set { this["institution"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Journal { - get { return this["journal"]; } - set { this["journal"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Mouth { - get { return this["mouth"]; } - set { this["mouth"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Note { - get { return this["note"]; } - set { this["note"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Number { - get { return this["number"]; } - set { this["number"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Organization { - get { return this["organization"]; } - set { this["organization"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Pages { - get { return this["pages"]; } - set { this["pages"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Publisher { - get { return this["publisher"]; } - set { this["publisher"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string School { - get { return this["shcool"]; } - set { this["school"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Series { - get { return this["series"]; } - set { this["series"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Title { - get { return this["title"]; } - set { this["title"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Volume { - get { return this["volume"]; } - set { this["volume"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } public string Year { - get { return this["year"]; } - set { this["year"] = value; } + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; + } + + public string Month + { + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; + } + + public string Abstract + { + get => this[GetFormattedName()]; + set => this[GetFormattedName()] = value; } /// @@ -173,6 +188,12 @@ public string Type #endregion #region Public Method + + private string GetFormattedName([CallerMemberName] string propertyName = null) + { + return propertyName.First().ToString().ToLower() + propertyName.Substring(1); + } + /// /// To BibTeX entry /// @@ -186,7 +207,7 @@ public override string ToString() bib.Append(","); bib.Append(Config.LineFeed); - foreach(var tag in _tags) + foreach (var tag in _tags) { bib.Append(Config.Retract); bib.Append(tag.Key); diff --git a/BibTeXLibrary/BibParser.cs b/BibTeXLibrary/BibParser.cs index 848338b..9aca604 100644 --- a/BibTeXLibrary/BibParser.cs +++ b/BibTeXLibrary/BibParser.cs @@ -22,30 +22,41 @@ public sealed class BibParser : IDisposable { {ParserState.Begin, new Action { { TokenType.Start, new Next(ParserState.InStart, BibBuilderState.Create) } } }, + {ParserState.InStart, new Action { { TokenType.Name, new Next(ParserState.InEntry, BibBuilderState.SetType) } } }, + {ParserState.InEntry, new Action { { TokenType.LeftBrace, new Next(ParserState.InKey, BibBuilderState.Skip) } } }, + {ParserState.InKey, new Action { { TokenType.RightBrace, new Next(ParserState.OutEntry, BibBuilderState.Build) }, { TokenType.Name, new Next(ParserState.OutKey, BibBuilderState.SetKey) }, { TokenType.Comma, new Next(ParserState.InTagName, BibBuilderState.Skip) } } }, + {ParserState.OutKey, new Action { { TokenType.Comma, new Next(ParserState.InTagName, BibBuilderState.Skip) } } }, + {ParserState.InTagName, new Action { { TokenType.Name, new Next(ParserState.InTagEqual, BibBuilderState.SetTagName) }, { TokenType.RightBrace, new Next(ParserState.OutEntry, BibBuilderState.Build) } } }, + {ParserState.InTagEqual, new Action { { TokenType.Equal, new Next(ParserState.InTagValue, BibBuilderState.Skip) } } }, + {ParserState.InTagValue, new Action { - { TokenType.String, new Next(ParserState.OutTagValue, BibBuilderState.SetTagValue) } } }, + { TokenType.String, new Next(ParserState.OutTagValue, BibBuilderState.SetTagValue) }, + { TokenType.Name, new Next(ParserState.OutTagValue, BibBuilderState.SetTagValue) } + } }, + {ParserState.OutTagValue, new Action { { TokenType.Concatenation, new Next(ParserState.InTagValue, BibBuilderState.Skip) }, { TokenType.Comma, new Next(ParserState.InTagName, BibBuilderState.SetTag) }, { TokenType.RightBrace, new Next(ParserState.OutEntry, BibBuilderState.Build) } } }, + {ParserState.OutEntry, new Action { { TokenType.Start, new Next(ParserState.InStart, BibBuilderState.Create) } } }, - }; + }; #endregion #region Private Field @@ -70,6 +81,7 @@ public BibParser(TextReader inputText) { _inputText = inputText; } + #endregion #region Public Static Method @@ -81,10 +93,10 @@ public BibParser(TextReader inputText) public static List Parse(TextReader inputText) { using (var parser = new BibParser(inputText)) - { + { return parser.GetAllResult(); } - } + } #endregion #region Public Method @@ -102,77 +114,84 @@ public List GetAllResult() #region Private Method private IEnumerable Parser() { - var curState = ParserState.Begin; - var nextState = ParserState.Begin; + try + { + var curState = ParserState.Begin; + var nextState = ParserState.Begin; - BibEntry bib = null; - var tagValueBuilder = new StringBuilder(); - var tagName = ""; + BibEntry bib = null; + var tagValueBuilder = new StringBuilder(); + var tagName = ""; - // Fetch token from Tokenizer and build BibEntry - foreach (var token in Tokenizer()) - { - // Transfer state - if(StateMap[curState].ContainsKey(token.Type)) + // Fetch token from Tokenizer and build BibEntry + foreach (var token in Tokenizer()) { - nextState = StateMap[curState][token.Type].Item1; - } - else - { - var expected = from pair in StateMap[curState] - select pair.Key; - throw new UnexpectedTokenException(_lineCount, _colCount, token.Type, expected.ToArray()); - } - // Build BibEntry - switch (StateMap[curState][token.Type].Item2) - { - case BibBuilderState.Create: - bib = new BibEntry(); - break; - - case BibBuilderState.SetType: - Debug.Assert(bib != null, "bib != null"); - bib.Type = token.Value; - break; - - case BibBuilderState.SetKey: - Debug.Assert(bib != null, "bib != null"); - bib.Key = token.Value; - break; - - case BibBuilderState.SetTagName: - tagName = token.Value; - break; - - case BibBuilderState.SetTagValue: - tagValueBuilder.Append(token.Value); - break; - - case BibBuilderState.SetTag: - Debug.Assert(bib != null, "bib != null"); - bib[tagName] = tagValueBuilder.ToString(); - tagValueBuilder.Clear(); - tagName = string.Empty; - break; - - case BibBuilderState.Build: - if(tagName != string.Empty) - { + // Transfer state + if (StateMap[curState].ContainsKey(token.Type)) + { + nextState = StateMap[curState][token.Type].Item1; + } + else + { + var expected = from pair in StateMap[curState] + select pair.Key; + throw new UnexpectedTokenException(_lineCount, _colCount, token.Type, expected.ToArray()); + } + // Build BibEntry + switch (StateMap[curState][token.Type].Item2) + { + case BibBuilderState.Create: + bib = new BibEntry(); + break; + + case BibBuilderState.SetType: + Debug.Assert(bib != null, "bib != null"); + bib.Type = token.Value; + break; + + case BibBuilderState.SetKey: + Debug.Assert(bib != null, "bib != null"); + bib.Key = token.Value; + break; + + case BibBuilderState.SetTagName: + tagName = token.Value; + break; + + case BibBuilderState.SetTagValue: + tagValueBuilder.Append(token.Value); + break; + + case BibBuilderState.SetTag: Debug.Assert(bib != null, "bib != null"); bib[tagName] = tagValueBuilder.ToString(); tagValueBuilder.Clear(); tagName = string.Empty; - } - yield return bib; - break; + break; + + case BibBuilderState.Build: + if (tagName != string.Empty) + { + Debug.Assert(bib != null, "bib != null"); + bib[tagName] = tagValueBuilder.ToString(); + tagValueBuilder.Clear(); + tagName = string.Empty; + } + yield return bib; + break; + } + curState = nextState; + } + if (curState != ParserState.OutEntry) + { + var expected = from pair in StateMap[curState] + select pair.Key; + throw new UnexpectedTokenException(_lineCount, _colCount, TokenType.EOF, expected.ToArray()); } - curState = nextState; } - if(curState != ParserState.OutEntry) + finally { - var expected = from pair in StateMap[curState] - select pair.Key; - throw new UnexpectedTokenException(_lineCount, _colCount, TokenType.EOF, expected.ToArray()); + Dispose(); } } @@ -185,13 +204,14 @@ private IEnumerable Tokenizer() int code; char c; var braceCount = 0; + bool skipRead = false; while ((code = Peek()) != -1) { c = (char)code; if (c == '@') - { + { yield return new Token(TokenType.Start); } else if (char.IsLetter(c)) @@ -259,7 +279,7 @@ private IEnumerable Tokenizer() while (braceCount > 1 && Peek() != -1) { c = (char)Read(); - if (c == '{') braceCount++; + if (c == '{') braceCount++; else if (c == '}') braceCount--; if (braceCount > 1) value.Append(c); } @@ -289,15 +309,23 @@ private IEnumerable Tokenizer() _colCount = 0; _lineCount++; } + else if (Config.BeginCommentCharacters.Any(item => item == c)) + { + _colCount = 0; + _lineCount++; + _inputText.ReadLine(); + skipRead = true; + } else if (!char.IsWhiteSpace(c)) { throw new UnrecognizableCharacterException(_lineCount, _colCount, c); } // Move to next char if possible - if (_inputText.Peek() != -1) + if (_inputText.Peek() != -1 && !skipRead) _inputText.Read(); + skipRead = false; // Don't move ContinueExcute: ; @@ -330,7 +358,7 @@ private int Read() /// public void Dispose() { - _inputText.Dispose(); + _inputText?.Dispose(); } #endregion } diff --git a/BibTeXLibrary/Config.cs b/BibTeXLibrary/Config.cs index 46a241e..d8b2aac 100644 --- a/BibTeXLibrary/Config.cs +++ b/BibTeXLibrary/Config.cs @@ -12,6 +12,8 @@ public static class Config public static bool Align { get; private set; } = false; + public static char[] BeginCommentCharacters { get; private set; } = {'%'}; + #endregion #region Public Static Method diff --git a/BibTeXLibrary/ParseErrorException.cs b/BibTeXLibrary/ParseErrorException.cs index d33ebfd..29d4653 100644 --- a/BibTeXLibrary/ParseErrorException.cs +++ b/BibTeXLibrary/ParseErrorException.cs @@ -21,7 +21,7 @@ public abstract class ParseErrorException : ApplicationException protected ParseErrorException(int lineNo, int colNo) { LineNo = lineNo; - ColNo = colNo; + ColNo = colNo; } #endregion } diff --git a/BibTeXLibrary/Token.cs b/BibTeXLibrary/Token.cs index 1fceff5..f2382d5 100644 --- a/BibTeXLibrary/Token.cs +++ b/BibTeXLibrary/Token.cs @@ -18,7 +18,7 @@ public Token(TokenType type, string value = "") public enum TokenType { Start, - + Name, String,