mirror of
https://github.com/danog/dart-sass.git
synced 2025-01-22 05:41:14 +01:00
Add if().
This commit is contained in:
parent
1987d0b055
commit
b47067ea4e
@ -12,6 +12,7 @@ export 'sass/expression/binary_operation.dart';
|
||||
export 'sass/expression/boolean.dart';
|
||||
export 'sass/expression/color.dart';
|
||||
export 'sass/expression/function.dart';
|
||||
export 'sass/expression/if.dart';
|
||||
export 'sass/expression/list.dart';
|
||||
export 'sass/expression/map.dart';
|
||||
export 'sass/expression/null.dart';
|
||||
|
27
lib/src/ast/sass/expression/if.dart
Normal file
27
lib/src/ast/sass/expression/if.dart
Normal file
@ -0,0 +1,27 @@
|
||||
// Copyright 2016 Google Inc. Use of this source code is governed by an
|
||||
// MIT-style license that can be found in the LICENSE file or at
|
||||
// https://opensource.org/licenses/MIT.
|
||||
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../../../parse.dart';
|
||||
import '../../../visitor/interface/expression.dart';
|
||||
import '../expression.dart';
|
||||
import '../argument_invocation.dart';
|
||||
import '../callable_invocation.dart';
|
||||
|
||||
class IfExpression implements Expression, CallableInvocation {
|
||||
static final declaration =
|
||||
parseArgumentDeclaration(r"($condition, $if-true, $if-false)");
|
||||
|
||||
final ArgumentInvocation arguments;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
IfExpression(this.arguments, this.span);
|
||||
|
||||
/*=T*/ accept/*<T>*/(ExpressionVisitor/*<T>*/ visitor) =>
|
||||
visitor.visitIfExpression(this);
|
||||
|
||||
String toString() => "if$arguments";
|
||||
}
|
@ -840,6 +840,11 @@ void defineCoreFunctions(Environment environment) {
|
||||
|
||||
// ## Miscellaneous
|
||||
|
||||
// This is only invoked using `call()`. Hand-authored `if()`s are parsed as
|
||||
// [IfExpression]s.
|
||||
environment.defineFunction("if", r"$condition, $if-true, $if-false",
|
||||
(arguments) => arguments[0].isTruthy ? arguments[1] : arguments[2]);
|
||||
|
||||
environment.defineFunction("unique-id", "", (arguments) {
|
||||
// Make it difficult to guess the next ID by randomizing the increase.
|
||||
_uniqueID += _random.nextInt(36);
|
||||
|
@ -1342,17 +1342,20 @@ abstract class StylesheetParser extends Parser {
|
||||
// TODO: url()
|
||||
var identifier = _interpolatedIdentifier();
|
||||
switch (identifier.asPlain) {
|
||||
case "false":
|
||||
return new BooleanExpression(false, identifier.span);
|
||||
case "if":
|
||||
var invocation = _argumentInvocation();
|
||||
return new IfExpression(
|
||||
invocation, spanForList([identifier, invocation]));
|
||||
case "not":
|
||||
whitespace();
|
||||
return new UnaryOperationExpression(
|
||||
UnaryOperator.not, _singleExpression(), identifier.span);
|
||||
|
||||
case "null":
|
||||
return new NullExpression(identifier.span);
|
||||
case "true":
|
||||
return new BooleanExpression(true, identifier.span);
|
||||
case "false":
|
||||
return new BooleanExpression(false, identifier.span);
|
||||
}
|
||||
|
||||
return scanner.peekChar() == $lparen
|
||||
|
@ -9,6 +9,7 @@ abstract class ExpressionVisitor<T> {
|
||||
T visitBooleanExpression(BooleanExpression node);
|
||||
T visitColorExpression(ColorExpression node);
|
||||
T visitFunctionExpression(FunctionExpression node);
|
||||
T visitIfExpression(IfExpression node);
|
||||
T visitListExpression(ListExpression node);
|
||||
T visitMapExpression(MapExpression node);
|
||||
T visitNullExpression(NullExpression node);
|
||||
|
@ -651,6 +651,21 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
SassBoolean visitBooleanExpression(BooleanExpression node) =>
|
||||
new SassBoolean(node.value);
|
||||
|
||||
Value visitIfExpression(IfExpression node) {
|
||||
var pair = _evaluateMacroArguments(node);
|
||||
var positional = pair.item1;
|
||||
var named = pair.item2;
|
||||
|
||||
_verifyArguments(
|
||||
positional.length, named, IfExpression.declaration, node.span);
|
||||
|
||||
var condition = positional.length > 0 ? positional[0] : named["condition"];
|
||||
var ifTrue = positional.length > 1 ? positional[1] : named["if-true"];
|
||||
var ifFalse = positional.length > 2 ? positional[2] : named["if-false"];
|
||||
|
||||
return (condition.accept(this).isTruthy ? ifTrue : ifFalse).accept(this);
|
||||
}
|
||||
|
||||
SassNull visitNullExpression(NullExpression node) => sassNull;
|
||||
|
||||
SassNumber visitNumberExpression(NumberExpression node) =>
|
||||
@ -725,7 +740,7 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
return _withEnvironment(callable.environment, () {
|
||||
return _environment.scope(() {
|
||||
_verifyArguments(
|
||||
positional, named, callable.arguments, invocation.span);
|
||||
positional.length, named, callable.arguments, invocation.span);
|
||||
|
||||
// TODO: if we get here and there are no rest params involved, mark
|
||||
// the callable as fast-path and don't do error checking or extra
|
||||
@ -778,13 +793,14 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
var triple = _evaluateArguments(invocation);
|
||||
var positional = triple.item1;
|
||||
var named = triple.item2;
|
||||
var namedSet = named;
|
||||
var separator = triple.item3;
|
||||
|
||||
int overloadIndex;
|
||||
for (var i = 0; i < callable.overloads.length - 1; i++) {
|
||||
try {
|
||||
_verifyArguments(
|
||||
positional, named, callable.overloads[i], invocation.span);
|
||||
_verifyArguments(positional.length, namedSet, callable.overloads[i],
|
||||
invocation.span);
|
||||
overloadIndex = i;
|
||||
break;
|
||||
} on SassRuntimeException catch (_) {
|
||||
@ -792,8 +808,8 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}
|
||||
}
|
||||
if (overloadIndex == null) {
|
||||
_verifyArguments(
|
||||
positional, named, callable.overloads.last, invocation.span);
|
||||
_verifyArguments(positional.length, namedSet, callable.overloads.last,
|
||||
invocation.span);
|
||||
overloadIndex = callable.overloads.length - 1;
|
||||
}
|
||||
|
||||
@ -878,10 +894,54 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
}
|
||||
}
|
||||
|
||||
void _addRestMap(Map<String, Value> values, SassMap map, FileSpan span) {
|
||||
Tuple2<List<Expression>, Map<String, Expression>> _evaluateMacroArguments(
|
||||
CallableInvocation invocation) {
|
||||
if (invocation.arguments.rest == null) {
|
||||
return new Tuple2(
|
||||
invocation.arguments.positional, invocation.arguments.named);
|
||||
}
|
||||
|
||||
var positional = invocation.arguments.positional.toList();
|
||||
var named = normalizedMap/*<Expression>*/()
|
||||
..addAll(invocation.arguments.named);
|
||||
var rest = invocation.arguments.rest.accept(this);
|
||||
if (rest is SassMap) {
|
||||
_addRestMap(
|
||||
named, rest, invocation.span, (value) => new ValueExpression(value));
|
||||
} else if (rest is SassList) {
|
||||
positional.addAll(rest.asList.map((value) => new ValueExpression(value)));
|
||||
if (rest is SassArgumentList) {
|
||||
rest.keywords.forEach((key, value) {
|
||||
named[key] = new ValueExpression(value);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
positional.add(new ValueExpression(rest));
|
||||
}
|
||||
|
||||
if (invocation.arguments.keywordRest == null) {
|
||||
return new Tuple2(positional, named);
|
||||
}
|
||||
|
||||
var keywordRest = invocation.arguments.keywordRest.accept(this);
|
||||
if (keywordRest is SassMap) {
|
||||
_addRestMap(named, keywordRest, invocation.span,
|
||||
(value) => new ValueExpression(value));
|
||||
return new Tuple2(positional, named);
|
||||
} else {
|
||||
throw _exception(
|
||||
"Variable keyword arguments must be a map (was $keywordRest).",
|
||||
invocation.span);
|
||||
}
|
||||
}
|
||||
|
||||
void _addRestMap/*<T>*/(
|
||||
Map<String, Object/*=T*/ > values, SassMap map, FileSpan span,
|
||||
[/*=T*/ convert(Value value)]) {
|
||||
convert ??= (value) => value as Object/*=T*/;
|
||||
map.contents.forEach((key, value) {
|
||||
if (key is SassString) {
|
||||
values[key.text] = value;
|
||||
values[key.text] = convert(value);
|
||||
} else {
|
||||
throw _exception(
|
||||
"Variable keyword argument map must have string keys.\n"
|
||||
@ -891,11 +951,11 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
});
|
||||
}
|
||||
|
||||
void _verifyArguments(List<Value> positional, Map<String, Value> named,
|
||||
void _verifyArguments(int positional, Map<String, dynamic> named,
|
||||
ArgumentDeclaration arguments, FileSpan span) {
|
||||
for (var i = 0; i < arguments.arguments.length; i++) {
|
||||
var argument = arguments.arguments[i];
|
||||
if (i < positional.length) {
|
||||
if (i < positional) {
|
||||
if (named.containsKey(argument.name)) {
|
||||
throw _exception(
|
||||
"Argument \$${argument.name} was passed both by position and by "
|
||||
@ -910,16 +970,16 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor<Value> {
|
||||
|
||||
if (arguments.restArgument != null) return;
|
||||
|
||||
if (positional.length > arguments.arguments.length) {
|
||||
if (positional > arguments.arguments.length) {
|
||||
throw _exception(
|
||||
"Only ${arguments.arguments.length} "
|
||||
"${pluralize('argument', arguments.arguments.length)} allowed, "
|
||||
"but ${positional.length} "
|
||||
"${pluralize('was', positional.length, plural: 'were')} passed.",
|
||||
"${pluralize('argument', arguments.arguments.length)} allowed, but "
|
||||
"${positional} ${pluralize('was', positional, plural: 'were')} "
|
||||
"passed.",
|
||||
span);
|
||||
}
|
||||
|
||||
if (arguments.arguments.length - positional.length < named.length) {
|
||||
if (arguments.arguments.length - positional < named.length) {
|
||||
var unknownNames = normalizedSet()
|
||||
..addAll(named.keys)
|
||||
..removeAll(arguments.arguments.map((argument) => argument.name));
|
||||
|
Loading…
x
Reference in New Issue
Block a user