-
Notifications
You must be signed in to change notification settings - Fork 96
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
A lot of things in MM are structured like :tag[value]trailer and this formalizes that structure
- Loading branch information
1 parent
106581c
commit ee3e340
Showing
8 changed files
with
672 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
using System; | ||
|
||
namespace ModuleManager.Tags | ||
{ | ||
public struct Tag | ||
{ | ||
public readonly string key; | ||
public readonly string value; | ||
public readonly string trailer; | ||
|
||
public Tag(string key, string value, string trailer) | ||
{ | ||
this.key = key ?? throw new ArgumentNullException(nameof(key)); | ||
if (key == string.Empty) throw new ArgumentException("can't be empty", nameof(key)); | ||
|
||
if (value == null && trailer != null) | ||
throw new ArgumentException("trailer must be null if value is null"); | ||
|
||
if (trailer == string.Empty) throw new ArgumentException("can't be empty (null allowed)", nameof(trailer)); | ||
|
||
this.value = value; | ||
this.trailer = trailer; | ||
} | ||
|
||
public override string ToString() | ||
{ | ||
string s = "< '" + key + "' "; | ||
if (value != null) s += "[ '" + value + "' ] "; | ||
if (trailer != null) s += "'" + trailer + "' "; | ||
s += ">"; | ||
return s; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
using System; | ||
using System.Collections; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using ModuleManager.Collections; | ||
|
||
namespace ModuleManager.Tags | ||
{ | ||
public interface ITagList : IEnumerable<Tag> | ||
{ | ||
Tag PrimaryTag { get; } | ||
} | ||
|
||
public class TagList : ITagList | ||
{ | ||
private readonly Tag[] tags; | ||
|
||
public TagList(Tag primaryTag, IEnumerable<Tag> tags) | ||
{ | ||
PrimaryTag = primaryTag; | ||
this.tags = tags?.ToArray() ?? throw new ArgumentNullException(nameof(tags)); | ||
} | ||
|
||
public Tag PrimaryTag { get; private set; } | ||
|
||
public ArrayEnumerator<Tag> GetEnumerator() => new ArrayEnumerator<Tag>(tags); | ||
IEnumerator<Tag> IEnumerable<Tag>.GetEnumerator() => GetEnumerator(); | ||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,160 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
|
||
namespace ModuleManager.Tags | ||
{ | ||
public interface ITagListParser | ||
{ | ||
ITagList Parse(string ToParse); | ||
} | ||
|
||
public class TagListParser : ITagListParser | ||
{ | ||
public ITagList Parse(string toParse) | ||
{ | ||
if (toParse == null) throw new ArgumentNullException(nameof(toParse)); | ||
if (toParse.Length == 0) throw new FormatException("can't create tag list from empty string"); | ||
if (toParse[0] == '[') throw new FormatException("can't create tag list beginning with ["); | ||
if (toParse[0] == ':') throw new FormatException("can't create tag list beginning with :"); | ||
if (toParse[toParse.Length - 1] == ':') throw new FormatException("tag list can't end with :"); | ||
|
||
List<Tag> tags = new List<Tag>(); | ||
Tag primaryTag = ParsePrimaryTag(toParse, ref tags); | ||
return new TagList(primaryTag, tags); | ||
} | ||
|
||
private static Tag ParsePrimaryTag(string toParse, ref List<Tag> tags) | ||
{ | ||
for (int i = 1; i < toParse.Length; i++) | ||
{ | ||
char c = toParse[i]; | ||
|
||
if (c == '[') | ||
{ | ||
int j = ClosingBracketIndex(toParse, i + 1); | ||
return ParsePrimaryTrailer(toParse, j + 1, ref tags, toParse.Substring(0, i), toParse.Substring(i + 1, j - i - 1)); | ||
} | ||
else if (c == ':') | ||
{ | ||
ParseTag(toParse, i + 1, ref tags); | ||
return new Tag(toParse.Substring(0, i), null, null); | ||
} | ||
else if (c == ']') | ||
{ | ||
throw new FormatException("encountered closing bracket in primary key"); | ||
} | ||
} | ||
|
||
return new Tag(toParse, null, null); | ||
} | ||
|
||
private static Tag ParsePrimaryTrailer(string toParse, int start, ref List<Tag> tags, string primaryKey, string primaryValue) | ||
{ | ||
for (int i = start; i < toParse.Length; i++) | ||
{ | ||
char c = toParse[i]; | ||
|
||
if (c == ':') | ||
{ | ||
string trailer = i == start ? null : toParse.Substring(start, i - start); | ||
ParseTag(toParse, i + 1, ref tags); | ||
return new Tag(primaryKey, primaryValue, trailer); | ||
} | ||
else if (c == '[') | ||
{ | ||
throw new FormatException("encountered opening bracket in primary trailer"); | ||
} | ||
else if (c == ']') | ||
{ | ||
throw new FormatException("encountered closing bracket in primary trailer"); | ||
} | ||
} | ||
|
||
string primaryTrailer = toParse.Length - start == 0 ? null : toParse.Substring(start); | ||
return new Tag(primaryKey, primaryValue, primaryTrailer); | ||
} | ||
|
||
private static void ParseTag(string toParse, int start, ref List<Tag> tags) | ||
{ | ||
for (int i = start; i < toParse.Length; i++) | ||
{ | ||
char c = toParse[i]; | ||
|
||
if (c == '[') | ||
{ | ||
if (i == start) | ||
throw new FormatException("tag can't start with ["); | ||
|
||
int j = ClosingBracketIndex(toParse, i + 1); | ||
ParseTrailer(toParse, j + 1, ref tags, toParse.Substring(start, i - start), toParse.Substring(i + 1, j - i - 1)); | ||
return; | ||
} | ||
else if (c == ':') | ||
{ | ||
if (i == start) | ||
throw new FormatException("tag can't start with :"); | ||
|
||
tags.Add(new Tag(toParse.Substring(start, i - start), null, null)); | ||
ParseTag(toParse, i + 1, ref tags); | ||
return; | ||
} | ||
else if (c == ']') | ||
{ | ||
throw new FormatException("encountered closing bracket in key"); | ||
} | ||
} | ||
|
||
tags.Add(new Tag(toParse.Substring(start), null, null)); | ||
} | ||
|
||
private static void ParseTrailer(string toParse, int start, ref List<Tag> tags, string key, string value) | ||
{ | ||
for (int i = start; i < toParse.Length; i++) | ||
{ | ||
char c = toParse[i]; | ||
|
||
if (c == ':') | ||
{ | ||
string trailer = i == start ? null : toParse.Substring(start, i - start); | ||
tags.Add(new Tag(key, value, trailer)); | ||
ParseTag(toParse, i + 1, ref tags); | ||
return; | ||
} | ||
else if (c == '[') | ||
{ | ||
throw new FormatException("encountered opening bracket in trailer"); | ||
} | ||
else if (c == ']') | ||
{ | ||
throw new FormatException("encountered closing bracket in trailer"); | ||
} | ||
} | ||
|
||
string finalTrailer = toParse.Length - start == 0 ? null : toParse.Substring(start); | ||
tags.Add(new Tag(key, value, finalTrailer)); | ||
} | ||
|
||
private static int ClosingBracketIndex(string toParse, int start) | ||
{ | ||
int bracketLevel = 0; | ||
|
||
for (int i = start; i < toParse.Length; i++) | ||
{ | ||
char c = toParse[i]; | ||
|
||
if (c == '[') | ||
{ | ||
bracketLevel++; | ||
} | ||
else if (c == ']') | ||
{ | ||
bracketLevel--; | ||
} | ||
|
||
if (bracketLevel == -1) return i; | ||
} | ||
|
||
throw new FormatException("reached end of the tag list without encountering a close bracket"); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.