mirror of
https://github.com/danog/dart-sass.git
synced 2024-11-27 12:44:42 +01:00
Add a RecursiveStatementVisitor (#313)
This will make it easier to write utilities that traverse the syntax tree.
This commit is contained in:
parent
166dac67d4
commit
ecbf1aebce
@ -6,7 +6,6 @@ export 'sass/argument.dart';
|
||||
export 'sass/argument_declaration.dart';
|
||||
export 'sass/argument_invocation.dart';
|
||||
export 'sass/at_root_query.dart';
|
||||
export 'sass/callable_declaration.dart';
|
||||
export 'sass/callable_invocation.dart';
|
||||
export 'sass/expression.dart';
|
||||
export 'sass/expression/binary_operation.dart';
|
||||
@ -31,6 +30,7 @@ export 'sass/node.dart';
|
||||
export 'sass/statement.dart';
|
||||
export 'sass/statement/at_root_rule.dart';
|
||||
export 'sass/statement/at_rule.dart';
|
||||
export 'sass/statement/callable_declaration.dart';
|
||||
export 'sass/statement/content_rule.dart';
|
||||
export 'sass/statement/debug_rule.dart';
|
||||
export 'sass/statement/declaration.dart';
|
||||
@ -45,6 +45,7 @@ export 'sass/statement/include_rule.dart';
|
||||
export 'sass/statement/loud_comment.dart';
|
||||
export 'sass/statement/media_rule.dart';
|
||||
export 'sass/statement/mixin_rule.dart';
|
||||
export 'sass/statement/parent.dart';
|
||||
export 'sass/statement/return_rule.dart';
|
||||
export 'sass/statement/silent_comment.dart';
|
||||
export 'sass/statement/style_rule.dart';
|
||||
|
@ -4,24 +4,22 @@
|
||||
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import 'argument_declaration.dart';
|
||||
import 'statement.dart';
|
||||
import '../argument_declaration.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// An abstract class for callables (functions or mixins) that are declared in
|
||||
/// user code.
|
||||
abstract class CallableDeclaration implements Statement {
|
||||
abstract class CallableDeclaration extends ParentStatement {
|
||||
/// The name of this callable.
|
||||
final String name;
|
||||
|
||||
/// The declared arguments this callable accepts.
|
||||
final ArgumentDeclaration arguments;
|
||||
|
||||
/// The child statements that are executed when this callable is invoked.
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
CallableDeclaration(
|
||||
this.name, this.arguments, Iterable<Statement> children, this.span)
|
||||
: children = new List.unmodifiable(children);
|
||||
: super(new List.unmodifiable(children));
|
||||
}
|
@ -6,8 +6,8 @@ import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../argument_declaration.dart';
|
||||
import '../callable_declaration.dart';
|
||||
import '../statement.dart';
|
||||
import 'callable_declaration.dart';
|
||||
|
||||
/// A function declaration.
|
||||
///
|
||||
|
@ -8,24 +8,21 @@ import '../../../visitor/interface/statement.dart';
|
||||
import '../argument_invocation.dart';
|
||||
import '../callable_invocation.dart';
|
||||
import '../statement.dart';
|
||||
import 'parent.dart';
|
||||
|
||||
/// A mixin invocation.
|
||||
class IncludeRule implements Statement, CallableInvocation {
|
||||
class IncludeRule extends ParentStatement implements CallableInvocation {
|
||||
/// The name of the mixin being invoked.
|
||||
final String name;
|
||||
|
||||
/// The arguments to pass to the mixin.
|
||||
final ArgumentInvocation arguments;
|
||||
|
||||
/// The content block to pass to the mixin, or `null` if there is no content
|
||||
/// block.
|
||||
final List<Statement> children;
|
||||
|
||||
final FileSpan span;
|
||||
|
||||
IncludeRule(this.name, this.arguments, this.span,
|
||||
{Iterable<Statement> children})
|
||||
: children = children == null ? null : new List.unmodifiable(children);
|
||||
: super(children == null ? null : new List.unmodifiable(children));
|
||||
|
||||
T accept<T>(StatementVisitor<T> visitor) => visitor.visitIncludeRule(this);
|
||||
|
||||
|
@ -5,9 +5,9 @@
|
||||
import 'package:source_span/source_span.dart';
|
||||
|
||||
import '../../../visitor/interface/statement.dart';
|
||||
import '../callable_declaration.dart';
|
||||
import '../argument_declaration.dart';
|
||||
import '../statement.dart';
|
||||
import 'callable_declaration.dart';
|
||||
|
||||
/// A mixin declaration.
|
||||
///
|
||||
|
220
lib/src/visitor/recursive_statement.dart
Normal file
220
lib/src/visitor/recursive_statement.dart
Normal file
@ -0,0 +1,220 @@
|
||||
// Copyright 2018 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:meta/meta.dart';
|
||||
|
||||
import '../ast/sass.dart';
|
||||
import 'interface/statement.dart';
|
||||
|
||||
/// A visitor that recursively traverses each statement in a Sass AST.
|
||||
///
|
||||
/// In addition to the methods from [StatementVisitor], this has more general
|
||||
/// protected methods that can be overriden to add behavior for a wide variety
|
||||
/// of AST nodes:
|
||||
///
|
||||
/// * [visitCallableDeclaration]
|
||||
/// * [visitSupportsCondition]
|
||||
/// * [visitChildren]
|
||||
/// * [visitInterpolation]
|
||||
/// * [visitExpression]
|
||||
///
|
||||
/// The default implementation of the visit methods all return `null`.
|
||||
abstract class RecursiveStatementVisitor<T> implements StatementVisitor<T> {
|
||||
T visitAtRootRule(AtRootRule node) {
|
||||
visitInterpolation(node.query);
|
||||
return visitChildren(node);
|
||||
}
|
||||
|
||||
T visitAtRule(AtRule node) {
|
||||
visitInterpolation(node.value);
|
||||
return visitChildren(node);
|
||||
}
|
||||
|
||||
T visitContentRule(ContentRule node) => null;
|
||||
|
||||
T visitDebugRule(DebugRule node) {
|
||||
visitExpression(node.expression);
|
||||
return null;
|
||||
}
|
||||
|
||||
T visitDeclaration(Declaration node) {
|
||||
visitInterpolation(node.name);
|
||||
visitExpression(node.value);
|
||||
return visitChildren(node);
|
||||
}
|
||||
|
||||
T visitEachRule(EachRule node) {
|
||||
visitExpression(node.list);
|
||||
return visitChildren(node);
|
||||
}
|
||||
|
||||
T visitErrorRule(ErrorRule node) {
|
||||
visitExpression(node.expression);
|
||||
return null;
|
||||
}
|
||||
|
||||
T visitExtendRule(ExtendRule node) {
|
||||
visitInterpolation(node.selector);
|
||||
return null;
|
||||
}
|
||||
|
||||
T visitForRule(ForRule node) {
|
||||
visitExpression(node.from);
|
||||
visitExpression(node.to);
|
||||
return visitChildren(node);
|
||||
}
|
||||
|
||||
T visitFunctionRule(FunctionRule node) => visitCallableDeclaration(node);
|
||||
|
||||
T visitIfRule(IfRule node) {
|
||||
for (var clause in node.clauses) {
|
||||
_visitIfClause(clause);
|
||||
}
|
||||
if (node.lastClause != null) _visitIfClause(node.lastClause);
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Visits [clause]'s expression and children.
|
||||
void _visitIfClause(IfClause clause) {
|
||||
if (clause.expression != null) visitExpression(clause.expression);
|
||||
for (var child in clause.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
T visitImportRule(ImportRule node) {
|
||||
for (var import in node.imports) {
|
||||
if (import is StaticImport) {
|
||||
visitInterpolation(import.url);
|
||||
if (import.supports != null) visitSupportsCondition(import.supports);
|
||||
if (import.media != null) visitInterpolation(import.media);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
T visitIncludeRule(IncludeRule node) {
|
||||
for (var expression in node.arguments.positional) {
|
||||
visitExpression(expression);
|
||||
}
|
||||
for (var expression in node.arguments.named.values) {
|
||||
visitExpression(expression);
|
||||
}
|
||||
if (node.arguments.rest != null) {
|
||||
visitExpression(node.arguments.rest);
|
||||
}
|
||||
if (node.arguments.keywordRest != null) {
|
||||
visitExpression(node.arguments.keywordRest);
|
||||
}
|
||||
|
||||
return node.children == null ? null : visitChildren(node);
|
||||
}
|
||||
|
||||
T visitLoudComment(LoudComment node) {
|
||||
visitInterpolation(node.text);
|
||||
return null;
|
||||
}
|
||||
|
||||
T visitMediaRule(MediaRule node) {
|
||||
visitInterpolation(node.query);
|
||||
return visitChildren(node);
|
||||
}
|
||||
|
||||
T visitMixinRule(MixinRule node) => visitCallableDeclaration(node);
|
||||
|
||||
T visitReturnRule(ReturnRule node) {
|
||||
visitExpression(node.expression);
|
||||
return null;
|
||||
}
|
||||
|
||||
T visitSilentComment(SilentComment node) => null;
|
||||
|
||||
T visitStyleRule(StyleRule node) {
|
||||
visitInterpolation(node.selector);
|
||||
return visitChildren(node);
|
||||
}
|
||||
|
||||
T visitStylesheet(Stylesheet node) => visitChildren(node);
|
||||
|
||||
T visitSupportsRule(SupportsRule node) {
|
||||
visitSupportsCondition(node.condition);
|
||||
return visitChildren(node);
|
||||
}
|
||||
|
||||
T visitVariableDeclaration(VariableDeclaration node) {
|
||||
visitExpression(node.expression);
|
||||
return null;
|
||||
}
|
||||
|
||||
T visitWarnRule(WarnRule node) {
|
||||
visitExpression(node.expression);
|
||||
return null;
|
||||
}
|
||||
|
||||
T visitWhileRule(WhileRule node) {
|
||||
visitExpression(node.condition);
|
||||
return visitChildren(node);
|
||||
}
|
||||
|
||||
/// Visits each of [node]'s expressions and children.
|
||||
///
|
||||
/// The default implementations of [visitFunctionRule] and [visitMixinRule]
|
||||
/// call this.
|
||||
@protected
|
||||
T visitCallableDeclaration(CallableDeclaration node) {
|
||||
for (var argument in node.arguments.arguments) {
|
||||
if (argument.defaultValue != null) visitExpression(argument.defaultValue);
|
||||
}
|
||||
return visitChildren(node);
|
||||
}
|
||||
|
||||
/// Visits each expression in [condition].
|
||||
///
|
||||
/// The default implementation of the visit methods call this to visit any
|
||||
/// [SupportsCondition] they encounter.
|
||||
@protected
|
||||
void visitSupportsCondition(SupportsCondition condition) {
|
||||
if (condition is SupportsOperation) {
|
||||
visitSupportsCondition(condition.left);
|
||||
visitSupportsCondition(condition.right);
|
||||
} else if (condition is SupportsNegation) {
|
||||
visitSupportsCondition(condition.condition);
|
||||
} else if (condition is SupportsInterpolation) {
|
||||
visitExpression(condition.expression);
|
||||
} else if (condition is SupportsDeclaration) {
|
||||
visitExpression(condition.name);
|
||||
visitExpression(condition.value);
|
||||
}
|
||||
}
|
||||
|
||||
/// Visits each of [node]'s children.
|
||||
///
|
||||
/// The default implementation of the visit methods for all [ParentStatement]s
|
||||
/// call this and return its result.
|
||||
@protected
|
||||
T visitChildren(ParentStatement node) {
|
||||
for (var child in node.children) {
|
||||
child.accept(this);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Visits each expression in an [interpolation].
|
||||
///
|
||||
/// The default implementation of the visit methods call this to visit any
|
||||
/// interpolation in a statement.
|
||||
@protected
|
||||
void visitInterpolation(Interpolation interpolation) {
|
||||
for (var node in interpolation.contents) {
|
||||
if (node is Expression) visitExpression(node);
|
||||
}
|
||||
}
|
||||
|
||||
/// Visits [expression].
|
||||
///
|
||||
/// The default implementation of the visit methods call this to visit any
|
||||
/// expression in a statement.
|
||||
@protected
|
||||
void visitExpression(Expression expression) {}
|
||||
}
|
Loading…
Reference in New Issue
Block a user