Skip to content

Commit

Permalink
Added basic support for mixin argument expansion - elipsis #288
Browse files Browse the repository at this point in the history
  • Loading branch information
meri committed Sep 24, 2015
1 parent e2573c1 commit 095efc7
Show file tree
Hide file tree
Showing 13 changed files with 173 additions and 81 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,16 @@ public void setValue(Expression value) {
this.value = value;
}

@NotAstProperty
public boolean isCollector() {
return getVariable().isCollector();
}

@NotAstProperty
public void setCollector(boolean collector) {
getVariable().setCollector(collector);
}

@Override
@NotAstProperty
public List<? extends ASTCssNode> getChilds() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package com.github.sommeri.less4j.core.ast;

import com.github.sommeri.less4j.core.ast.annotations.NotAstProperty;
import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;

public class ArgumentDeclaration extends AbstractVariableDeclaration {
Expand All @@ -17,16 +16,6 @@ public ArgumentDeclaration(Variable variable, Expression value) {
this(variable.getUnderlyingStructure(), variable, value);
}

@NotAstProperty
public boolean isCollector() {
return getVariable().isCollector();
}

@NotAstProperty
public void setCollector(boolean collector) {
getVariable().setCollector(collector);
}

@Override
public ASTCssNodeType getType() {
return ASTCssNodeType.ARGUMENT_DECLARATION;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.IdentifierExpression;
import com.github.sommeri.less4j.core.ast.KeywordExpression;
import com.github.sommeri.less4j.core.ast.ListExpression;
import com.github.sommeri.less4j.core.ast.ListExpressionOperator;
Expand All @@ -17,14 +16,17 @@ public class ExpressionManipulator {

public Expression findRightmostListedExpression(Expression expression) {
Expression result = expression;
while (result.getType() == ASTCssNodeType.LIST_EXPRESSION) {
while (result!=null && result.getType() == ASTCssNodeType.LIST_EXPRESSION) {
ListExpression parentList = (ListExpression) result;
result = ArraysUtils.last(parentList.getExpressions());
}
return result;
}

public ListExpression findRightmostSpaceSeparatedList(Expression expression) {
if (expression==null)
return null;

ListExpression result = null;
Expression rightmost = expression;
while (rightmost.getType() == ASTCssNodeType.LIST_EXPRESSION) {
Expand Down Expand Up @@ -109,9 +111,6 @@ private boolean sameListOperator(ListExpression list, ListExpression sublist) {

//FIXME: should probably deep clone allArguments and setup parent child relationships
public Expression joinAll(List<Expression> allArguments, ASTCssNode parent) {
if (allArguments.isEmpty())
return new IdentifierExpression(parent.getUnderlyingStructure(), "");

return new ListExpression(parent.getUnderlyingStructure(), allArguments, new ListExpressionOperator(parent.getUnderlyingStructure(), ListExpressionOperator.Operator.EMPTY_OPERATOR));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class TypeFunctions extends BuiltInFunctionsPack {
protected static final String ISEM = "isem";
protected static final String ISURL = "isurl";
protected static final String ISUNIT = "isunit";
protected static final String ISLIST = "islist";

private static Map<String, Function> FUNCTIONS = new HashMap<String, Function>();
static {
Expand All @@ -38,6 +39,7 @@ public class TypeFunctions extends BuiltInFunctionsPack {
FUNCTIONS.put(ISEM, new IsEm());
FUNCTIONS.put(ISURL, new IsUrl());
FUNCTIONS.put(ISUNIT, new IsUnit());
FUNCTIONS.put(ISLIST, new IsList());
}

public TypeFunctions(ProblemsHandler problemsHandler) {
Expand Down Expand Up @@ -69,6 +71,15 @@ protected boolean checkType(Expression parameter) {

}

class IsList extends AbstractTypeFunction {

@Override
protected boolean checkType(Expression parameter) {
return parameter.getType() == ASTCssNodeType.LIST_EXPRESSION;
}

}

class IsKeyword extends AbstractTypeFunction {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ public Expression getValue(String name) {
Expression value = getLocalValue(name);

if (value == null && hasParent()) {
//System.out.println("searching parent: " + getParent());
value = getParent().getValue(name);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public void toIndependentWorkingCopy() {
}

public void toIndependentWorkingCopyAllParents() {
//System.out.println(" +++ saving: " + this);
this.saveableLocalScope.save();

if (hasParent()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
package com.github.sommeri.less4j.core.compiler.stages;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.ListExpression;
import com.github.sommeri.less4j.core.ast.MixinReference;
import com.github.sommeri.less4j.core.ast.Variable;
import com.github.sommeri.less4j.core.compiler.expressions.ExpressionEvaluator;
Expand All @@ -22,14 +25,39 @@ public EvaluatedMixinReferenceCall(MixinReference reference, ExpressionEvaluator
this.reference = reference;

for (Expression expression : reference.getPositionalParameters()) {
positionalParameters.add(evaluator.evaluate(expression));
Expression value = evaluator.evaluate(expression);
addPositional(expression, value);
}

for (Entry<String, Expression> entry : reference.getNamedParameters().entrySet()) {
namedParameters.put(entry.getKey(), evaluator.evaluate(entry.getValue()));
}
}

private void addPositional(Expression original, Expression evaluated) {
if (isEllipsisVariable(original)) {
positionalParameters.addAll(expandList(evaluated));
} else {
positionalParameters.add(evaluated);
}
}

private List<Expression> expandList(Expression value) {
if (value.getType()!=ASTCssNodeType.LIST_EXPRESSION) {
return Arrays.asList(value);
}
ListExpression list = (ListExpression) value;
return list.getExpressions();
}

private boolean isEllipsisVariable(Expression expression) {
if (expression.getType()!=ASTCssNodeType.VARIABLE)
return false;

Variable variable = (Variable) expression;
return variable.isCollector();
}

public MixinReference getReference() {
return reference;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.github.sommeri.less4j.core.compiler.scopes.InScopeSnapshotRunner.ITask;
import com.github.sommeri.less4j.core.compiler.scopes.ScopeFactory;
import com.github.sommeri.less4j.core.compiler.scopes.view.ScopeView;
import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;
import com.github.sommeri.less4j.core.problems.ProblemsHandler;
import com.github.sommeri.less4j.core.problems.UnableToFinish;
import com.github.sommeri.less4j.utils.ArraysUtils;
Expand Down Expand Up @@ -108,20 +109,22 @@ public GeneralBody buildMixinReferenceReplacement(final EvaluatedMixinReferenceC
final MixinReference reference = evaluatedReference.getReference();
if (Thread.currentThread().isInterrupted())
throw new UnableToFinish("Thread Interrupted", (ASTCssNode) null);

final GeneralBody result = new GeneralBody(reference.getUnderlyingStructure());
if (mixins.isEmpty())
return result;

//candidate mixins with information about their default() function use are stored here
// candidate mixins with information about their default() function use are
// stored here
final List<BodyCompilationData> compiledMixins = new ArrayList<BodyCompilationData>();

for (final FoundMixin fullMixin : mixins) {
final ReusableStructure mixin = fullMixin.getMixin();
final IScope mixinScope = fullMixin.getScope();

final BodyCompilationData data = new BodyCompilationData(mixin);
// the following needs to run in snapshot because calculateMixinsWorkingScope modifies that scope
// the following needs to run in snapshot because
// calculateMixinsWorkingScope modifies that scope
InScopeSnapshotRunner.runInLocalDataSnapshot(mixinScope.getParent(), new ITask() {

@Override
Expand All @@ -131,8 +134,10 @@ public void run() {
data.setArguments(mixinArguments);
mixinScope.getParent().add(mixinArguments);
ScopeView mixinWorkingScope = scopeManipulation.joinIfIndependent(callerScope, mixinScope);
//it the mixin calls itself recursively, each copy should work on independent copy of local data
//that is matters mostly for scope placeholders - if both close placeholders in same copy error happen
// it the mixin calls itself recursively, each copy should work on
// independent copy of local data
// that is matters mostly for scope placeholders - if both close
// placeholders in same copy error happen
mixinWorkingScope.toIndependentWorkingCopy();
data.setMixinWorkingScope(mixinWorkingScope);

Expand All @@ -142,36 +147,41 @@ public void run() {

compiledMixins.add(data);
}
}); //end of InScopeSnapshotRunner.runInLocalDataSnapshot
}); // end of InScopeSnapshotRunner.runInLocalDataSnapshot
}

// filter out mixins we do not want to use
// filter out mixins we do not want to use
List<BodyCompilationData> mixinsToBeUsed = defaultGuardHelper.chooseMixinsToBeUsed(compiledMixins, reference);

for (final BodyCompilationData data : mixinsToBeUsed) {
final ScopeView mixinWorkingScope = data.getMixinWorkingScope();

// compilation must run in another localDataSnapshot, because imported detached ruleset stored in
// compilation must run in another localDataSnapshot, because imported
// detached ruleset stored in
// variables point to original scope - making snapshot above is not enough
// since they point to scope as defined during definition, they would not know parameters
// since they point to scope as defined during definition, they would not
// know parameters
// of mixins that define them
InScopeSnapshotRunner.runInLocalDataSnapshot(mixinWorkingScope.getParent(), new ITask() {

@Override
public void run() {
BodyOwner<?> mixin = data.getCompiledBodyOwner();
// add arguments again - detached rulesets imported into this one
// via returned variables from sub-calls need would not see arguments otherwise
// bc they keep link to original copy and there is no other way how to access them
// add arguments again - detached rulesets imported into this one
// via returned variables from sub-calls need would not see arguments
// otherwise
// bc they keep link to original copy and there is no other way how to
// access them
IScope arguments = data.getArguments();
mixinWorkingScope.getParent().add(arguments);

Couple<List<ASTCssNode>, IScope> compiled = resolveCalledBody(callerScope, mixin, mixinWorkingScope, ReturnMode.MIXINS_AND_VARIABLES);
// update mixin replacements and update scope with imported variables and mixins
// update mixin replacements and update scope with imported variables
// and mixins
result.addMembers(compiled.getT());
callerScope.addToDataPlaceholder(compiled.getM());
}
}); //end of InScopeSnapshotRunner.runInLocalData........Snapshot
}); // end of InScopeSnapshotRunner.runInLocalData........Snapshot

}

Expand All @@ -191,7 +201,7 @@ public GeneralBody buildDetachedRulesetReplacement(DetachedRulesetReference refe
callerScope.addToDataPlaceholder(compiled.getM());
callerScope.closeDataPlaceholder();

//resolveImportance(reference, result);
// resolveImportance(reference, result);
shiftComments(reference, result);

return result;
Expand Down Expand Up @@ -221,12 +231,12 @@ private void addImportantKeyword(Declaration declaration) {
if (expressionManipulator.isImportant(expression))
return;

//FIXME !!!!!!!!!! correct underlying - or correct keyword!!!
KeywordExpression important = new KeywordExpression(expression.getUnderlyingStructure(), "!important", true);
HiddenTokenAwareTree underlying = expression != null ? expression.getUnderlyingStructure() : declaration.getUnderlyingStructure();
KeywordExpression important = createImportantKeyword(underlying);

ListExpression list = expressionManipulator.findRightmostSpaceSeparatedList(expression);
if (list == null) {
list = new ListExpression(expression.getUnderlyingStructure(), ArraysUtils.asList(expression), new ListExpressionOperator(expression.getUnderlyingStructure(), ListExpressionOperator.Operator.EMPTY_OPERATOR));
list = new ListExpression(underlying, ArraysUtils.asNonNullList(expression), new ListExpressionOperator(underlying, ListExpressionOperator.Operator.EMPTY_OPERATOR));
}
list.addExpression(important);
list.configureParentToAllChilds();
Expand All @@ -235,6 +245,10 @@ private void addImportantKeyword(Declaration declaration) {
list.setParent(declaration);
}

private KeywordExpression createImportantKeyword(HiddenTokenAwareTree underlyingStructure) {
return new KeywordExpression(underlyingStructure, "!important", true);
}

class ImportedScopeFilter implements ExpressionFilter {

private final ExpressionEvaluator expressionEvaluator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,10 +179,6 @@ private Expression createListExpression(HiddenTokenAwareTree parent, LinkedList<
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 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
collapsed-list {
one-parameter: 1 2 3;
}
expanded-list {
three-parameters-1: 1;
three-parameters-2: 2;
three-parameters-3: 3;
}
expanded-comma-list {
three-parameters-1: 1;
three-parameters-2: 2;
three-parameters-3: 3;
}
expanded-not-list {
one-parameter: not-list;
}
pass-wrapper {
one-parameter: 1 2 3;
}
.tail-loop-here {
a: A;
tail: b B c C;
b: B;
tail: c C;
c: C;
tail: ;
the: end;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
@list: 1 2 3;
@comma-list: 1, 2, 3;
@not-list: not-list;

collapsed-list {.mixin(@list)} // 1 argument
expanded-list {.mixin(@list...)} // 3 arguments
expanded-comma-list {.mixin(@comma-list...)} // 3 arguments
expanded-not-list {.mixin(@not-list...)} // 3 arguments
pass-wrapper {.wrapper(@list)} // 1 argument


.mixin(@a1) {
one-parameter: @a1;
}

.mixin(@a1, @a2, @a3) {
three-parameters-1: @a1;
three-parameters-2: @a2;
three-parameters-3: @a3;
}

.wrapper(@args...) {
.mixin(@args...); // 1 argument of `c` expands as 3 arguments
}

// with tail
.tail-loop() { the: end; }
.tail-loop(@head, @tail...) {
@first: extract(@head, 1);
@second: extract(@head, 2);

@{first}: @second;
tail: @tail;
.tail-loop(@tail...);
}

.tail-loop-here {
@values : a A, b B, c C;
.tail-loop(@values...);
}

Loading

0 comments on commit 095efc7

Please sign in to comment.