2016-06-23 12:47:54 -04:00
|
|
|
/**
|
|
|
|
* @license
|
|
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
|
|
* found in the LICENSE file at https://angular.io/license
|
|
|
|
*/
|
2016-12-12 18:59:12 -05:00
|
|
|
import * as chars from './chars';
|
2017-03-14 12:16:15 -04:00
|
|
|
import {CompileIdentifierMetadata, identifierModuleUrl, identifierName} from './compile_metadata';
|
2016-06-23 12:47:54 -04:00
|
|
|
|
2015-10-07 12:34:21 -04:00
|
|
|
export class ParseLocation {
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor(
|
|
|
|
public file: ParseSourceFile, public offset: number, public line: number,
|
|
|
|
public col: number) {}
|
2015-10-07 12:34:21 -04:00
|
|
|
|
2016-06-28 12:54:42 -04:00
|
|
|
toString(): string {
|
2017-03-02 12:37:01 -05:00
|
|
|
return this.offset != null ? `${this.file.url}@${this.line}:${this.col}` : this.file.url;
|
2016-06-28 12:54:42 -04:00
|
|
|
}
|
2016-12-12 18:59:12 -05:00
|
|
|
|
|
|
|
moveBy(delta: number): ParseLocation {
|
|
|
|
const source = this.file.content;
|
|
|
|
const len = source.length;
|
|
|
|
let offset = this.offset;
|
|
|
|
let line = this.line;
|
|
|
|
let col = this.col;
|
|
|
|
while (offset > 0 && delta < 0) {
|
|
|
|
offset--;
|
|
|
|
delta++;
|
|
|
|
const ch = source.charCodeAt(offset);
|
|
|
|
if (ch == chars.$LF) {
|
|
|
|
line--;
|
|
|
|
const priorLine = source.substr(0, offset - 1).lastIndexOf(String.fromCharCode(chars.$LF));
|
|
|
|
col = priorLine > 0 ? offset - priorLine : offset;
|
|
|
|
} else {
|
|
|
|
col--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
while (offset < len && delta > 0) {
|
|
|
|
const ch = source.charCodeAt(offset);
|
|
|
|
offset++;
|
|
|
|
delta--;
|
|
|
|
if (ch == chars.$LF) {
|
|
|
|
line++;
|
|
|
|
col = 0;
|
|
|
|
} else {
|
|
|
|
col++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return new ParseLocation(this.file, offset, line, col);
|
|
|
|
}
|
2017-01-26 18:05:02 -05:00
|
|
|
|
|
|
|
// Return the source around the location
|
|
|
|
// Up to `maxChars` or `maxLines` on each side of the location
|
2017-03-24 12:59:58 -04:00
|
|
|
getContext(maxChars: number, maxLines: number): {before: string, after: string}|null {
|
2017-01-26 18:05:02 -05:00
|
|
|
const content = this.file.content;
|
|
|
|
let startOffset = this.offset;
|
|
|
|
|
2017-03-02 12:37:01 -05:00
|
|
|
if (startOffset != null) {
|
2017-01-26 18:05:02 -05:00
|
|
|
if (startOffset > content.length - 1) {
|
|
|
|
startOffset = content.length - 1;
|
|
|
|
}
|
|
|
|
let endOffset = startOffset;
|
|
|
|
let ctxChars = 0;
|
|
|
|
let ctxLines = 0;
|
|
|
|
|
|
|
|
while (ctxChars < maxChars && startOffset > 0) {
|
|
|
|
startOffset--;
|
|
|
|
ctxChars++;
|
|
|
|
if (content[startOffset] == '\n') {
|
|
|
|
if (++ctxLines == maxLines) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ctxChars = 0;
|
|
|
|
ctxLines = 0;
|
|
|
|
while (ctxChars < maxChars && endOffset < content.length - 1) {
|
|
|
|
endOffset++;
|
|
|
|
ctxChars++;
|
|
|
|
if (content[endOffset] == '\n') {
|
|
|
|
if (++ctxLines == maxLines) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return {
|
|
|
|
before: content.substring(startOffset, this.offset),
|
|
|
|
after: content.substring(this.offset, endOffset + 1),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
2015-10-07 12:34:21 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
export class ParseSourceFile {
|
|
|
|
constructor(public content: string, public url: string) {}
|
|
|
|
}
|
|
|
|
|
2016-02-16 19:46:51 -05:00
|
|
|
export class ParseSourceSpan {
|
2016-07-28 05:27:07 -04:00
|
|
|
constructor(
|
2017-03-24 12:59:58 -04:00
|
|
|
public start: ParseLocation, public end: ParseLocation, public details: string|null = null) {}
|
2016-02-16 19:46:51 -05:00
|
|
|
|
|
|
|
toString(): string {
|
|
|
|
return this.start.file.content.substring(this.start.offset, this.end.offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-25 22:52:24 -04:00
|
|
|
export enum ParseErrorLevel {
|
|
|
|
WARNING,
|
2017-03-14 20:12:44 -04:00
|
|
|
ERROR,
|
2016-04-25 22:52:24 -04:00
|
|
|
}
|
|
|
|
|
2016-10-21 14:41:14 -04:00
|
|
|
export class ParseError {
|
2016-06-08 19:38:52 -04:00
|
|
|
constructor(
|
|
|
|
public span: ParseSourceSpan, public msg: string,
|
2017-03-14 20:12:44 -04:00
|
|
|
public level: ParseErrorLevel = ParseErrorLevel.ERROR) {}
|
2015-10-07 12:34:21 -04:00
|
|
|
|
2017-06-09 17:50:57 -04:00
|
|
|
contextualMessage(): string {
|
2017-01-26 18:05:02 -05:00
|
|
|
const ctx = this.span.start.getContext(100, 3);
|
2017-11-09 17:06:02 -05:00
|
|
|
return ctx ? `${this.msg} ("${ctx.before}[${ParseErrorLevel[this.level]} ->]${ctx.after}")` :
|
|
|
|
this.msg;
|
2017-06-09 17:50:57 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
toString(): string {
|
2017-01-26 18:05:02 -05:00
|
|
|
const details = this.span.details ? `, ${this.span.details}` : '';
|
2017-11-09 17:06:02 -05:00
|
|
|
return `${this.contextualMessage()}: ${this.span.start}${details}`;
|
2015-10-07 12:34:21 -04:00
|
|
|
}
|
|
|
|
}
|
2017-03-14 12:16:15 -04:00
|
|
|
|
|
|
|
export function typeSourceSpan(kind: string, type: CompileIdentifierMetadata): ParseSourceSpan {
|
|
|
|
const moduleUrl = identifierModuleUrl(type);
|
|
|
|
const sourceFileName = moduleUrl != null ? `in ${kind} ${identifierName(type)} in ${moduleUrl}` :
|
|
|
|
`in ${kind} ${identifierName(type)}`;
|
|
|
|
const sourceFile = new ParseSourceFile('', sourceFileName);
|
|
|
|
return new ParseSourceSpan(
|
2017-03-24 12:59:58 -04:00
|
|
|
new ParseLocation(sourceFile, -1, -1, -1), new ParseLocation(sourceFile, -1, -1, -1));
|
2017-03-14 12:16:15 -04:00
|
|
|
}
|