| // Copyright (c) 2017, 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. | |
| import 'package:built_collection/built_collection.dart'; | |
| import 'package:built_value/built_value.dart'; | |
| import 'package:meta/meta.dart'; | |
| import '../allocator.dart'; | |
| import '../base.dart'; | |
| import '../emitter.dart'; | |
| import '../visitors.dart'; | |
| import 'expression.dart'; | |
| import 'reference.dart'; | |
| part 'code.g.dart'; | |
| /// Returns a scoped symbol to [Reference], with an import prefix if needed. | |
| /// | |
| /// This is short-hand for [Allocator.allocate] in most implementations. | |
| typedef Allocate = String Function(Reference); | |
| /// Represents arbitrary Dart code (either expressions or statements). | |
| /// | |
| /// See the various constructors for details. | |
| abstract class Code implements Spec { | |
| /// Create a simple code body based on a static string. | |
| const factory Code(String code) = StaticCode._; | |
| /// Create a code based that may use a provided [Allocator] for scoping: | |
| /// | |
| /// ```dart | |
| /// // Emits `_i123.FooType()`, where `_i123` is the import prefix. | |
| /// | |
| /// Code.scope((a) { | |
| /// return '${a.allocate(fooType)}()' | |
| /// }); | |
| /// ``` | |
| const factory Code.scope(String Function(Allocate) scope) = ScopedCode._; | |
| @override | |
| R accept<R>(covariant CodeVisitor<R> visitor, [R? context]); | |
| } | |
| /// Represents blocks of statements of Dart code. | |
| abstract class Block implements Built<Block, BlockBuilder>, Code, Spec { | |
| factory Block([void Function(BlockBuilder) updates]) = _$Block; | |
| factory Block.of(Iterable<Code> statements) => | |
| Block((b) => b..statements.addAll(statements)); | |
| Block._(); | |
| @override | |
| R accept<R>(covariant CodeVisitor<R> visitor, [R? context]) => | |
| visitor.visitBlock(this, context); | |
| BuiltList<Code> get statements; | |
| } | |
| abstract class BlockBuilder implements Builder<Block, BlockBuilder> { | |
| factory BlockBuilder() = _$BlockBuilder; | |
| BlockBuilder._(); | |
| /// Adds an [expression] to [statements]. | |
| /// | |
| /// **NOTE**: Not all expressions are _useful_ statements. | |
| void addExpression(Expression expression) { | |
| statements.add(expression.statement); | |
| } | |
| ListBuilder<Code> statements = ListBuilder<Code>(); | |
| } | |
| /// Knowledge of different types of blocks of code in Dart. | |
| /// | |
| /// **INTERNAL ONLY**. | |
| abstract class CodeVisitor<T> implements SpecVisitor<T> { | |
| T visitBlock(Block code, [T? context]); | |
| T visitStaticCode(StaticCode code, [T? context]); | |
| T visitScopedCode(ScopedCode code, [T? context]); | |
| } | |
| /// Knowledge of how to write valid Dart code from [CodeVisitor]. | |
| abstract mixin class CodeEmitter implements CodeVisitor<StringSink> { | |
| @protected | |
| Allocator get allocator; | |
| @override | |
| StringSink visitBlock(Block block, [StringSink? output]) { | |
| output ??= StringBuffer(); | |
| return visitAll<Code>(block.statements, output, (statement) { | |
| statement.accept(this, output); | |
| }, '\n'); | |
| } | |
| @override | |
| StringSink visitStaticCode(StaticCode code, [StringSink? output]) { | |
| output ??= StringBuffer(); | |
| return output..write(code.code); | |
| } | |
| @override | |
| StringSink visitScopedCode(ScopedCode code, [StringSink? output]) { | |
| output ??= StringBuffer(); | |
| return output..write(code.code(allocator.allocate)); | |
| } | |
| } | |
| /// Returns a generic [Code] that is lazily generated when visited. | |
| Code lazyCode(Code Function() generate) => _LazyCode(generate); | |
| class _LazyCode implements Code { | |
| final Code Function() generate; | |
| const _LazyCode(this.generate); | |
| @override | |
| R accept<R>(CodeVisitor<R> visitor, [R? context]) => | |
| generate().accept(visitor, context); | |
| } | |
| /// Represents a simple, literal code block to be inserted as-is. | |
| class StaticCode implements Code { | |
| final String code; | |
| const StaticCode._(this.code); | |
| @override | |
| R accept<R>(CodeVisitor<R> visitor, [R? context]) => | |
| visitor.visitStaticCode(this, context); | |
| @override | |
| String toString() => code; | |
| } | |
| /// Represents a code block that may require scoping. | |
| class ScopedCode implements Code { | |
| final String Function(Allocate) code; | |
| const ScopedCode._(this.code); | |
| @override | |
| R accept<R>(CodeVisitor<R> visitor, [R? context]) => | |
| visitor.visitScopedCode(this, context); | |
| @override | |
| String toString() => code((ref) => ref.symbol!); | |
| } |