Package com.github.sommeri.less4j.core.parser

Source Code of com.github.sommeri.less4j.core.parser.ASTBuilderSwitch

package com.github.sommeri.less4j.core.parser;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.antlr.runtime.CommonToken;

import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.ArgumentDeclaration;
import com.github.sommeri.less4j.core.ast.BinaryExpression;
import com.github.sommeri.less4j.core.ast.BinaryExpressionOperator;
import com.github.sommeri.less4j.core.ast.CharsetDeclaration;
import com.github.sommeri.less4j.core.ast.ComparisonExpression;
import com.github.sommeri.less4j.core.ast.ComparisonExpressionOperator;
import com.github.sommeri.less4j.core.ast.CssClass;
import com.github.sommeri.less4j.core.ast.Declaration;
import com.github.sommeri.less4j.core.ast.DetachedRuleset;
import com.github.sommeri.less4j.core.ast.DetachedRulesetReference;
import com.github.sommeri.less4j.core.ast.Document;
import com.github.sommeri.less4j.core.ast.ElementSubsequent;
import com.github.sommeri.less4j.core.ast.EmptyExpression;
import com.github.sommeri.less4j.core.ast.EscapedSelector;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.Extend;
import com.github.sommeri.less4j.core.ast.FixedMediaExpression;
import com.github.sommeri.less4j.core.ast.FixedNamePart;
import com.github.sommeri.less4j.core.ast.FontFace;
import com.github.sommeri.less4j.core.ast.FunctionExpression;
import com.github.sommeri.less4j.core.ast.GeneralBody;
import com.github.sommeri.less4j.core.ast.Guard;
import com.github.sommeri.less4j.core.ast.GuardCondition;
import com.github.sommeri.less4j.core.ast.IdSelector;
import com.github.sommeri.less4j.core.ast.IdentifierExpression;
import com.github.sommeri.less4j.core.ast.Import;
import com.github.sommeri.less4j.core.ast.Import.ImportContent;
import com.github.sommeri.less4j.core.ast.IndirectVariable;
import com.github.sommeri.less4j.core.ast.InterpolableName;
import com.github.sommeri.less4j.core.ast.InterpolatedMediaExpression;
import com.github.sommeri.less4j.core.ast.Keyframes;
import com.github.sommeri.less4j.core.ast.KeyframesName;
import com.github.sommeri.less4j.core.ast.ListExpression;
import com.github.sommeri.less4j.core.ast.ListExpressionOperator;
import com.github.sommeri.less4j.core.ast.Media;
import com.github.sommeri.less4j.core.ast.MediaExpressionFeature;
import com.github.sommeri.less4j.core.ast.MediaQuery;
import com.github.sommeri.less4j.core.ast.Medium;
import com.github.sommeri.less4j.core.ast.MediumModifier;
import com.github.sommeri.less4j.core.ast.MediumType;
import com.github.sommeri.less4j.core.ast.MixinReference;
import com.github.sommeri.less4j.core.ast.Name;
import com.github.sommeri.less4j.core.ast.NamedExpression;
import com.github.sommeri.less4j.core.ast.NestedSelectorAppender;
import com.github.sommeri.less4j.core.ast.Nth;
import com.github.sommeri.less4j.core.ast.Nth.Form;
import com.github.sommeri.less4j.core.ast.NumberExpression;
import com.github.sommeri.less4j.core.ast.Page;
import com.github.sommeri.less4j.core.ast.PageMarginBox;
import com.github.sommeri.less4j.core.ast.Pseudo;
import com.github.sommeri.less4j.core.ast.PseudoClass;
import com.github.sommeri.less4j.core.ast.PseudoElement;
import com.github.sommeri.less4j.core.ast.ReusableStructure;
import com.github.sommeri.less4j.core.ast.ReusableStructureName;
import com.github.sommeri.less4j.core.ast.RuleSet;
import com.github.sommeri.less4j.core.ast.Selector;
import com.github.sommeri.less4j.core.ast.SelectorAttribute;
import com.github.sommeri.less4j.core.ast.SelectorOperator;
import com.github.sommeri.less4j.core.ast.SelectorPart;
import com.github.sommeri.less4j.core.ast.SignedExpression;
import com.github.sommeri.less4j.core.ast.SimpleSelector;
import com.github.sommeri.less4j.core.ast.StyleSheet;
import com.github.sommeri.less4j.core.ast.Supports;
import com.github.sommeri.less4j.core.ast.SupportsCondition;
import com.github.sommeri.less4j.core.ast.SupportsConditionInParentheses;
import com.github.sommeri.less4j.core.ast.SupportsConditionNegation;
import com.github.sommeri.less4j.core.ast.SupportsLogicalCondition;
import com.github.sommeri.less4j.core.ast.SupportsLogicalOperator;
import com.github.sommeri.less4j.core.ast.SupportsLogicalOperator.Operator;
import com.github.sommeri.less4j.core.ast.SupportsQuery;
import com.github.sommeri.less4j.core.ast.SyntaxOnlyElement;
import com.github.sommeri.less4j.core.ast.UnknownAtRule;
import com.github.sommeri.less4j.core.ast.Variable;
import com.github.sommeri.less4j.core.ast.VariableDeclaration;
import com.github.sommeri.less4j.core.ast.VariableNamePart;
import com.github.sommeri.less4j.core.ast.Viewport;
import com.github.sommeri.less4j.core.compiler.stages.AstLogic;
import com.github.sommeri.less4j.core.problems.BugHappened;
import com.github.sommeri.less4j.core.problems.ProblemsHandler;
import com.github.sommeri.less4j.utils.ArraysUtils;

//FIXME: better error message for required (...)+ loop did not match anything at input errors
//FIXME: better error message missing EOF at blahblah
class ASTBuilderSwitch extends TokenTypeSwitch<ASTCssNode> {

  protected static final String GRAMMAR_MISMATCH = "ASTBuilderSwitch grammar mismatch";
  private final ProblemsHandler problemsHandler;
  private final MixinsParametersBuilder mixinsParametersBuilder;
  private final TermBuilder termBuilder;
  // as stated here: http://www.w3.org/TR/css3-selectors/#pseudo-elements
  private static Set<String> COLONLESS_PSEUDOELEMENTS = new HashSet<String>();
  static {
    COLONLESS_PSEUDOELEMENTS.add("first-line");
    COLONLESS_PSEUDOELEMENTS.add("first-letter");
    COLONLESS_PSEUDOELEMENTS.add("before");
    COLONLESS_PSEUDOELEMENTS.add("after");
  }

  private final static String EXTEND_ALL_KEYWORD = "all";

