public void match_methods_must_set_an_error() { var m = new StringMatcher( "A" ); DateTimeStamp ts; CheckMatchError( m, () => m.MatchDateTimeStamp( out ts ) ); DateTime dt; CheckMatchError( m, () => m.MatchFileNameUniqueTimeUtcFormat( out dt ) ); CheckMatchError( m, () => m.MatchText( "B" ) ); }
private static void CheckMatchError( StringMatcher m, Func<bool> fail ) { int idx = m.StartIndex; int len = m.Length; Assert.That( fail(), Is.False ); Assert.That( m.IsError ); Assert.That( m.ErrorMessage, Is.Not.Null.Or.Empty ); Assert.That( m.StartIndex == idx, "Head must not move on error." ); Assert.That( m.Length == len, "Length must not change on error." ); m.SetSuccess(); }
private static void CheckDateTimeStamp( DateTimeStamp t ) { string s = t.ToString(); var m = new StringMatcher( "X" + s + "Y" ); Assert.That( m.MatchChar( 'X' ) ); DateTimeStamp parsed; Assert.That( m.MatchDateTimeStamp( out parsed ) && parsed == t ); Assert.That( m.MatchChar( 'Y' ) ); m = new StringMatcher( s.Insert( 2, "X" ) ); Assert.That( m.MatchDateTimeStamp( out parsed ), Is.False ); Assert.That( m.ErrorMessage, Is.Not.Null ); int i; Assert.That( m.MatchInt32( out i ) && i == 20 ); }
public void matching_FileNameUniqueTimeUtcFormat() { DateTime t = DateTime.UtcNow; string s = t.ToString( FileUtil.FileNameUniqueTimeUtcFormat ); var m = new StringMatcher( "X" + s + "Y" ); Assert.That( m.MatchChar( 'X' ) ); DateTime parsed; Assert.That( m.MatchFileNameUniqueTimeUtcFormat( out parsed ) && parsed == t ); Assert.That( m.MatchChar( 'Y' ) ); m = new StringMatcher( s.Insert( 2, "X" ) ); Assert.That( m.MatchFileNameUniqueTimeUtcFormat( out parsed ), Is.False ); int i; Assert.That( m.MatchInt32( out i ) && i == 20 ); }
/// <summary> /// Matches a very simple version of a JSON object content: this match stops at the first closing }. /// Whitespaces and JS comments (//... or /* ... */) are skipped. /// </summary> /// <param name="this">This <see cref="StringMatcher"/>.</param> /// <param name="o">The read object on success as a list of KeyValuePair.</param> /// <returns>True on success, false on error.</returns> public static bool MatchJSONObjectContent(this StringMatcher @this, out List <KeyValuePair <string, object> > o) { o = null; while ([email protected]) { @this.SkipWhiteSpacesAndJSComments(); string propName; object value; if (@this.TryMatchChar('}')) { if (o == null) { o = new List <KeyValuePair <string, object> >(); } return(true); } if ([email protected](out propName)) { o = null; return(@this.SetError("Quoted Property Name.")); } @this.SkipWhiteSpacesAndJSComments(); if ([email protected](':') || !MatchJSONObject(@this, out value)) { o = null; return(false); } if (o == null) { o = new List <KeyValuePair <string, object> >(); } o.Add(new KeyValuePair <string, object>(propName, value)); @this.SkipWhiteSpacesAndJSComments(); // This accepts e trailing comma at the end of a property list: ..."a":0,} is not an error. @this.TryMatchChar(','); } return(@this.SetError("JSON object definition but reached end of match.")); }
/// <summary> /// Tries to match a //.... or /* ... */ comment. /// Proper termination of comment (by a new line or the closing */) is not required: /// a ending /*... is considered valid. /// </summary> /// <param name="this">This <see cref="StringMatcher"/>.</param> /// <returns>True on success, false if the <see cref="StringMatcher.Head"/> is not on a /.</returns> public static bool TryMatchJSComment(this StringMatcher @this) { if ([email protected]('/')) { return(false); } if (@this.TryMatchChar('/')) { while ([email protected] && @this.Head != '\n') { @this.UncheckedMove(1); } if ([email protected]) { @this.UncheckedMove(1); } return(true); } else if (@this.TryMatchChar('*')) { while ([email protected]) { if (@this.Head == '*') { @this.UncheckedMove(1); if (@this.IsEnd || @this.TryMatchChar('/')) { return(true); } } @this.UncheckedMove(1); } return(true); } @this.UncheckedMove(-1); return(false); }
/// <summary> /// Matches a JSON quoted string without setting an error if match fails. /// </summary> /// <param name="this">This <see cref="StringMatcher"/>.</param> /// <param name="content">Extracted content.</param> /// <param name="allowNull">True to allow 'null'.</param> /// <returns><c>true</c> when matched, <c>false</c> otherwise.</returns> public static bool TryMatchJSONQuotedString(this StringMatcher @this, out string content, bool allowNull = false) { content = null; if (@this.IsEnd) { return(false); } int i = @this.StartIndex; if (@this.Text[i++] != '"') { return(allowNull && @this.TryMatchText("null")); } int len = @this.Length - 1; StringBuilder b = null; while (len >= 0) { if (len == 0) { return(false); } char c = @this.Text[i++]; --len; if (c == '"') { break; } if (c == '\\') { if (len == 0) { return(false); } if (b == null) { b = new StringBuilder(@this.Text, @this.StartIndex + 1, i - @this.StartIndex - 2, 1024); } switch ((c = @this.Text[i++])) { case 'r': c = '\r'; break; case 'n': c = '\n'; break; case 'b': c = '\b'; break; case 't': c = '\t'; break; case 'f': c = '\f'; break; case 'u': { if (--len == 0) { return(false); } int cN; cN = ReadHexDigit(@this.Text[i++]); if (cN < 0 || cN > 15) { return(false); } int val = cN << 12; if (--len == 0) { return(false); } cN = ReadHexDigit(@this.Text[i++]); if (cN < 0 || cN > 15) { return(false); } val |= cN << 8; if (--len == 0) { return(false); } cN = ReadHexDigit(@this.Text[i++]); if (cN < 0 || cN > 15) { return(false); } val |= cN << 4; if (--len == 0) { return(false); } cN = ReadHexDigit(@this.Text[i++]); if (cN < 0 || cN > 15) { return(false); } val |= cN; c = (char)val; break; } } } if (b != null) { b.Append(c); } } int lenS = i - @this.StartIndex; if (b != null) { content = b.ToString(); } else { content = @this.Text.Substring(@this.StartIndex + 1, lenS - 2); } return(@this.UncheckedMove(lenS)); }
/// <summary> /// Initializes a new <see cref="JSONVisitor"/> bound to a <see cref="Matcher"/>. /// </summary> /// <param name="m">The string matcher.</param> public JSONVisitor(StringMatcher m) { _m = m; _path = new List <Parent>(); }
/// <summary> /// Initializes a new <see cref="JSONVisitor"/> bound to a <see cref="Matcher"/>. /// </summary> /// <param name="m">The string matcher.</param> public JSONVisitor( StringMatcher m ) { _m = m; _path = new List<Parent>(); }
public static bool TryMatchJSONValue(this StringMatcher @this, out object value) => TryMatchJSONTerminalValue(@this, out value);