Skip to content

Commit

Permalink
Solving js interpolation for #22. It is as general as possible, to allow
Browse files Browse the repository at this point in the history
different languages support.
  • Loading branch information
meri committed Mar 27, 2014
1 parent e50f038 commit 9341888
Show file tree
Hide file tree
Showing 15 changed files with 171 additions and 36 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.github.sommeri.less4j;

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

public interface EmbeddedScripting {

String toScriptExpression(Expression value, LessProblems problemsHandler);

}
11 changes: 10 additions & 1 deletion src/main/java/com/github/sommeri/less4j/LessCompiler.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public class Configuration {

private LessSource cssResultLocation;
private boolean linkSourceMap = true;
private List<LessFunction> functionPackages = new ArrayList<LessFunction>();
private List<LessFunction> functionPackages = new ArrayList<LessFunction>();
private EmbeddedScripting embeddedScripting;

/**
* This is needed in for source map.
Expand Down Expand Up @@ -69,6 +70,14 @@ public void addCustomFunction(LessFunction functionPackage) {
this.functionPackages.add(functionPackage);
}

public EmbeddedScripting getEmbeddedScripting() {
return embeddedScripting;
}

public void setEmbeddedScripting(EmbeddedScripting embeddedScripting) {
this.embeddedScripting = embeddedScripting;
}

}

public class CompilationResult {
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/com/github/sommeri/less4j/LessStringsEvaluator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package com.github.sommeri.less4j;

import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.utils.InStringCssPrinter;

public class LessStringsEvaluator implements EmbeddedScripting {

public LessStringsEvaluator() {
super();
}

@Override
public String toScriptExpression(Expression value, LessProblems problemsHandler) {
InStringCssPrinter builder = new InStringCssPrinter();
builder.append(value);
String replacement = builder.toString();
return replacement;
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
package com.github.sommeri.less4j.core.ast;

public enum ASTCssNodeType {
UNKNOWN, CSS_CLASS, DECLARATION, STYLE_SHEET, RULE_SET, SELECTOR, SIMPLE_SELECTOR, PSEUDO_CLASS, PSEUDO_ELEMENT, SELECTOR_ATTRIBUTE, ID_SELECTOR, CHARSET_DECLARATION, FONT_FACE, IDENTIFIER_EXPRESSION, COMPOSED_EXPRESSION, STRING_EXPRESSION, NUMBER, COLOR_EXPRESSION, FUNCTION, MEDIA, COMMENT, SELECTOR_OPERATOR, SELECTOR_COMBINATOR, EXPRESSION_OPERATOR, NTH, NAMED_EXPRESSION, MEDIA_QUERY, FIXED_MEDIA_EXPRESSION, MEDIUM, MEDIUM_MODIFIER, MEDIUM_TYPE, MEDIUM_EX_FEATURE, VARIABLE_DECLARATION, VARIABLE, INDIRECT_VARIABLE, PARENTHESES_EXPRESSION, SIGNED_EXPRESSION, ARGUMENT_DECLARATION, MIXIN_REFERENCE, GUARD_CONDITION, COMPARISON_EXPRESSION, GUARD, NESTED_SELECTOR_APPENDER, REUSABLE_STRUCTURE, FAULTY_EXPRESSION, ESCAPED_SELECTOR, ESCAPED_VALUE, INTERPOLABLE_NAME, FIXED_NAME_PART, VARIABLE_NAME_PART, KEYFRAMES, KEYFRAMES_NAME, REUSABLE_STRUCTURE_NAME, VIEWPORT, GENERAL_BODY, PAGE, NAME, PAGE_MARGIN_BOX, IMPORT, FAULTY_NODE, ANONYMOUS, EMPTY_EXPRESSION, SYNTAX_ONLY_ELEMENT, UNICODE_RANGE_EXPRESSION, INTERPOLATED_MEDIA_EXPRESSION, DOCUMENT, SUPPORTS, SUPPORTS_QUERY, SUPPORTS_CONDITION_NEGATION, SUPPORTS_CONDITION_PARENTHESES, SUPPORTS_CONDITION_LOGICAL, SUPPORTS_LOGICAL_OPERATOR, EXTEND, INLINE_CONTENT
UNKNOWN, CSS_CLASS, DECLARATION, STYLE_SHEET, RULE_SET, SELECTOR, SIMPLE_SELECTOR, PSEUDO_CLASS, PSEUDO_ELEMENT, SELECTOR_ATTRIBUTE, ID_SELECTOR, CHARSET_DECLARATION, FONT_FACE, IDENTIFIER_EXPRESSION, COMPOSED_EXPRESSION, STRING_EXPRESSION, NUMBER, COLOR_EXPRESSION, FUNCTION, MEDIA, COMMENT, SELECTOR_OPERATOR, SELECTOR_COMBINATOR, EXPRESSION_OPERATOR, NTH, NAMED_EXPRESSION, MEDIA_QUERY, FIXED_MEDIA_EXPRESSION, MEDIUM, MEDIUM_MODIFIER, MEDIUM_TYPE, MEDIUM_EX_FEATURE, VARIABLE_DECLARATION, VARIABLE, INDIRECT_VARIABLE, PARENTHESES_EXPRESSION, SIGNED_EXPRESSION, ARGUMENT_DECLARATION, MIXIN_REFERENCE, GUARD_CONDITION, COMPARISON_EXPRESSION, GUARD, NESTED_SELECTOR_APPENDER, REUSABLE_STRUCTURE, FAULTY_EXPRESSION, ESCAPED_SELECTOR, ESCAPED_VALUE, INTERPOLABLE_NAME, FIXED_NAME_PART, VARIABLE_NAME_PART, KEYFRAMES, KEYFRAMES_NAME, REUSABLE_STRUCTURE_NAME, VIEWPORT, GENERAL_BODY, PAGE, NAME, PAGE_MARGIN_BOX, IMPORT, FAULTY_NODE, ANONYMOUS, EMPTY_EXPRESSION, SYNTAX_ONLY_ELEMENT, UNICODE_RANGE_EXPRESSION, INTERPOLATED_MEDIA_EXPRESSION, DOCUMENT, SUPPORTS, SUPPORTS_QUERY, SUPPORTS_CONDITION_NEGATION, SUPPORTS_CONDITION_PARENTHESES, SUPPORTS_CONDITION_LOGICAL, SUPPORTS_LOGICAL_OPERATOR, EXTEND, INLINE_CONTENT, EMBEDDED_SCRIPT
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,30 @@ public void setRight(Expression right) {
this.right = right;
}

public List<Expression> splitList() {
LinkedList<Expression> byComma = doSplitByListOperator(Operator.COMMA);
if (byComma.size()==1)
return doSplitByListOperator(Operator.EMPTY_OPERATOR);

return byComma;
}

@Override
public List<Expression> splitByComma() {
return doSplitByComma();
return doSplitByListOperator(Operator.COMMA);
}

private LinkedList<Expression> doSplitByComma() {
private LinkedList<Expression> doSplitByListOperator(Operator splitOper) {
LinkedList<Expression> result = new LinkedList<Expression>();
if (operator.getOperator()!=Operator.COMMA && operator.getOperator()!=Operator.EMPTY_OPERATOR) {
result.add(this);
return result;
}

LinkedList<Expression> left = splitByComma(getLeft());
LinkedList<Expression> right = splitByComma(getRight());
LinkedList<Expression> left = splitByComma(getLeft(), splitOper);
LinkedList<Expression> right = splitByComma(getRight(), splitOper);

if (operator.getOperator()!=Operator.EMPTY_OPERATOR) {
if (operator.getOperator()==splitOper) {
result.addAll(left);
result.addAll(right);
return result;
Expand All @@ -80,10 +88,10 @@ private LinkedList<Expression> doSplitByComma() {
return result;
}

private LinkedList<Expression> splitByComma(Expression expression) {
private LinkedList<Expression> splitByComma(Expression expression, Operator splitOper) {
if (expression.getType()==ASTCssNodeType.COMPOSED_EXPRESSION) {
ComposedExpression composed = (ComposedExpression) expression;
return composed.doSplitByComma();
return composed.doSplitByListOperator(splitOper);
} else {
LinkedList<Expression> result = new LinkedList<Expression>();
result.add(expression);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.github.sommeri.less4j.core.ast;

import java.util.Collections;
import java.util.List;

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

public class EmbeddedScript extends Expression {

private String value;

public EmbeddedScript(HiddenTokenAwareTree token, String value) {
super(token);
this.value = value;
}

public String getValue() {
return value;
}

public void setValue(String value) {
this.value = value;
}

@Override
public ASTCssNodeType getType() {
return ASTCssNodeType.EMBEDDED_SCRIPT;
}

@Override
@NotAstProperty
public List<? extends ASTCssNode> getChilds() {
return Collections.emptyList();
}

@Override
public String toString() {
return value;
}

@Override
public EmbeddedScript clone() {
return (EmbeddedScript) super.clone();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.CssString;
import com.github.sommeri.less4j.core.ast.EmbeddedScript;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.FaultyExpression;
import com.github.sommeri.less4j.core.ast.FunctionExpression;
import com.github.sommeri.less4j.core.problems.ProblemsHandler;

Expand Down Expand Up @@ -48,20 +50,19 @@ public Expression evaluate(List<Expression> parameters, ProblemsHandler problems
problemsHandler.wrongNumberOfArgumentsToFunction(call.getParameter(), call.getName(), 1);

Expression parameter = parameters.get(0);
if (!(parameter instanceof CssString)) {
problemsHandler.wrongArgumentTypeToFunction(parameter, name+"...`", parameter.getType(),ASTCssNodeType.STRING_EXPRESSION);
if (!(parameter instanceof EmbeddedScript)) {
problemsHandler.wrongArgumentTypeToFunction(parameter, name+"...`", parameter.getType(),ASTCssNodeType.EMBEDDED_SCRIPT);
return new FaultyExpression(call);
}

warn(call, problemsHandler);

CssString parameterAsStr = (CssString) parameter;
EmbeddedScript parameterAsStr = (EmbeddedScript) parameter;
String value = name+parameterAsStr.getValue()+"`";
return new CssString(call.getUnderlyingStructure(), value, "");
}

private void warn(FunctionExpression call, ProblemsHandler problemsHandler) {
// ALTERNATIVE_NAMES_FOR_ERROR_REPORTING.put("EMBEDDED_SCRIPT", "embedded script `...`");
// ALTERNATIVE_NAMES_FOR_ERROR_REPORTING.put("ESCAPED_SCRIPT", "escaped script ~`...`");
problemsHandler.warnScriptingNotSupported(call, errorName);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
import java.util.Iterator;
import java.util.List;

import com.github.sommeri.less4j.EmbeddedScripting;
import com.github.sommeri.less4j.LessCompiler.Configuration;
import com.github.sommeri.less4j.LessStringsEvaluator;
import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.AbstractVariableDeclaration;
import com.github.sommeri.less4j.core.ast.ComparisonExpression;
import com.github.sommeri.less4j.core.ast.ComparisonExpressionOperator;
import com.github.sommeri.less4j.core.ast.ComposedExpression;
import com.github.sommeri.less4j.core.ast.CssString;
import com.github.sommeri.less4j.core.ast.EmbeddedScript;
import com.github.sommeri.less4j.core.ast.EscapedValue;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.ExpressionOperator;
Expand Down Expand Up @@ -54,7 +57,9 @@ public class ExpressionEvaluator {
private ColorsCalculator colorsCalculator;
private ExpressionComparator comparator = new GuardsComparator();
private List<FunctionsPackage> functions = new ArrayList<FunctionsPackage>();
private StringInterpolator stringInterpolator = new StringInterpolator();
private StringInterpolator stringInterpolator;
private StringInterpolator embeddedScriptInterpolator;
private EmbeddedScripting embeddedScripting;

public ExpressionEvaluator(ProblemsHandler problemsHandler, Configuration configuration) {
this(new NullScope(), problemsHandler, configuration);
Expand All @@ -66,6 +71,9 @@ public ExpressionEvaluator(IScope scope, ProblemsHandler problemsHandler, Config
this.problemsHandler = problemsHandler;
arithmeticCalculator = new ArithmeticCalculator(problemsHandler);
colorsCalculator = new ColorsCalculator(problemsHandler);
embeddedScripting = configuration.getEmbeddedScripting() == null ? new LessStringsEvaluator() : configuration.getEmbeddedScripting();
stringInterpolator = new StringInterpolator(problemsHandler);
embeddedScriptInterpolator = new StringInterpolator(embeddedScripting, problemsHandler);

functions.add(new CustomFunctions(problemsHandler, configuration.getCustomFunctions()));
functions.add(new MathFunctions(problemsHandler));
Expand Down Expand Up @@ -128,6 +136,11 @@ public Expression evaluate(EscapedValue input) {
return new EscapedValue(input.getUnderlyingStructure(), value);
}

public Expression evaluate(EmbeddedScript input) {
String value = embeddedScriptInterpolator.replaceIn(input.getValue(), this, input.getUnderlyingStructure());
return new EmbeddedScript(input.getUnderlyingStructure(), value);
}

public Expression evaluate(Variable input) {
if (cycleDetector.wouldCycle(input)) {
problemsHandler.variablesCycle(cycleDetector.getCycleFor(input));
Expand Down Expand Up @@ -201,6 +214,9 @@ public Expression evaluate(Expression input) {
case ESCAPED_VALUE:
return evaluate((EscapedValue) input);

case EMBEDDED_SCRIPT:
return evaluate((EmbeddedScript) input);

//the value is already there, nothing to evaluate
case IDENTIFIER_EXPRESSION:
case COLOR_EXPRESSION:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,28 @@

import java.util.regex.Pattern;

import com.github.sommeri.less4j.core.ast.CssString;
import com.github.sommeri.less4j.EmbeddedScripting;
import com.github.sommeri.less4j.LessStringsEvaluator;
import com.github.sommeri.less4j.core.ast.Expression;
import com.github.sommeri.less4j.core.ast.Variable;
import com.github.sommeri.less4j.core.compiler.expressions.ExpressionEvaluator;
import com.github.sommeri.less4j.core.parser.HiddenTokenAwareTree;
import com.github.sommeri.less4j.utils.InStringCssPrinter;
import com.github.sommeri.less4j.core.problems.ProblemsHandler;

public class StringInterpolator extends AbstractStringReplacer<ExpressionEvaluator> {

private static final Pattern STR_INTERPOLATION = Pattern.compile("@\\{([^\\{\\}@])*\\}");
private final EmbeddedScripting embeddedScriptEvaluator;
private final ProblemsHandler problemsHandler;

public StringInterpolator(ProblemsHandler problemsHandler) {
this(new LessStringsEvaluator(), problemsHandler);
}

public StringInterpolator(EmbeddedScripting embeddedScriptEvaluator, ProblemsHandler problemsHandler) {
this.embeddedScriptEvaluator = embeddedScriptEvaluator;
this.problemsHandler = problemsHandler;
}

@Override
protected Pattern getPattern() {
Expand All @@ -20,23 +32,17 @@ protected Pattern getPattern() {

@Override
protected String extractMatchName(String group) {
return "@"+group.substring(2, group.length()-1);
return "@" + group.substring(2, group.length() - 1);
}

@Override
protected String replacementValue(ExpressionEvaluator expressionEvaluator, HiddenTokenAwareTree technicalUnderlying, MatchRange matchRange) {
Expression value = expressionEvaluator.evaluateIfPresent(new Variable(technicalUnderlying, matchRange.getName()));
if (value!=null && (value instanceof CssString)) {
CssString string = (CssString) value;
return replaceIn(string.getValue(), expressionEvaluator, technicalUnderlying);
} else if (value==null) {
if (value == null) {
return matchRange.getFullMatch();
} else {
InStringCssPrinter builder = new InStringCssPrinter();
builder.append(value);
String replacement = builder.toString();
return replacement;
}

return embeddedScriptEvaluator.toScriptExpression(value, problemsHandler);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.github.sommeri.less4j.core.ast.ASTCssNode;
import com.github.sommeri.less4j.core.ast.ASTCssNodeType;
import com.github.sommeri.less4j.core.ast.CssString;
import com.github.sommeri.less4j.core.ast.EmbeddedScript;
import com.github.sommeri.less4j.core.ast.EscapedSelector;
import com.github.sommeri.less4j.core.ast.EscapedValue;
import com.github.sommeri.less4j.core.ast.Expression;
Expand Down Expand Up @@ -42,11 +43,12 @@ public class ReferencesSolver {
private final ProblemsHandler problemsHandler;
private final Configuration configuration;
private final AstNodesStack semiCompiledNodes = new AstNodesStack();
private StringInterpolator stringInterpolator = new StringInterpolator();
private final StringInterpolator stringInterpolator;

public ReferencesSolver(ProblemsHandler problemsHandler, Configuration configuration) {
this.problemsHandler = problemsHandler;
this.configuration = configuration;
this.stringInterpolator = new StringInterpolator(problemsHandler);
this.mixinsSolver = new MixinsSolver(this, semiCompiledNodes, problemsHandler, configuration);
}

Expand Down Expand Up @@ -212,6 +214,11 @@ private boolean solveIfVariableReference(ASTCssNode node, IScope scope) {
manipulator.replaceAndSynchronizeSilentness(node, replacement);
return true;
}
case EMBEDDED_SCRIPT: {
Expression replacement = expressionEvaluator.evaluate((EmbeddedScript) node);
manipulator.replaceAndSynchronizeSilentness(node, replacement);
return true;
}
case ESCAPED_SELECTOR: {
SimpleSelector replacement = interpolateEscapedSelector((EscapedSelector) node, expressionEvaluator);
manipulator.replaceAndSynchronizeSilentness(node, replacement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import com.github.sommeri.less4j.core.ast.ColorExpression;
import com.github.sommeri.less4j.core.ast.CssString;
import com.github.sommeri.less4j.core.ast.EmbeddedScript;
import com.github.sommeri.less4j.core.ast.EmptyExpression;
import com.github.sommeri.less4j.core.ast.EscapedValue;
import com.github.sommeri.less4j.core.ast.Expression;
Expand Down Expand Up @@ -215,13 +216,13 @@ public CssString createCssString(HiddenTokenAwareTree token, String quotedText)
private Expression buildFromEscapedScript(HiddenTokenAwareTree token, HiddenTokenAwareTree first) {
String text = first.getText();
text = text.substring(2, text.length() - 1);
return new FunctionExpression(token, "~`", new CssString(token, text, ""));
return new FunctionExpression(token, "~`", new EmbeddedScript(token, text));
}

private Expression buildFromEmbeddedScript(HiddenTokenAwareTree token, HiddenTokenAwareTree first) {
String text = first.getText();
text = text.substring(1, text.length() - 1);
return new FunctionExpression(token, "`", new CssString(token, text, ""));
return new FunctionExpression(token, "`", new EmbeddedScript(token, text));
}

private Expression buildFromIdentifier(HiddenTokenAwareTree parent, HiddenTokenAwareTree first) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ public void wrongArgumentTypeToFunction(Expression param, String function, ASTCs
collector.addError(new CompilationError(param, "Wrong argument type to function '" + function + "', expected " + PrintUtils.toTypeNames(expected) + " saw " + PrintUtils.toTypeName(received) + "."));
}

public void warnScriptingNotSupported(FunctionExpression call, String errorName) {
public void warnScriptingNotSupported(ASTCssNode call, String errorName) {
collector.addError(new CompilationError(call, errorName + "are not supported. The problem can be solved using custom functions. Compilation resulted in incorrect CSS."));
}

Expand Down
Loading

0 comments on commit 9341888

Please sign in to comment.