  private final static String IMPORT_OPTION_REFERENCE = "reference";
  private final static String IMPORT_OPTION_INLINE = "inline";
  private final static String IMPORT_OPTION_LESS = "less";
  private final static String IMPORT_OPTION_CSS = "css";
  private final static String IMPORT_OPTION_ONCE = "once";
  private final static String IMPORT_OPTION_MULTIPLE = "multiple";

  public ASTBuilderSwitch(ProblemsHandler problemsHandler) {
    super();
    this.problemsHandler = problemsHandler;
    termBuilder = new TermBuilder(this, problemsHandler);
    mixinsParametersBuilder = new MixinsParametersBuilder(this, problemsHandler);
  }

  public StyleSheet handleStyleSheet(HiddenTokenAwareTree token) {
    StyleSheet result = new StyleSheet(token);
    if (token.getChildren() == null || token.getChildren().isEmpty())
      return result;

    for (HiddenTokenAwareTree kid : token.getChildren()) {
      result.addMember(switchOn(kid));
    }

    return result;
  }

  public Expression handleTerm(HiddenTokenAwareTree token) {
    return termBuilder.buildFromChildTerm(token);
  }

  public Expression handleExpression(HiddenTokenAwareTree token) {
    LinkedList<HiddenTokenAwareTree> children = new LinkedList<HiddenTokenAwareTree>(token.getChildren());
    if (children.size() == 0)
      throw new BugHappened(GRAMMAR_MISMATCH, token);

    if (children.size() == 1) {
      Expression head = (Expression) switchOn(children.get(0));
      // we have to switch to parent token, because we would loose it otherwise.
      // for example, comments before simple expressions would not be accessible
      // anymore
      head.setUnderlyingStructure(token);
      return head;
    }
   
    if (null!=toListExpressionOperator(children.get(1)))
      return createListExpression(token, children);

    return createBinaryExpression(token, children);
  }

  private Expression createListExpression(HiddenTokenAwareTree parent, LinkedList<HiddenTokenAwareTree> members) {
    Iterator<HiddenTokenAwareTree> iterator = members.iterator();
    // this must represent a term. Otherwise we are doomed anyway.
    Expression head = (Expression) switchOn(iterator.next());
    List<Expression> spaceSeparatedExpressions = new ArrayList<Expression>();
    spaceSeparatedExpressions.add(head);
    ListExpressionOperator space=null;
   
    List<Expression> commaSeparatedExpressions = new ArrayList<Expression>();
    ListExpressionOperator comma=null;

    while (iterator.hasNext()) {
      HiddenTokenAwareTree operatorToken = iterator.next();
      operatorToken.pushHiddenToSiblings();
      ListExpressionOperator operator = createListOperator(operatorToken);
      if (operator==null) {
        System.out.println(operatorToken);
        System.out.println("");
      }
      if (operator.getOperator()==ListExpressionOperator.Operator.EMPTY_OPERATOR) {
        space = operator;
      } else {
        comma = operator;
        if (!spaceSeparatedExpressions.isEmpty())
          commaSeparatedExpressions.add(maybeList(spaceSeparatedExpressions, space));
        spaceSeparatedExpressions = new ArrayList<Expression>();
      }
      if (iterator.hasNext())
        spaceSeparatedExpressions.add((Expression) switchOn(iterator.next()));
    }
    if (!spaceSeparatedExpressions.isEmpty())
      commaSeparatedExpressions.add(maybeList(spaceSeparatedExpressions, space));
    return maybeList(commaSeparatedExpressions, comma);
  }

  private Expression maybeList(List<Expression> expressions, ListExpressionOperator operator) {
    if (expressions.isEmpty())
      return null;
   
    Expression first = expressions.get(0);
    if (expressions.size()==1) {
      return first;
    }
   
    return new ListExpression(first.getUnderlyingStructure(), expressions, operator);
  }

  private Expression createBinaryExpression(HiddenTokenAwareTree parent, LinkedList<HiddenTokenAwareTree> members) {
    // this must represent a term. Otherwise we are doomed anyway.
    Expression head = (Expression) switchOn(members.removeFirst());
    while (!members.isEmpty()) {
      BinaryExpressionOperator operator = createBinaryOperator(members.removeFirst());
      if (members.isEmpty())
        return new BinaryExpression(parent, head, operator, null);

      Expression next = (Expression) switchOn(members.removeFirst());
      head = new BinaryExpression(parent, head, operator, next);
    }
    return head;
  }

  public BinaryExpressionOperator createBinaryOperator(HiddenTokenAwareTree token) {
    BinaryExpressionOperator operator = new BinaryExpressionOperator(token, toExpressionOperator(token));
    return operator;
  }

  private BinaryExpressionOperator.Operator toExpressionOperator(HiddenTokenAwareTree token) {
    switch (token.getType()) {
    case LessLexer.SOLIDUS:
      return BinaryExpressionOperator.Operator.SOLIDUS;

    case LessLexer.STAR:
      return BinaryExpressionOperator.Operator.STAR;

    case LessLexer.MINUS:
      return BinaryExpressionOperator.Operator.MINUS;

    case LessLexer.PLUS:
      return BinaryExpressionOperator.Operator.PLUS;

    default:
      break;
    }

    throw new BugHappened(GRAMMAR_MISMATCH, token);
  }

  public ListExpressionOperator createListOperator(HiddenTokenAwareTree token) {
    ListExpressionOperator.Operator operator = toListExpressionOperator(token);
    if (operator==null)
      return null;
   
    ListExpressionOperator result = new ListExpressionOperator(token, operator);
    return result;
  }

  private ListExpressionOperator.Operator toListExpressionOperator(HiddenTokenAwareTree token) {
    switch (token.getType()) {
    case LessLexer.COMMA:
      return ListExpressionOperator.Operator.COMMA;

    case LessLexer.EMPTY_SEPARATOR:
      return ListExpressionOperator.Operator.EMPTY_OPERATOR;

    default:
      return null;
    }
  }

  public Variable handleVariable(HiddenTokenAwareTree token) {
    return termBuilder.buildFromVariable(token);
  }

  public IndirectVariable handleIndirectVariable(HiddenTokenAwareTree token) {
    return termBuilder.buildFromIndirectVariable(token);
  }

