Line data Source code
1 : // Copyright (c) 2014, 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 : import 'package:source_span/source_span.dart'; 6 : 7 : import 'eager_span_scanner.dart'; 8 : import 'exception.dart'; 9 : import 'line_scanner.dart'; 10 : import 'relative_span_scanner.dart'; 11 : import 'string_scanner.dart'; 12 : import 'utils.dart'; 13 : 14 : /// A subclass of [LineScanner] that exposes matched ranges as source map 15 : /// [FileSpan]s. 16 : class SpanScanner extends StringScanner implements LineScanner { 17 : /// The source of the scanner. 18 : /// 19 : /// This caches line break information and is used to generate [FileSpan]s. 20 : final SourceFile _sourceFile; 21 : 22 0 : @override 23 0 : int get line => _sourceFile.getLine(position); 24 0 : @override 25 0 : int get column => _sourceFile.getColumn(position); 26 : 27 5 : @override 28 10 : LineScannerState get state => _SpanScannerState(this, position); 29 : 30 0 : @override 31 : set state(LineScannerState state) { 32 0 : if (state is! _SpanScannerState || !identical(state._scanner, this)) { 33 0 : throw ArgumentError('The given LineScannerState was not returned by ' 34 : 'this LineScanner.'); 35 : } 36 : 37 0 : position = state.position; 38 : } 39 : 40 : /// The [FileSpan] for [lastMatch]. 41 : /// 42 : /// This is the span for the entire match. There's no way to get spans for 43 : /// subgroups since [Match] exposes no information about their positions. 44 5 : FileSpan? get lastSpan { 45 5 : if (lastMatch == null) _lastSpan = null; 46 5 : return _lastSpan; 47 : } 48 : 49 : FileSpan? _lastSpan; 50 : 51 : /// The current location of the scanner. 52 0 : FileLocation get location => _sourceFile.location(position); 53 : 54 : /// Returns an empty span at the current location. 55 0 : FileSpan get emptySpan => location.pointSpan(); 56 : 57 : /// Creates a new [SpanScanner] that starts scanning from [position]. 58 : /// 59 : /// [sourceUrl] is used as [SourceLocation.sourceUrl] for the returned 60 : /// [FileSpan]s as well as for error reporting. It can be a [String], a 61 : /// [Uri], or `null`. 62 5 : SpanScanner(String string, {sourceUrl, int? position}) 63 5 : : _sourceFile = SourceFile.fromString(string, url: sourceUrl), 64 5 : super(string, sourceUrl: sourceUrl, position: position); 65 : 66 : /// Creates a new [SpanScanner] that eagerly computes line and column numbers. 67 : /// 68 : /// In general [new SpanScanner] will be more efficient, since it avoids extra 69 : /// computation on every scan. However, eager scanning can be useful for 70 : /// situations where the normal course of parsing frequently involves 71 : /// accessing the current line and column numbers. 72 : /// 73 : /// Note that *only* the `line` and `column` fields on the `SpanScanner` 74 : /// itself and its `LineScannerState` are eagerly computed. To limit their 75 : /// memory footprint, returned spans and locations will still lazily compute 76 : /// their line and column numbers. 77 : factory SpanScanner.eager(String string, {sourceUrl, int? position}) = 78 : EagerSpanScanner; 79 : 80 : /// Creates a new [SpanScanner] that scans within [span]. 81 : /// 82 : /// This scans through [span]`.text, but emits new spans from [span]`.file` in 83 : /// their appropriate relative positions. The [string] field contains only 84 : /// [span]`.text`, and [position], [line], and [column] are all relative to 85 : /// the span. 86 : factory SpanScanner.within(FileSpan span) = RelativeSpanScanner; 87 : 88 : /// Creates a [FileSpan] representing the source range between [startState] 89 : /// and the current position. 90 5 : FileSpan spanFrom(LineScannerState startState, [LineScannerState? endState]) { 91 5 : final endPosition = endState == null ? position : endState.position; 92 15 : return _sourceFile.span(startState.position, endPosition); 93 : } 94 : 95 5 : @override 96 : bool matches(Pattern pattern) { 97 5 : if (!super.matches(pattern)) { 98 5 : _lastSpan = null; 99 : return false; 100 : } 101 : 102 30 : _lastSpan = _sourceFile.span(position, lastMatch!.end); 103 : return true; 104 : } 105 : 106 0 : @override 107 : Never error(String message, {Match? match, int? position, int? length}) { 108 0 : validateErrorArgs(string, match, position, length); 109 : 110 0 : if (match == null && position == null && length == null) match = lastMatch; 111 0 : position ??= match == null ? this.position : match.start; 112 0 : length ??= match == null ? 0 : match.end - match.start; 113 : 114 0 : final span = _sourceFile.span(position, position + length); 115 0 : throw StringScannerException(message, span, string); 116 : } 117 : } 118 : 119 : /// A class representing the state of a [SpanScanner]. 120 : class _SpanScannerState implements LineScannerState { 121 : /// The [SpanScanner] that created this. 122 : final SpanScanner _scanner; 123 : 124 : @override 125 : final int position; 126 0 : @override 127 0 : int get line => _scanner._sourceFile.getLine(position); 128 0 : @override 129 0 : int get column => _scanner._sourceFile.getColumn(position); 130 : 131 5 : _SpanScannerState(this._scanner, this.position); 132 : }