blob: bb10100e62ad904371f06ad7ca44e1089dfab08b [file] [log] [blame]
// Copyright (c) 2014, 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 analyzer.src.dart.scanner.scanner;
import 'package:analyzer/error/error.dart';
import 'package:analyzer/error/listener.dart';
import 'package:analyzer/src/dart/error/syntactic_errors.dart';
import 'package:analyzer/src/dart/scanner/reader.dart';
import 'package:analyzer/src/generated/source.dart';
import 'package:front_end/src/fasta/scanner.dart' as fasta;
import 'package:front_end/src/fasta/scanner/precedence.dart' as fasta;
import 'package:front_end/src/scanner/errors.dart' show translateErrorToken;
import 'package:front_end/src/scanner/scanner.dart' as fe;
import 'package:front_end/src/scanner/token.dart' show Token;
export 'package:analyzer/src/dart/error/syntactic_errors.dart';
export 'package:front_end/src/scanner/scanner.dart' show KeywordState;
/**
* The class `Scanner` implements a scanner for Dart code.
*
* The lexical structure of Dart is ambiguous without knowledge of the context
* in which a token is being scanned. For example, without context we cannot
* determine whether source of the form "<<" should be scanned as a single
* left-shift operator or as two left angle brackets. This scanner does not have
* any context, so it always resolves such conflicts by scanning the longest
* possible token.
*/
class Scanner extends fe.Scanner {
/**
* The source being scanned.
*/
final Source source;
/**
* The error listener that will be informed of any errors that are found
* during the scan.
*/
final AnalysisErrorListener _errorListener;
/**
* Initialize a newly created scanner to scan characters from the given
* [source]. The given character [reader] will be used to read the characters
* in the source. The given [_errorListener] will be informed of any errors
* that are found.
*/
factory Scanner(Source source, CharacterReader reader,
AnalysisErrorListener errorListener) =>
fe.Scanner.useFasta
? new _Scanner2(source, reader.getContents(), errorListener)
: new Scanner._(source, reader, errorListener);
Scanner._(this.source, CharacterReader reader, this._errorListener)
: super.create(reader);
@override
void reportError(
ScannerErrorCode errorCode, int offset, List<Object> arguments) {
_errorListener
.onError(new AnalysisError(source, offset, 1, errorCode, arguments));
}
}
/**
* Replacement scanner based on fasta.
*/
class _Scanner2 implements Scanner {
@override
final Source source;
/**
* The text to be scanned.
*/
final String _contents;
/**
* The error listener that will be informed of any errors that are found
* during the scan.
*/
final AnalysisErrorListener _errorListener;
/**
* The flag specifying whether documentation comments should be parsed.
*/
bool _preserveComments = true;
@override
final List<int> lineStarts = <int>[];
@override
Token firstToken;
@override
bool scanGenericMethodComments = false;
@override
bool scanLazyAssignmentOperators = false;
_Scanner2(this.source, this._contents, this._errorListener) {
lineStarts.add(0);
}
@override
void appendToken(Token token) {
throw 'unsupported operation';
}
@override
int bigSwitch(int next) {
throw 'unsupported operation';
}
@override
bool get hasUnmatchedGroups {
throw 'unsupported operation';
}
@override
void recordStartOfLine() {
throw 'unsupported operation';
}
@override
void reportError(
ScannerErrorCode errorCode, int offset, List<Object> arguments) {
_errorListener
.onError(new AnalysisError(source, offset, 1, errorCode, arguments));
}
@override
void setSourceStart(int line, int column) {
throw 'unsupported operation';
}
@override
Token get tail {
throw 'unsupported operation';
}
@override
Token tokenize() {
// Note: Fasta always supports lazy assignment operators (`&&=` and `||=`),
// so we can ignore the `scanLazyAssignmentOperators` flag.
if (scanGenericMethodComments) {
// Fasta doesn't support generic method comments.
// TODO(danrubel): remove this once fasts support has been added.
throw 'No generic method comment support in Fasta';
}
fasta.ScannerResult result =
fasta.scanString(_contents, includeComments: _preserveComments);
// fasta pretends there is an additional line at EOF
lineStarts
.addAll(result.lineStarts.sublist(1, result.lineStarts.length - 1));
fasta.Token token = result.tokens;
// The default recovery strategy used by scanString
// places all error tokens at the head of the stream.
while (token.info == fasta.BAD_INPUT_INFO) {
translateErrorToken(token, reportError);
token = token.next;
}
firstToken = token;
return firstToken;
}
@override
set preserveComments(bool preserveComments) {
this._preserveComments = preserveComments;
}
}