  public Declaration handleDeclaration(HiddenTokenAwareTree token) {
    Iterator<HiddenTokenAwareTree> iterator = token.getChildren().iterator();
    HiddenTokenAwareTree nameToken = iterator.next();
    InterpolableName name = toInterpolableName(nameToken, nameToken.getChildren());

    if (!iterator.hasNext())
      return new Declaration(token, name);

    HiddenTokenAwareTree expressionToken = iterator.next();
    ListExpressionOperator.Operator mergeOperator = null;
    if (expressionToken.getType() == LessLexer.PLUS) {
      expressionToken = iterator.next();
      mergeOperator = ListExpressionOperator.Operator.COMMA;
      if (expressionToken.getType() == LessLexer.UNDERSCORE) {
        expressionToken = iterator.next();
        mergeOperator = ListExpressionOperator.Operator.EMPTY_OPERATOR;
      }
    }

    if (expressionToken.getType() == LessLexer.IMPORTANT_SYM)
      return new Declaration(token, name, null, true, mergeOperator);

    Expression expression = (Expression) switchOn(expressionToken);
    if (!iterator.hasNext())
      return new Declaration(token, name, expression, mergeOperator);

    HiddenTokenAwareTree importantToken = iterator.next();
    if (importantToken.getType() == LessLexer.IMPORTANT_SYM)
      return new Declaration(token, name, expression, true, mergeOperator);

    throw new BugHappened(GRAMMAR_MISMATCH, token);
  }

  public FontFace handleFontFace(HiddenTokenAwareTree token) {
    FontFace result = new FontFace(token);

    List<HiddenTokenAwareTree> children = token.getChildren();
    result.setBody(handleGeneralBody(children.get(0)));
    return result;
  }

  public CharsetDeclaration handleCharsetDeclaration(HiddenTokenAwareTree token) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    if (children.isEmpty())
      throw new BugHappened(GRAMMAR_MISMATCH, token);

