diff --git a/lib/src/ast/sass.dart b/lib/src/ast/sass.dart index 95d36e5e..e7c777e9 100644 --- a/lib/src/ast/sass.dart +++ b/lib/src/ast/sass.dart @@ -19,6 +19,7 @@ export 'sass/expression/number.dart'; export 'sass/expression/selector.dart'; export 'sass/expression/string.dart'; export 'sass/expression/unary_operation.dart'; +export 'sass/expression/value.dart'; export 'sass/expression/variable.dart'; export 'sass/interpolation.dart'; export 'sass/media_query.dart'; diff --git a/lib/src/ast/sass/expression/value.dart b/lib/src/ast/sass/expression/value.dart new file mode 100644 index 00000000..d78a9481 --- /dev/null +++ b/lib/src/ast/sass/expression/value.dart @@ -0,0 +1,26 @@ +// 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 '../../../visitor/interface/expression.dart'; +import '../../../value.dart'; +import '../expression.dart'; + +/// An expression that directly embeds a [Value]. +/// +/// This is never constructed by the parser. It's only used when ASTs are +/// constructed dynamically, as for the `call()` function. +class ValueExpression implements Expression { + final Value value; + + FileSpan get span => null; + + ValueExpression(this.value); + + /*=T*/ accept/**/(ExpressionVisitor/**/ visitor) => + visitor.visitValueExpression(this); + + String toString() => value.toString(); +} diff --git a/lib/src/functions.dart b/lib/src/functions.dart index 71f37e04..785427d4 100644 --- a/lib/src/functions.dart +++ b/lib/src/functions.dart @@ -832,6 +832,8 @@ void defineCoreFunctions(Environment environment) { var number2 = arguments[1].assertNumber("number2"); return new SassBoolean(number1.isComparableTo(number2)); }); + + // call() is defined in PerformVisitor to provide it access to private APIs. } num _percentageOrUnitless(SassNumber number, num max, String name) { diff --git a/lib/src/utils.dart b/lib/src/utils.dart index 012ddd14..672c962e 100644 --- a/lib/src/utils.dart +++ b/lib/src/utils.dart @@ -61,6 +61,9 @@ int listHash(List list) => const ListEquality().hash(list); FileSpan spanForList(List nodes) { if (nodes.isEmpty) return null; + // Spans may be null for dynamically-constructed ASTs. + if (nodes.first.span == null) return null; + if (nodes.last.span == null) return null; return nodes.first.span.expand(nodes.last.span); } diff --git a/lib/src/visitor/interface/expression.dart b/lib/src/visitor/interface/expression.dart index fbdedd0e..353042af 100644 --- a/lib/src/visitor/interface/expression.dart +++ b/lib/src/visitor/interface/expression.dart @@ -16,5 +16,6 @@ abstract class ExpressionVisitor { T visitSelectorExpression(SelectorExpression node); T visitStringExpression(StringExpression node); T visitUnaryOperationExpression(UnaryOperationExpression node); + T visitValueExpression(ValueExpression node); T visitVariableExpression(VariableExpression node); } diff --git a/lib/src/visitor/perform.dart b/lib/src/visitor/perform.dart index 22d6b547..fcef41e4 100644 --- a/lib/src/visitor/perform.dart +++ b/lib/src/visitor/perform.dart @@ -58,7 +58,18 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor { PerformVisitor({Iterable loadPaths, Environment environment}) : _loadPaths = loadPaths == null ? const [] : new List.from(loadPaths), - _environment = environment ?? new Environment(); + _environment = environment ?? new Environment() { + _environment.defineFunction("call", r"$name, $args...", (arguments) { + var name = arguments[0].assertString("name"); + var args = arguments[1] as SassArgumentList; + + var expression = new FunctionExpression( + new Interpolation([name.text], null), + new ArgumentInvocation([], {}, null, + rest: new ValueExpression(args))); + return expression.accept(this); + }); + } void visit(node) { if (node is Statement) { @@ -613,6 +624,8 @@ class PerformVisitor implements StatementVisitor, ExpressionVisitor { }, node.span); } + Value visitValueExpression(ValueExpression node) => node.value; + Value visitVariableExpression(VariableExpression node) { var result = _environment.getVariable(node.name); if (result != null) return result;