public override Node Evaluate(Env env) { foreach (var frame in env.Frames) { NodeList mixins; if ((mixins = frame.Find(Selector, null)).Count == 0) continue; var rules = new NodeList(); foreach (var node in mixins) { if(!(node is Ruleset)) continue; var ruleset = node as Ruleset; if(!ruleset.MatchArguements(Arguments, env)) continue; if (node is Mixin.Definition) { var mixin = node as Mixin.Definition; rules.AddRange(mixin.Evaluate(Arguments, env).Rules); } else { if (ruleset.Rules != null) rules.AddRange(ruleset.Rules); } // todo fix for other Ruleset types? } return rules; } throw new ParsingException(Selector.ToCSS().Trim() + " is undefined"); }
// // The `primary` rule is the *entry* and *exit* point of the parser. // The rules here can appear at any level of the parse tree. // // The recursive nature of the grammar is an interplay between the `block` // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule, // as represented by this simplified grammar: // // primary → (ruleset | rule)+ // ruleset → selector+ block // block → '{' primary '}' // // Only at one point is the primary rule not called from the // block rule: at the root level. // public NodeList Primary(Parser parser) { Node node; var root = new NodeList(); NodeList comments = null; GatherComments(parser); while (node = MixinDefinition(parser) || Rule(parser) || PullComments() || Ruleset(parser) || MixinCall(parser) || Directive(parser)) { if (comments = PullComments()) { root.AddRange(comments); } comments = node as NodeList; if (comments) root.AddRange(comments); else root.Add(node); GatherComments(parser); } return root; }
public override Node Evaluate(Env env) { var found = false; var closures = env.FindRulesets(Selector); if(closures == null) throw new ParsingException(Selector.ToCSS(env).Trim() + " is undefined", Index); env.Rule = this; var rules = new NodeList(); foreach (var closure in closures) { var ruleset = closure.Ruleset; if (!ruleset.MatchArguements(Arguments, env)) continue; found = true; if (ruleset is MixinDefinition) { try { var mixin = ruleset as MixinDefinition; rules.AddRange(mixin.Evaluate(Arguments, env, closure.Context).Rules); } catch (ParsingException e) { throw new ParsingException(e.Message, e.Index, Index); } } else { if (ruleset.Rules != null) { var nodes = new List<Node>(ruleset.Rules); NodeHelper.ExpandNodes<MixinCall>(env, nodes); rules.AddRange(nodes); } } } env.Rule = null; if (!found) { var message = String.Format("No matching definition was found for `{0}({1})`", Selector.ToCSS(env).Trim(), StringExtensions.JoinStrings(Arguments.Select(a => a.ToCSS(env)), ", ")); throw new ParsingException(message, Index); } return rules; }
public CartoDefinition(CartoSelector selector, CartoRule[] rules) { m_rules = new NodeList<CartoRule>(); m_elements = selector.Elements; m_filters = selector.Filters; m_rules.AddRange(rules); m_zoom = selector.Zoom; m_attachment = selector.Attachment ?? "__default__"; m_ruleIndex = new List<string>(); for (int i = 0; i < m_rules.Count; i++) { m_rules[i].Zoom = selector.Zoom; m_ruleIndex.Add(m_rules[i].Id); } m_specificity = selector.Specificity(); }
public Ruleset Evaluate(List<NamedArgument> args, Env env, List<Ruleset> closureContext) { var frame = EvaluateParams(env, args); var frames = new[] { this, frame }.Concat(env.Frames).Concat(closureContext).Reverse(); var context = env.CreateChildEnv(new Stack<Ruleset>(frames)); var newRules = new NodeList(); foreach (var rule in Rules) { if (rule is MixinDefinition) { var mixin = rule as MixinDefinition; var parameters = Enumerable.Concat(mixin.Params, frame.Rules.Cast<Rule>()); newRules.Add(new MixinDefinition(mixin.Name, new NodeList<Rule>(parameters), mixin.Rules, mixin.Condition)); } else if (rule is Directive) { newRules.Add(rule); } else if (rule is Ruleset) { var ruleset = (rule as Ruleset); context.Frames.Push(ruleset); var rules = new NodeList(NodeHelper.NonDestructiveExpandNodes<MixinCall>(context, ruleset.Rules) .Select(r => r.Evaluate(context))); context.Frames.Pop(); newRules.Add(new Ruleset(ruleset.Selectors, rules)); } else if (rule is MixinCall) { newRules.AddRange((NodeList)rule.Evaluate(context)); } else { newRules.Add(rule.Evaluate(context)); } } return new Ruleset(null, newRules); }
public override Node Evaluate(Env env) { var found = false; var closures = env.FindRulesets(Selector); if(closures == null) throw new ParsingException(Selector.ToCSS(env).Trim() + " is undefined", Location); env.Rule = this; var rules = new NodeList(); if (PreComments) rules.AddRange(PreComments); foreach (var closure in closures) { var ruleset = closure.Ruleset; var matchType = ruleset.MatchArguments(Arguments, env); if (matchType == MixinMatch.ArgumentMismatch) continue; found = true; if (matchType == MixinMatch.GuardFail) continue; if (ruleset is MixinDefinition) { try { var mixin = ruleset as MixinDefinition; rules.AddRange(mixin.Evaluate(Arguments, env, closure.Context).Rules); } catch (ParsingException e) { throw new ParsingException(e.Message, e.Location, Location); } } else { if (ruleset.Rules != null) { var nodes = new NodeList(ruleset.Rules); NodeHelper.ExpandNodes<MixinCall>(env, nodes); rules.AddRange(nodes); } } } if (PostComments) rules.AddRange(PostComments); env.Rule = null; if (!found) { var message = String.Format("No matching definition was found for `{0}({1})`", Selector.ToCSS(env).Trim(), Arguments.Select(a => a.Value.ToCSS(env)).JoinStrings(env.Compress ? "," : ", ")); throw new ParsingException(message, Location); } if (Important) { var importantRules = new NodeList(); foreach (Node node in rules) { Rule r = node as Rule; if (r != null) { var valueNode = r.Value; var value = valueNode as Value; value = value != null ? new Value(value.Values, "!important").ReducedFrom<Value>(value) : new Value(new NodeList {valueNode}, "!important"); importantRules.Add((new Rule(r.Name, value)).ReducedFrom<Rule>(r)); } else { importantRules.Add(node); } } return importantRules; } return rules; }
/// <summary> /// Flattens a list of nodes seperated by comma so that all the conditions are on the bottom. /// e.g. /// (A) and (B) and (C) => A and B and C /// (A, B) and (D) and (C) => A and D and C, B and D and C /// (A) and (B) and (C, D) => A and B and C, A and B and D /// /// It does this by generating a list of permutations for the last n-1 then n-2 /// and with each call it multiplies out the OR'd elements /// </summary> private NodeList Permute(NodeList<NodeList> arr) { // in simple cases return if (arr.Count == 0) return new NodeList(); if (arr.Count == 1) { return arr[0]; } NodeList returner = new NodeList(); // run permute on the next n-1 NodeList<NodeList> sliced = new NodeList<NodeList>(arr.Skip(1)); NodeList rest = Permute(sliced); //now multiply for (int i = 0; i < rest.Count; i++) { NodeList inner = arr[0]; for (int j = 0; j < inner.Count; j++) { NodeList newl = new NodeList(); newl.Add(inner[j]); NodeList addition = rest[i] as NodeList; if (addition) { newl.AddRange(addition); } else { newl.Add(rest[i]); } //add an expression so it seperated by spaces returner.Add(new Expression(newl)); } } return returner; }
public Ruleset Evaluate(List<NamedArgument> args, Env env, List<Ruleset> closureContext) { // if (args != null && args.Any()) // Guard.ExpectMaxArguments(Params.Count, args.Count, String.Format("'{0}'", Name), Index); var arguments = new Dictionary<string, Node>(); args = args ?? new List<NamedArgument>(); var hasNamedArgs = false; foreach (var arg in args) { if (!string.IsNullOrEmpty(arg.Name)) { hasNamedArgs = true; arguments[arg.Name] = new Rule(arg.Name, arg.Value.Evaluate(env)) { Index = arg.Value.Index }; } else if (hasNamedArgs) throw new ParsingException("Positional arguments must appear before all named arguments.", arg.Value.Index); } for (var i = 0; i < Params.Count; i++) { if (String.IsNullOrEmpty(Params[i].Name)) continue; if (arguments.ContainsKey(Params[i].Name)) continue; Node val; if (i < args.Count && string.IsNullOrEmpty(args[i].Name)) val = args[i].Value; else val = Params[i].Value; if (val) arguments[Params[i].Name] = new Rule(Params[i].Name, val.Evaluate(env)) {Index = val.Index}; else throw new ParsingException( String.Format("wrong number of arguments for {0} ({1} for {2})", Name, args != null ? args.Count : 0, _arity), Index); } var _arguments = new List<Node>(); for(var i = 0; i < Math.Max(Params.Count, args.Count); i++) { _arguments.Add(i < args.Count ? args[i].Value : Params[i].Value); } var frame = new Ruleset(null, new NodeList()); frame.Rules.Insert(0, new Rule("@arguments", new Expression(_arguments.Where(a => a != null)).Evaluate(env))); foreach (var arg in arguments) { frame.Rules.Add(arg.Value); } var frames = new[] { this, frame }.Concat(env.Frames).Concat(closureContext).Reverse(); var context = new Env {Frames = new Stack<Ruleset>(frames)}; var newRules = new NodeList(); foreach (var rule in Rules) { if (rule is MixinDefinition) { var mixin = rule as MixinDefinition; var parameters = Enumerable.Concat(mixin.Params, frame.Rules.Cast<Rule>()); newRules.Add(new MixinDefinition(mixin.Name, new NodeList<Rule>(parameters), mixin.Rules)); } else if (rule is Ruleset) { var ruleset = (rule as Ruleset); context.Frames.Push(ruleset); var rules = new NodeList(NodeHelper.NonDestructiveExpandNodes<MixinCall>(context, ruleset.Rules) .Select(r => r.Evaluate(context))); context.Frames.Pop(); newRules.Add(new Ruleset(ruleset.Selectors, rules)); } else if (rule is MixinCall) { newRules.AddRange((NodeList) rule.Evaluate(context)); } else { newRules.Add(rule.Evaluate(context)); } } return new Ruleset(null, newRules); }
public override Node Evaluate(Env env) { var closures = env.FindRulesets(Selector); if (closures == null) throw new ParsingException(Selector.ToCSS(env).Trim() + " is undefined", Location); env.Rule = this; var rules = new NodeList(); if (PreComments) rules.AddRange(PreComments); var rulesetList = closures.ToList(); // To address bug https://github.com/dotless/dotless/issues/136, where a mixin and ruleset selector may have the same name, we // need to favour matching a MixinDefinition with the required Selector and only fall back to considering other Ruleset types // if no match is found. // However, in order to support having a regular ruleset with the same name as a parameterized // mixin (see https://github.com/dotless/dotless/issues/387), we need to take argument counts into account, so we make the // decision after evaluating for argument match. var mixins = rulesetList.Where(c => c.Ruleset is MixinDefinition).ToList(); var defaults = new List<Closure>(); bool foundMatches = false, foundExactMatches = false, foundDefaultMatches = false; foreach (var closure in mixins) { var ruleset = (MixinDefinition)closure.Ruleset; var matchType = ruleset.MatchArguments(Arguments, env); if (matchType == MixinMatch.ArgumentMismatch) { continue; } if (matchType == MixinMatch.Default) { defaults.Add(closure); foundDefaultMatches = true; continue; } foundMatches = true; if (matchType == MixinMatch.GuardFail) { continue; } foundExactMatches = true; try { var closureEnvironment = env.CreateChildEnvWithClosure(closure); rules.AddRange(ruleset.Evaluate(Arguments, closureEnvironment).Rules); } catch (ParsingException e) { throw new ParsingException(e.Message, e.Location, Location); } } if (!foundExactMatches && foundDefaultMatches) { foreach (var closure in defaults) { try { var closureEnvironment = env.CreateChildEnvWithClosure(closure); var ruleset = (MixinDefinition) closure.Ruleset; rules.AddRange(ruleset.Evaluate(Arguments, closureEnvironment).Rules); } catch (ParsingException e) { throw new ParsingException(e.Message, e.Location, Location); } } foundMatches = true; } if (!foundMatches) { var regularRulesets = rulesetList.Except(mixins); foreach (var closure in regularRulesets) { if (closure.Ruleset.Rules != null) { var nodes = (NodeList)closure.Ruleset.Rules.Clone(); NodeHelper.ExpandNodes<MixinCall>(env, nodes); rules.AddRange(nodes); } foundMatches = true; } } if (PostComments) rules.AddRange(PostComments); env.Rule = null; if (!foundMatches) { var message = String.Format("No matching definition was found for `{0}({1})`", Selector.ToCSS(env).Trim(), Arguments.Select(a => a.Value.ToCSS(env)).JoinStrings(env.Compress ? "," : ", ")); throw new ParsingException(message, Location); } rules.Accept(new ReferenceVisitor(IsReference)); if (Important) { return MakeRulesImportant(rules); } return rules; }
public Ruleset Evaluate(List<NamedArgument> args, Env env, List<Ruleset> closureContext) { var frame = EvaluateParams(env, args); var frames = new[] { this, frame }.Concat(env.Frames).Concat(closureContext).Reverse(); var context = env.CreateChildEnv(new Stack<Ruleset>(frames)); var newRules = new NodeList(); foreach (var rule in Rules) { if (rule is MixinDefinition) { var mixin = rule as MixinDefinition; var parameters = Enumerable.Concat(mixin.Params, frame.Rules.Cast<Rule>()); newRules.Add(new MixinDefinition(mixin.Name, new NodeList<Rule>(parameters), mixin.Rules, mixin.Condition, mixin.Variadic)); } else if (rule is Import) { var potentiolNodeList = rule.Evaluate(context); var nodeList = potentiolNodeList as NodeList; if (nodeList != null) { newRules.AddRange(nodeList); } else { newRules.Add(potentiolNodeList); } } else if (rule is Directive || rule is Media) { newRules.Add(rule.Evaluate(context)); } else if (rule is Ruleset) { var ruleset = (rule as Ruleset); context.Frames.Push(ruleset); newRules.Add(ruleset.Evaluate(context)); context.Frames.Pop(); } else if (rule is MixinCall) { newRules.AddRange((NodeList)rule.Evaluate(context)); } else { newRules.Add(rule.Evaluate(context)); } } return new Ruleset(null, newRules); }
public NodeList Find(Selector selector, Ruleset self) { self = self ?? this; var rules = new NodeList(); var key = selector.ToCSS(); if (_lookups.ContainsKey(key)) return _lookups[key]; foreach (var rule in Rulesets().Where(rule => rule != self)) { if (rule.Selectors && rule.Selectors.Any(selector.Match)) { if (selector.Elements.Count > 1) { var remainingSelectors = new Selector(new NodeList<Element>(selector.Elements.Skip(1))); rules.AddRange(rule.Find(remainingSelectors, self)); } else rules.Add(rule); } } return _lookups[key] = rules; }
public override Node Evaluate(Env env) { var found = false; var closures = env.FindRulesets(Selector); if (closures == null) { throw new ParsingException(Selector.ToCSS(env).Trim() + " is undefined", Index); } env.Rule = this; var rules = new NodeList(); if (PreComments) { rules.Add(PreComments); } foreach (var closure in closures) { var ruleset = closure.Ruleset; if (!ruleset.MatchArguments(Arguments, env)) { continue; } found = true; if (ruleset is MixinDefinition) { try { var mixin = ruleset as MixinDefinition; rules.AddRange(mixin.Evaluate(Arguments, env, closure.Context).Rules); } catch (ParsingException e) { throw new ParsingException(e.Message, e.Index, Index); } } else { if (ruleset.Rules != null) { var nodes = new NodeList(ruleset.Rules); NodeHelper.ExpandNodes <MixinCall>(env, nodes); rules.AddRange(nodes); } } } if (PostComments) { rules.Add(PostComments); } env.Rule = null; if (!found) { var message = String.Format("No matching definition was found for `{0}({1})`", Selector.ToCSS(env).Trim(), StringExtensions.JoinStrings(Arguments.Select(a => a.Value.ToCSS(env)), ", ")); throw new ParsingException(message, Index); } return(rules); }