| Index: pkg/codegen/lib/printer.dart
|
| diff --git a/pkg/codegen/lib/printer.dart b/pkg/codegen/lib/printer.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..3e956dc7587e9fc63da71c56512043264cc20955
|
| --- /dev/null
|
| +++ b/pkg/codegen/lib/printer.dart
|
| @@ -0,0 +1,245 @@
|
| +// Copyright (c) 2013, 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.
|
| +
|
| +library codegen.printer;
|
| +
|
| +import 'package:source_maps/source_maps.dart';
|
| +
|
| +/**
|
| + * Helper class to format generated code and keep track of source map
|
| + * information.
|
| + */
|
| +class CodePrinter {
|
| +
|
| + /**
|
| + * Items recoded by this printer, which can be [String] literals,
|
| + * other printing helpers such as [Declarations] and [CodePrinter],
|
| + * and source map information like [Location] and [Span].
|
| + */
|
| + List _items = [];
|
| +
|
| + /** Internal buffer to merge consecutive strings added to this printer. */
|
| + StringBuffer _buff;
|
| +
|
| + /** Current indentation, which can be updated from outside this class. */
|
| + int indent = 0;
|
| +
|
| + /**
|
| + * Item used to indicate that the following item is copied from the original
|
| + * source code, and hence we should preserve source-maps on every new line.
|
| + */
|
| + static final _ORIGINAL = new Object();
|
| +
|
| + CodePrinter(this.indent);
|
| +
|
| + /**
|
| + * Adds [object] to this printer. [object] can be a [String], [Declarations],
|
| + * or a [CodePrinter]. If [object] is a [String], the value is appended
|
| + * directly, without doing any formatting changes. If you wish to add a line
|
| + * of code with automatic indentation, use [addLine] instead. [Declarations]
|
| + * and [CodePrinter] are not processed until [build] gets called later on.
|
| + * We ensure that [build] emits every object in the order that they were added
|
| + * to this printer.
|
| + *
|
| + * The [location] and [span] parameters indicate the corresponding source map
|
| + * location of [object] in the original input. Only one, [location] or
|
| + * [span], should be provided at a time.
|
| + *
|
| + * Indicate [isOriginal] when [object] is copied directly from the user code.
|
| + * Setting [isOriginal] will make this printer propagate source map locations
|
| + * on every line-break.
|
| + */
|
| + void add(object, {Location location, Span span, bool isOriginal: false}) {
|
| + if (object is! String || location != null || span != null || isOriginal) {
|
| + _flush();
|
| + assert(location == null || span == null);
|
| + if (location != null) _items.add(location);
|
| + if (span != null) _items.add(span);
|
| + if (isOriginal) _items.add(_ORIGINAL);
|
| + }
|
| +
|
| + if (object is String) {
|
| + _appendString(object);
|
| + } else {
|
| + _items.add(object);
|
| + }
|
| + }
|
| +
|
| + /** Append `2 * indent` spaces to this printer. */
|
| + void insertIndent() => _indent(indent);
|
| +
|
| + /**
|
| + * Add a [line], autoindenting to the current value of [indent]. Note,
|
| + * indentation is not inferred, so if a line opens or closes an indentation
|
| + * block, you need to also update [indent] accordingly. Also, indentation is
|
| + * not adapted for nested code printers. If you add a [CodePrinter] to this
|
| + * printer, its indentation is set separately and will not include any
|
| + * the indentation set here.
|
| + *
|
| + * The [location] and [span] parameters indicate the corresponding source map
|
| + * location of [object] in the original input. Only one, [location] or
|
| + * [span], should be provided at a time.
|
| + */
|
| + void addLine(String line, {Location location, Span span}) {
|
| + if (location != null || span != null) {
|
| + _flush();
|
| + assert(location == null || span == null);
|
| + if (location != null) _items.add(location);
|
| + if (span != null) _items.add(span);
|
| + }
|
| + if (line == null) return;
|
| + if (line != '') {
|
| + // We don't indent empty lines.
|
| + _indent(indent);
|
| + _appendString(line);
|
| + }
|
| + _appendString('\n');
|
| + }
|
| +
|
| + /** Appends a string merging it with any previous strings, if possible. */
|
| + void _appendString(String s) {
|
| + if (_buff == null) _buff = new StringBuffer();
|
| + _buff.write(s);
|
| + }
|
| +
|
| + /** Adds all of the current [_buff] contents as a string item. */
|
| + void _flush() {
|
| + if (_buff != null) {
|
| + _items.add(_buff.toString());
|
| + _buff = null;
|
| + }
|
| + }
|
| +
|
| + void _indent(int indent) {
|
| + for (int i = 0; i < indent; i++) _appendString(' ');
|
| + }
|
| +
|
| + /**
|
| + * Returns a string representation of all the contents appended to this
|
| + * printer, including source map location tokens.
|
| + */
|
| + String toString() {
|
| + _flush();
|
| + return (new StringBuffer()..writeAll(_items)).toString();
|
| + }
|
| +
|
| + /** [Printer] used during the last call to [build], if any. */
|
| + Printer _printer;
|
| +
|
| + /** Returns the text produced after calling [build]. */
|
| + String get text => _printer.text;
|
| +
|
| + /** Returns the source-map information produced after calling [build]. */
|
| + String get map => _printer.map;
|
| +
|
| + /**
|
| + * Builds the output of this printer and source map information. After calling
|
| + * this function, you can use [text] and [map] to retrieve the geenrated code
|
| + * and source map information, respectively.
|
| + */
|
| + void build(String filename) {
|
| + _build(_printer = new Printer(filename));
|
| + }
|
| +
|
| + void _build(Printer printer) {
|
| + _flush();
|
| + bool propagate = false;
|
| + for (var item in _items) {
|
| + if (item is Declarations) {
|
| + item._build(printer);
|
| + } else if (item is CodePrinter) {
|
| + item._build(printer);
|
| + } else if (item is String) {
|
| + printer.add(item, projectMarks: propagate);
|
| + propagate = false;
|
| + } else if (item is Location || item is Span) {
|
| + printer.mark(item);
|
| + } else if (item == _ORIGINAL) {
|
| + // we insert booleans when we are about to quote text that was copied
|
| + // from the original source. In such case, we will propagate marks on
|
| + // every new-line.
|
| + propagate = true;
|
| + } else {
|
| + throw new UnsupportedError('Unknown item type: $item');
|
| + }
|
| + }
|
| + }
|
| +}
|
| +
|
| +/** A declaration of a field or local variable. */
|
| +class Declaration implements Comparable {
|
| + final String type;
|
| + final String name;
|
| + final Span sourceSpan;
|
| + final String initializer;
|
| +
|
| + Declaration(this.type, this.name, this.sourceSpan, [this.initializer]);
|
| +
|
| + /**
|
| + * Sort declarations by type, so they can be merged together in a declaration
|
| + * group.
|
| + */
|
| + int compareTo(Declaration other) {
|
| + if (type != other.type) return type.compareTo(other.type);
|
| + return name.compareTo(other.name);
|
| + }
|
| +}
|
| +
|
| +/** A set of declarations grouped together. */
|
| +class Declarations {
|
| +
|
| + /** All declarations in this group. */
|
| + final List<Declaration> declarations = <Declaration>[];
|
| +
|
| + /** Indentation associated with this declaration group. */
|
| + final int indent;
|
| +
|
| + /** Whether these declarations are local variables or fields in a class. */
|
| + final bool isLocal;
|
| +
|
| + /** Whether types should be prefixed with the "static" keyword. */
|
| + final bool staticKeyword;
|
| +
|
| + Declarations(this.indent, {this.isLocal: false, this.staticKeyword: false});
|
| +
|
| + /** Add a declaration to this group. */
|
| + void add(String type, String identifier, Span sourceSpan, [String init]) {
|
| + declarations.add(
|
| + new Declaration(isLocal ? 'var' : type, identifier, sourceSpan, init));
|
| + }
|
| +
|
| + String toString() {
|
| + var printer = new Printer(null);
|
| + _build(printer);
|
| + return printer.text;
|
| + }
|
| +
|
| + void _build(Printer printer) {
|
| + if (declarations.length == 0) return;
|
| + declarations.sort();
|
| + var lastType = null;
|
| + printer.addSpaces(2 * indent);
|
| + for (var d in declarations) {
|
| + if (d.type != lastType) {
|
| + if (lastType != null) {
|
| + printer.add(';\n');
|
| + printer.addSpaces(2 * indent);
|
| + }
|
| + if (staticKeyword) printer.add('static ');
|
| + printer.add(d.type);
|
| + lastType = d.type;
|
| + } else {
|
| + printer.add(',');
|
| + }
|
| + printer.add(' ');
|
| + if (d.sourceSpan != null) printer.mark(d.sourceSpan);
|
| + printer.add(d.name);
|
| + if (d.initializer != null) {
|
| + printer..add(' = ')..add(d.initializer);
|
| + }
|
| + }
|
| + printer.add(';\n');
|
| + }
|
| +}
|
| +
|
|
|