Skip to content

Commit

Permalink
Refactoring to prepare for #186 - removing guards logic from MixinsSo…
Browse files Browse the repository at this point in the history
…lver
  • Loading branch information
meri committed Apr 14, 2014
1 parent 8c015b8 commit 22cc81d
Show file tree
Hide file tree
Showing 16 changed files with 341 additions and 170 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.github.sommeri.less4j.core.compiler.expressions;

import com.github.sommeri.less4j.core.compiler.stages.MixinCompilationResult;
import com.github.sommeri.less4j.utils.ArraysUtils.Filter;

public enum GuardValue {
USE, DO_NOT_USE, USE_IF_DEFAULT, USE_IF_NOT_DEFAULT;

public Filter<MixinCompilationResult> filter() {
return new DefaultFunctionUseFilter(this);
}
}

class DefaultFunctionUseFilter implements Filter<MixinCompilationResult> {

private final GuardValue value;

public DefaultFunctionUseFilter(GuardValue value) {
super();
this.value = value;
}

@Override
public boolean accept(MixinCompilationResult t) {
return t.getGuardValue().equals(value);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,4 +31,28 @@ public DefaultPoweredExpressionEvaluator(IScope scope, ProblemsHandler problemsH

}

public GuardValue evaluateGuards(ReusableStructure mixin) {
boolean ifDefaultGuardValue = guardsSatisfied(mixin, true);
boolean ifNotDefaultGuardValue = guardsSatisfied(mixin, false);
return toDefaultFunctionUse(ifDefaultGuardValue, ifNotDefaultGuardValue);
}

/**
* Re-implementing less.js heuristic. If guards value does not depend on default value, then less.js
* assumes the default was not used. It does not check whether the default function was really used, so
* this: not(default()), (default()) can be used multiple times.
*/
private GuardValue toDefaultFunctionUse(boolean ifDefaultGuardValue, boolean ifNotDefaultGuardValue) {
if (ifDefaultGuardValue && ifNotDefaultGuardValue) {//default was NOT used
return GuardValue.USE;
} else if (!ifDefaultGuardValue && !ifNotDefaultGuardValue) {//default was NOT used
return GuardValue.DO_NOT_USE;
} else if (ifDefaultGuardValue) {//default is required
return GuardValue.USE_IF_DEFAULT;
} else {//if (must not be default)
return GuardValue.USE_IF_NOT_DEFAULT;
}//
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package com.github.sommeri.less4j.core.compiler.scopes;

import com.github.sommeri.less4j.core.ast.DetachedRuleset;

public class FullDetachedRulesetDefinition {
private final DetachedRuleset detached;
private final IScope bodyScope;

public FullDetachedRulesetDefinition(DetachedRuleset detached, IScope mixinsBodyScope) {
super();
this.detached = detached;
this.bodyScope = mixinsBodyScope;
}

public DetachedRuleset getDetached() {
return detached;
}

public IScope getScope() {
return bodyScope;
}

@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("FullDetachedDefinition [detached [");
builder.append(detached.getSourceLine()).append(":").append(detached.getSourceColumn());
builder.append("], bodyScope=");
builder.append(bodyScope);
builder.append("]");
return builder.toString();
}

}
Original file line number Diff line number Diff line change
@@ -1,17 +1,25 @@
package com.github.sommeri.less4j.core.compiler.scopes.local;

import java.util.HashMap;
import java.util.Map;

import com.github.sommeri.less4j.core.ast.DetachedRuleset;
import com.github.sommeri.less4j.core.compiler.scopes.FullDetachedRulesetDefinition;


public class LocalScopeData implements Cloneable {

private VariablesDeclarationsStorage variables = new VariablesDeclarationsStorage();
private MixinsDefinitionsStorage mixins = new MixinsDefinitionsStorage();
private Map<DetachedRuleset, FullDetachedRulesetDefinition> detachedRulesetsScopes = new HashMap<DetachedRuleset, FullDetachedRulesetDefinition>();

@Override
public LocalScopeData clone() {
try {
LocalScopeData clone = (LocalScopeData) super.clone();
clone.variables = variables.clone();
clone.mixins = mixins.clone();
clone.detachedRulesetsScopes = new HashMap<DetachedRuleset, FullDetachedRulesetDefinition>(detachedRulesetsScopes);
return clone;

} catch (CloneNotSupportedException e) {
Expand All @@ -32,6 +40,7 @@ public String toString() {
StringBuilder result = new StringBuilder(getClass().getSimpleName()).append("\n");
result.append("**Variables storage: ").append(variables).append("\n\n");
result.append("**Mixins storage: ").append(mixins).append("\n\n");
result.append("**DetachedRulesets size: ").append(detachedRulesetsScopes.size()).append("\n\n");
return result.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
package com.github.sommeri.less4j.core.compiler.stages;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import com.github.sommeri.less4j.core.ast.MixinReference;
import com.github.sommeri.less4j.core.ast.ReusableStructure;
import com.github.sommeri.less4j.core.compiler.expressions.GuardValue;
import com.github.sommeri.less4j.core.problems.BugHappened;
import com.github.sommeri.less4j.core.problems.ProblemsHandler;
import com.github.sommeri.less4j.utils.ArraysUtils;

public class DefaultGuardHelper {

private final ProblemsHandler problemsHandler;

public DefaultGuardHelper(ProblemsHandler problemsHandler) {
this.problemsHandler = problemsHandler;
}

public List<MixinCompilationResult> chooseMixinsToBeUsed(List<MixinCompilationResult> compiledMixins, final MixinReference reference) {
// count how many mixins of each kind we encountered
int normalMixinsCnt = ArraysUtils.count(compiledMixins, GuardValue.USE.filter());
int ifNotCnt = ArraysUtils.count(compiledMixins, GuardValue.USE_IF_NOT_DEFAULT.filter());
int ifDefaultCnt = ArraysUtils.count(compiledMixins, GuardValue.USE_IF_DEFAULT.filter());

//sanity check - could be removed - keeping only for debugging purposes
if (normalMixinsCnt+ifNotCnt+ifDefaultCnt!=compiledMixins.size())
throw new BugHappened("Unexpected mixin type in compiled mixins list.", reference);

// We know now that default() value is false. We do not care whether there was some potentional ambiguity or not and return anything that is not default.
if (normalMixinsCnt > 0) {
return keepOnly(compiledMixins, GuardValue.USE, GuardValue.USE_IF_NOT_DEFAULT);
}

//there are multiple mixins using default() function and nothing else - that is ambiguous (period).
if (ifDefaultCnt+ifNotCnt > 1) {
List<MixinCompilationResult> errorSet = keepOnly(compiledMixins, GuardValue.USE_IF_DEFAULT,GuardValue.USE_IF_NOT_DEFAULT);
problemsHandler.ambiguousDefaultSet(reference, extractOriginalMixins(errorSet));
//no mixins are going to be used
return Collections.emptyList();
}

//now we know that default function returns true
return keepOnly(compiledMixins, GuardValue.USE_IF_DEFAULT);
}

/**
* Removes all comiled mixins from compiledMixins list with wrong use of default function.
* Warning: Modifies the compiledMixins list.
* @param compiledMixins - list of compiled mixins - will be modified.
* @param kind - types of mixins that are going to stay.
* @return compiledMixins - for convenience
*/
private List<MixinCompilationResult> keepOnly(List<MixinCompilationResult> compiledMixins, GuardValue... kind) {
Set<GuardValue> expectedUses = ArraysUtils.asSet(kind);
Iterator<MixinCompilationResult> iterator = compiledMixins.iterator();
while (iterator.hasNext()) {
MixinCompilationResult compiled = iterator.next();
if (!expectedUses.contains(compiled.getGuardValue())) {
iterator.remove();
}
}
return compiledMixins;
}

private List<ReusableStructure> extractOriginalMixins(List<MixinCompilationResult> compiledMixins) {
List<ReusableStructure> result = new ArrayList<ReusableStructure>();
for (MixinCompilationResult compiled : compiledMixins) {
result.add(compiled.getMixin());
}
return result;
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.github.sommeri.less4j.core.compiler.stages;

import java.util.List;

import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.ReusableStructure;
import com.github.sommeri.less4j.core.compiler.expressions.GuardValue;
import com.github.sommeri.less4j.core.compiler.scopes.IScope;
import com.github.sommeri.less4j.utils.ArraysUtils;

public class MixinCompilationResult {

private ReusableStructure mixin;
private List<ASTCssNode> replacement;
private IScope returnValues;
private GuardValue guardValue;

public MixinCompilationResult(ReusableStructure mixin, List<ASTCssNode> replacement, IScope returnValues) {
this.mixin = mixin;
this.replacement = replacement;
this.returnValues = returnValues;
}

public void setGuardValue(GuardValue guardValue) {
this.guardValue = guardValue;
}

public GuardValue getGuardValue() {
return guardValue;
}

public List<ASTCssNode> getReplacement() {
return replacement;
}

public void setReplacement(List<ASTCssNode> replacement) {
this.replacement = replacement;
}

public IScope getReturnValues() {
return returnValues;
}

public void setReturnValues(IScope returnValues) {
this.returnValues = returnValues;
}

public ReusableStructure getMixin() {
return mixin;
}

public void setMixin(ReusableStructure mixin) {
this.mixin = mixin;
}

@Override
protected MixinCompilationResult clone() {
return new MixinCompilationResult(mixin.clone(), ArraysUtils.deeplyClonedList(replacement), returnValues);
}

}
Loading

0 comments on commit 22cc81d

Please sign in to comment.