blob: 02f6d92fd5362c53086d9aeb9e5cd121e294d1d3 [file] [log] [blame]
// Copyright (c) 2021, 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:dart_style/dart_style.dart' show DartFormatter;
import 'ast_model.dart';
/// Generates a visitor library into [sb] based on [astModel] and [strategy].
///
/// If [format] is `false`, the generated output will _not_ be formatted using
/// the Dart formatter. Use this during development to support incomplete
/// generation.
String generateVisitor(AstModel astModel, VisitorStrategy strategy,
{bool format: true}) {
StringBuffer sb = new StringBuffer();
strategy.generateHeader(astModel, sb);
void addVisitNode(AstClass astClass) {
switch (astClass.kind) {
case AstClassKind.root:
case AstClassKind.inner:
if (astClass.hasVisitMethod) {
strategy.generateDefaultVisit(astModel, astClass, sb);
}
for (AstClass subclass in astClass.subclasses) {
addVisitNode(subclass);
}
break;
case AstClassKind.public:
case AstClassKind.named:
case AstClassKind.declarative:
if (astClass.hasVisitMethod) {
strategy.generateVisit(astModel, astClass, sb);
}
break;
case AstClassKind.implementation:
case AstClassKind.interface:
case AstClassKind.utilityAsStructure:
case AstClassKind.utilityAsValue:
break;
}
}
void addVisitReference(AstClass astClass) {
switch (astClass.kind) {
case AstClassKind.root:
case AstClassKind.inner:
if (astClass.hasVisitReferenceMethod) {
strategy.generateDefaultVisitReference(astModel, astClass, sb);
}
for (AstClass subclass in astClass.subclasses) {
addVisitReference(subclass);
}
break;
case AstClassKind.public:
case AstClassKind.named:
case AstClassKind.declarative:
if (astClass.hasVisitReferenceMethod) {
strategy.generateVisitReference(astModel, astClass, sb);
}
break;
case AstClassKind.implementation:
case AstClassKind.interface:
case AstClassKind.utilityAsStructure:
case AstClassKind.utilityAsValue:
break;
}
}
addVisitNode(astModel.nodeClass);
addVisitReference(astModel.namedNodeClass);
addVisitReference(astModel.constantClass);
strategy.generateFooter(astModel, sb);
String result = sb.toString();
if (format) {
result = new DartFormatter().format(result);
}
return result;
}
/// Strategy for generating a visitor in its own library based on an [AstModel].
abstract class VisitorStrategy {
const VisitorStrategy();
/// Preamble comment used in the generated file.
String get preamble => '''
// Copyright (c) 2021, 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.
// NOTE: THIS FILE IS GENERATED. DO NOT EDIT.
//
// Run '$generatorCommand' to update.
''';
/// The command used to generate the visitor.
///
/// This is inserted in the [preamble] along with a comment that the file
/// is generated.
String get generatorCommand;
/// Comment used as doc comment for the generated visitor class.
String get visitorComment => '';
/// Generates the header of the visitor library, including preamble, imports
/// and visitor class declaration start.
void generateHeader(AstModel astModel, StringBuffer sb);
/// Generates a `defaultX` visitor method for [astClass].
void generateDefaultVisit(
AstModel astModel, AstClass astClass, StringBuffer sb);
/// Generates a `visitX` visitor method for [astClass].
void generateVisit(AstModel astModel, AstClass astClass, StringBuffer sb);
/// Generates a `defaultXReference` visitor method for [astClass].
void generateDefaultVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb);
/// Generates a `visitXReference` visitor method for [astClass].
void generateVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb);
/// Generates the footer of the visitor library, including the visitor class
/// declaration end.
void generateFooter(AstModel astModel, StringBuffer sb);
}
/// Base strategy for creating a [Visitor] implementation.
abstract class Visitor0Strategy extends VisitorStrategy {
const Visitor0Strategy();
/// The name of the generated visitor class.
String get visitorName;
/// The type parameters of the generated visitor class.
String get visitorTypeParameters => '';
/// The return type for the visitor methods.
///
/// The generated visitor will implement `Visitor<$returnType>`.
String get returnType;
@override
void generateHeader(AstModel astModel, StringBuffer sb) {
sb.writeln('''
$preamble
import 'package:kernel/ast.dart';
$visitorComment
class $visitorName$visitorTypeParameters implements Visitor<$returnType> {''');
}
@override
void generateFooter(AstModel astModel, StringBuffer sb) {
sb.writeln('''
}''');
}
@override
void generateDefaultVisit(
AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} default${astClass.name}(
${astClass.name} node) {''');
handleDefaultVisit(astModel, astClass, sb);
sb.writeln('}');
}
/// Generates the body of a `defaultX` visitor method of [astClass].
void handleDefaultVisit(
AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
void generateVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} visit${astClass.name}(
${astClass.name} node) {''');
handleVisit(astModel, astClass, sb);
sb.writeln('}');
}
/// Generates the body of a `visitX` visitor method of [astClass].
void handleVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
void generateDefaultVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} default${astClass.name}Reference(
${astClass.name} node) {''');
handleDefaultVisitReference(astModel, astClass, sb);
sb.writeln('}');
}
/// Generates the body of a `defaultXReference` visitor method of [astClass].
void handleDefaultVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
void generateVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} visit${astClass.name}Reference(
${astClass.name} node) {''');
handleVisitReference(astModel, astClass, sb);
sb.writeln('}');
}
/// Generates the body of a `visitXReference` visitor method of [astClass].
void handleVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb) {}
}
/// Strategy for creating an empty `Visitor<void>` implementation.
abstract class VoidVisitor0Strategy extends Visitor0Strategy {
const VoidVisitor0Strategy();
@override
String get visitorName => 'VoidVisitor';
@override
String get returnType => 'void';
}
/// Base strategy for creating a [Visitor1] implementation.
abstract class Visitor1Strategy extends VisitorStrategy {
const Visitor1Strategy();
/// The name of the generated visitor class.
String get visitorName;
/// The type parameters of the generated visitor class.
String get visitorTypeParameters => '';
/// The type of the argument of the visitor methods.
///
/// The generated visitor will implement
/// `Visitor1<$returnType, $argumentType>`.
String get argumentType;
/// The name of the argument parameter name.
String get argumentName => 'arg';
/// The return type for the visitor methods.
///
/// The generated visitor will implement
/// `Visitor1<$returnType, $argumentType>`.
String get returnType;
@override
void generateHeader(AstModel astModel, StringBuffer sb) {
sb.writeln('''
import 'package:kernel/ast.dart';
$visitorComment
class $visitorName$visitorTypeParameters
implements Visitor1<$returnType, $argumentType> {''');
}
@override
void generateFooter(AstModel astModel, StringBuffer sb) {
sb.writeln('''
}''');
}
@override
void generateDefaultVisit(
AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} default${astClass.name}(
${astClass.name} node, $argumentType $argumentName) {''');
handleDefaultVisit(astModel, astClass, sb);
sb.writeln('''
}''');
}
/// Generates the body of a `defaultX` visitor method of [astClass].
void handleDefaultVisit(
AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
void generateVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} visit${astClass.name}(
${astClass.name} node, $argumentType $argumentName) {''');
handleVisit(astModel, astClass, sb);
sb.writeln('''
}''');
}
/// Generates the body of a `visitX` visitor method of [astClass].
void handleVisit(AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
void generateDefaultVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} default${astClass.name}Reference(
${astClass.name} node, $argumentType $argumentName) {''');
handleDefaultVisitReference(astModel, astClass, sb);
sb.writeln('''
}''');
}
/// Generates the body of a `defaultXReference` visitor method of [astClass].
void handleDefaultVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb) {}
@override
void generateVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb) {
sb.writeln('''
@override
${returnType} visit${astClass.name}Reference(
${astClass.name} node, $argumentType $argumentName) {''');
handleVisitReference(astModel, astClass, sb);
sb.writeln('''
}''');
}
/// Generates the body of a `visitXReference` visitor method of [astClass].
void handleVisitReference(
AstModel astModel, AstClass astClass, StringBuffer sb) {}
}
/// Strategy for creating an empty `Visitor1<void,Null>` implementation.
abstract class VoidVisitor1Strategy extends Visitor1Strategy {
const VoidVisitor1Strategy();
@override
String get visitorName => 'VoidVisitor';
@override
String get returnType => 'void';
@override
String get argumentType => 'Null';
@override
String get argumentName => '_';
}