void EmitBuffer(int i, CssTokenName tokenName) { //flush existing buffer if (this._appendLength > 0) { _emitHandler(tokenName, this._startIndex, this._appendLength); } this._appendLength = 0; }
void Emit(CssTokenName tkname, int i) { _emitHandler(tkname, i, 1); }
public void Lex(char[] cssSourceBuffer) { //---------------------- //clear previous result this._appendLength = 0; this._startIndex = 0; this.latestEscapeChar = '\0'; //---------------------- CssLexState lexState = CssLexState.Init; int j = cssSourceBuffer.Length; for (int i = 0; i < j; ++i) { char c = cssSourceBuffer[i]; #if DEBUG // Console.Write(c); #endif //-------------------------------------- switch (lexState) { default: { throw new NotSupportedException(); } case CssLexState.Init: { //-------------------------------------- //1. first name CssTokenName terminalTokenName = GetTerminalTokenName(c); //-------------------------------------- switch (terminalTokenName) { default: { Emit(terminalTokenName, i); } break; case CssTokenName.Colon: { if (i < j - 1) { char c1 = cssSourceBuffer[i + 1]; if (c1 == ':') { i++; Emit(CssTokenName.DoubleColon, i); continue; } } Emit(terminalTokenName, i); } break; case CssTokenName.DoubleQuote: { latestEscapeChar = '"'; lexState = CssLexState.CollectString; } break; case CssTokenName.Quote: { latestEscapeChar = '\''; lexState = CssLexState.CollectString; } break; case CssTokenName.Divide: { //is open comment or not if (i < j - 1) { if (cssSourceBuffer[i + 1] == '*') { i++; //Emit(CssTokenName.LComment, i); lexState = CssLexState.Comment; continue; } } Emit(CssTokenName.Divide, i); } break; case CssTokenName.Sharp: { AppendBuffer(i); lexState = CssLexState.Iden; } break; case CssTokenName.Dot: { if (i < j - 1) { char c1 = cssSourceBuffer[i + 1]; if (char.IsNumber(c1)) { AppendBuffer(i); i++; AppendBuffer(i); lexState = CssLexState.Number; continue; } } Emit(terminalTokenName, i); } break; case CssTokenName.Minus: { //as iden AppendBuffer(i); lexState = CssLexState.Iden; } break; case CssTokenName.Unknown: { //this is not terminal AppendBuffer(i); if (char.IsNumber(c)) { lexState = CssLexState.Number; } else { lexState = CssLexState.Iden; } } break; case CssTokenName.Whitespace: case CssTokenName.Newline: { isCollectionWhitespace = true; } break; } } break; case CssLexState.CollectString: { if (c == latestEscapeChar) { //exit collect string lexState = CssLexState.Init; EmitBuffer(i, CssTokenName.LiteralString); } else { AppendBuffer(i); } } break; case CssLexState.Comment: { if (c == '*') { if (i < j - 1) { char c1 = cssSourceBuffer[i + 1]; if (c1 == '/') { i++; //Emit(CssTokenName.RComment, i); lexState = CssLexState.Init; continue; } } } //skip comment? } break; case CssLexState.Iden: { CssTokenName terminalTokenName = GetTerminalTokenName(c); switch (terminalTokenName) { case CssTokenName.Whitespace: case CssTokenName.Newline: { EmitBuffer(i, CssTokenName.Iden); lexState = CssLexState.Init; } break; case CssTokenName.Divide: { //is open comment or not throw new NotSupportedException(); } case CssTokenName.Star: { //is close comment or not throw new NotSupportedException(); } case CssTokenName.Minus: { //iden can contains minus AppendBuffer(i); } break; default: { //flush exising buffer EmitBuffer(i, CssTokenName.Iden); Emit(terminalTokenName, i); lexState = CssLexState.Init; } break; case CssTokenName.Unknown: { //this is not terminal AppendBuffer(i); lexState = CssLexState.Iden; } break; } } break; case CssLexState.Number: { if (char.IsNumber(c)) { AppendBuffer(i); continue; } //---------------------------------------------------------- CssTokenName terminalTokenName = GetTerminalTokenName(c); switch (terminalTokenName) { case CssTokenName.Whitespace: case CssTokenName.Newline: { if (this._appendLength > 0) { EmitBuffer(i, CssTokenName.Number); } lexState = CssLexState.Init; } break; case CssTokenName.Divide: { //is open comment or not throw new NotSupportedException(); } case CssTokenName.Star: { //is close comment or not throw new NotSupportedException(); } case CssTokenName.Dot: { //after number if (i < j - 1) { char c1 = cssSourceBuffer[i + 1]; if (char.IsNumber(c1)) { AppendBuffer(i); i++; AppendBuffer(i); lexState = CssLexState.Number; continue; } } EmitBuffer(i, CssTokenName.Number); Emit(terminalTokenName, i); } break; default: { //flush exising buffer EmitBuffer(i, CssTokenName.Number); Emit(terminalTokenName, i); lexState = CssLexState.Init; } break; case CssTokenName.Unknown: { EmitBuffer(i, CssTokenName.Number); //iden after number may be unit of number*** AppendBuffer(i); lexState = CssLexState.UnitAfterNumber; } break; } } break; case CssLexState.UnitAfterNumber: { if (char.IsLetter(c)) { AppendBuffer(i); } else { //terminate EmitBuffer(i, CssTokenName.NumberUnit); //------------------------------------------- CssTokenName terminalTokenName = GetTerminalTokenName(c); switch (terminalTokenName) { case CssTokenName.Whitespace: case CssTokenName.Newline: { } break; default: { Emit(terminalTokenName, i); } break; } lexState = CssLexState.Init; } } break; } } if (this._appendLength > 0) { switch (lexState) { case CssLexState.UnitAfterNumber: EmitBuffer(cssSourceBuffer.Length - 1, CssTokenName.NumberUnit); break; case CssLexState.Number: EmitBuffer(cssSourceBuffer.Length - 1, CssTokenName.Number); break; case CssLexState.Iden: default: EmitBuffer(cssSourceBuffer.Length - 1, CssTokenName.Iden); break; } } }
void LexerEmitHandler(CssTokenName tkname, int start, int len) { switch (parseState) { default: { throw new NotSupportedException(); } break; case CssParseState.Init: { switch (tkname) { case CssTokenName.Comment: { //comment token } break; case CssTokenName.RBrace: { //exit from current ruleset block if (this._mediaStack.Count > 0) { this._currentAtMedia = this._mediaStack.Pop(); } } break; case CssTokenName.Star: { //start new code block CssRuleSet newblock; this._currentAtMedia.AddRuleSet(this._currentRuleSet = newblock = new CssRuleSet()); newblock.AddSelector(this._currentSelectorExpr = new CssSimpleElementSelector(SimpleElementSelectorKind.All)); parseState = CssParseState.MoreBlockName; } break; case CssTokenName.At: { //at rule parseState = CssParseState.ExpectAtRuleName; } break; //-------------------------------------------------- //1. case CssTokenName.Colon: { CssRuleSet newblock; _currentAtMedia.AddRuleSet(this._currentRuleSet = newblock = new CssRuleSet()); newblock.AddSelector(this._currentSelectorExpr = new CssSimpleElementSelector(SimpleElementSelectorKind.PseudoClass)); parseState = CssParseState.ExpectIdenAfterSpecialBlockNameSymbol; } break; //2. case CssTokenName.Dot: { CssRuleSet newblock; _currentAtMedia.AddRuleSet(this._currentRuleSet = newblock = new CssRuleSet()); newblock.AddSelector(this._currentSelectorExpr = new CssSimpleElementSelector(SimpleElementSelectorKind.ClassName)); parseState = CssParseState.ExpectIdenAfterSpecialBlockNameSymbol; } break; //3. case CssTokenName.DoubleColon: { CssRuleSet newblock; _currentAtMedia.AddRuleSet(this._currentRuleSet = newblock = new CssRuleSet()); newblock.AddSelector(this._currentSelectorExpr = new CssSimpleElementSelector(SimpleElementSelectorKind.Extend)); parseState = CssParseState.ExpectIdenAfterSpecialBlockNameSymbol; } break; //4. case CssTokenName.Iden: { //block name CssRuleSet newblock; _currentAtMedia.AddRuleSet(this._currentRuleSet = newblock = new CssRuleSet()); newblock.AddSelector(this._currentSelectorExpr = new CssSimpleElementSelector()); this._currentSelectorExpr.Name = new string(this.textBuffer, start, len); parseState = CssParseState.MoreBlockName; } break; //5. case CssTokenName.Sharp: { CssRuleSet newblock; _currentAtMedia.AddRuleSet(this._currentRuleSet = newblock = new CssRuleSet()); newblock.AddSelector(this._currentSelectorExpr = new CssSimpleElementSelector(SimpleElementSelectorKind.Id)); parseState = CssParseState.ExpectIdenAfterSpecialBlockNameSymbol; } break; } } break; case CssParseState.MoreBlockName: { //more switch (tkname) { case CssTokenName.LBrace: { //block body parseState = CssParseState.BlockBody; } break; case CssTokenName.LBracket: { //element attr parseState = CssParseState.ExpectBlockAttrIden; } break; //1. case CssTokenName.Colon: { //wait iden after colon var cssSelector = new CssSimpleElementSelector(); cssSelector.selectorType = SimpleElementSelectorKind.PseudoClass; _currentRuleSet.AddSelector(cssSelector); this._currentSelectorExpr = cssSelector; parseState = CssParseState.ExpectIdenAfterSpecialBlockNameSymbol; } break; //2. case CssTokenName.Dot: { var cssSelector = new CssSimpleElementSelector(); cssSelector.selectorType = SimpleElementSelectorKind.ClassName; _currentRuleSet.AddSelector(cssSelector); this._currentSelectorExpr = cssSelector; parseState = CssParseState.ExpectIdenAfterSpecialBlockNameSymbol; } break; //3. case CssTokenName.DoubleColon: { var cssSelector = new CssSimpleElementSelector(); cssSelector.selectorType = SimpleElementSelectorKind.Extend; _currentRuleSet.AddSelector(cssSelector); this._currentSelectorExpr = cssSelector; parseState = CssParseState.ExpectIdenAfterSpecialBlockNameSymbol; } break; //4. case CssTokenName.Iden: { //add more block name var cssSelector = new CssSimpleElementSelector(); cssSelector.selectorType = SimpleElementSelectorKind.TagName; cssSelector.Name = new string(this.textBuffer, start, len); _currentRuleSet.AddSelector(cssSelector); this._currentSelectorExpr = cssSelector; } break; //5. case CssTokenName.Sharp: { //id var cssSelector = new CssSimpleElementSelector(); cssSelector.selectorType = SimpleElementSelectorKind.Id; _currentRuleSet.AddSelector(cssSelector); this._currentSelectorExpr = cssSelector; parseState = CssParseState.ExpectIdenAfterSpecialBlockNameSymbol; } break; //---------------------------------------------------- //element combinator operators case CssTokenName.Comma: { this._currentRuleSet.PrepareExpression(CssCombinatorOperator.List); } break; case CssTokenName.Star: { }break; case CssTokenName.RAngle: { } break; case CssTokenName.Plus: { } break; case CssTokenName.Tile: { } break; //---------------------------------------------------- default: { throw new NotSupportedException(); } break; } } break; case CssParseState.ExpectIdenAfterSpecialBlockNameSymbol: { switch (tkname) { case CssTokenName.Iden: { this._currentSelectorExpr.Name = new string(this.textBuffer, start, len); parseState = CssParseState.MoreBlockName; } break; default: { throw new NotSupportedException(); } } } break; case CssParseState.ExpectBlockAttrIden: { switch (tkname) { case CssTokenName.Iden: { //attribute parseState = CssParseState.AfterAttrName; this._currentSelectorExpr.AddAttribute(this._currentSelectorAttr = new CssAttributeSelectorExpression()); this._currentSelectorAttr.AttributeName = new string(this.textBuffer, start, len); } break; default: { throw new NotSupportedException(); } break; } } break; case CssParseState.AfterAttrName: { switch (tkname) { case CssTokenName.OpEq: { parseState = CssParseState.ExpectedBlockAttrValue; //expected attr value } break; case CssTokenName.RBracket: { //no attr value parseState = CssParseState.MoreBlockName; } break; default: { throw new NotSupportedException(); } break; } } break; case CssParseState.ExpectedBlockAttrValue: { switch (tkname) { case CssTokenName.LiteralString: { this._currentSelectorAttr.valueExpression = this._latestPropertyValue = new CssCodePrimitiveExpression(new string(this.textBuffer, start, len), CssValueHint.LiteralString); this._currentSelectorAttr = null; } break; default: { } break; } parseState = CssParseState.AfterBlockNameAttr; } break; case CssParseState.AfterBlockNameAttr: { switch (tkname) { default: { } break; case CssTokenName.RBracket: { parseState = CssParseState.MoreBlockName; this._currentSelectorAttr = null; } break; } } break; case CssParseState.BlockBody: { switch (tkname) { case CssTokenName.Iden: { //block name //create css property string cssPropName = new string(this.textBuffer, start, len); var wellknownName = UserMapUtil.GetWellKnownPropName(cssPropName); if (wellknownName == WellknownCssPropertyName.Unknown) { _currentRuleSet.AddCssCodeProperty(this._currentProperty = new CssPropertyDeclaration(cssPropName)); } else { _currentRuleSet.AddCssCodeProperty(this._currentProperty = new CssPropertyDeclaration(wellknownName)); } this._latestPropertyValue = null; parseState = CssParseState.AfterPropertyName; } break; case CssTokenName.RBrace: { //close current block this._currentProperty = null; this._currentSelectorAttr = null; this._currentSelectorExpr = null; parseState = CssParseState.Init; } break; case CssTokenName.SemiColon: { //another semi colon just skip } break; default: { throw new NotSupportedException(); } break; } } break; case CssParseState.AfterPropertyName: { if (tkname == CssTokenName.Colon) { parseState = CssParseState.ExpectPropertyValue; } else { throw new NotSupportedException(); } } break; case CssParseState.ExpectPropertyValue: { switch (tkname) { default: { throw new NotSupportedException(); } case CssTokenName.Sharp: { //follow by hex color value parseState = CssParseState.ExpectValueOfHexColor; } break; case CssTokenName.LiteralString: { this._currentProperty.AddValue(this._latestPropertyValue = new CssCodePrimitiveExpression(new string(this.textBuffer, start, len), CssValueHint.LiteralString)); parseState = CssParseState.AfterPropertyValue; } break; case CssTokenName.Number: { float number = float.Parse(new string(this.textBuffer, start, len)); this._currentProperty.AddValue(this._latestPropertyValue = new CssCodePrimitiveExpression(number)); parseState = CssParseState.AfterPropertyValue; } break; case CssTokenName.NumberUnit: { } break; case CssTokenName.Iden: { //property value this._currentProperty.AddValue(this._latestPropertyValue = new CssCodePrimitiveExpression(new string(this.textBuffer, start, len), CssValueHint.Iden)); parseState = CssParseState.AfterPropertyValue; } break; } } break; case CssParseState.ExpectValueOfHexColor: { switch (tkname) { case CssTokenName.Iden: { this._currentProperty.AddValue(this._latestPropertyValue = new CssCodePrimitiveExpression("#" + new string(this.textBuffer, start, len), CssValueHint.HexColor)); } break; case CssTokenName.Number: { this._currentProperty.AddValue(this._latestPropertyValue = new CssCodePrimitiveExpression("#" + new string(this.textBuffer, start, len), CssValueHint.HexColor)); } break; default: { throw new NotSupportedException(); } } parseState = CssParseState.AfterPropertyValue; } break; case CssParseState.AfterPropertyValue: { switch (tkname) { default: { throw new NotSupportedException(); } case CssTokenName.Comma: { //skip comma } break; case CssTokenName.Percent: { if (_latestPropertyValue is CssCodePrimitiveExpression) { ((CssCodePrimitiveExpression)_latestPropertyValue).Unit = "%"; } } break; case CssTokenName.Divide: { //eg. font: style variant weight size/line-height family; CssCodeBinaryExpression codeBinaryOpExpr = new CssCodeBinaryExpression(); codeBinaryOpExpr.OpName = CssValueOpName.Divide; codeBinaryOpExpr.Left = this._latestPropertyValue; //replace previous add value *** int valueCount = this._currentProperty.ValueCount; //replace this._currentProperty.ReplaceValue(valueCount - 1, codeBinaryOpExpr); this._latestPropertyValue = codeBinaryOpExpr; } break; case CssTokenName.LiteralString: { var literalValue = new string(this.textBuffer, start, len); throw new NotSupportedException(); } break; case CssTokenName.LParen: { //function parseState = CssParseState.ExpectedFuncParameter; //make current prop value as func CssCodeFunctionCallExpression funcCallExpr = new CssCodeFunctionCallExpression( this._latestPropertyValue.ToString()); int valueCount = this._currentProperty.ValueCount; this._currentProperty.ReplaceValue(valueCount - 1, funcCallExpr); this._latestPropertyValue = funcCallExpr; } break; case CssTokenName.RBrace: { //close block parseState = CssParseState.Init; } break; case CssTokenName.SemiColon: { //start new proeprty parseState = CssParseState.BlockBody; this._currentProperty = null; } break; case CssTokenName.Iden: { //another property value this._currentProperty.AddValue(this._latestPropertyValue = new CssCodePrimitiveExpression(new string(this.textBuffer, start, len), CssValueHint.Iden)); } break; case CssTokenName.Number: { //another property value float number = float.Parse(new string(this.textBuffer, start, len)); this._currentProperty.AddValue(this._latestPropertyValue = new CssCodePrimitiveExpression(number)); } break; case CssTokenName.NumberUnit: { //number unit if (_latestPropertyValue is CssCodePrimitiveExpression) { ((CssCodePrimitiveExpression)_latestPropertyValue).Unit = new string(this.textBuffer, start, len); } } break; case CssTokenName.Sharp: { parseState = CssParseState.ExpectValueOfHexColor; } break; } } break; case CssParseState.ExpectAtRuleName: { //iden switch (tkname) { default: { throw new NotSupportedException(); } case CssTokenName.Iden: { string iden = new string(this.textBuffer, start, len); //create new rule _currentRuleSet = null; _currentProperty = null; _currentSelectorAttr = null; _currentSelectorExpr = null; switch (iden) { case "media": { parseState = CssParseState.MediaList; //store previous media if (this._currentAtMedia != null) { this._mediaStack.Push(this._currentAtMedia); } this.cssDocument.Add(this._currentAtMedia = new CssAtMedia()); } break; case "import": { parseState = CssParseState.ExpectImportURL; } break; case "page": { throw new NotSupportedException(); } break; default: { throw new NotSupportedException(); } break; } } break; } } break; case CssParseState.MediaList: { //medialist sep by comma switch (tkname) { default: { throw new NotSupportedException(); } case CssTokenName.Iden: { //media name this._currentAtMedia.AddMedia(new string(this.textBuffer, start, len)); } break; case CssTokenName.Comma: { //wait for another media } break; case CssTokenName.LBrace: { //begin rule set part parseState = CssParseState.Init; } break; } } break; case CssParseState.ExpectedFuncParameter: { string funcArg = new string(this.textBuffer, start, len); switch (tkname) { default: { throw new NotSupportedException(); } case CssTokenName.RParen: { this.parseState = CssParseState.AfterPropertyValue; } break; case CssTokenName.LiteralString: { ((CssCodeFunctionCallExpression)this._latestPropertyValue).AddFuncArg( new CssCodePrimitiveExpression(funcArg, CssValueHint.LiteralString)); this.parseState = CssParseState.AfterFuncParameter; } break; case CssTokenName.Number: { float number = float.Parse(funcArg); ((CssCodeFunctionCallExpression)this._latestPropertyValue).AddFuncArg( new CssCodePrimitiveExpression(number)); this.parseState = CssParseState.AfterFuncParameter; } break; case CssTokenName.Iden: { ((CssCodeFunctionCallExpression)this._latestPropertyValue).AddFuncArg( new CssCodePrimitiveExpression(funcArg, CssValueHint.Iden)); this.parseState = CssParseState.AfterFuncParameter; } break; } } break; case CssParseState.AfterFuncParameter: { switch (tkname) { default: { throw new NotSupportedException(); } case CssTokenName.RParen: { this.parseState = CssParseState.AfterPropertyValue; } break; case CssTokenName.Comma: { this.parseState = CssParseState.ExpectedFuncParameter; } break; } } break; } }