/// <summary> /// 4.4.13. Number-rest state /// </summary> CssToken NumberRest(Char current) { while (true) { if (current.IsDigit()) _stringBuffer.Append(current); else if (current.IsNameStart()) { var number = FlushBuffer(); _stringBuffer.Append(current); return Dimension(Next, number); } else if (IsValidEscape(current)) { current = Next; var number = FlushBuffer(); _stringBuffer.Append(ConsumeEscape(current)); return Dimension(Next, number); } else break; current = Next; } switch (current) { case Specification.Dot: current = Next; if (current.IsDigit()) { _stringBuffer.Append(Specification.Dot).Append(current); return NumberFraction(Next); } Back(); return CssToken.Number(FlushBuffer()); case '%': return CssUnitToken.Percentage(FlushBuffer()); case 'e': case 'E': return NumberExponential(current); case Specification.Minus: return NumberDash(current); default: Back(); return CssToken.Number(FlushBuffer()); } }
/// <summary> /// 4.4.9. Ident state /// </summary> CssToken IdentStart(Char current) { if (current == Specification.Minus) { current = Next; if (current.IsNameStart() || IsValidEscape(current)) { _stringBuffer.Append(Specification.Minus); return IdentRest(current); } Back(); return CssToken.Delim(Specification.Minus); } else if (current.IsNameStart()) { _stringBuffer.Append(current); return IdentRest(Next); } else if (current == Specification.ReverseSolidus) { if (IsValidEscape(current)) { current = Next; _stringBuffer.Append(ConsumeEscape(current)); return IdentRest(Next); } } return Data(current); }
/// <summary> /// Substate of several Number states. /// </summary> CssToken NumberDash(Char current) { current = Next; if (current.IsNameStart()) { var number = FlushBuffer(); _stringBuffer.Append(Specification.Minus).Append(current); return Dimension(Next, number); } else if (IsValidEscape(current)) { current = Next; var number = FlushBuffer(); _stringBuffer.Append(Specification.Minus).Append(ConsumeEscape(current)); return Dimension(Next, number); } else { Back(2); return CssToken.Number(FlushBuffer()); } }
/// <summary> /// 4.4.1. Data state /// </summary> CssToken Data(Char current) { switch (current) { case Specification.LineFeed: case Specification.CarriageReturn: case Specification.Tab: case Specification.Space: do { current = Next; } while (current.IsSpaceCharacter()); if (_ignoreWs) return Data(current); Back(); return CssSpecialCharacter.Whitespace; case Specification.DoubleQuote: return StringDQ(Next); case Specification.Num: return HashStart(Next); case Specification.Dollar: current = Next; if (current == Specification.Equality) return CssMatchToken.Suffix; return CssToken.Delim(Previous); case Specification.SingleQuote: return StringSQ(Next); case Specification.RoundBracketOpen: return CssBracketToken.OpenRound; case Specification.RoundBracketClose: return CssBracketToken.CloseRound; case Specification.Asterisk: current = Next; if (current == Specification.Equality) return CssMatchToken.Substring; return CssToken.Delim(Previous); case Specification.Plus: { var c1 = Next; if (c1 == Specification.EndOfFile) { Back(); } else { var c2 = Next; Back(2); if (c1.IsDigit() || (c1 == Specification.Dot && c2.IsDigit())) return NumberStart(current); } return CssToken.Delim(current); } case Specification.Comma: return CssSpecialCharacter.Comma; case Specification.Dot: { var c = Next; if (c.IsDigit()) return NumberStart(Previous); return CssToken.Delim(Previous); } case Specification.Minus: { var c1 = Next; if (c1 == Specification.EndOfFile) { Back(); } else { var c2 = Next; Back(2); if (c1.IsDigit() || (c1 == Specification.Dot && c2.IsDigit())) return NumberStart(current); else if (c1.IsNameStart()) return IdentStart(current); else if (c1 == Specification.ReverseSolidus && !c2.IsLineBreak() && c2 != Specification.EndOfFile) return IdentStart(current); else if (c1 == Specification.Minus && c2 == Specification.GreaterThan) { Advance(2); if (_ignoreCs) return Data(Next); return CssCommentToken.Close; } } return CssToken.Delim(current); } case Specification.Solidus: current = Next; if (current == Specification.Asterisk) return Comment(Next); return CssToken.Delim(Previous); case Specification.ReverseSolidus: current = Next; if (current.IsLineBreak() || current == Specification.EndOfFile) { RaiseErrorOccurred(current == Specification.EndOfFile ? ErrorCode.EOF : ErrorCode.LineBreakUnexpected); return CssToken.Delim(Previous); } return IdentStart(Previous); case Specification.Colon: return CssSpecialCharacter.Colon; case Specification.Semicolon: return CssSpecialCharacter.Semicolon; case Specification.LessThan: current = Next; if (current == Specification.ExclamationMark) { current = Next; if (current == Specification.Minus) { current = Next; if (current == Specification.Minus) { if (_ignoreCs) return Data(Next); return CssCommentToken.Open; } current = Previous; } current = Previous; } return CssToken.Delim(Previous); case Specification.At: return AtKeywordStart(Next); case Specification.SquareBracketOpen: return CssBracketToken.OpenSquare; case Specification.SquareBracketClose: return CssBracketToken.CloseSquare; case Specification.Accent: current = Next; if (current == Specification.Equality) return CssMatchToken.Prefix; return CssToken.Delim(Previous); case Specification.CurlyBracketOpen: return CssBracketToken.OpenCurly; case Specification.CurlyBracketClose: return CssBracketToken.CloseCurly; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return NumberStart(current); case 'U': case 'u': current = Next; if (current == Specification.Plus) { current = Next; if (current.IsHex() || current == Specification.QuestionMark) return UnicodeRange(current); current = Previous; } return IdentStart(Previous); case Specification.Pipe: current = Next; if (current == Specification.Equality) return CssMatchToken.Dash; else if (current == Specification.Pipe) return CssToken.Column; return CssToken.Delim(Previous); case Specification.Tilde: current = Next; if (current == Specification.Equality) return CssMatchToken.Include; return CssToken.Delim(Previous); case Specification.EndOfFile: return null; case Specification.ExclamationMark: current = Next; if (current == Specification.Equality) return CssMatchToken.Not; return CssToken.Delim(Previous); default: if (current.IsNameStart()) return IdentStart(current); return CssToken.Delim(current); } }
/// <summary> /// 4.4.4. Hash state /// </summary> CssToken HashStart(Char current) { if (current.IsNameStart()) { _stringBuffer.Append(current); return HashRest(Next); } else if (IsValidEscape(current)) { current = Next; _stringBuffer.Append(ConsumeEscape(current)); return HashRest(Next); } else if (current == Specification.ReverseSolidus) { RaiseErrorOccurred(ErrorCode.InvalidCharacter); Back(); return CssToken.Delim(Specification.Num); } else { Back(); return CssToken.Delim(Specification.Num); } }
/// <summary> /// 4.4.1. Data state /// </summary> CssToken Data(Char current) { _position = GetCurrentPosition(); switch (current) { case Symbols.FormFeed: case Symbols.LineFeed: case Symbols.CarriageReturn: case Symbols.Tab: case Symbols.Space: return NewWhitespace(current); case Symbols.DoubleQuote: return StringDQ(); case Symbols.Num: return _valueMode ? ColorLiteral() : HashStart(); case Symbols.Dollar: current = GetNext(); if (current == Symbols.Equality) return NewSuffix(); return NewDelimiter(GetPrevious()); case Symbols.SingleQuote: return StringSQ(); case Symbols.RoundBracketOpen: return NewOpenRound(); case Symbols.RoundBracketClose: return NewCloseRound(); case Symbols.Asterisk: current = GetNext(); if (current == Symbols.Equality) return NewSubstring(); return NewDelimiter(GetPrevious()); case Symbols.Plus: { var c1 = GetNext(); if (c1 != Symbols.EndOfFile) { var c2 = GetNext(); Back(2); if (c1.IsDigit() || (c1 == Symbols.Dot && c2.IsDigit())) return NumberStart(current); } else Back(); return NewDelimiter(current); } case Symbols.Comma: return NewComma(); case Symbols.Dot: { var c = GetNext(); if (c.IsDigit()) return NumberStart(GetPrevious()); return NewDelimiter(GetPrevious()); } case Symbols.Minus: { var c1 = GetNext(); if (c1 != Symbols.EndOfFile) { var c2 = GetNext(); Back(2); if (c1.IsDigit() || (c1 == Symbols.Dot && c2.IsDigit())) return NumberStart(current); else if (c1.IsNameStart()) return IdentStart(current); else if (c1 == Symbols.ReverseSolidus && !c2.IsLineBreak() && c2 != Symbols.EndOfFile) return IdentStart(current); else if (c1 == Symbols.Minus && c2 == Symbols.GreaterThan) { Advance(2); return NewCloseComment(); } } else Back(); return NewDelimiter(current); } case Symbols.Solidus: current = GetNext(); if (current == Symbols.Asterisk) return Comment(); return NewDelimiter(GetPrevious()); case Symbols.ReverseSolidus: current = GetNext(); if (current.IsLineBreak()) { RaiseErrorOccurred(CssParseError.LineBreakUnexpected); return NewDelimiter(GetPrevious()); } else if (current == Symbols.EndOfFile) { RaiseErrorOccurred(CssParseError.EOF); return NewDelimiter(GetPrevious()); } return IdentStart(GetPrevious()); case Symbols.Colon: return NewColon(); case Symbols.Semicolon: return NewSemicolon(); case Symbols.LessThan: current = GetNext(); if (current == Symbols.ExclamationMark) { current = GetNext(); if (current == Symbols.Minus) { current = GetNext(); if (current == Symbols.Minus) return NewOpenComment(); current = GetPrevious(); } current = GetPrevious(); } return NewDelimiter(GetPrevious()); case Symbols.At: return AtKeywordStart(); case Symbols.SquareBracketOpen: return NewOpenSquare(); case Symbols.SquareBracketClose: return NewCloseSquare(); case Symbols.Accent: current = GetNext(); if (current == Symbols.Equality) return NewPrefix(); return NewDelimiter(GetPrevious()); case Symbols.CurlyBracketOpen: return NewOpenCurly(); case Symbols.CurlyBracketClose: return NewCloseCurly(); case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return NumberStart(current); case 'U': case 'u': current = GetNext(); if (current == Symbols.Plus) { current = GetNext(); if (current.IsHex() || current == Symbols.QuestionMark) return UnicodeRange(current); current = GetPrevious(); } return IdentStart(GetPrevious()); case Symbols.Pipe: current = GetNext(); if (current == Symbols.Equality) return NewDash(); else if (current == Symbols.Pipe) return NewColumn(); return NewDelimiter(GetPrevious()); case Symbols.Tilde: current = GetNext(); if (current == Symbols.Equality) return NewInclude(); return NewDelimiter(GetPrevious()); case Symbols.EndOfFile: return NewEof(); case Symbols.ExclamationMark: current = GetNext(); if (current == Symbols.Equality) return NewNot(); return NewDelimiter(GetPrevious()); default: if (current.IsNameStart()) return IdentStart(current); return NewDelimiter(current); } }
/// <summary> /// 4.4.7. At-keyword state /// </summary> CssToken AtKeywordStart(Char current) { if (current == Specification.Minus) { current = Next; if (current.IsNameStart() || IsValidEscape(current)) { _stringBuffer.Append(Specification.Minus); return AtKeywordRest(current); } Back(2); return CssToken.Delim(Specification.At); } else if (current.IsNameStart()) { _stringBuffer.Append(current); return AtKeywordRest(Next); } else if (IsValidEscape(current)) { current = Next; _stringBuffer.Append(ConsumeEscape(current)); return AtKeywordRest(Next); } else { Back(); return CssToken.Delim(Specification.At); } }
/// <summary> /// 4.4.9. Ident state /// </summary> CssToken IdentStart(Char current) { if (current == Specification.MINUS) { current = _src.Next; if (current.IsNameStart() || IsValidEscape(current)) { _stringBuffer.Append(Specification.MINUS); return IdentRest(current); } _src.Back(); return CssToken.Delim(Specification.MINUS); } else if (current.IsNameStart()) { _stringBuffer.Append(current); return IdentRest(_src.Next); } else if (current == Specification.RSOLIDUS) { if (IsValidEscape(current)) { current = _src.Next; _stringBuffer.Append(ConsumeEscape(current)); return IdentRest(_src.Next); } } return Data(current); }
/// <summary> /// 4.4.9. Ident state /// </summary> CssToken IdentStart(Char current) { if (current == Symbols.Minus) { current = GetNext(); if (current.IsNameStart() || IsValidEscape(current)) { _stringBuffer.Append(Symbols.Minus); return IdentRest(current); } Back(); return NewDelimiter(Symbols.Minus); } else if (current.IsNameStart()) { _stringBuffer.Append(current); return IdentRest(GetNext()); } else if (current == Symbols.ReverseSolidus) { if (IsValidEscape(current)) { current = GetNext(); _stringBuffer.Append(ConsumeEscape(current)); return IdentRest(GetNext()); } } return Data(current); }
/// <summary> /// 4.4.1. Data state /// </summary> CssToken Data(Char current) { switch (current) { case Specification.LF: case Specification.CR: case Specification.TAB: case Specification.SPACE: do { current = _src.Next; } while (current.IsSpaceCharacter()); if (_ignoreWs) return Data(current); _src.Back(); return CssSpecialCharacter.Whitespace; case Specification.DQ: return StringDQ(_src.Next); case Specification.NUM: return HashStart(_src.Next); case Specification.DOLLAR: current = _src.Next; if (current == Specification.EQ) return CssMatchToken.Suffix; return CssToken.Delim(_src.Previous); case Specification.SQ: return StringSQ(_src.Next); case Specification.RBO: return CssBracketToken.OpenRound; case Specification.RBC: return CssBracketToken.CloseRound; case Specification.ASTERISK: current = _src.Next; if (current == Specification.EQ) return CssMatchToken.Substring; return CssToken.Delim(_src.Previous); case Specification.PLUS: { var c1 = _src.Next; if (c1 == Specification.EOF) { _src.Back(); } else { var c2 = _src.Next; _src.Back(2); if (c1.IsDigit() || (c1 == Specification.DOT && c2.IsDigit())) return NumberStart(current); } return CssToken.Delim(current); } case Specification.COMMA: return CssSpecialCharacter.Comma; case Specification.DOT: { var c = _src.Next; if (c.IsDigit()) return NumberStart(_src.Previous); return CssToken.Delim(_src.Previous); } case Specification.MINUS: { var c1 = _src.Next; if (c1 == Specification.EOF) { _src.Back(); } else { var c2 = _src.Next; _src.Back(2); if (c1.IsDigit() || (c1 == Specification.DOT && c2.IsDigit())) return NumberStart(current); else if (c1.IsNameStart()) return IdentStart(current); else if (c1 == Specification.RSOLIDUS && !c2.IsLineBreak() && c2 != Specification.EOF) return IdentStart(current); else if (c1 == Specification.MINUS && c2 == Specification.GT) { _src.Advance(2); if (_ignoreCs) return Data(_src.Next); return CssCommentToken.Close; } } return CssToken.Delim(current); } case Specification.SOLIDUS: current = _src.Next; if (current == Specification.ASTERISK) return Comment(_src.Next); return CssToken.Delim(_src.Previous); case Specification.RSOLIDUS: current = _src.Next; if (current.IsLineBreak() || current == Specification.EOF) { RaiseErrorOccurred(current == Specification.EOF ? ErrorCode.EOF : ErrorCode.LineBreakUnexpected); return CssToken.Delim(_src.Previous); } return IdentStart(_src.Previous); case Specification.COLON: return CssSpecialCharacter.Colon; case Specification.SC: return CssSpecialCharacter.Semicolon; case Specification.LT: current = _src.Next; if (current == Specification.EM) { current = _src.Next; if (current == Specification.MINUS) { current = _src.Next; if (current == Specification.MINUS) { if (_ignoreCs) return Data(_src.Next); return CssCommentToken.Open; } current = _src.Previous; } current = _src.Previous; } return CssToken.Delim(_src.Previous); case Specification.AT: return AtKeywordStart(_src.Next); case Specification.SBO: return CssBracketToken.OpenSquare; case Specification.SBC: return CssBracketToken.CloseSquare; case Specification.ACCENT: current = _src.Next; if (current == Specification.EQ) return CssMatchToken.Prefix; return CssToken.Delim(_src.Previous); case Specification.CBO: return CssBracketToken.OpenCurly; case Specification.CBC: return CssBracketToken.CloseCurly; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return NumberStart(current); case 'U': case 'u': current = _src.Next; if (current == Specification.PLUS) { current = _src.Next; if (current.IsHex() || current == Specification.QM) return UnicodeRange(current); current = _src.Previous; } return IdentStart(_src.Previous); case Specification.PIPE: current = _src.Next; if (current == Specification.EQ) return CssMatchToken.Dash; else if (current == Specification.PIPE) return CssToken.Column; return CssToken.Delim(_src.Previous); case Specification.TILDE: current = _src.Next; if (current == Specification.EQ) return CssMatchToken.Include; return CssToken.Delim(_src.Previous); case Specification.EOF: return null; case Specification.EM: current = _src.Next; if (current == Specification.EQ) return CssMatchToken.Not; return CssToken.Delim(_src.Previous); default: if (current.IsNameStart()) return IdentStart(current); return CssToken.Delim(current); } }
/// <summary> /// 4.4.4. Hash state /// </summary> CssToken HashStart(Char current) { if (current.IsNameStart()) { _stringBuffer.Append(current); return HashRest(_src.Next); } else if (IsValidEscape(current)) { current = _src.Next; _stringBuffer.Append(ConsumeEscape(current)); return HashRest(_src.Next); } else if (current == Specification.RSOLIDUS) { RaiseErrorOccurred(ErrorCode.InvalidCharacter); _src.Back(); return CssToken.Delim(Specification.NUM); } else { _src.Back(); return CssToken.Delim(Specification.NUM); } }
ElementDeclarationEntry TypeDeclarationChildren(Char c) { var entries = new List<ElementDeclarationEntry>(); var connection = Specification.NULL; while (true) { if (entries.Count > 0) { if (c != Specification.PIPE && c != Specification.COMMA) throw new ArgumentException("Invalid content specification in element type declaration."); if (entries.Count == 1) connection = c; else if (connection != c) throw new ArgumentException("Invalid content specification in element type declaration."); c = _src.Next; } while (c.IsSpaceCharacter()) c = _src.Next; if (c.IsNameStart()) { var name = TypeDeclarationName(c); entries.Add(name); } else if (c == Specification.RBO) entries.Add(TypeDeclarationChildren(_src.Next)); else throw new ArgumentException("Invalid content specification in element type declaration."); c = _src.Current; while (c.IsSpaceCharacter()) c = _src.Next; if (c == Specification.RBC) break; } c = _src.Next; if (entries.Count == 1) return entries[0]; else if (entries.Count == 0) throw new ArgumentException("Invalid content specification in element type declaration."); else if (connection == Specification.COMMA) { var sequence = new ElementSequenceDeclarationEntry(); sequence.Sequence.AddRange(entries); sequence.Quantifier = TypeDeclarationQuantifier(c); return sequence; } var choice = new ElementChoiceDeclarationEntry(); choice.Choice.AddRange(entries); choice.Quantifier = TypeDeclarationQuantifier(c); return choice; }
/// <summary> /// More http://www.w3.org/TR/REC-xml/#attdecls. /// </summary> /// <param name="c">The next input character.</param> DtdAttributeToken AttributeDeclaration(Char c) { var decl = new DtdAttributeToken(); var canContinue = false; if (c.IsSpaceCharacter()) canContinue = DeclarationNameBefore(_src.Next, decl); else if (c == Specification.EOF) throw new ArgumentException("The document ended unexpectedly."); else { RaiseErrorOccurred(ErrorCode.UndefinedMarkupDeclaration); canContinue = DeclarationNameBefore(c, decl); } c = _src.Current; if (canContinue) { while (true) { while (c.IsSpaceCharacter()) c = _src.Next; if (c.IsNameStart()) { _stringBuffer.Clear(); decl.Attributes.Add(AttributeDeclarationName(c)); c = _src.Current; continue; } break; } } return AttributeDeclarationAfter(c, decl); }
DtdToken EntityDeclarationAfter(Char c, DtdEntityToken decl) { while (c.IsSpaceCharacter()) c = _src.Next; if (c == Specification.EOF) throw new ArgumentException("The document ended unexpectedly."); else if (c == Specification.GT) return decl; else if (decl.IsExtern && String.IsNullOrEmpty(decl.ExternNotation)) { if (_src.ContinuesWith("NDATA")) { _src.Advance(4); c = _src.Next; while (c.IsSpaceCharacter()) c = _src.Next; if (c.IsNameStart()) { _stringBuffer.Clear(); do { _stringBuffer.Append(c); c = _src.Next; } while (c.IsName()); decl.ExternNotation = _stringBuffer.ToString(); return EntityDeclarationAfter(c, decl); } } } throw new ArgumentException("Declaration invalid."); }
ElementMixedDeclarationEntry TypeDeclarationMixed(Char c) { var entry = new ElementMixedDeclarationEntry(); while (true) { while (c.IsSpaceCharacter()) c = _src.Next; if (c == Specification.RBC) { c = _src.Next; if (c == Specification.ASTERISK) { entry.Quantifier = ElementDeclarationEntry.ElementQuantifier.ZeroOrMore; _src.Advance(); return entry; } if (entry.Names.Count > 0) RaiseErrorOccurred(ErrorCode.QuantifierMissing); break; } else if (c == Specification.PIPE) { c = _src.Next; while (c.IsSpaceCharacter()) c = _src.Next; _stringBuffer.Clear(); if (c.IsNameStart()) { _stringBuffer.Append(c); while ((c = _src.Next).IsName()) _stringBuffer.Append(c); entry.Names.Add(_stringBuffer.ToString()); } else throw new ArgumentException("Invalid content specification in element type declaration."); } } return entry; }
XmlEntityToken CharacterReference(Char c) { _stringBuffer.Clear(); if (c == Specification.NUM) { c = _src.Next; while (c.IsHex()) { _stringBuffer.Append(c); c = _src.Next; } if (_stringBuffer.Length > 0 && c == Specification.SC) return new XmlEntityToken { Value = _stringBuffer.ToString(), IsNumeric = true }; } else if (c.IsNameStart()) { do { _stringBuffer.Append(c); c = _src.Next; } while (c.IsName()); if (c == Specification.SC) return new XmlEntityToken { Value = _stringBuffer.ToString() }; } throw new ArgumentException("Invalid entity reference."); }