| // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | |
| // for details. All rights reserved. Use of this source code is governed by a | |
| // BSD-style license that can be found in the LICENSE file. | |
| // ignore_for_file: prefer_asserts_in_initializer_lists | |
| part of '../visitor.dart'; | |
| ///////////////////////////////////////////////////////////////////////// | |
| // CSS specific types: | |
| ///////////////////////////////////////////////////////////////////////// | |
| class Identifier extends TreeNode { | |
| String name; | |
| Identifier(this.name, SourceSpan? span) : super(span); | |
| @override | |
| Identifier clone() => Identifier(name, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitIdentifier(this); | |
| @override | |
| String toString() => | |
| // Try to use the identifier's original lexeme to preserve any escape | |
| // codes as authored. The name, which may include escaped values, may no | |
| // longer be a valid identifier. | |
| span?.text ?? name; | |
| } | |
| class Wildcard extends TreeNode { | |
| Wildcard(super.span); | |
| @override | |
| Wildcard clone() => Wildcard(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitWildcard(this); | |
| String get name => '*'; | |
| } | |
| class ThisOperator extends TreeNode { | |
| ThisOperator(super.span); | |
| @override | |
| ThisOperator clone() => ThisOperator(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitThisOperator(this); | |
| String get name => '&'; | |
| } | |
| class Negation extends TreeNode { | |
| Negation(super.span); | |
| @override | |
| Negation clone() => Negation(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitNegation(this); | |
| String get name => 'not'; | |
| } | |
| // calc(...) | |
| // TODO(terry): Hack to handle calc however the expressions should be fully | |
| // parsed and in the AST. | |
| class CalcTerm extends LiteralTerm { | |
| final LiteralTerm expr; | |
| CalcTerm(Object value, String text, this.expr, SourceSpan? span) | |
| : super(value, text, span); | |
| @override | |
| CalcTerm clone() => CalcTerm(value, text, expr.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitCalcTerm(this); | |
| @override | |
| String toString() => '$text($expr)'; | |
| } | |
| // /* .... */ | |
| class CssComment extends TreeNode { | |
| final String comment; | |
| CssComment(this.comment, SourceSpan? span) : super(span); | |
| @override | |
| CssComment clone() => CssComment(comment, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitCssComment(this); | |
| } | |
| // CDO/CDC (Comment Definition Open <!-- and Comment Definition Close -->). | |
| class CommentDefinition extends CssComment { | |
| CommentDefinition(super.comment, super.span); | |
| @override | |
| CommentDefinition clone() => CommentDefinition(comment, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitCommentDefinition(this); | |
| } | |
| class SelectorGroup extends TreeNode { | |
| final List<Selector> selectors; | |
| SelectorGroup(this.selectors, SourceSpan? span) : super(span); | |
| @override | |
| SelectorGroup clone() => SelectorGroup(selectors, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitSelectorGroup(this); | |
| } | |
| class Selector extends TreeNode { | |
| final List<SimpleSelectorSequence> simpleSelectorSequences; | |
| Selector(this.simpleSelectorSequences, SourceSpan? span) : super(span); | |
| void add(SimpleSelectorSequence seq) => simpleSelectorSequences.add(seq); | |
| int get length => simpleSelectorSequences.length; | |
| @override | |
| Selector clone() { | |
| var simpleSequences = | |
| simpleSelectorSequences.map((ss) => ss.clone()).toList(); | |
| return Selector(simpleSequences, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitSelector(this); | |
| } | |
| class SimpleSelectorSequence extends TreeNode { | |
| /// +, >, ~, NONE | |
| int combinator; | |
| final SimpleSelector simpleSelector; | |
| SimpleSelectorSequence(this.simpleSelector, SourceSpan? span, | |
| [this.combinator = TokenKind.COMBINATOR_NONE]) | |
| : super(span); | |
| bool get isCombinatorNone => combinator == TokenKind.COMBINATOR_NONE; | |
| bool get isCombinatorPlus => combinator == TokenKind.COMBINATOR_PLUS; | |
| bool get isCombinatorGreater => combinator == TokenKind.COMBINATOR_GREATER; | |
| bool get isCombinatorTilde => combinator == TokenKind.COMBINATOR_TILDE; | |
| bool get isCombinatorDescendant => | |
| combinator == TokenKind.COMBINATOR_DESCENDANT; | |
| String get _combinatorToString => switch (combinator) { | |
| TokenKind.COMBINATOR_DESCENDANT => ' ', | |
| TokenKind.COMBINATOR_GREATER => ' > ', | |
| TokenKind.COMBINATOR_PLUS => ' + ', | |
| TokenKind.COMBINATOR_TILDE => ' ~ ', | |
| _ => '' | |
| }; | |
| @override | |
| SimpleSelectorSequence clone() => | |
| SimpleSelectorSequence(simpleSelector, span, combinator); | |
| @override | |
| dynamic visit(VisitorBase visitor) => | |
| visitor.visitSimpleSelectorSequence(this); | |
| @override | |
| String toString() => simpleSelector.name; | |
| } | |
| // All other selectors (element, #id, .class, attribute, pseudo, negation, | |
| // namespace, *) are derived from this selector. | |
| abstract class SimpleSelector extends TreeNode { | |
| final dynamic _name; // Wildcard, ThisOperator, Identifier, Negation, others? | |
| SimpleSelector(this._name, SourceSpan? span) : super(span); | |
| // TOOD(srawlins): Figure this one out. | |
| // ignore: avoid_dynamic_calls | |
| String get name => _name.name as String; | |
| bool get isWildcard => _name is Wildcard; | |
| bool get isThis => _name is ThisOperator; | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitSimpleSelector(this); | |
| } | |
| // element name | |
| class ElementSelector extends SimpleSelector { | |
| ElementSelector(super.name, super.span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitElementSelector(this); | |
| @override | |
| ElementSelector clone() => ElementSelector(_name, span); | |
| @override | |
| String toString() => name; | |
| } | |
| // namespace|element | |
| class NamespaceSelector extends SimpleSelector { | |
| final dynamic _namespace; // null, Wildcard or Identifier | |
| NamespaceSelector(this._namespace, Object name, SourceSpan? span) | |
| : super(name, span); | |
| String get namespace => _namespace is Wildcard | |
| ? '*' | |
| : _namespace == null | |
| ? '' | |
| : (_namespace as Identifier).name; | |
| bool get isNamespaceWildcard => _namespace is Wildcard; | |
| SimpleSelector? get nameAsSimpleSelector => _name as SimpleSelector?; | |
| @override | |
| NamespaceSelector clone() => NamespaceSelector(_namespace, '', span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitNamespaceSelector(this); | |
| @override | |
| String toString() => '$namespace|${nameAsSimpleSelector!.name}'; | |
| } | |
| // [attr op value] | |
| class AttributeSelector extends SimpleSelector { | |
| final int _op; | |
| final dynamic value; | |
| AttributeSelector(Identifier name, this._op, this.value, SourceSpan? span) | |
| : super(name, span); | |
| int get operatorKind => _op; | |
| String? matchOperator() => switch (_op) { | |
| TokenKind.EQUALS => '=', | |
| TokenKind.INCLUDES => '~=', | |
| TokenKind.DASH_MATCH => '|=', | |
| TokenKind.PREFIX_MATCH => '^=', | |
| TokenKind.SUFFIX_MATCH => '\$=', | |
| TokenKind.SUBSTRING_MATCH => '*=', | |
| TokenKind.NO_MATCH => '', | |
| _ => null | |
| }; | |
| // Return the TokenKind for operator used by visitAttributeSelector. | |
| String? matchOperatorAsTokenString() => switch (_op) { | |
| TokenKind.EQUALS => 'EQUALS', | |
| TokenKind.INCLUDES => 'INCLUDES', | |
| TokenKind.DASH_MATCH => 'DASH_MATCH', | |
| TokenKind.PREFIX_MATCH => 'PREFIX_MATCH', | |
| TokenKind.SUFFIX_MATCH => 'SUFFIX_MATCH', | |
| TokenKind.SUBSTRING_MATCH => 'SUBSTRING_MATCH', | |
| _ => null | |
| }; | |
| String valueToString() { | |
| if (value != null) { | |
| if (value is Identifier) { | |
| return value.toString(); | |
| } else { | |
| return '"$value"'; | |
| } | |
| } else { | |
| return ''; | |
| } | |
| } | |
| @override | |
| AttributeSelector clone() => | |
| AttributeSelector(_name as Identifier, _op, value, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitAttributeSelector(this); | |
| @override | |
| String toString() => '[$name${matchOperator()}${valueToString()}]'; | |
| } | |
| // #id | |
| class IdSelector extends SimpleSelector { | |
| IdSelector(Identifier super.name, super.span); | |
| @override | |
| IdSelector clone() => IdSelector(_name as Identifier, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitIdSelector(this); | |
| @override | |
| String toString() => '#$_name'; | |
| } | |
| // .class | |
| class ClassSelector extends SimpleSelector { | |
| ClassSelector(Identifier super.name, super.span); | |
| @override | |
| ClassSelector clone() => ClassSelector(_name as Identifier, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitClassSelector(this); | |
| @override | |
| String toString() => '.$_name'; | |
| } | |
| // :pseudoClass | |
| class PseudoClassSelector extends SimpleSelector { | |
| PseudoClassSelector(Identifier super.name, super.span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitPseudoClassSelector(this); | |
| @override | |
| PseudoClassSelector clone() => PseudoClassSelector(_name as Identifier, span); | |
| @override | |
| String toString() => ':$name'; | |
| } | |
| // ::pseudoElement | |
| class PseudoElementSelector extends SimpleSelector { | |
| // If true, this is a CSS2.1 pseudo-element with only a single ':'. | |
| final bool isLegacy; | |
| PseudoElementSelector(Identifier super.name, super.span, | |
| {this.isLegacy = false}); | |
| @override | |
| dynamic visit(VisitorBase visitor) => | |
| visitor.visitPseudoElementSelector(this); | |
| @override | |
| PseudoElementSelector clone() => | |
| PseudoElementSelector(_name as Identifier, span); | |
| @override | |
| String toString() => "${isLegacy ? ':' : '::'}$name"; | |
| } | |
| // :pseudoClassFunction(argument) | |
| class PseudoClassFunctionSelector extends PseudoClassSelector { | |
| final TreeNode argument; // Selector, SelectorExpression | |
| PseudoClassFunctionSelector(Identifier name, this.argument, SourceSpan? span) | |
| : super(name, span); | |
| @override | |
| PseudoClassFunctionSelector clone() => | |
| PseudoClassFunctionSelector(_name as Identifier, argument, span); | |
| Selector get selector => argument as Selector; | |
| SelectorExpression get expression => argument as SelectorExpression; | |
| @override | |
| dynamic visit(VisitorBase visitor) => | |
| visitor.visitPseudoClassFunctionSelector(this); | |
| } | |
| // ::pseudoElementFunction(expression) | |
| class PseudoElementFunctionSelector extends PseudoElementSelector { | |
| final SelectorExpression expression; | |
| PseudoElementFunctionSelector( | |
| Identifier name, this.expression, SourceSpan? span) | |
| : super(name, span); | |
| @override | |
| PseudoElementFunctionSelector clone() => | |
| PseudoElementFunctionSelector(_name as Identifier, expression, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => | |
| visitor.visitPseudoElementFunctionSelector(this); | |
| } | |
| class SelectorExpression extends TreeNode { | |
| final List<Expression> expressions; | |
| SelectorExpression(this.expressions, SourceSpan? span) : super(span); | |
| @override | |
| SourceSpan get span => super.span!; | |
| @override | |
| SelectorExpression clone() => | |
| SelectorExpression(expressions.map((e) => e.clone()).toList(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitSelectorExpression(this); | |
| } | |
| // :NOT(negation_arg) | |
| class NegationSelector extends SimpleSelector { | |
| final SimpleSelector? negationArg; | |
| NegationSelector(this.negationArg, SourceSpan? span) | |
| : super(Negation(span), span); | |
| @override | |
| NegationSelector clone() => NegationSelector(negationArg, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitNegationSelector(this); | |
| } | |
| class NoOp extends TreeNode { | |
| NoOp() : super(null); | |
| @override | |
| NoOp clone() => NoOp(); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitNoOp(this); | |
| } | |
| class StyleSheet extends TreeNode { | |
| /// Contains charset, ruleset, directives (media, page, etc.), and selectors. | |
| final List<TreeNode> topLevels; | |
| StyleSheet(this.topLevels, SourceSpan? span) : super(span) { | |
| for (final node in topLevels) { | |
| assert(node is TopLevelProduction || node is Directive); | |
| } | |
| } | |
| /// Selectors only in this tree. | |
| StyleSheet.selector(this.topLevels, SourceSpan? span) : super(span); | |
| @override | |
| SourceSpan get span => super.span!; | |
| @override | |
| StyleSheet clone() { | |
| var clonedTopLevels = topLevels.map((e) => e.clone()).toList(); | |
| return StyleSheet(clonedTopLevels, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitStyleSheet(this); | |
| } | |
| class TopLevelProduction extends TreeNode { | |
| TopLevelProduction(super.span); | |
| @override | |
| SourceSpan get span => super.span!; | |
| @override | |
| TopLevelProduction clone() => TopLevelProduction(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitTopLevelProduction(this); | |
| } | |
| class RuleSet extends TopLevelProduction { | |
| final SelectorGroup? selectorGroup; | |
| final DeclarationGroup declarationGroup; | |
| RuleSet(this.selectorGroup, this.declarationGroup, SourceSpan? span) | |
| : super(span); | |
| @override | |
| RuleSet clone() { | |
| var cloneSelectorGroup = selectorGroup!.clone(); | |
| var cloneDeclarationGroup = declarationGroup.clone(); | |
| return RuleSet(cloneSelectorGroup, cloneDeclarationGroup, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitRuleSet(this); | |
| } | |
| class Directive extends TreeNode { | |
| Directive(super.span); | |
| bool get isBuiltIn => true; // Known CSS directive? | |
| bool get isExtension => false; // SCSS extension? | |
| @override | |
| SourceSpan get span => super.span!; | |
| @override | |
| Directive clone() => Directive(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitDirective(this); | |
| } | |
| class DocumentDirective extends Directive { | |
| final List<LiteralTerm> functions; | |
| final List<TreeNode> groupRuleBody; | |
| DocumentDirective(this.functions, this.groupRuleBody, SourceSpan? span) | |
| : super(span); | |
| @override | |
| DocumentDirective clone() { | |
| var clonedFunctions = <LiteralTerm>[]; | |
| for (var function in functions) { | |
| clonedFunctions.add(function.clone()); | |
| } | |
| var clonedGroupRuleBody = <TreeNode>[]; | |
| for (var rule in groupRuleBody) { | |
| clonedGroupRuleBody.add(rule.clone()); | |
| } | |
| return DocumentDirective(clonedFunctions, clonedGroupRuleBody, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitDocumentDirective(this); | |
| } | |
| class SupportsDirective extends Directive { | |
| final SupportsCondition? condition; | |
| final List<TreeNode> groupRuleBody; | |
| SupportsDirective(this.condition, this.groupRuleBody, SourceSpan? span) | |
| : super(span); | |
| @override | |
| SupportsDirective clone() { | |
| var clonedCondition = condition!.clone() as SupportsCondition; | |
| var clonedGroupRuleBody = <TreeNode>[]; | |
| for (var rule in groupRuleBody) { | |
| clonedGroupRuleBody.add(rule.clone()); | |
| } | |
| return SupportsDirective(clonedCondition, clonedGroupRuleBody, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitSupportsDirective(this); | |
| } | |
| abstract class SupportsCondition extends TreeNode { | |
| SupportsCondition(super.span); | |
| @override | |
| SourceSpan get span => super.span!; | |
| } | |
| class SupportsConditionInParens extends SupportsCondition { | |
| /// A [Declaration] or nested [SupportsCondition]. | |
| final TreeNode? condition; | |
| SupportsConditionInParens(Declaration? declaration, super.span) | |
| : condition = declaration; | |
| SupportsConditionInParens.nested(this.condition, SourceSpan? span) | |
| : super(span); | |
| @override | |
| SupportsConditionInParens clone() => | |
| SupportsConditionInParens(condition!.clone() as Declaration, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => | |
| visitor.visitSupportsConditionInParens(this); | |
| } | |
| class SupportsNegation extends SupportsCondition { | |
| final SupportsConditionInParens condition; | |
| SupportsNegation(this.condition, SourceSpan? span) : super(span); | |
| @override | |
| SupportsNegation clone() => SupportsNegation(condition.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitSupportsNegation(this); | |
| } | |
| class SupportsConjunction extends SupportsCondition { | |
| final List<SupportsConditionInParens> conditions; | |
| SupportsConjunction(this.conditions, SourceSpan? span) : super(span); | |
| @override | |
| SupportsConjunction clone() { | |
| var clonedConditions = <SupportsConditionInParens>[]; | |
| for (var condition in conditions) { | |
| clonedConditions.add(condition.clone()); | |
| } | |
| return SupportsConjunction(clonedConditions, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitSupportsConjunction(this); | |
| } | |
| class SupportsDisjunction extends SupportsCondition { | |
| final List<SupportsConditionInParens> conditions; | |
| SupportsDisjunction(this.conditions, SourceSpan? span) : super(span); | |
| @override | |
| SupportsDisjunction clone() { | |
| var clonedConditions = <SupportsConditionInParens>[]; | |
| for (var condition in conditions) { | |
| clonedConditions.add(condition.clone()); | |
| } | |
| return SupportsDisjunction(clonedConditions, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitSupportsDisjunction(this); | |
| } | |
| class ViewportDirective extends Directive { | |
| final String name; | |
| final DeclarationGroup declarations; | |
| ViewportDirective(this.name, this.declarations, SourceSpan? span) | |
| : super(span); | |
| @override | |
| ViewportDirective clone() => | |
| ViewportDirective(name, declarations.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitViewportDirective(this); | |
| } | |
| class ImportDirective extends Directive { | |
| /// import name specified. | |
| final String import; | |
| /// Any media queries for this import. | |
| final List<MediaQuery> mediaQueries; | |
| ImportDirective(this.import, this.mediaQueries, SourceSpan? span) | |
| : super(span); | |
| @override | |
| ImportDirective clone() { | |
| var cloneMediaQueries = <MediaQuery>[]; | |
| for (var mediaQuery in mediaQueries) { | |
| cloneMediaQueries.add(mediaQuery.clone()); | |
| } | |
| return ImportDirective(import, cloneMediaQueries, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitImportDirective(this); | |
| } | |
| /// MediaExpression grammar: | |
| /// | |
| /// '(' S* media_feature S* [ ':' S* expr ]? ')' S* | |
| class MediaExpression extends TreeNode { | |
| final bool andOperator; | |
| final Identifier _mediaFeature; | |
| final Expressions exprs; | |
| MediaExpression( | |
| this.andOperator, this._mediaFeature, this.exprs, SourceSpan? span) | |
| : super(span); | |
| String get mediaFeature => _mediaFeature.name; | |
| @override | |
| SourceSpan get span => super.span!; | |
| @override | |
| MediaExpression clone() { | |
| var clonedExprs = exprs.clone(); | |
| return MediaExpression(andOperator, _mediaFeature, clonedExprs, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitMediaExpression(this); | |
| } | |
| /// MediaQuery grammar: | |
| /// | |
| /// : [ONLY | NOT]? S* media_type S* [ AND S* media_expression ]* | |
| /// | media_expression [ AND S* media_expression ]* | |
| /// media_type | |
| /// : IDENT | |
| /// media_expression | |
| /// : '(' S* media_feature S* [ ':' S* expr ]? ')' S* | |
| /// media_feature | |
| /// : IDENT | |
| class MediaQuery extends TreeNode { | |
| /// not, only or no operator. | |
| final int _mediaUnary; | |
| final Identifier? _mediaType; | |
| final List<MediaExpression> expressions; | |
| MediaQuery( | |
| this._mediaUnary, this._mediaType, this.expressions, SourceSpan? span) | |
| : super(span); | |
| bool get hasMediaType => _mediaType != null; | |
| String get mediaType => _mediaType!.name; | |
| bool get hasUnary => _mediaUnary != -1; | |
| String get unary => | |
| TokenKind.idToValue(TokenKind.MEDIA_OPERATORS, _mediaUnary)! | |
| .toUpperCase(); | |
| @override | |
| SourceSpan get span => super.span!; | |
| @override | |
| MediaQuery clone() { | |
| var cloneExpressions = <MediaExpression>[]; | |
| for (var expr in expressions) { | |
| cloneExpressions.add(expr.clone()); | |
| } | |
| return MediaQuery(_mediaUnary, _mediaType, cloneExpressions, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitMediaQuery(this); | |
| } | |
| class MediaDirective extends Directive { | |
| final List<MediaQuery> mediaQueries; | |
| final List<TreeNode> rules; | |
| MediaDirective(this.mediaQueries, this.rules, SourceSpan? span) : super(span); | |
| @override | |
| MediaDirective clone() { | |
| var cloneQueries = <MediaQuery>[]; | |
| for (var mediaQuery in mediaQueries) { | |
| cloneQueries.add(mediaQuery.clone()); | |
| } | |
| var cloneRules = <TreeNode>[]; | |
| for (var rule in rules) { | |
| cloneRules.add(rule.clone()); | |
| } | |
| return MediaDirective(cloneQueries, cloneRules, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitMediaDirective(this); | |
| } | |
| class HostDirective extends Directive { | |
| final List<TreeNode> rules; | |
| HostDirective(this.rules, SourceSpan? span) : super(span); | |
| @override | |
| HostDirective clone() { | |
| var cloneRules = <TreeNode>[]; | |
| for (var rule in rules) { | |
| cloneRules.add(rule.clone()); | |
| } | |
| return HostDirective(cloneRules, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitHostDirective(this); | |
| } | |
| class PageDirective extends Directive { | |
| final String? _ident; | |
| final String? _pseudoPage; | |
| final List<DeclarationGroup> _declsMargin; | |
| PageDirective( | |
| this._ident, this._pseudoPage, this._declsMargin, SourceSpan? span) | |
| : super(span); | |
| @override | |
| PageDirective clone() { | |
| var cloneDeclsMargin = <DeclarationGroup>[]; | |
| for (var declMargin in _declsMargin) { | |
| cloneDeclsMargin.add(declMargin.clone()); | |
| } | |
| return PageDirective(_ident, _pseudoPage, cloneDeclsMargin, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitPageDirective(this); | |
| bool get hasIdent => _ident?.isNotEmpty ?? false; | |
| bool get hasPseudoPage => _pseudoPage?.isNotEmpty ?? false; | |
| } | |
| class CharsetDirective extends Directive { | |
| final String charEncoding; | |
| CharsetDirective(this.charEncoding, SourceSpan? span) : super(span); | |
| @override | |
| CharsetDirective clone() => CharsetDirective(charEncoding, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitCharsetDirective(this); | |
| } | |
| class KeyFrameDirective extends Directive { | |
| // Either @keyframe or keyframe prefixed with @-webkit-, @-moz-, @-ms-, @-o-. | |
| final int _keyframeName; | |
| final Identifier? name; | |
| final List<KeyFrameBlock> _blocks; | |
| KeyFrameDirective(this._keyframeName, this.name, SourceSpan? span) | |
| : _blocks = [], | |
| super(span); | |
| void add(KeyFrameBlock block) { | |
| _blocks.add(block); | |
| } | |
| String? get keyFrameName => switch (_keyframeName) { | |
| TokenKind.DIRECTIVE_KEYFRAMES || | |
| TokenKind.DIRECTIVE_MS_KEYFRAMES => | |
| '@keyframes', | |
| TokenKind.DIRECTIVE_WEB_KIT_KEYFRAMES => '@-webkit-keyframes', | |
| TokenKind.DIRECTIVE_MOZ_KEYFRAMES => '@-moz-keyframes', | |
| TokenKind.DIRECTIVE_O_KEYFRAMES => '@-o-keyframes', | |
| _ => null | |
| }; | |
| @override | |
| KeyFrameDirective clone() { | |
| var directive = KeyFrameDirective(_keyframeName, name!.clone(), span); | |
| for (var block in _blocks) { | |
| directive.add(block.clone()); | |
| } | |
| return directive; | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitKeyFrameDirective(this); | |
| } | |
| class KeyFrameBlock extends Expression { | |
| final Expressions _blockSelectors; | |
| final DeclarationGroup _declarations; | |
| KeyFrameBlock(this._blockSelectors, this._declarations, SourceSpan? span) | |
| : super(span); | |
| @override | |
| KeyFrameBlock clone() => | |
| KeyFrameBlock(_blockSelectors.clone(), _declarations.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitKeyFrameBlock(this); | |
| } | |
| class FontFaceDirective extends Directive { | |
| final DeclarationGroup _declarations; | |
| FontFaceDirective(this._declarations, SourceSpan? span) : super(span); | |
| @override | |
| FontFaceDirective clone() => FontFaceDirective(_declarations.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitFontFaceDirective(this); | |
| } | |
| class StyletDirective extends Directive { | |
| final String dartClassName; | |
| final List<TreeNode> rules; | |
| StyletDirective(this.dartClassName, this.rules, SourceSpan? span) | |
| : super(span); | |
| @override | |
| bool get isBuiltIn => false; | |
| @override | |
| bool get isExtension => true; | |
| @override | |
| StyletDirective clone() { | |
| var cloneRules = <TreeNode>[]; | |
| for (var rule in rules) { | |
| cloneRules.add(rule.clone()); | |
| } | |
| return StyletDirective(dartClassName, cloneRules, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitStyletDirective(this); | |
| } | |
| class NamespaceDirective extends Directive { | |
| /// Namespace prefix. | |
| final String _prefix; | |
| /// URI associated with this namespace. | |
| final String? _uri; | |
| NamespaceDirective(this._prefix, this._uri, SourceSpan? span) : super(span); | |
| @override | |
| NamespaceDirective clone() => NamespaceDirective(_prefix, _uri, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitNamespaceDirective(this); | |
| String get prefix => _prefix.isNotEmpty ? '$_prefix ' : ''; | |
| } | |
| /// To support Less syntax @name: expression | |
| class VarDefinitionDirective extends Directive { | |
| final VarDefinition def; | |
| VarDefinitionDirective(this.def, SourceSpan? span) : super(span); | |
| @override | |
| VarDefinitionDirective clone() => VarDefinitionDirective(def.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => | |
| visitor.visitVarDefinitionDirective(this); | |
| } | |
| class MixinDefinition extends Directive { | |
| final String name; | |
| final List<TreeNode> definedArgs; | |
| final bool varArgs; | |
| MixinDefinition(this.name, this.definedArgs, this.varArgs, SourceSpan? span) | |
| : super(span); | |
| @override | |
| MixinDefinition clone() { | |
| var cloneDefinedArgs = <TreeNode>[]; | |
| for (var definedArg in definedArgs) { | |
| cloneDefinedArgs.add(definedArg.clone()); | |
| } | |
| return MixinDefinition(name, cloneDefinedArgs, varArgs, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitMixinDefinition(this); | |
| } | |
| /// Support a Sass @mixin. See http://sass-lang.com for description. | |
| class MixinRulesetDirective extends MixinDefinition { | |
| final List<TreeNode> rulesets; | |
| MixinRulesetDirective(String name, List<TreeNode> args, bool varArgs, | |
| this.rulesets, SourceSpan? span) | |
| : super(name, args, varArgs, span); | |
| @override | |
| MixinRulesetDirective clone() { | |
| var clonedArgs = <VarDefinition>[]; | |
| for (var arg in definedArgs) { | |
| clonedArgs.add(arg.clone() as VarDefinition); | |
| } | |
| var clonedRulesets = <TreeNode>[]; | |
| for (var ruleset in rulesets) { | |
| clonedRulesets.add(ruleset.clone()); | |
| } | |
| return MixinRulesetDirective( | |
| name, clonedArgs, varArgs, clonedRulesets, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => | |
| visitor.visitMixinRulesetDirective(this); | |
| } | |
| class MixinDeclarationDirective extends MixinDefinition { | |
| final DeclarationGroup declarations; | |
| MixinDeclarationDirective(String name, List<TreeNode> args, bool varArgs, | |
| this.declarations, SourceSpan? span) | |
| : super(name, args, varArgs, span); | |
| @override | |
| MixinDeclarationDirective clone() { | |
| var clonedArgs = <TreeNode>[]; | |
| for (var arg in definedArgs) { | |
| clonedArgs.add(arg.clone()); | |
| } | |
| return MixinDeclarationDirective( | |
| name, clonedArgs, varArgs, declarations.clone(), span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => | |
| visitor.visitMixinDeclarationDirective(this); | |
| } | |
| /// To support consuming a Sass mixin @include. | |
| class IncludeDirective extends Directive { | |
| final String name; | |
| final List<List<Expression>> args; | |
| IncludeDirective(this.name, this.args, SourceSpan? span) : super(span); | |
| @override | |
| IncludeDirective clone() { | |
| var cloneArgs = <List<Expression>>[]; | |
| for (var arg in args) { | |
| cloneArgs.add(arg.map((term) => term.clone()).toList()); | |
| } | |
| return IncludeDirective(name, cloneArgs, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitIncludeDirective(this); | |
| } | |
| /// To support Sass @content. | |
| class ContentDirective extends Directive { | |
| ContentDirective(super.span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitContentDirective(this); | |
| } | |
| class Declaration extends TreeNode { | |
| final Identifier? _property; | |
| final Expression? expression; | |
| /// Style exposed to Dart. | |
| DartStyleExpression? dartStyle; | |
| final bool important; | |
| /// IE CSS hacks that can only be read by a particular IE version. | |
| /// 7 implies IE 7 or older property (e.g., `*background: blue;`) | |
| /// | |
| /// * Note: IE 8 or older property (e.g., `background: green\9;`) is handled | |
| /// by IE8Term in declaration expression handling. | |
| /// * Note: IE 6 only property with a leading underscore is a valid IDENT | |
| /// since an ident can start with underscore (e.g., `_background: red;`) | |
| final bool isIE7; | |
| Declaration(this._property, this.expression, this.dartStyle, SourceSpan? span, | |
| {this.important = false, bool ie7 = false}) | |
| : isIE7 = ie7, | |
| super(span); | |
| String get property => isIE7 ? '*${_property!.name}' : _property!.name; | |
| bool get hasDartStyle => dartStyle != null; | |
| @override | |
| SourceSpan get span => super.span!; | |
| @override | |
| Declaration clone() => | |
| Declaration(_property!.clone(), expression!.clone(), dartStyle, span, | |
| important: important); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitDeclaration(this); | |
| } | |
| // TODO(terry): Consider 2 kinds of VarDefinitions static at top-level and | |
| // dynamic when in a declaration. Currently, Less syntax | |
| // '@foo: expression' and 'var-foo: expression' in a declaration | |
| // are statically resolved. Better solution, if @foo or var-foo | |
| // are top-level are then statically resolved and var-foo in a | |
| // declaration group (surrounded by a selector) would be dynamic. | |
| class VarDefinition extends Declaration { | |
| bool badUsage = false; | |
| VarDefinition(Identifier? definedName, Expression? expr, SourceSpan? span) | |
| : super(definedName, expr, null, span); | |
| String get definedName => _property!.name; | |
| @override | |
| VarDefinition clone() => | |
| VarDefinition(_property!.clone(), expression?.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitVarDefinition(this); | |
| } | |
| /// Node for usage of @include mixin[(args,...)] found in a declaration group | |
| /// instead of at a ruleset (toplevel) e.g., | |
| /// | |
| /// div { | |
| /// @include mixin1; | |
| /// } | |
| class IncludeMixinAtDeclaration extends Declaration { | |
| final IncludeDirective include; | |
| IncludeMixinAtDeclaration(this.include, SourceSpan? span) | |
| : super(null, null, null, span); | |
| @override | |
| IncludeMixinAtDeclaration clone() => | |
| IncludeMixinAtDeclaration(include.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => | |
| visitor.visitIncludeMixinAtDeclaration(this); | |
| } | |
| class ExtendDeclaration extends Declaration { | |
| final List<TreeNode> selectors; | |
| ExtendDeclaration(this.selectors, SourceSpan? span) | |
| : super(null, null, null, span); | |
| @override | |
| ExtendDeclaration clone() { | |
| var newSelector = selectors.map((s) => s.clone()).toList(); | |
| return ExtendDeclaration(newSelector, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitExtendDeclaration(this); | |
| } | |
| class DeclarationGroup extends TreeNode { | |
| /// Can be either Declaration or RuleSet (if nested selector). | |
| final List<TreeNode> declarations; | |
| DeclarationGroup(this.declarations, SourceSpan? span) : super(span); | |
| @override | |
| SourceSpan get span => super.span!; | |
| @override | |
| DeclarationGroup clone() { | |
| var clonedDecls = declarations.map((d) => d.clone()).toList(); | |
| return DeclarationGroup(clonedDecls, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitDeclarationGroup(this); | |
| } | |
| class MarginGroup extends DeclarationGroup { | |
| // ignore: non_constant_identifier_names | |
| final int margin_sym; // TokenType for for @margin sym. | |
| MarginGroup(this.margin_sym, List<TreeNode> decls, SourceSpan? span) | |
| : super(decls, span); | |
| @override | |
| MarginGroup clone() => | |
| MarginGroup(margin_sym, super.clone().declarations, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitMarginGroup(this); | |
| } | |
| class VarUsage extends Expression { | |
| final String name; | |
| final List<Expression> defaultValues; | |
| VarUsage(this.name, this.defaultValues, SourceSpan? span) : super(span); | |
| @override | |
| VarUsage clone() { | |
| var clonedValues = <Expression>[]; | |
| for (var expr in defaultValues) { | |
| clonedValues.add(expr.clone()); | |
| } | |
| return VarUsage(name, clonedValues, span); | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitVarUsage(this); | |
| } | |
| class OperatorSlash extends Expression { | |
| OperatorSlash(super.span); | |
| @override | |
| OperatorSlash clone() => OperatorSlash(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitOperatorSlash(this); | |
| } | |
| class OperatorComma extends Expression { | |
| OperatorComma(super.span); | |
| @override | |
| OperatorComma clone() => OperatorComma(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitOperatorComma(this); | |
| } | |
| class OperatorPlus extends Expression { | |
| OperatorPlus(super.span); | |
| @override | |
| OperatorPlus clone() => OperatorPlus(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitOperatorPlus(this); | |
| } | |
| class OperatorMinus extends Expression { | |
| OperatorMinus(super.span); | |
| @override | |
| OperatorMinus clone() => OperatorMinus(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitOperatorMinus(this); | |
| } | |
| class UnicodeRangeTerm extends Expression { | |
| final String? first; | |
| final String? second; | |
| UnicodeRangeTerm(this.first, this.second, SourceSpan? span) : super(span); | |
| bool get hasSecond => second != null; | |
| @override | |
| UnicodeRangeTerm clone() => UnicodeRangeTerm(first, second, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitUnicodeRangeTerm(this); | |
| } | |
| class LiteralTerm extends Expression { | |
| // TODO(terry): value and text fields can be made final once all CSS resources | |
| // are copied/symlink'd in the build tool and UriVisitor in | |
| // web_ui is removed. | |
| Object value; | |
| String text; | |
| LiteralTerm(this.value, this.text, SourceSpan? span) : super(span); | |
| @override | |
| LiteralTerm clone() => LiteralTerm(value, text, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitLiteralTerm(this); | |
| } | |
| class NumberTerm extends LiteralTerm { | |
| NumberTerm(super.value, super.text, super.span); | |
| @override | |
| NumberTerm clone() => NumberTerm(value, text, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitNumberTerm(this); | |
| } | |
| class UnitTerm extends LiteralTerm { | |
| final int unit; | |
| UnitTerm(super.value, super.text, super.span, this.unit); | |
| @override | |
| UnitTerm clone() => UnitTerm(value, text, span, unit); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitUnitTerm(this); | |
| String? unitToString() => TokenKind.unitToString(unit); | |
| @override | |
| String toString() => '$text${unitToString()}'; | |
| } | |
| class LengthTerm extends UnitTerm { | |
| LengthTerm(super.value, super.text, super.span, | |
| [super.unit = TokenKind.UNIT_LENGTH_PX]) { | |
| assert(unit == TokenKind.UNIT_LENGTH_PX || | |
| unit == TokenKind.UNIT_LENGTH_CM || | |
| unit == TokenKind.UNIT_LENGTH_MM || | |
| unit == TokenKind.UNIT_LENGTH_IN || | |
| unit == TokenKind.UNIT_LENGTH_PT || | |
| unit == TokenKind.UNIT_LENGTH_PC); | |
| } | |
| @override | |
| LengthTerm clone() => LengthTerm(value, text, span, unit); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitLengthTerm(this); | |
| } | |
| class PercentageTerm extends LiteralTerm { | |
| PercentageTerm(super.value, super.text, super.span); | |
| @override | |
| PercentageTerm clone() => PercentageTerm(value, text, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitPercentageTerm(this); | |
| } | |
| class EmTerm extends LiteralTerm { | |
| EmTerm(super.value, super.text, super.span); | |
| @override | |
| EmTerm clone() => EmTerm(value, text, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitEmTerm(this); | |
| } | |
| class ExTerm extends LiteralTerm { | |
| ExTerm(super.value, super.text, super.span); | |
| @override | |
| ExTerm clone() => ExTerm(value, text, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitExTerm(this); | |
| } | |
| class AngleTerm extends UnitTerm { | |
| AngleTerm(super.value, super.text, super.span, | |
| [super.unit = TokenKind.UNIT_LENGTH_PX]) { | |
| assert(unit == TokenKind.UNIT_ANGLE_DEG || | |
| unit == TokenKind.UNIT_ANGLE_RAD || | |
| unit == TokenKind.UNIT_ANGLE_GRAD || | |
| unit == TokenKind.UNIT_ANGLE_TURN); | |
| } | |
| @override | |
| AngleTerm clone() => AngleTerm(value, text, span, unit); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitAngleTerm(this); | |
| } | |
| class TimeTerm extends UnitTerm { | |
| TimeTerm(super.value, super.text, super.span, | |
| [super.unit = TokenKind.UNIT_LENGTH_PX]) { | |
| assert(unit == TokenKind.UNIT_ANGLE_DEG || | |
| unit == TokenKind.UNIT_TIME_MS || | |
| unit == TokenKind.UNIT_TIME_S); | |
| } | |
| @override | |
| TimeTerm clone() => TimeTerm(value, text, span, unit); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitTimeTerm(this); | |
| } | |
| class FreqTerm extends UnitTerm { | |
| FreqTerm(Object value, String text, SourceSpan? span, | |
| [int unit = TokenKind.UNIT_LENGTH_PX]) | |
| : super(value, text, span, unit) { | |
| assert(unit == TokenKind.UNIT_FREQ_HZ || unit == TokenKind.UNIT_FREQ_KHZ); | |
| } | |
| @override | |
| FreqTerm clone() => FreqTerm(value, text, span, unit); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitFreqTerm(this); | |
| } | |
| class FractionTerm extends LiteralTerm { | |
| FractionTerm(super.value, super.text, super.span); | |
| @override | |
| FractionTerm clone() => FractionTerm(value, text, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitFractionTerm(this); | |
| } | |
| class UriTerm extends LiteralTerm { | |
| UriTerm(String value, SourceSpan? span) : super(value, value, span); | |
| @override | |
| UriTerm clone() => UriTerm(value as String, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitUriTerm(this); | |
| } | |
| class ResolutionTerm extends UnitTerm { | |
| ResolutionTerm(Object value, String text, SourceSpan? span, | |
| [int unit = TokenKind.UNIT_LENGTH_PX]) | |
| : super(value, text, span, unit) { | |
| assert(unit == TokenKind.UNIT_RESOLUTION_DPI || | |
| unit == TokenKind.UNIT_RESOLUTION_DPCM || | |
| unit == TokenKind.UNIT_RESOLUTION_DPPX); | |
| } | |
| @override | |
| ResolutionTerm clone() => ResolutionTerm(value, text, span, unit); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitResolutionTerm(this); | |
| } | |
| class ChTerm extends UnitTerm { | |
| ChTerm(Object value, String text, SourceSpan? span, | |
| [int unit = TokenKind.UNIT_LENGTH_PX]) | |
| : super(value, text, span, unit) { | |
| assert(unit == TokenKind.UNIT_CH); | |
| } | |
| @override | |
| ChTerm clone() => ChTerm(value, text, span, unit); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitChTerm(this); | |
| } | |
| class RemTerm extends UnitTerm { | |
| RemTerm(Object value, String text, SourceSpan? span, | |
| [int unit = TokenKind.UNIT_LENGTH_PX]) | |
| : super(value, text, span, unit) { | |
| assert(unit == TokenKind.UNIT_REM); | |
| } | |
| @override | |
| RemTerm clone() => RemTerm(value, text, span, unit); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitRemTerm(this); | |
| } | |
| class LineHeightTerm extends UnitTerm { | |
| LineHeightTerm(Object value, String text, SourceSpan? span, int unit) | |
| : super(value, text, span, unit) { | |
| assert(unit == TokenKind.UNIT_LH || unit == TokenKind.UNIT_RLH); | |
| } | |
| @override | |
| LineHeightTerm clone() => LineHeightTerm(value, text, span, unit); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitLineHeightTerm(this); | |
| } | |
| class ViewportTerm extends UnitTerm { | |
| ViewportTerm(Object value, String text, SourceSpan? span, | |
| [int unit = TokenKind.UNIT_LENGTH_PX]) | |
| : super(value, text, span, unit) { | |
| assert(unit == TokenKind.UNIT_VIEWPORT_VW || | |
| unit == TokenKind.UNIT_VIEWPORT_VH || | |
| unit == TokenKind.UNIT_VIEWPORT_VMIN || | |
| unit == TokenKind.UNIT_VIEWPORT_VMAX); | |
| } | |
| @override | |
| ViewportTerm clone() => ViewportTerm(value, text, span, unit); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitViewportTerm(this); | |
| } | |
| /// Type to signal a bad hex value for HexColorTerm.value. | |
| // ignore: camel_case_types | |
| class BAD_HEX_VALUE {} | |
| class HexColorTerm extends LiteralTerm { | |
| HexColorTerm(super.value, super.text, super.span); | |
| @override | |
| HexColorTerm clone() => HexColorTerm(value, text, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitHexColorTerm(this); | |
| } | |
| class FunctionTerm extends LiteralTerm { | |
| final Expressions _params; | |
| FunctionTerm(Object value, String text, this._params, SourceSpan? span) | |
| : super(value, text, span); | |
| @override | |
| FunctionTerm clone() => FunctionTerm(value, text, _params.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitFunctionTerm(this); | |
| } | |
| /// A "\9" was encountered at the end of the expression and before a semi-colon. | |
| /// This is an IE trick to ignore a property or value except by IE 8 and older | |
| /// browsers. | |
| class IE8Term extends LiteralTerm { | |
| IE8Term(SourceSpan? span) : super('\\9', '\\9', span); | |
| @override | |
| IE8Term clone() => IE8Term(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitIE8Term(this); | |
| } | |
| class GroupTerm extends Expression { | |
| final List<LiteralTerm> _terms; | |
| GroupTerm(super.span) : _terms = []; | |
| void add(LiteralTerm term) { | |
| _terms.add(term); | |
| } | |
| @override | |
| GroupTerm clone() => GroupTerm(span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitGroupTerm(this); | |
| } | |
| class ItemTerm extends NumberTerm { | |
| ItemTerm(super.value, super.text, super.span); | |
| @override | |
| ItemTerm clone() => ItemTerm(value, text, span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitItemTerm(this); | |
| } | |
| class Expressions extends Expression { | |
| final List<Expression> expressions = []; | |
| Expressions(super.span); | |
| void add(Expression expression) { | |
| expressions.add(expression); | |
| } | |
| @override | |
| Expressions clone() { | |
| var clonedExprs = Expressions(span); | |
| for (var expr in expressions) { | |
| clonedExprs.add(expr.clone()); | |
| } | |
| return clonedExprs; | |
| } | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitExpressions(this); | |
| } | |
| class BinaryExpression extends Expression { | |
| final Token op; | |
| final Expression x; | |
| final Expression y; | |
| BinaryExpression(this.op, this.x, this.y, SourceSpan? span) : super(span); | |
| @override | |
| BinaryExpression clone() => BinaryExpression(op, x.clone(), y.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitBinaryExpression(this); | |
| } | |
| class UnaryExpression extends Expression { | |
| final Token op; | |
| final Expression self; | |
| UnaryExpression(this.op, this.self, SourceSpan? span) : super(span); | |
| @override | |
| UnaryExpression clone() => UnaryExpression(op, self.clone(), span); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitUnaryExpression(this); | |
| } | |
| abstract class DartStyleExpression extends TreeNode { | |
| static const int unknownType = 0; | |
| static const int fontStyle = 1; | |
| static const int marginStyle = 2; | |
| static const int borderStyle = 3; | |
| static const int paddingStyle = 4; | |
| static const int heightStyle = 5; | |
| static const int widthStyle = 6; | |
| final int? _styleType; | |
| int? priority; | |
| DartStyleExpression(this._styleType, SourceSpan? span) : super(span); | |
| // Merges give 2 DartStyleExpression (or derived from DartStyleExpression, | |
| // e.g., FontExpression, etc.) will merge if the two expressions are of the | |
| // same property name (implies same exact type e.g, FontExpression). | |
| DartStyleExpression? merged(DartStyleExpression newDartExpr); | |
| bool get isUnknown => _styleType == 0 || _styleType == null; | |
| bool get isFont => _styleType == fontStyle; | |
| bool get isMargin => _styleType == marginStyle; | |
| bool get isBorder => _styleType == borderStyle; | |
| bool get isPadding => _styleType == paddingStyle; | |
| bool get isHeight => _styleType == heightStyle; | |
| bool get isWidth => _styleType == widthStyle; | |
| bool get isBoxExpression => isMargin || isBorder || isPadding; | |
| bool isSame(DartStyleExpression other) => _styleType == other._styleType; | |
| @override | |
| SourceSpan get span => super.span!; | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitDartStyleExpression(this); | |
| } | |
| class FontExpression extends DartStyleExpression { | |
| final Font font; | |
| // font-style font-variant font-weight font-size/line-height font-family | |
| // TODO(terry): Only px/pt for now need to handle all possible units to | |
| // support calc expressions on units. | |
| FontExpression(SourceSpan? span, | |
| {Object? /* LengthTerm | num */ size, | |
| List<String>? family, | |
| int? weight, | |
| String? style, | |
| String? variant, | |
| LineHeight? lineHeight}) | |
| : font = Font( | |
| size: (size is LengthTerm ? size.value : size) as num?, | |
| family: family, | |
| weight: weight, | |
| style: style, | |
| variant: variant, | |
| lineHeight: lineHeight), | |
| super(DartStyleExpression.fontStyle, span); | |
| @override | |
| FontExpression? merged(DartStyleExpression newDartExpr) { | |
| if (newDartExpr is FontExpression && isFont && newDartExpr.isFont) { | |
| return FontExpression.merge(this, newDartExpr); | |
| } | |
| return null; | |
| } | |
| /// Merge the two FontExpression and return the result. | |
| factory FontExpression.merge(FontExpression x, FontExpression y) => | |
| FontExpression._merge(x, y, y.span); | |
| FontExpression._merge(FontExpression x, FontExpression y, SourceSpan? span) | |
| : font = Font.merge(x.font, y.font)!, | |
| super(DartStyleExpression.fontStyle, span); | |
| @override | |
| FontExpression clone() => FontExpression(span, | |
| size: font.size, | |
| family: font.family, | |
| weight: font.weight, | |
| style: font.style, | |
| variant: font.variant, | |
| lineHeight: font.lineHeight); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitFontExpression(this); | |
| } | |
| abstract class BoxExpression extends DartStyleExpression { | |
| final BoxEdge? box; | |
| BoxExpression(super.styleType, super.span, this.box); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitBoxExpression(this); | |
| String get formattedBoxEdge { | |
| if (box!.top == box!.left && | |
| box!.top == box!.bottom && | |
| box!.top == box!.right) { | |
| return '.uniform(${box!.top})'; | |
| } else { | |
| var left = box!.left ?? 0; | |
| var top = box!.top ?? 0; | |
| var right = box!.right ?? 0; | |
| var bottom = box!.bottom ?? 0; | |
| return '.clockwiseFromTop($top,$right,$bottom,$left)'; | |
| } | |
| } | |
| } | |
| class MarginExpression extends BoxExpression { | |
| // TODO(terry): Does auto for margin need to be exposed to Dart UI framework? | |
| /// Margin expression ripped apart. | |
| MarginExpression(SourceSpan? span, | |
| {num? top, num? right, num? bottom, num? left}) | |
| : super(DartStyleExpression.marginStyle, span, | |
| BoxEdge(left, top, right, bottom)); | |
| MarginExpression.boxEdge(SourceSpan? span, BoxEdge? box) | |
| : super(DartStyleExpression.marginStyle, span, box); | |
| @override | |
| MarginExpression? merged(DartStyleExpression newDartExpr) { | |
| if (newDartExpr is MarginExpression && isMargin && newDartExpr.isMargin) { | |
| return MarginExpression.merge(this, newDartExpr); | |
| } | |
| return null; | |
| } | |
| /// Merge the two MarginExpressions and return the result. | |
| factory MarginExpression.merge(MarginExpression x, MarginExpression y) => | |
| MarginExpression._merge(x, y, y.span); | |
| MarginExpression._merge( | |
| MarginExpression x, MarginExpression y, SourceSpan? span) | |
| : super(x._styleType, span, BoxEdge.merge(x.box, y.box)); | |
| @override | |
| MarginExpression clone() => MarginExpression(span, | |
| top: box!.top, right: box!.right, bottom: box!.bottom, left: box!.left); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitMarginExpression(this); | |
| } | |
| class BorderExpression extends BoxExpression { | |
| /// Border expression ripped apart. | |
| BorderExpression(SourceSpan? span, | |
| {num? top, num? right, num? bottom, num? left}) | |
| : super(DartStyleExpression.borderStyle, span, | |
| BoxEdge(left, top, right, bottom)); | |
| BorderExpression.boxEdge(SourceSpan? span, BoxEdge box) | |
| : super(DartStyleExpression.borderStyle, span, box); | |
| @override | |
| BorderExpression? merged(DartStyleExpression newDartExpr) { | |
| if (newDartExpr is BorderExpression && isBorder && newDartExpr.isBorder) { | |
| return BorderExpression.merge(this, newDartExpr); | |
| } | |
| return null; | |
| } | |
| /// Merge the two BorderExpression and return the result. | |
| factory BorderExpression.merge(BorderExpression x, BorderExpression y) => | |
| BorderExpression._merge(x, y, y.span); | |
| BorderExpression._merge( | |
| BorderExpression x, BorderExpression y, SourceSpan? span) | |
| : super( | |
| DartStyleExpression.borderStyle, span, BoxEdge.merge(x.box, y.box)); | |
| @override | |
| BorderExpression clone() => BorderExpression(span, | |
| top: box!.top, right: box!.right, bottom: box!.bottom, left: box!.left); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitBorderExpression(this); | |
| } | |
| class HeightExpression extends DartStyleExpression { | |
| final dynamic height; | |
| HeightExpression(SourceSpan? span, this.height) | |
| : super(DartStyleExpression.heightStyle, span); | |
| @override | |
| HeightExpression? merged(DartStyleExpression newDartExpr) { | |
| if (isHeight && newDartExpr.isHeight) { | |
| return newDartExpr as HeightExpression; | |
| } | |
| return null; | |
| } | |
| @override | |
| HeightExpression clone() => HeightExpression(span, height); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitHeightExpression(this); | |
| } | |
| class WidthExpression extends DartStyleExpression { | |
| final dynamic width; | |
| WidthExpression(SourceSpan? span, this.width) | |
| : super(DartStyleExpression.widthStyle, span); | |
| @override | |
| WidthExpression? merged(DartStyleExpression newDartExpr) { | |
| if (newDartExpr is WidthExpression && isWidth && newDartExpr.isWidth) { | |
| return newDartExpr; | |
| } | |
| return null; | |
| } | |
| @override | |
| WidthExpression clone() => WidthExpression(span, width); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitWidthExpression(this); | |
| } | |
| class PaddingExpression extends BoxExpression { | |
| /// Padding expression ripped apart. | |
| PaddingExpression(SourceSpan? span, | |
| {num? top, num? right, num? bottom, num? left}) | |
| : super(DartStyleExpression.paddingStyle, span, | |
| BoxEdge(left, top, right, bottom)); | |
| PaddingExpression.boxEdge(SourceSpan? span, BoxEdge? box) | |
| : super(DartStyleExpression.paddingStyle, span, box); | |
| @override | |
| PaddingExpression? merged(DartStyleExpression newDartExpr) { | |
| if (newDartExpr is PaddingExpression && | |
| isPadding && | |
| newDartExpr.isPadding) { | |
| return PaddingExpression.merge(this, newDartExpr); | |
| } | |
| return null; | |
| } | |
| /// Merge the two PaddingExpression and return the result. | |
| factory PaddingExpression.merge(PaddingExpression x, PaddingExpression y) => | |
| PaddingExpression._merge(x, y, y.span); | |
| PaddingExpression._merge( | |
| PaddingExpression x, PaddingExpression y, SourceSpan? span) | |
| : super(DartStyleExpression.paddingStyle, span, | |
| BoxEdge.merge(x.box, y.box)); | |
| @override | |
| PaddingExpression clone() => PaddingExpression(span, | |
| top: box!.top, right: box!.right, bottom: box!.bottom, left: box!.left); | |
| @override | |
| dynamic visit(VisitorBase visitor) => visitor.visitPaddingExpression(this); | |
| } |