    HiddenTokenAwareTree charset = children.get(1);
    return new CharsetDeclaration(token, termBuilder.createCssString(charset, charset.getText()));
  }

  public RuleSet handleRuleSet(HiddenTokenAwareTree token) {
    RuleSet ruleSet = new RuleSet(token);

    List<Selector> selectors = new ArrayList<Selector>();
    List<Guard> guards = new ArrayList<Guard>();
    List<HiddenTokenAwareTree> children = token.getChildren();

    ASTCssNode previousKid = null;
    for (HiddenTokenAwareTree kid : children) {
      if (kid.getType() == LessLexer.SELECTOR) {
        Selector selector = handleSelector(kid);
        if (selector != null)
          selectors.add(selector);
        previousKid = selector;
      } else if (kid.getType() == LessLexer.BODY) {
        GeneralBody body = handleGeneralBody(kid);
        ruleSet.setBody(body);
        previousKid = body;
      } else if (kid.getType() == LessLexer.GUARD) {
        guards.add(handleGuard(kid));
      } else if (kid.getType() == LessLexer.COMMA) {
        if (previousKid != null)
          previousKid.getUnderlyingStructure().addFollowing(kid.getPreceding());
      }
    }

    ruleSet.addSelectors(selectors);
    ruleSet.addGuards(guards);
    return ruleSet;
  }

  public ReusableStructure handleReusableStructureDeclaration(HiddenTokenAwareTree token) {
    ReusableStructure result = new ReusableStructure(token);

    List<HiddenTokenAwareTree> children = token.getChildren();
    for (HiddenTokenAwareTree kid : children) {
      if (kid.getType() == LessLexer.REUSABLE_STRUCTURE_NAME) {
        result.addName(handleReusableStructureName(kid));
      } else if (kid.getType() == LessLexer.BODY) {
        result.setBody(handleGeneralBody(kid));
      } else if (kid.getType() == LessLexer.GUARD) {
        result.addGuard(handleGuard(kid));
      } else if (kid.getType() == LessLexer.SEMI_SPLIT_MIXIN_DECLARATION_ARGUMENTS) {
        mixinsParametersBuilder.handleMixinDeclarationArguments(kid, result);
      }
    }

    return result;
  }

  public ElementSubsequent handleElementSubsequent(HiddenTokenAwareTree token) {
    return (ElementSubsequent) switchOn(token.getChild(0));
  }

  public ReusableStructureName handleReusableStructureName(HiddenTokenAwareTree token) {
    ReusableStructureName result = new ReusableStructureName(token);
    List<HiddenTokenAwareTree> children = token.getChildren();
    for (HiddenTokenAwareTree kid : children) {
      result.addNamePart(handleElementSubsequent(kid));
    }
    return result;
  }

  public MixinReference handleMixinReference(HiddenTokenAwareTree token) {
    MixinReference result = new MixinReference(token);
    List<HiddenTokenAwareTree> children = token.getChildren();
    for (HiddenTokenAwareTree kid : children) {
      if (kid.getType() == LessLexer.REUSABLE_STRUCTURE_NAME) {
        result.setFinalName(handleReusableStructureName(kid));
      } else if (kid.getType() == LessLexer.IMPORTANT_SYM) {
        result.setImportant(true);
      } else if (kid.getType() == LessLexer.SEMI_SPLIT_MIXIN_REFERENCE_ARGUMENTS) {
        mixinsParametersBuilder.handleMixinReferenceArguments(kid, result);
      }
    }

    return result;
  }

  public MixinReference handleNamespaceReference(HiddenTokenAwareTree token) {
    MixinReference reference = null;
    List<ReusableStructureName> nameChain = new ArrayList<ReusableStructureName>();

    List<HiddenTokenAwareTree> children = token.getChildren();
    for (HiddenTokenAwareTree kid : children) {
      ASTCssNode buildKid = switchOn(kid);
      if (buildKid.getType() == ASTCssNodeType.MIXIN_REFERENCE) {
        reference = (MixinReference) switchOn(kid);
      } else if (buildKid.getType() == ASTCssNodeType.REUSABLE_STRUCTURE_NAME) {
        nameChain.add(handleReusableStructureName(kid));
      } else {
        throw new BugHappened(GRAMMAR_MISMATCH, token);
      }
    }

    reference.setUnderlyingStructure(token);
    reference.addNames(nameChain);
    return reference;
 

  public Expression handleMixinPattern(HiddenTokenAwareTree token) {
    return termBuilder.buildFromTerm(token.getChild(0));
  }

  public DetachedRulesetReference handleDetachedRulesetReference(HiddenTokenAwareTree token) {
    DetachedRulesetReference result = new DetachedRulesetReference(token, termBuilder.buildFromVariable(token.getChild(0)));
    if (token.getChildCount()<3) {
      problemsHandler.detachedRulesetCallWithoutParentheses(result);
    }
    return result;
  }

  public DetachedRuleset handleDetachedRuleset(HiddenTokenAwareTree token) {
    return new DetachedRuleset(token, handleGeneralBody(token.getChild(0)));
  }

  public Guard handleGuard(HiddenTokenAwareTree token) {
    Guard result = new Guard(token);
    Iterator<HiddenTokenAwareTree> iterator = token.getChildren().iterator();
    result.addCondition(handleGuardCondition(iterator.next()));
    while (iterator.hasNext()) {
      validateGuardAnd(iterator.next());
      result.addCondition(handleGuardCondition(iterator.next()));
    }

    return result;
  }

  public GuardCondition handleGuardCondition(HiddenTokenAwareTree token) {
    Iterator<HiddenTokenAwareTree> iterator = token.getChildren().iterator();
    HiddenTokenAwareTree kid = iterator.next();

    boolean isNegated = false;
    if (kid.getType() != LessLexer.EXPRESSION) {
      validateGuardNegation(kid);
      isNegated = true;
      kid = iterator.next();
    }

    Expression condition = handleExpression(kid);
    if (iterator.hasNext()) {
      HiddenTokenAwareTree operatorToken = iterator.next();
      Expression followingExpression = handleExpression(iterator.next());
      condition = new ComparisonExpression(token, condition, toComparisonOperator(operatorToken), followingExpression);
    }

    return new GuardCondition(token, isNegated, condition);
  }

  private ComparisonExpressionOperator toComparisonOperator(HiddenTokenAwareTree token) {
    switch (token.getType()) {
    case LessLexer.GREATER:
      return new ComparisonExpressionOperator(token, ComparisonExpressionOperator.Operator.GREATER);

    case LessLexer.GREATER_OR_EQUAL:
      return new ComparisonExpressionOperator(token, ComparisonExpressionOperator.Operator.GREATER_OR_EQUAL);

    case LessLexer.OPEQ:
      return new ComparisonExpressionOperator(token, ComparisonExpressionOperator.Operator.OPEQ);

    case LessLexer.LOWER_OR_EQUAL:
      return new ComparisonExpressionOperator(token, ComparisonExpressionOperator.Operator.LOWER_OR_EQUAL);

    case LessLexer.LOWER:
      return new ComparisonExpressionOperator(token, ComparisonExpressionOperator.Operator.LOWER);

    default:
      break;
    }

    throw new BugHappened(GRAMMAR_MISMATCH, token);
  }

  public void validateGuardNegation(HiddenTokenAwareTree token) {
    String operator = token.getText().trim();
    if (!"not".equals(operator))
      throw new BugHappened(GRAMMAR_MISMATCH, token);
  }

  public void validateGuardAnd(HiddenTokenAwareTree token) {
    String operator = token.getText().trim();
    if (!"and".equals(operator))
      throw new BugHappened(GRAMMAR_MISMATCH, token);
  }

  public GeneralBody handleGeneralBody(HiddenTokenAwareTree token) {
    return createGeneralBody(token);
  }

  private List<ASTCssNode> handleBodyMembers(HiddenTokenAwareTree token) {
    if (token.getChildren() == null)
      return new ArrayList<ASTCssNode>();

    List<ASTCssNode> members = new ArrayList<ASTCssNode>();
    Iterator<HiddenTokenAwareTree> iterator = token.getChildren().iterator();

    while (iterator.hasNext()) {
      members.add(switchOn(iterator.next()));
    }
    return members;
  }

  public SyntaxOnlyElement handleLbrace(HiddenTokenAwareTree token) {
    return toSyntaxOnlyElement(token);
  }

  public SyntaxOnlyElement handleRbrace(HiddenTokenAwareTree token) {
    return toSyntaxOnlyElement(token);
  }

  public Selector handleSelector(HiddenTokenAwareTree token) {
    SelectorBuilder builder = new SelectorBuilder(token, this);
    return builder.buildSelector();
  }

  public Extend handleExtendTargetSelector(HiddenTokenAwareTree token) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    Selector selector = (Selector) switchOn(children.get(0));
    if (selector.isExtending()) {
      problemsHandler.warnExtendInsideExtend(selector);
    }

    SelectorPart lastPart = selector.getLastPart();
    if (lastPart == null || !(lastPart instanceof SimpleSelector))
      return new Extend(token, selector);

    SimpleSelector possibleAll = (SimpleSelector) lastPart;
    if (possibleAll.hasSubsequent() || !possibleAll.hasElement())
      return new Extend(token, selector);

    if (!EXTEND_ALL_KEYWORD.equals(possibleAll.getElementName().getName()))
      return new Extend(token, selector);

    if (AstLogic.hasNonSpaceCombinator(possibleAll)) {
      possibleAll.setElementName(null);
    } else {
      selector.getParts().remove(possibleAll);
    }
    return new Extend(token, selector, true);
  }

  public CssClass handleCssClass(HiddenTokenAwareTree token) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    List<HiddenTokenAwareTree> childrenWithoutdot = ArraysUtils.safeSublist(children, 1, children.size());
    return new CssClass(token, toInterpolableName(token, childrenWithoutdot));
  }

  public SelectorAttribute handleSelectorAttribute(HiddenTokenAwareTree token) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    if (children.size() == 0)
      throw new BugHappened(GRAMMAR_MISMATCH, token);

    if (children.size() == 1)
      return new SelectorAttribute(token, children.get(0).getText());

    if (children.size() < 3)
      throw new BugHappened(GRAMMAR_MISMATCH, token);

    Expression value = handleTerm(children.get(2));
    switch (value.getType()) {
    case IDENTIFIER_EXPRESSION:
    case STRING_EXPRESSION:
    case NUMBER:
      //those are OK
      break;

    default:
      problemsHandler.warnLessjsIncompatibleSelectorAttributeValue(value);
      break;
    }
    return new SelectorAttribute(token, children.get(0).getText(), handleSelectorOperator(children.get(1)), value);
  }

  public SelectorOperator handleSelectorOperator(HiddenTokenAwareTree token) {
    return new SelectorOperator(token, toSelectorOperator(token));
  }

  private SelectorOperator.Operator toSelectorOperator(HiddenTokenAwareTree token) {
    switch (token.getType()) {
    case LessLexer.OPEQ:
      return SelectorOperator.Operator.EQUALS;

    case LessLexer.INCLUDES:
      return SelectorOperator.Operator.INCLUDES;

    case LessLexer.DASHMATCH:
      return SelectorOperator.Operator.SPECIAL_PREFIX;

    case LessLexer.PREFIXMATCH:
      return SelectorOperator.Operator.PREFIXMATCH;

    case LessLexer.SUFFIXMATCH:
      return SelectorOperator.Operator.SUFFIXMATCH;

    case LessLexer.SUBSTRINGMATCH:
      return SelectorOperator.Operator.SUBSTRINGMATCH;

    default:
      break;
    }

    throw new BugHappened(GRAMMAR_MISMATCH, token);
  }

  public Pseudo handlePseudo(HiddenTokenAwareTree token) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    if (children.size() == 0 || children.size() == 1)
      throw new BugHappened(GRAMMAR_MISMATCH, token);

    // the child number 0 is a :
    HiddenTokenAwareTree t = children.get(1);
    if (t.getType() == LessLexer.COLON) {
      return createPseudoElement(token, 2, false);
    }

    if (COLONLESS_PSEUDOELEMENTS.contains(t.getText().toLowerCase())) {
      return createPseudoElement(token, 1, true);
    }

    if (children.size() == 2)
      return new PseudoClass(token, children.get(1).getText());

    if (children.size() == 3) {
      HiddenTokenAwareTree parameter = children.get(2);
      //FIXME: this is not really sufficient for all cases less.js supports (1@{num}n+3)
      if (parameter.getType() == LessLexer.INTERPOLATED_VARIABLE)
        return new PseudoClass(token, children.get(1).getText(), toInterpolabledVariable(parameter, parameter.getText()));
      return new PseudoClass(token, children.get(1).getText(), switchOn(parameter));
    }

    throw new BugHappened(GRAMMAR_MISMATCH, token);
  }

  public Nth handleNth(HiddenTokenAwareTree token) {
    Expression first = null;
    Expression second = null;
    if (hasChildren(token.getChild(0))) {
      first = termBuilder.buildFromChildTerm(token.getChild(0));
      String sign = "";
      if (first.getType() == ASTCssNodeType.SIGNED_EXPRESSION) {
        SignedExpression negated = (SignedExpression) first;
        first = negated.getExpression();
        sign = negated.getSign().toSymbol();
      }
      if (first.getType() == ASTCssNodeType.IDENTIFIER_EXPRESSION) {
        IdentifierExpression ident = (IdentifierExpression) first;
        String lowerCaseValue = ident.getValue().toLowerCase();
        lowerCaseValue = sign + lowerCaseValue;
        if ("even".equals(lowerCaseValue)) {
          return new Nth(token, null, null, Form.EVEN);
        } else if ("odd".equals(lowerCaseValue)) {
          return new Nth(token, null, null, Form.ODD);
        } else if ("n".equals(lowerCaseValue) || "-n".equals(lowerCaseValue) || "+n".equals(lowerCaseValue)) {
          boolean expliciteSign = !"n".equals(lowerCaseValue);
          first = new NumberExpression(token.getChild(0), lowerCaseValue, NumberExpression.Dimension.REPEATER, expliciteSign);
        } else
          throw new IllegalStateException("Unexpected identifier value for nth: " + ident.getValue());
      }
    }

    if (token.getChild(1) != null && hasChildren(token.getChild(1))) {
      second = termBuilder.buildFromChildTerm(token.getChild(1));
    }

    return new Nth(token, (NumberExpression) first, (NumberExpression) second);
  }

  private boolean hasChildren(HiddenTokenAwareTree token) {
    return token.getChildren() != null && !token.getChildren().isEmpty();
  }

  private PseudoElement createPseudoElement(HiddenTokenAwareTree token, int startIndex, boolean level12Form) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    String name = children.get(startIndex).getText();
    return new PseudoElement(token, name, level12Form);
  }

  public IdSelector handleIdSelector(HiddenTokenAwareTree token) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    return new IdSelector(token, toInterpolableName(token, children));
  }

  private InterpolableName toInterpolableName(HiddenTokenAwareTree token, List<HiddenTokenAwareTree> children) {
    InterpolableName result = new InterpolableName(token);
    for (HiddenTokenAwareTree kid : children) {
      String text = kid.getText();
      if (text == null || text.length() < 1)
        throw new BugHappened(GRAMMAR_MISMATCH, kid);

      if (kid.getType() == LessLexer.INTERPOLATED_VARIABLE) {
        result.add(new VariableNamePart(kid, toInterpolabledVariable(kid, text)));
      } else if (kid.getType() == LessLexer.HASH_SYMBOL) {
        // do nothing
      } else {
        result.add(new FixedNamePart(kid, toFixedName(kid.getType(), text)));
      }
    }
    return result;
  }

  private Variable toInterpolabledVariable(HiddenTokenAwareTree token, String text) {
    return new Variable(token, "@" + text.substring(2, text.length() - 1), true);
  }

  private String toFixedName(int typeCode, String text) {
    if (typeCode == LessLexer.HASH)
      return text.substring(1);

    return text;
  }

  public Media handleMedia(HiddenTokenAwareTree token) {
    Iterator<HiddenTokenAwareTree> children = token.getChildren().iterator();

    Media result = new Media(token);
    handleMediaDeclaration(result, children.next());
    result.setBody(handleGeneralBody(children.next()));

    return result;
  }

  private void handleMediaDeclaration(Media result, HiddenTokenAwareTree declaration) {
    declaration.pushHiddenToKids();
    List<HiddenTokenAwareTree> children = declaration.getChildren();

    MediaQuery previousKid = null;
    for (HiddenTokenAwareTree kid : children) {
      if (kid.getType() == LessLexer.COMMA) {
        previousKid.getUnderlyingStructure().addFollowing(kid.getPreceding());
      } else {
        previousKid = handleMediaQuery(kid);
        result.addMediaQuery(previousKid);
      }
    }
  }

  public MediaQuery handleMediaQuery(HiddenTokenAwareTree token) {
    MediaQuery result = new MediaQuery(token);
    List<HiddenTokenAwareTree> originalChildren = token.getChildren();

    // each AND identifier may hold preceding comments must be pushed to other
    // tokens
    LinkedList<HiddenTokenAwareTree> children = new LinkedList<HiddenTokenAwareTree>();
    for (HiddenTokenAwareTree kid : originalChildren) {
      if (kid.getType() == LessLexer.IDENT) {
        HiddenTokenAwareTree lastKid = children.peekLast();
        if (lastKid != null) {
          lastKid.addFollowing(kid.getPreceding());
        }
      } else {
        children.add(kid);
      }
    }
    // we have three types of children:
    // * MEDIUM_TYPE
    // * identifier AND whose only function is to hold comments
    // * FIXED_MEDIA_EXPRESSION
    for (HiddenTokenAwareTree kid : children) {
      if (kid.getType() != LessLexer.IDENT) {
        result.addMember(switchOn(kid));
      } else {
        // we have to copy comments from the AND identifier to surrounding
        // elements.
      }
    }
    return result;
  }

  public Medium handleMedium(HiddenTokenAwareTree token) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    if (children.size() == 1) {
      HiddenTokenAwareTree type = children.get(0);
      return new Medium(token, new MediumModifier(type), new MediumType(type, type.getText()));
    }

    HiddenTokenAwareTree type = children.get(1);
    return new Medium(token, toMediumModifier(children.get(0)), new MediumType(type, type.getText()));
  }

  public FixedMediaExpression handleMediaExpression(HiddenTokenAwareTree token) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    HiddenTokenAwareTree featureNode = children.get(0);
    if (children.size() == 1)
      return new FixedMediaExpression(token, new MediaExpressionFeature(featureNode, featureNode.getText()), null);

    if (children.size() == 2)
      throw new BugHappened(GRAMMAR_MISMATCH, token);

    HiddenTokenAwareTree colonNode = children.get(1);
    featureNode.addFollowing(colonNode.getPreceding());

    HiddenTokenAwareTree expressionNode = children.get(2);
    Expression expression = (Expression) switchOn(expressionNode);
    return new FixedMediaExpression(token, new MediaExpressionFeature(featureNode, featureNode.getText()), expression);
  }

  public InterpolatedMediaExpression handleInterpolatedMediaExpression(HiddenTokenAwareTree token) {
    Iterator<HiddenTokenAwareTree> children = token.getChildren().iterator();
    List<Expression> expressions = new ArrayList<Expression>();
       
    while (children.hasNext()) {
      Variable expression = (Variable) switchOn(children.next());
      expressions.add(expression);
    }
    ListExpression list = new ListExpression(token, expressions, new ListExpressionOperator(token, ListExpressionOperator.Operator.EMPTY_OPERATOR));
    return new InterpolatedMediaExpression(token, list);
  }

  private MediumModifier toMediumModifier(HiddenTokenAwareTree token) {
    String modifier = token.getText().toLowerCase();

    if ("not".equals(modifier))
      return new MediumModifier(token, MediumModifier.Modifier.NOT);

    if ("only".equals(modifier))
      return new MediumModifier(token, MediumModifier.Modifier.ONLY);

    throw new IllegalStateException("Unexpected medium modifier: " + modifier);
  }

  // this handles both variable declaration and a mixin parameter with default
  // value
  public VariableDeclaration handleVariableDeclaration(HiddenTokenAwareTree token) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    HiddenTokenAwareTree name = children.get(0);
    HiddenTokenAwareTree colon = children.get(1);
    HiddenTokenAwareTree expression = children.get(2);
    if (children.size() > 3) {
      HiddenTokenAwareTree semi = children.get(3);
      colon.giveHidden(name, expression);
      semi.giveHidden(expression, null);
      token.addBeforeFollowing(semi.getFollowing());
    }

    Variable variable = new Variable(name, name.getText());
    Expression value = variableValue(token, expression);
    return new VariableDeclaration(token, variable, value);
  }

  private Expression variableValue(HiddenTokenAwareTree underlyingIfEmpty, HiddenTokenAwareTree expression) {
    if (expression.getType()==LessLexer.SEMI) {
      return new EmptyExpression(underlyingIfEmpty);
    }
   
    return (Expression) switchOn(expression);
  }

  // FIXME: fail on wrong distribution of arguments (e.g. collector must be
  // last, those with default must be first)
  public ArgumentDeclaration handleArgumentDeclaration(HiddenTokenAwareTree token) {
    List<HiddenTokenAwareTree> children = token.getChildren();
    HiddenTokenAwareTree firstChild = children.get(0);
    if (firstChild.getType() == LessLexer.DOT3)
      return new ArgumentDeclaration(firstChild, new Variable(firstChild, "@"), null, true);

    HiddenTokenAwareTree name = firstChild;

    if (children.size() == 1)
      return new ArgumentDeclaration(token, new Variable(name, name.getText()), null);

    HiddenTokenAwareTree separator = children.get(1);
    if (separator.getType() == LessLexer.DOT3) {
      return new ArgumentDeclaration(token, new Variable(name, name.getText()), null, true);
    }
    HiddenTokenAwareTree expression = children.get(2);
    separator.giveHidden(name, expression);

    return new ArgumentDeclaration(token, new Variable(name, name.getText()), (Expression) switchOn(expression));
  }

  @Override
  public ASTCssNode handleNestedAppender(HiddenTokenAwareTree token) {
    boolean directlyBefore = true;
    boolean directlyAfter = true;
    if (token.getChildren().size() == 2) {
      directlyBefore = isMeaningfullWhitespace(token);
      directlyAfter = !directlyBefore;
    } else if (token.getChildren().size() == 3) {
      directlyBefore = false;
      directlyAfter = false;
    }

    return new NestedSelectorAppender(token, directlyBefore, directlyAfter, null);
  }

  public ASTCssNode handleSimpleSelector(HiddenTokenAwareTree token) {
    SimpleSelector result = null;
    token.pushHiddenToKids();
    Iterator<HiddenTokenAwareTree> iterator = token.getChildren().iterator();
    HiddenTokenAwareTree kid = iterator.next();
    if (kid.getType() == LessLexer.ELEMENT_NAME) {
      List<HiddenTokenAwareTree> elementNameParts = kid.getChildren();
      InterpolableName interpolableName = toInterpolableName(kid, elementNameParts);
      result = new SimpleSelector(kid, null, interpolableName, isStarElementName(elementNameParts));
      if (!iterator.hasNext())
        return result;
      kid = iterator.next();
    } else {
      result = new SimpleSelector(kid, null, null, true);
      result.setEmptyForm(true);
    }

    addSubsequent(result, kid);
    while (iterator.hasNext()) {
      kid = iterator.next();
      addSubsequent(result, kid);
    }

    return result;
  }

  private void addSubsequent(SimpleSelector result, HiddenTokenAwareTree kid) {
    ElementSubsequent subsequent = (ElementSubsequent) switchOn(kid);
    result.addSubsequent(subsequent);
  }

  private boolean isStarElementName(List<HiddenTokenAwareTree> elementNameParts) {
    if (elementNameParts.size() != 1)
      return false;

    return elementNameParts.get(0).getType() == LessLexer.STAR;
  }

  public EscapedSelector handleEscapedSelector(HiddenTokenAwareTree token) {
    token.pushHiddenToKids();
    HiddenTokenAwareTree valueToken = token.getChild(0);
    String quotedText = valueToken.getText();
    return new EscapedSelector(valueToken, quotedText.substring(2, quotedText.length() - 1), "" + quotedText.charAt(1), null);
  }

  private boolean isMeaningfullWhitespace(HiddenTokenAwareTree kid) {
    int type = kid.getChild(0).getType();
    return type == LessLexer.MEANINGFULL_WHITESPACE || type == LessLexer.DUMMY_MEANINGFULL_WHITESPACE;
  }

  @Override
  public Keyframes handleKeyframes(HiddenTokenAwareTree token) {
    Iterator<HiddenTokenAwareTree> children = token.getChildren().iterator();
    Keyframes result = new Keyframes(token, children.next().getText());
    result.addNames(handleKeyframesDeclaration(children.next()));
    result.setBody(handleGeneralBody(children.next()));

    return result;
  }

  @Override
  public Supports handleSupports(HiddenTokenAwareTree token) {
    Iterator<HiddenTokenAwareTree> children = token.getChildren().iterator();
    Supports result = new Supports(token, children.next().getText());
    result.setCondition((SupportsCondition) switchOn(children.next()));
    result.setBody(handleGeneralBody(children.next()));

    return result;
  }

  public SupportsCondition handleSupportsCondition(HiddenTokenAwareTree token) {
    token.pushHiddenToKids();
    Iterator<HiddenTokenAwareTree> children = token.getChildren().iterator();
    if (token.getChildCount() == 1)
      return (SupportsCondition) switchOn(children.next());

    SupportsLogicalCondition result = new SupportsLogicalCondition(token, (SupportsCondition) switchOn(children.next()));
    while (children.hasNext()) {
      SupportsLogicalOperator logicalOperator = toSupportsLogicalOperator(children.next());
      if (!children.hasNext())
        throw new BugHappened(GRAMMAR_MISMATCH, token);
      SupportsCondition condition = (SupportsCondition) switchOn(children.next());
      result.addCondition(logicalOperator, condition);
    }
    return result;
  }

  private SupportsLogicalOperator toSupportsLogicalOperator(HiddenTokenAwareTree token) {
    String text = token.getText();
    Map<String, Operator> operatorsBySymbol = SupportsLogicalOperator.Operator.getSymbolsMap();
    if (text == null || !operatorsBySymbol.containsKey(text.toLowerCase())) {
      SupportsLogicalOperator result = new SupportsLogicalOperator(token, null);
      problemsHandler.errWrongSupportsLogicalOperator(result, token.getText());
      return result;
    }

    SupportsLogicalOperator result = new SupportsLogicalOperator(token, operatorsBySymbol.get(text.toLowerCase()));
    return result;
  }

  public SupportsCondition handleSupportsSimpleCondition(HiddenTokenAwareTree token) {
    token.pushHiddenToKids();
    Iterator<HiddenTokenAwareTree> children = token.getChildren().iterator();
    HiddenTokenAwareTree first = children.next();
    if (first.getType() == LessLexer.LPAREN) {
      SyntaxOnlyElement openingParentheses = toSyntaxOnlyElement(first);
      SupportsCondition condition = (SupportsCondition) switchOn(children.next());
      SyntaxOnlyElement closingParentheses = toSyntaxOnlyElement(children.next());

      SupportsConditionInParentheses result = new SupportsConditionInParentheses(token, openingParentheses, condition, closingParentheses);
      return result;
    } else if (first.getType() == LessLexer.IDENT) {
      //TODO: warning on wrong operator (anything that is not 'not')
      SyntaxOnlyElement negation = toSyntaxOnlyElement(first);
      SupportsCondition condition = (SupportsCondition) switchOn(children.next());

      SupportsConditionNegation result = new SupportsConditionNegation(token, negation, condition);
      return result;
    }

    return (SupportsCondition) switchOn(first);
  }

  public SupportsCondition handleSupportsQuery(HiddenTokenAwareTree token) {
    Iterator<HiddenTokenAwareTree> children = token.getChildren().iterator();
    SyntaxOnlyElement openingParentheses = toSyntaxOnlyElement(children.next());
    Declaration declaration = (Declaration) switchOn(children.next());
    SyntaxOnlyElement closingParentheses = toSyntaxOnlyElement(children.next());
    SupportsQuery result = new SupportsQuery(token, openingParentheses, closingParentheses, declaration);
    return result;
  }

  private SyntaxOnlyElement toSyntaxOnlyElement(HiddenTokenAwareTree token) {
    return new SyntaxOnlyElement(token, token.getText());
  }

  @Override
  public Document handleDocument(HiddenTokenAwareTree token) {
    Iterator<HiddenTokenAwareTree> children = token.getChildren().iterator();
    Document result = new Document(token, children.next().getText());
    result.addUrlMatchFunctions(handleUrlMatchFunctionsDeclaration(children.next()));
    result.setBody(handleGeneralBody(children.next()));

    return result;
  }

  private List<FunctionExpression> handleUrlMatchFunctionsDeclaration(HiddenTokenAwareTree declaration) {
    List<FunctionExpression> result = new ArrayList<FunctionExpression>();
    Iterator<HiddenTokenAwareTree> iterator = declaration.getChildren().iterator();
    while (iterator.hasNext()) {
      HiddenTokenAwareTree token = iterator.next();
      if (token.getType() == LessLexer.COMMA) {
        token.pushHiddenToSiblings();
      } else {
        ASTCssNode urlMatchFunction = switchOn(token);
        if (urlMatchFunction.getType() == ASTCssNodeType.FUNCTION)
          result.add((FunctionExpression) termBuilder.buildFromChildTerm(token));
        else
          throw new BugHappened(GRAMMAR_MISMATCH, token);
      }

    }

    return result;
  }

  @Override
  public Viewport handleViewport(HiddenTokenAwareTree token) {
    Viewport result = new Viewport(token, token.getChild(0).getText());
    HiddenTokenAwareTree body = token.getChild(1);
    result.setBody(createGeneralBody(body));
    return result;
  }

  private GeneralBody createGeneralBody(HiddenTokenAwareTree token) {
    List<ASTCssNode> list = handleBodyMembers(token);

    if (list.size() < 2)
      throw new BugHappened(GRAMMAR_MISMATCH, token);

    SyntaxOnlyElement lbrace = (SyntaxOnlyElement) list.remove(0);
    SyntaxOnlyElement rbrace = (SyntaxOnlyElement) list.remove(list.size() - 1);
    List<CommonToken> orphansOrFollowLastMember = rbrace.getUnderlyingStructure().chopPreceedingUpToLastOfType(LessLexer.NEW_LINE);
    if (list.isEmpty()) {
      token.addOrphans(orphansOrFollowLastMember);
    } else {
      list.get(list.size() - 1).getUnderlyingStructure().addFollowing(orphansOrFollowLastMember);
    }
    return new GeneralBody(token, lbrace, rbrace, list);
  }

  private List<KeyframesName> handleKeyframesDeclaration(HiddenTokenAwareTree declaration) {
    List<KeyframesName> result = new ArrayList<KeyframesName>();
    Iterator<HiddenTokenAwareTree> iterator = declaration.getChildren().iterator();
    while (iterator.hasNext()) {
      HiddenTokenAwareTree token = iterator.next();
      if (token.getType() == LessLexer.COMMA) {
        token.pushHiddenToSiblings();
      } else if (token.getType() == LessLexer.IDENT || token.getType() == LessLexer.AT_NAME || token.getType()==LessLexer.INDIRECT_VARIABLE) {
        result.add(new KeyframesName(token.commentsLessClone(), termBuilder.buildFromTerm(token)));
      } else {
        throw new BugHappened(GRAMMAR_MISMATCH, token);
      }

    }

    return result;
  }

  public Page handlePage(HiddenTokenAwareTree token) {
    Page result = new Page(token);
    List<HiddenTokenAwareTree> children = token.getChildren();
    for (HiddenTokenAwareTree kid : children) {
      if (kid.getType() == LessLexer.IDENT) {
        result.setName(new Name(kid, kid.getText()));
      } else if (kid.getType() == LessLexer.PSEUDO_PAGE) {
        int pseudoPageIndex = 1;
        if (kid.getChild(0).getType() == LessLexer.MEANINGFULL_WHITESPACE) {
          pseudoPageIndex = 2;
          result.setDockedPseudopage(false);
        }
        result.setPseudopage(new Name(kid, ":" + kid.getChild(pseudoPageIndex).getText()));
      } else if (kid.getType() == LessLexer.BODY) {
        result.setBody(createGeneralBody(kid));
      } else {
        throw new BugHappened(GRAMMAR_MISMATCH, kid);
      }
    }
    return result;
  }

  public PageMarginBox handlePageMarginBox(HiddenTokenAwareTree token) {
    PageMarginBox result = new PageMarginBox(token);
    List<HiddenTokenAwareTree> children = token.getChildren();
    for (HiddenTokenAwareTree kid : children) {
      if (kid.getType() == LessLexer.AT_NAME) {
        result.setName(new Name(kid, kid.getText()));
      } else if (kid.getType() == LessLexer.BODY) {
        result.setBody(createGeneralBody(kid));
      } else {
        throw new BugHappened(GRAMMAR_MISMATCH, kid);
      }
    }
    return result;
  }

  public Import handleImport(HiddenTokenAwareTree token) {
    Import result = new Import(token);
    switch (token.getType()) {
    case LessLexer.IMPORT_SYM:
      result.setMultiplicity(Import.ImportMultiplicity.IMPORT);
      break;
    case LessLexer.IMPORT_ONCE_SYM:
      result.setMultiplicity(Import.ImportMultiplicity.IMPORT_ONCE);
      problemsHandler.deprecatedImportOnce(result);
      break;
    case LessLexer.IMPORT_MULTIPLE_SYM:
      result.setMultiplicity(Import.ImportMultiplicity.IMPORT_MULTIPLE);
      problemsHandler.deprecatedImportMultiple(result);
      break;
    default:
      throw new BugHappened(GRAMMAR_MISMATCH, token);
    }
    Iterator<HiddenTokenAwareTree> children = token.getChildren().iterator();
    HiddenTokenAwareTree nextChild = children.next();
    if (nextChild.getType() == LessLexer.IMPORT_OPTIONS) {
      configureImportOptions(result, nextChild.getChildren());
      nextChild = children.next();
    }

    result.setUrlExpression(handleTerm(nextChild));
    while (children.hasNext()) {
      HiddenTokenAwareTree kid = children.next();
      if (kid.getType() == LessLexer.COMMA) {
        kid.pushHiddenToSiblings();
      } else if (kid.getType() == LessLexer.MEDIA_QUERY) {
        result.add(handleMediaQuery(kid));
      } else {
        throw new BugHappened(GRAMMAR_MISMATCH, token);
      }
    }

    return result;
  }

  private void configureImportOptions(Import node, List<HiddenTokenAwareTree> options) {
    for (HiddenTokenAwareTree token : options) {
      String text = token.getText();
      if (IMPORT_OPTION_INLINE.equals(text)) {
        node.setInline(true);
      } else if (IMPORT_OPTION_ONCE.equals(text)) {
        node.setMultiplicity(Import.ImportMultiplicity.IMPORT_ONCE);
      } else if (IMPORT_OPTION_MULTIPLE.equals(text)) {
        node.setMultiplicity(Import.ImportMultiplicity.IMPORT_MULTIPLE);
      } else if (IMPORT_OPTION_LESS.equals(text)) {
        node.setContentKind(ImportContent.LESS);
      } else if (IMPORT_OPTION_CSS.equals(text)) {
        node.setContentKind(ImportContent.CSS);
      } else if (IMPORT_OPTION_REFERENCE.equals(text)) {
        node.setReferenceOnly(true);
      } else {
        problemsHandler.unknownImportOption(node, text);
      }
    }
  }

  public NamedExpression handleNamedExpression(HiddenTokenAwareTree token) {
    HiddenTokenAwareTree nameToken = token.getChild(0);
    HiddenTokenAwareTree valueToken = token.getChild(1);
    Expression value = (Expression) switchOn(valueToken);

    return new NamedExpression(token, nameToken.getText(), value);

  }

  @Override
  public Extend handleExtendInDeclaration(HiddenTokenAwareTree token) {
    HiddenTokenAwareTree child = token.getChild(0);
    Pseudo extendAsPseudo = handlePseudo(child);

    if (!(extendAsPseudo instanceof PseudoClass))
      throw new BugHappened(GRAMMAR_MISMATCH, extendAsPseudo);

    PseudoClass asPseudoclass = (PseudoClass) extendAsPseudo;
    if (!(asPseudoclass.getParameter() instanceof Extend))
      throw new BugHappened(GRAMMAR_MISMATCH, extendAsPseudo);

    return (Extend) asPseudoclass.getParameter();
  }

  @Override
  public UnknownAtRule handleUnknownAtRule(HiddenTokenAwareTree token) {
    Iterator<HiddenTokenAwareTree> children = token.getChildren().iterator();
    UnknownAtRule result = new UnknownAtRule(token, children.next().getText());
    while (children.hasNext()) {
      HiddenTokenAwareTree next = children.next();
      switch (next.getType()) {
      case LessLexer.UNKNOWN_AT_RULE_NAMES_SET:
        result.addNames(handleUnknownAtRuleDeclaration(next));
        break;

      case LessLexer.BODY:
        result.setBody(handleGeneralBody(next));
        break;

      case LessLexer.SEMI:
        result.setSemicolon(toSyntaxOnlyElement(next));
        break;

      default:
        throw new BugHappened(GRAMMAR_MISMATCH, next);
      }
     
    }
    problemsHandler.warnUnknowAtRule(result);
    return result;
  }

  private List<Expression> handleUnknownAtRuleDeclaration(HiddenTokenAwareTree declaration) {
    List<Expression> result = new ArrayList<Expression>();
    Iterator<HiddenTokenAwareTree> iterator = declaration.getChildren().iterator();
    while (iterator.hasNext()) {
      HiddenTokenAwareTree token = iterator.next();
      if (token.getType() == LessLexer.COMMA) {
        token.pushHiddenToSiblings();
      } else {
        result.add(handleExpression(token));
      }
    }

    return result;
  }

}
TOP

Related Classes of com.github.sommeri.less4j.core.parser.ASTBuilderSwitch

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.