/// <summary> /// 4.4.3. Single quoted string state /// </summary> CssToken StringSQ(Char current) { while (true) { switch (current) { case Specification.SingleQuote: case Specification.EndOfFile: return CssStringToken.Plain(FlushBuffer()); case Specification.FormFeed: case Specification.LineFeed: RaiseErrorOccurred(ErrorCode.LineBreakUnexpected); Back(); return (CssStringToken.Plain(FlushBuffer(), true)); case Specification.ReverseSolidus: current = Next; if (current.IsLineBreak()) _stringBuffer.AppendLine(); else if (current != Specification.EndOfFile) _stringBuffer.Append(ConsumeEscape(current)); else { RaiseErrorOccurred(ErrorCode.EOF); Back(); return(CssStringToken.Plain(FlushBuffer(), true)); } break; default: _stringBuffer.Append(current); break; } current = Next; } }
/// <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> /// Checks if the current position is the beginning of a valid escape sequence. /// </summary> /// <returns>The result of the check.</returns> Boolean IsValidEscape(Char current) { if (current != Specification.ReverseSolidus) return false; current = Next; Back(); if (current == Specification.EndOfFile) return false; else if (current.IsLineBreak()) return false; return true; }
/// <summary> /// Just goes back one character without checking /// if the beginning is already reached. /// </summary> void BackUnsafe() { _source.Index -= 1; if (_source.Index == 0) { _column = 0; _current = Symbols.Null; return; } _current = _source[_source.Index - 1]; if (_current == Symbols.CarriageReturn) { BackUnsafe(); return; } if (_current.IsLineBreak()) { _column = _columns.Count != 0 ? _columns.Pop() : (UInt16)1; _row--; } else { _column--; } }
/// <summary> /// 4.4.19. URL-single-quoted state /// </summary> CssToken UrlSQ(Char current, CssTokenType type) { while (true) { if (current.IsLineBreak()) { RaiseErrorOccurred(ErrorCode.LineBreakUnexpected); return UrlBad(Next, type); } else if (Specification.EndOfFile == current) { return CssStringToken.Url(type, FlushBuffer()); } else if (current == Specification.SingleQuote) { return UrlEnd(Next, type); } else if (current == Specification.ReverseSolidus) { current = Next; if (current == Specification.EndOfFile) { Back(2); RaiseErrorOccurred(ErrorCode.EOF); return CssStringToken.Url(type, FlushBuffer(), true); } else if (current.IsLineBreak()) _stringBuffer.AppendLine(); else _stringBuffer.Append(ConsumeEscape(current)); } else _stringBuffer.Append(current); current = Next; } }
/// <summary> /// Checks if the current position is the beginning of a valid escape sequence. /// </summary> /// <returns>The result of the check.</returns> Boolean IsValidEscape(Char current) { if (current != Symbols.ReverseSolidus) return false; current = GetNext(); Back(); if (current == Symbols.EndOfFile) return false; else if (current.IsLineBreak()) return false; return true; }
/// <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.3. Single quoted string state /// </summary> CssToken StringSQ(Char current) { while (true) { switch (current) { case Specification.SQ: case Specification.EOF: return CssStringToken.Plain(FlushBuffer()); case Specification.FF: case Specification.LF: RaiseErrorOccurred(ErrorCode.LineBreakUnexpected); _src.Back(); return (CssStringToken.Plain(FlushBuffer(), true)); case Specification.RSOLIDUS: current = _src.Next; if (current.IsLineBreak()) _stringBuffer.AppendLine(); else if (current != Specification.EOF) _stringBuffer.Append(ConsumeEscape(current)); else { RaiseErrorOccurred(ErrorCode.EOF); _src.Back(); return(CssStringToken.Plain(FlushBuffer(), true)); } break; default: _stringBuffer.Append(current); break; } current = _src.Next; } }
/// <summary> /// Just goes back one character without checking /// if the beginning is already reached. /// </summary> void BackUnsafe() { _reader.Index -= 1; if (_reader.Index == 0) { _column = 0; _current = Specification.Null; return; } _current = _reader[_reader.Index - 1]; if (_current.IsLineBreak()) { _column = _collengths.Count != 0 ? _collengths.Pop() : 1; _row--; } else _column--; }
/// <summary> /// Checks if the current position is the beginning of a valid escape sequence. /// </summary> /// <returns>The result of the check.</returns> Boolean IsValidEscape(Char current) { if (current != Specification.RSOLIDUS) return false; current = _src.Next; _src.Back(); if (current == Specification.EOF) return false; else if (current.IsLineBreak()) return false; return true; }
/// <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.19. URL-single-quoted state /// </summary> CssToken UrlSQ(Char current, CssTokenType type) { while (true) { if (current.IsLineBreak()) { RaiseErrorOccurred(ErrorCode.LineBreakUnexpected); return UrlBad(_src.Next, type); } else if (Specification.EOF == current) { return CssStringToken.Url(type, FlushBuffer()); } else if (current == Specification.SQ) { return UrlEnd(_src.Next, type); } else if (current == Specification.RSOLIDUS) { current = _src.Next; if (current == Specification.EOF) { _src.Back(2); RaiseErrorOccurred(ErrorCode.EOF); return CssStringToken.Url(type, FlushBuffer(), true); } else if (current.IsLineBreak()) _stringBuffer.AppendLine(); else _stringBuffer.Append(ConsumeEscape(current)); } else _stringBuffer.Append(current); current = _src.Next; } }
void BackUnsafe() { _insertion--; if (_insertion == 0) { _column = 0; _current = Specification.NULL; return; } _current = _buffer[_insertion - 1]; if (_current.IsLineBreak()) { _column = _collengths.Count != 0 ? _collengths.Pop() : 1; _row--; } else _column--; }
Boolean IsValidEscape(Char current) { if (current == Symbols.ReverseSolidus) { current = GetNext(); Back(); return current != Symbols.EndOfFile && !current.IsLineBreak(); } return false; }