| Index: lib/src/dart_parser.dart
|
| diff --git a/lib/src/dart_parser.dart b/lib/src/dart_parser.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a6f2c9aad4cb8169156b097389b3515a37ed1af5
|
| --- /dev/null
|
| +++ b/lib/src/dart_parser.dart
|
| @@ -0,0 +1,262 @@
|
| +// Copyright (c) 2012, 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.
|
| +
|
| +/**
|
| + * Parser for Dart code based on the dart2js parser.
|
| + *
|
| + * Use [DartCodeParser.parse] to parse top-level code, and the returned
|
| + * instance to parse additonal structures such as classes.
|
| + */
|
| +library dart_parser;
|
| +
|
| +import 'dart:uri';
|
| +import 'dart:utf';
|
| +import 'package:compiler_unsupported/implementation/dart2jslib.dart' as dart2js;
|
| +import 'package:compiler_unsupported/implementation/elements/elements.dart';
|
| +import 'package:compiler_unsupported/implementation/elements/modelx.dart';
|
| +import 'package:compiler_unsupported/implementation/scanner/scannerlib.dart';
|
| +import 'package:compiler_unsupported/implementation/source_file.dart';
|
| +import 'package:compiler_unsupported/implementation/tree/tree.dart';
|
| +import 'package:compiler_unsupported/implementation/util/util.dart';
|
| +import 'package:compiler_unsupported/implementation/util/characters.dart';
|
| +import 'package:compiler_unsupported/compiler.dart' as api;
|
| +import 'package:html5lib/dom_parsing.dart' as dom_parsing;
|
| +import 'messages.dart' show Messages;
|
| +import 'file_system/path.dart' as fs;
|
| +
|
| +// TODO(jmesserly): reconcile this with DartCodeInfo
|
| +class DartCodeParser {
|
| + final DiagnosticListener diagnostics;
|
| + final CompilationUnitElement unit;
|
| + final String code;
|
| + bool _success;
|
| +
|
| + bool get success => _success;
|
| +
|
| + Messages get messages => diagnostics.messages;
|
| +
|
| + DartCodeParser._parse(this.diagnostics, this.unit, this.code) {
|
| + _success = _parseCompilationUnit();
|
| + }
|
| +
|
| + /**
|
| + * Performs a partial parse of Dart code and returns the parser.
|
| + * From their you can determine if the parse was a [success] and get the
|
| + * compilation [unit].
|
| + *
|
| + * The partial parse only parses the top-level structure. Futher parsing can
|
| + * then be performed using the parser, such as [parseClass].
|
| + *
|
| + * You can provide a object to receive warning and error [messages] from the
|
| + * parser, otherwise [this.messages] will be a new messages instance that is
|
| + * silent (does not print).
|
| + */
|
| + factory DartCodeParser.parse(String path, String code, {Messages messages}) {
|
| + if (messages == null) messages = new Messages.silent();
|
| + var uri = new Uri.fromComponents(path: path);
|
| + var script = new dart2js.Script(uri, new SourceFile(uri.toString(), code));
|
| + var unit = new LibraryElementX(script, uri).entryCompilationUnit;
|
| + var fileSpanInfo = _createSourceFileInfo(code);
|
| + var diagnostics = new DiagnosticListener(fileSpanInfo, unit, messages);
|
| + return new DartCodeParser._parse(diagnostics, unit, code);
|
| + }
|
| +
|
| + bool _parseCompilationUnit() {
|
| + int nextFreeClassId = 0;
|
| + var idGenerator = () => nextFreeClassId++;
|
| + var listener = new ElementListener(diagnostics, unit, idGenerator);
|
| +
|
| + // Try parsing the code. Note that the parser can throw an exception to bail
|
| + // out of the call stack.
|
| + try {
|
| + var tokens = new StringScanner(code).tokenize();
|
| + new PartialParser(listener).parseUnit(tokens);
|
| + return true;
|
| + } on dart2js.CompilerCancelledException catch (e) {
|
| + return false;
|
| + }
|
| + }
|
| +
|
| + ClassNode parseClass(PartialClassElement element) {
|
| + if (element.cachedNode != null) return element.cachedNode;
|
| + var listener = new MemberListener(diagnostics, element);
|
| + var parser = new ClassElementParser(listener);
|
| + var token = parser.parseTopLevelDeclaration(element.beginToken);
|
| + assert(identical(token, element.endToken.next));
|
| + var classNode = listener.popNode();
|
| + assert(listener.nodes.isEmpty);
|
| + return element.cachedNode = classNode;
|
| + }
|
| +}
|
| +
|
| +
|
| +/**
|
| + * DiagnosticListener for dart2js. Figures out warning/error spans, and adapts
|
| + * them to the [Messages] format used in web_ui.
|
| + *
|
| + * This class is madness. It has to deal with spans/messages/paths/uris from
|
| + * 3+ compilers colliding (for those keeping score: dart2js, frog remnants
|
| + * inside dart2js, html5lib, and web_ui ...). Also a lot of this code had to be
|
| + * copied from the dart2js Compiler class to make DiagnosticListener actually
|
| + * work.
|
| + *
|
| + * Avert your eyes!
|
| + */
|
| +class DiagnosticListener implements dart2js.DiagnosticListener {
|
| + final dom_parsing.SourceFileInfo fileInfo;
|
| + final CompilationUnitElement currentElement;
|
| + final Messages messages;
|
| +
|
| + DiagnosticListener(this.fileInfo, this.currentElement, this.messages);
|
| +
|
| + void cancel(String reason, {node, token, instruction, element}) {
|
| + SourceSpan span = null;
|
| + if (node != null) {
|
| + span = spanFromNode(node);
|
| + } else if (token != null) {
|
| + span = spanFromTokens(token, token);
|
| + } else if (instruction != null) {
|
| + span = spanFromHInstruction(instruction);
|
| + } else if (element != null) {
|
| + span = spanFromElement(element);
|
| + } else {
|
| + throw 'No error location for error: $reason';
|
| + }
|
| + reportMessageString(span, reason, api.Diagnostic.ERROR);
|
| + throw new CompilerCancelledException(reason);
|
| + }
|
| +
|
| + SourceSpan spanFromTokens(Token begin, Token end, [Uri uri]) {
|
| + if (begin == null || end == null) {
|
| + // TODO(ahe): We can almost always do better. Often it is only
|
| + // end that is null. Otherwise, we probably know the current
|
| + // URI.
|
| + throw 'Cannot find tokens to produce error message.';
|
| + }
|
| + if (uri == null && currentElement != null) {
|
| + uri = currentElement.getCompilationUnit().script.uri;
|
| + }
|
| + return SourceSpan.withCharacterOffsets(begin, end,
|
| + (beginOffset, endOffset) => new SourceSpan(uri, beginOffset, endOffset));
|
| + }
|
| +
|
| + SourceSpan spanFromNode(Node node, [Uri uri]) {
|
| + return spanFromTokens(node.getBeginToken(), node.getEndToken(), uri);
|
| + }
|
| +
|
| + SourceSpan spanFromElement(Element element) {
|
| + if (Elements.isErroneousElement(element)) {
|
| + element = element.enclosingElement;
|
| + }
|
| + if (element.position() == null && !element.isCompilationUnit()) {
|
| + // Sometimes, the backend fakes up elements that have no
|
| + // position. So we use the enclosing element instead. It is
|
| + // not a good error location, but cancel really is "internal
|
| + // error" or "not implemented yet", so the vicinity is good
|
| + // enough for now.
|
| + element = element.enclosingElement;
|
| + // TODO(ahe): I plan to overhaul this infrastructure anyways.
|
| + }
|
| + if (element == null) {
|
| + element = currentElement;
|
| + }
|
| + Token position = element.position();
|
| + Uri uri = element.getCompilationUnit().script.uri;
|
| + return (position == null)
|
| + ? new SourceSpan(uri, 0, 0)
|
| + : spanFromTokens(position, position, uri);
|
| + }
|
| +
|
| + SourceSpan spanFromHInstruction(HInstruction instruction) {
|
| + Element element = instruction.sourceElement;
|
| + if (element == null) element = currentElement;
|
| + var position = instruction.sourcePosition;
|
| + if (position == null) return spanFromElement(element);
|
| + Token token = position.token;
|
| + if (token == null) return spanFromElement(element);
|
| + Uri uri = element.getCompilationUnit().script.uri;
|
| + return spanFromTokens(token, token, uri);
|
| + }
|
| +
|
| + void log(message) {
|
| + print(message);
|
| + }
|
| +
|
| + void internalError(String message,
|
| + {Node node, Token token, HInstruction instruction,
|
| + Element element}) {
|
| + cancel('Internal error: $message', node: node, token: token,
|
| + instruction: instruction, element: element);
|
| + }
|
| +
|
| + void internalErrorOnElement(Element element, String message) {
|
| + internalError(message, element: element);
|
| + }
|
| +
|
| + // What you say? One kind of "Diagnostic" isn't enough? Well, have two!
|
| + void reportMessage(SourceSpan span, Diagnostic message, api.Diagnostic kind) {
|
| + reportMessageString(span, "$message", kind);
|
| + }
|
| +
|
| + // Note: renamed this, because otherwise you had "reportDiagnostic" reporting
|
| + // message strings and "reportMessage" reporting Diagnostics...
|
| + void reportMessageString(SourceSpan span, String message,
|
| + api.Diagnostic kind) {
|
| +
|
| + // TODO(jmesserly): we should validate that the Uri is what we expected.
|
| + var msg = message.toString();
|
| + var ourSpan = convertToOurSpan(span);
|
| + var file = new fs.Path(span.uri.toString());
|
| +
|
| + switch (kind) {
|
| + case api.Diagnostic.ERROR:
|
| + case api.Diagnostic.CRASH:
|
| + messages.error(msg, ourSpan, file: file);
|
| + return;
|
| + case api.Diagnostic.WARNING:
|
| + messages.error(msg, ourSpan, file: file);
|
| + return;
|
| + case api.Diagnostic.LINT:
|
| + case api.Diagnostic.INFO:
|
| + case api.Diagnostic.VERBOSE_INFO:
|
| + messages.info(msg, ourSpan, file: file);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + bool onDeprecatedFeature(Spannable span, String feature) {
|
| + // Not our job to warn about deprecated Dart features
|
| + return false;
|
| + }
|
| +
|
| + dom_parsing.SourceSpan convertToOurSpan(SourceSpan span) {
|
| + // TODO(jmesserly): make html5lib spans less crazy.
|
| + return new dom_parsing.SourceSpan(fileInfo, span.begin, span.end);
|
| + }
|
| +}
|
| +
|
| +
|
| +// TODO(jmesserly): fix dom_parsing.SourceFileInfo. It should compute line
|
| +// locations itself on demand and caching. At the very least this function
|
| +// should move into that class.
|
| +dom_parsing.SourceFileInfo _createSourceFileInfo(String text) {
|
| + var lineStarts = <int>[0];
|
| + var chars = stringToCodepoints(text);
|
| +
|
| + for (int i = 0; i < chars.length; i++) {
|
| + var c = chars[i];
|
| +
|
| + if (c == $CR) {
|
| + // Return not followed by newline is treated as a newline
|
| + int j = i + 1;
|
| + if (j >= chars.length || chars[j] != $LF) {
|
| + c = $LF;
|
| + }
|
| + }
|
| +
|
| + if (c == $LF) lineStarts.add(i + 1);
|
| + }
|
| +
|
| + return new dom_parsing.SourceFileInfo(lineStarts, chars);
|
| +}
|
|
|