// // A Selector Element // // div // + h1 // #socks // input[type="text"] // // Elements are the building blocks for Selectors, // they are made out of a `Combinator` (see combinator rule), // and an element name, such as a tag a class, or `*`. // public Element Element(Parser parser) { var index = parser.Tokenizer.Location.Index; GatherComments(parser); Combinator c = Combinator(parser); PushComments(); GatherComments(parser); // to collect, combinator must have picked up something which would require memory anyway var e = parser.Tokenizer.Match(@"[.#:]?[a-zA-Z0-9_-]+") || parser.Tokenizer.Match('*') || Attribute(parser) || parser.Tokenizer.MatchAny(@"\([^)@]+\)"); bool isCombinatorAnd = !e && c.Value.StartsWith("&"); if (e || isCombinatorAnd) { c.PostComments = PullComments(); PopComments(); c.PreComments = PullComments(); return(NodeProvider.Element(c, isCombinatorAnd ? null : e.Value, index)); } PopComments(); return(null); }
// // div, .class, body > p {...} // public Ruleset Ruleset(Parser parser) { var selectors = new NodeList <Selector>(); var memo = Remember(parser); var index = memo.TokenizerLocation.Index; if (parser.Tokenizer.Peek(@"([a-z.#: _-]+)[\s\n]*\{")) //simple case with no comments { var match = parser.Tokenizer.Match(@"[a-z.#: _-]+"); selectors = new NodeList <Selector>( NodeProvider.Selector(new NodeList <Element>(NodeProvider.Element(null, match.Value, index)), index)); } else { Selector s; while (s = Selector(parser)) { selectors.Add(s); if (!parser.Tokenizer.Match(',')) { break; } GatherComments(parser); } } NodeList rules; if (selectors.Count > 0 && (rules = Block(parser)) != null) { return(NodeProvider.Ruleset(selectors, rules, index)); } Recall(parser, memo); return(null); }
// // Mixins // // // A Mixin call, with an optional argument list // // #mixins > .square(#fff); // .rounded(4px, black); // .button; // // The `while` loop is there because mixins can be // namespaced, but we only support the child and descendant // selector for now. // public MixinCall MixinCall(Parser parser) { var elements = new NodeList <Element>(); var index = parser.Tokenizer.Location.Index; RegexMatchResult e; Combinator c = null; PushComments(); for (var i = parser.Tokenizer.Location.Index; e = parser.Tokenizer.Match(@"[#.][a-zA-Z0-9_-]+"); i = parser.Tokenizer.Location.Index) { elements.Add(NodeProvider.Element(c, e.Value, i)); i = parser.Tokenizer.Location.Index; var match = parser.Tokenizer.Match('>'); c = match != null?NodeProvider.Combinator(match.Value, i) : null; } var args = new List <NamedArgument>(); if (parser.Tokenizer.Match('(')) { Expression arg; while (arg = Expression(parser)) { var value = arg; string name = null; if (arg.Value.Count == 1 && arg.Value[0] is Variable) { if (parser.Tokenizer.Match(':')) { if (value = Expression(parser)) { name = (arg.Value[0] as Variable).Name; } else { throw new ParsingException("Expected value", parser.Tokenizer.Location.Index); } } } args.Add(new NamedArgument { Name = name, Value = value }); if (!parser.Tokenizer.Match(',')) { break; } } if (!parser.Tokenizer.Match(')')) { throw new ParsingException("Expected ')'", parser.Tokenizer.Location.Index); } } if (elements.Count > 0) { // if elements then we've picked up chars so don't need to worry about remembering var postComments = GatherAndPullComments(parser); if (End(parser)) { var mixinCall = NodeProvider.MixinCall(elements, args, index); mixinCall.PostComments = postComments; PopComments(); return(mixinCall); } } PopComments(); return(null); }