private static int GetHexChar(ref Token token, int index) { var ch = token.StringValue[index]; if (ch >= '0' && ch <= '9') return ch - '0'; if (ch >= 'a' && ch <= 'f') return ch - 'a' + 10; if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; throw new TokenException("invalid hex color", token); }
protected static Expression ParseLiteral(ref Token token, TokensQueue queue) { if (!queue.Empty) { var preview = queue.Peek(); if (preview.Type == TokenType.OpenParenthesis) { return Func(token, queue); } } return new LiteralExpression(token.StringValue); }
private static SelectorExpression ParseAttributeSelector(Token token, TokensQueue tokens) { var attrName = tokens.Read(TokenType.Literal).StringValue; var operation = tokens.Read(); if (operation.Type == TokenType.CloseSquareBracket) return new AttributeExistsSelector(attrName); if (operation.Type == TokenType.Equal) { var val = tokens.Read(); if (val.Type != TokenType.Literal && val.Type != TokenType.String) { throw new TokenException("expected literal or string token", val); } tokens.Read(TokenType.CloseSquareBracket); return new AttributeEqualsSelector(attrName, val.StringValue); } throw new TokenException("unknown attribute operator", operation); }
protected static Expression Func(Token nameToken, TokensQueue tokens) { tokens.Read(TokenType.OpenParenthesis); var args = ParseArguments(tokens); tokens.Read(TokenType.CloseParenthesis); switch (nameToken.StringValue.ToLower()) { case "round": if (args.Count != 1) { throw new TokenException("expected 1 argument", nameToken); } return new RoundFunctionExpression(args.First()); default: throw new TokenException("unknown function " + nameToken.StringValue, nameToken); } }
protected static SelectorExpression ParsePseudoSelector(Token token, TokensQueue queue) { var next = queue.Read(TokenType.Literal); if (next.StringValue == "not") { var preview = queue.Peek(); if (preview.Type == TokenType.OpenParenthesis) { queue.Read(TokenType.OpenParenthesis); var expr = Parse(queue); if (!(expr is SimpleSelector)) throw new TokenException("simple selector expected", preview); queue.Read(TokenType.CloseParenthesis); return new NotExpression((SimpleSelector)expr); } } return new PseudoClassSelector(next.StringValue); }
private static Expression ParseHashColor(ref Token token, TokensQueue queue) { var val = queue.Read(TokenType.Literal); if (val.StringValue.Length == 6) { var rh = GetHexChar(ref val, 0); var rl = GetHexChar(ref val, 1); var gh = GetHexChar(ref val, 2); var gl = GetHexChar(ref val, 3); var bh = GetHexChar(ref val, 4); var bl = GetHexChar(ref val, 5); return new ColorExpression(rh * 16 + rl, gh * 16 + gl, bh * 16 + bl); } if (val.StringValue.Length == 3) { var rhl = GetHexChar(ref val, 0); var ghl = GetHexChar(ref val, 1); var bhl = GetHexChar(ref val, 2); return new ColorExpression(rhl * 17, ghl * 17, bhl * 17); } throw new TokenException("invalid hex color", token); }
private static Expression ProcessBinaryExpression(Token opToken, Expression left, TokensQueue tokens) { var tokenPriority = GetPriority(opToken.Type); var other = ParseWithPriority(tokens, tokenPriority + 1); switch (opToken.Type) { case TokenType.Plus: return new AddExpression(left, other); case TokenType.Minus: return new SubExpression(left, other); case TokenType.Multiply: return new MulExpression(left, other); case TokenType.Divide: return new DivExpression(left, other); case TokenType.Percentage: return new ModExpression(left, other); case TokenType.Whitespace: if (left is SpaceGroupExpression) { return ((SpaceGroupExpression)left).Add(other); } return new SpaceGroupExpression(left, other); case TokenType.Comma: if (left is CommaGroupExpression) { return ((CommaGroupExpression)left).Add(other); } return new CommaGroupExpression(left, other); default: throw new TokenException("unexpected operator " + opToken.Type, opToken); } }
private static CssValueType ParseUnit(ref Token token) { switch (token.StringValue) { case "px": return CssValueType.Pixel; case "em": return CssValueType.Em; case "rem": return CssValueType.Rem; case "in": return CssValueType.Inch; case "cm": return CssValueType.Centimeter; case "%": return CssValueType.Percentage; case "vh": return CssValueType.ViewportHeight; case "vw": return CssValueType.ViewportWidth; default: throw new TokenException("invalid unit", token); } }
private static Expression ParseNumber(ref Token token, TokensQueue queue) { var inner = new NumberExpression(token.NumberValue); if (!queue.Empty) { var preview = queue.Peek(); if (preview.Type == TokenType.Literal || preview.Type == TokenType.Percentage) { var unitToken = queue.Read(); var unit = ParseUnit(ref unitToken); return new UnitExpression(inner, unit); } } return inner; }
protected virtual bool AcceptToken(ref Token token) { return false; }
protected static SelectorExpression ParseTypeSelector(Token token, TokensQueue queue) { return new TypeSelector(token.StringValue); }
protected static SelectorExpression ParseIdSelector(Token token, TokensQueue queue) { var next = queue.Read(TokenType.Literal); return new IdSelector(next.StringValue); }
public Token Read() { LastReadToken = _queue[_index++]; return LastReadToken; }
public CommentNode(Token token) { Token = token; }
private static Token ParseWhitespace(TokenizerContext context) { var originalPos = context.Position; var len = context.File.Content.Length; var position = context.CreatePosition(); while (context.Position < len) { var ch = context.File.Content[context.Position++]; if (ch == '\r' || ch == '\n') { if (context.Position < len) { var nextCh = context.File.Content[context.Position]; if ((nextCh == '\r' || nextCh == '\n') && nextCh != ch) { context.Position++; } } context.IncrementLine(); } else if (ch == ' ' || ch == '\t') { } else { context.Position--; break; } } var res = new Token { Type = TokenType.Whitespace, StringValue = context.File.Content.Substring(originalPos, context.Position - originalPos), Position = position }; return res; }
public TokenException(string message, Token token) : base($"{message} at line: {token.Position.Line}, column: {token.Position.Column}") { Token = token; }