// 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(covariant CodeVisitor visitor, [R? context]); } /// Represents blocks of statements of Dart code. abstract class Block implements Built, Code, Spec { factory Block([void Function(BlockBuilder) updates]) = _$Block; factory Block.of(Iterable statements) => Block((b) => b..statements.addAll(statements)); Block._(); @override R accept(covariant CodeVisitor visitor, [R? context]) => visitor.visitBlock(this, context); BuiltList get statements; } abstract class BlockBuilder implements Builder { 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 statements = ListBuilder(); } /// Knowledge of different types of blocks of code in Dart. /// /// **INTERNAL ONLY**. abstract class CodeVisitor implements SpecVisitor { 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 { @protected Allocator get allocator; @override StringSink visitBlock(Block block, [StringSink? output]) { output ??= StringBuffer(); return visitAll(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)); } } /// Represents a code block that requires lazy visiting. class LazyCode implements Code { final Spec Function(SpecVisitor) generate; const LazyCode._(this.generate); @override R accept(CodeVisitor visitor, [R? context]) => generate(visitor).accept(visitor, context); } /// 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(CodeVisitor 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(CodeVisitor 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(CodeVisitor visitor, [R? context]) => visitor.visitScopedCode(this, context); @override String toString() => code((ref) => ref.symbol!); }