OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 library codegen.printer; |
| 6 |
| 7 import 'package:source_maps/source_maps.dart'; |
| 8 |
| 9 /** |
| 10 * Helper class to format generated code and keep track of source map |
| 11 * information. |
| 12 */ |
| 13 class CodePrinter { |
| 14 |
| 15 /** |
| 16 * Items recoded by this printer, which can be [String] literals, |
| 17 * other printing helpers such as [Declarations] and [CodePrinter], |
| 18 * and source map information like [Location] and [Span]. |
| 19 */ |
| 20 List _items = []; |
| 21 |
| 22 /** Internal buffer to merge consecutive strings added to this printer. */ |
| 23 StringBuffer _buff; |
| 24 |
| 25 /** Current indentation, which can be updated from outside this class. */ |
| 26 int indent = 0; |
| 27 |
| 28 /** |
| 29 * Item used to indicate that the following item is copied from the original |
| 30 * source code, and hence we should preserve source-maps on every new line. |
| 31 */ |
| 32 static final _ORIGINAL = new Object(); |
| 33 |
| 34 CodePrinter(this.indent); |
| 35 |
| 36 /** |
| 37 * Adds [object] to this printer. [object] can be a [String], [Declarations], |
| 38 * or a [CodePrinter]. If [object] is a [String], the value is appended |
| 39 * directly, without doing any formatting changes. If you wish to add a line |
| 40 * of code with automatic indentation, use [addLine] instead. [Declarations] |
| 41 * and [CodePrinter] are not processed until [build] gets called later on. |
| 42 * We ensure that [build] emits every object in the order that they were added |
| 43 * to this printer. |
| 44 * |
| 45 * The [location] and [span] parameters indicate the corresponding source map |
| 46 * location of [object] in the original input. Only one, [location] or |
| 47 * [span], should be provided at a time. |
| 48 * |
| 49 * Indicate [isOriginal] when [object] is copied directly from the user code. |
| 50 * Setting [isOriginal] will make this printer propagate source map locations |
| 51 * on every line-break. |
| 52 */ |
| 53 void add(object, {Location location, Span span, bool isOriginal: false}) { |
| 54 if (object is! String || location != null || span != null || isOriginal) { |
| 55 _flush(); |
| 56 assert(location == null || span == null); |
| 57 if (location != null) _items.add(location); |
| 58 if (span != null) _items.add(span); |
| 59 if (isOriginal) _items.add(_ORIGINAL); |
| 60 } |
| 61 |
| 62 if (object is String) { |
| 63 _appendString(object); |
| 64 } else { |
| 65 _items.add(object); |
| 66 } |
| 67 } |
| 68 |
| 69 /** Append `2 * indent` spaces to this printer. */ |
| 70 void insertIndent() => _indent(indent); |
| 71 |
| 72 /** |
| 73 * Add a [line], autoindenting to the current value of [indent]. Note, |
| 74 * indentation is not inferred, so if a line opens or closes an indentation |
| 75 * block, you need to also update [indent] accordingly. Also, indentation is |
| 76 * not adapted for nested code printers. If you add a [CodePrinter] to this |
| 77 * printer, its indentation is set separately and will not include any |
| 78 * the indentation set here. |
| 79 * |
| 80 * The [location] and [span] parameters indicate the corresponding source map |
| 81 * location of [object] in the original input. Only one, [location] or |
| 82 * [span], should be provided at a time. |
| 83 */ |
| 84 void addLine(String line, {Location location, Span span}) { |
| 85 if (location != null || span != null) { |
| 86 _flush(); |
| 87 assert(location == null || span == null); |
| 88 if (location != null) _items.add(location); |
| 89 if (span != null) _items.add(span); |
| 90 } |
| 91 if (line == null) return; |
| 92 if (line != '') { |
| 93 // We don't indent empty lines. |
| 94 _indent(indent); |
| 95 _appendString(line); |
| 96 } |
| 97 _appendString('\n'); |
| 98 } |
| 99 |
| 100 /** Appends a string merging it with any previous strings, if possible. */ |
| 101 void _appendString(String s) { |
| 102 if (_buff == null) _buff = new StringBuffer(); |
| 103 _buff.write(s); |
| 104 } |
| 105 |
| 106 /** Adds all of the current [_buff] contents as a string item. */ |
| 107 void _flush() { |
| 108 if (_buff != null) { |
| 109 _items.add(_buff.toString()); |
| 110 _buff = null; |
| 111 } |
| 112 } |
| 113 |
| 114 void _indent(int indent) { |
| 115 for (int i = 0; i < indent; i++) _appendString(' '); |
| 116 } |
| 117 |
| 118 /** |
| 119 * Returns a string representation of all the contents appended to this |
| 120 * printer, including source map location tokens. |
| 121 */ |
| 122 String toString() { |
| 123 _flush(); |
| 124 return (new StringBuffer()..writeAll(_items)).toString(); |
| 125 } |
| 126 |
| 127 /** [Printer] used during the last call to [build], if any. */ |
| 128 Printer _printer; |
| 129 |
| 130 /** Returns the text produced after calling [build]. */ |
| 131 String get text => _printer.text; |
| 132 |
| 133 /** Returns the source-map information produced after calling [build]. */ |
| 134 String get map => _printer.map; |
| 135 |
| 136 /** |
| 137 * Builds the output of this printer and source map information. After calling |
| 138 * this function, you can use [text] and [map] to retrieve the geenrated code |
| 139 * and source map information, respectively. |
| 140 */ |
| 141 void build(String filename) { |
| 142 _build(_printer = new Printer(filename)); |
| 143 } |
| 144 |
| 145 void _build(Printer printer) { |
| 146 _flush(); |
| 147 bool propagate = false; |
| 148 for (var item in _items) { |
| 149 if (item is Declarations) { |
| 150 item._build(printer); |
| 151 } else if (item is CodePrinter) { |
| 152 item._build(printer); |
| 153 } else if (item is String) { |
| 154 printer.add(item, projectMarks: propagate); |
| 155 propagate = false; |
| 156 } else if (item is Location || item is Span) { |
| 157 printer.mark(item); |
| 158 } else if (item == _ORIGINAL) { |
| 159 // we insert booleans when we are about to quote text that was copied |
| 160 // from the original source. In such case, we will propagate marks on |
| 161 // every new-line. |
| 162 propagate = true; |
| 163 } else { |
| 164 throw new UnsupportedError('Unknown item type: $item'); |
| 165 } |
| 166 } |
| 167 } |
| 168 } |
| 169 |
| 170 /** A declaration of a field or local variable. */ |
| 171 class Declaration implements Comparable { |
| 172 final String type; |
| 173 final String name; |
| 174 final Span sourceSpan; |
| 175 final String initializer; |
| 176 |
| 177 Declaration(this.type, this.name, this.sourceSpan, [this.initializer]); |
| 178 |
| 179 /** |
| 180 * Sort declarations by type, so they can be merged together in a declaration |
| 181 * group. |
| 182 */ |
| 183 int compareTo(Declaration other) { |
| 184 if (type != other.type) return type.compareTo(other.type); |
| 185 return name.compareTo(other.name); |
| 186 } |
| 187 } |
| 188 |
| 189 /** A set of declarations grouped together. */ |
| 190 class Declarations { |
| 191 |
| 192 /** All declarations in this group. */ |
| 193 final List<Declaration> declarations = <Declaration>[]; |
| 194 |
| 195 /** Indentation associated with this declaration group. */ |
| 196 final int indent; |
| 197 |
| 198 /** Whether these declarations are local variables or fields in a class. */ |
| 199 final bool isLocal; |
| 200 |
| 201 /** Whether types should be prefixed with the "static" keyword. */ |
| 202 final bool staticKeyword; |
| 203 |
| 204 Declarations(this.indent, {this.isLocal: false, this.staticKeyword: false}); |
| 205 |
| 206 /** Add a declaration to this group. */ |
| 207 void add(String type, String identifier, Span sourceSpan, [String init]) { |
| 208 declarations.add( |
| 209 new Declaration(isLocal ? 'var' : type, identifier, sourceSpan, init)); |
| 210 } |
| 211 |
| 212 String toString() { |
| 213 var printer = new Printer(null); |
| 214 _build(printer); |
| 215 return printer.text; |
| 216 } |
| 217 |
| 218 void _build(Printer printer) { |
| 219 if (declarations.length == 0) return; |
| 220 declarations.sort(); |
| 221 var lastType = null; |
| 222 printer.addSpaces(2 * indent); |
| 223 for (var d in declarations) { |
| 224 if (d.type != lastType) { |
| 225 if (lastType != null) { |
| 226 printer.add(';\n'); |
| 227 printer.addSpaces(2 * indent); |
| 228 } |
| 229 if (staticKeyword) printer.add('static '); |
| 230 printer.add(d.type); |
| 231 lastType = d.type; |
| 232 } else { |
| 233 printer.add(','); |
| 234 } |
| 235 printer.add(' '); |
| 236 if (d.sourceSpan != null) printer.mark(d.sourceSpan); |
| 237 printer.add(d.name); |
| 238 if (d.initializer != null) { |
| 239 printer..add(' = ')..add(d.initializer); |
| 240 } |
| 241 } |
| 242 printer.add(';\n'); |
| 243 } |
| 244 } |
| 245 |
OLD | NEW |