You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							363 lines
						
					
					
						
							14 KiB
						
					
					
				
			
		
		
		
			
			
			
				
					
				
				
					
				
			
		
		
	
	
							363 lines
						
					
					
						
							14 KiB
						
					
					
				
								/*---------------------------------------------------------------------------------------------
							 | 
						|
								 *  Copyright (c) Microsoft Corporation. All rights reserved.
							 | 
						|
								 *  Licensed under the MIT License. See License.txt in the project root for license information.
							 | 
						|
								 *--------------------------------------------------------------------------------------------*/
							 | 
						|
								'use strict';
							 | 
						|
								/**
							 | 
						|
								 * Creates a JSON scanner on the given text.
							 | 
						|
								 * If ignoreTrivia is set, whitespaces or comments are ignored.
							 | 
						|
								 */
							 | 
						|
								export function createScanner(text, ignoreTrivia) {
							 | 
						|
								    if (ignoreTrivia === void 0) { ignoreTrivia = false; }
							 | 
						|
								    var len = text.length;
							 | 
						|
								    var pos = 0, value = '', tokenOffset = 0, token = 16 /* Unknown */, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0 /* None */;
							 | 
						|
								    function scanHexDigits(count, exact) {
							 | 
						|
								        var digits = 0;
							 | 
						|
								        var value = 0;
							 | 
						|
								        while (digits < count || !exact) {
							 | 
						|
								            var ch = text.charCodeAt(pos);
							 | 
						|
								            if (ch >= 48 /* _0 */ && ch <= 57 /* _9 */) {
							 | 
						|
								                value = value * 16 + ch - 48 /* _0 */;
							 | 
						|
								            }
							 | 
						|
								            else if (ch >= 65 /* A */ && ch <= 70 /* F */) {
							 | 
						|
								                value = value * 16 + ch - 65 /* A */ + 10;
							 | 
						|
								            }
							 | 
						|
								            else if (ch >= 97 /* a */ && ch <= 102 /* f */) {
							 | 
						|
								                value = value * 16 + ch - 97 /* a */ + 10;
							 | 
						|
								            }
							 | 
						|
								            else {
							 | 
						|
								                break;
							 | 
						|
								            }
							 | 
						|
								            pos++;
							 | 
						|
								            digits++;
							 | 
						|
								        }
							 | 
						|
								        if (digits < count) {
							 | 
						|
								            value = -1;
							 | 
						|
								        }
							 | 
						|
								        return value;
							 | 
						|
								    }
							 | 
						|
								    function setPosition(newPosition) {
							 | 
						|
								        pos = newPosition;
							 | 
						|
								        value = '';
							 | 
						|
								        tokenOffset = 0;
							 | 
						|
								        token = 16 /* Unknown */;
							 | 
						|
								        scanError = 0 /* None */;
							 | 
						|
								    }
							 | 
						|
								    function scanNumber() {
							 | 
						|
								        var start = pos;
							 | 
						|
								        if (text.charCodeAt(pos) === 48 /* _0 */) {
							 | 
						|
								            pos++;
							 | 
						|
								        }
							 | 
						|
								        else {
							 | 
						|
								            pos++;
							 | 
						|
								            while (pos < text.length && isDigit(text.charCodeAt(pos))) {
							 | 
						|
								                pos++;
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								        if (pos < text.length && text.charCodeAt(pos) === 46 /* dot */) {
							 | 
						|
								            pos++;
							 | 
						|
								            if (pos < text.length && isDigit(text.charCodeAt(pos))) {
							 | 
						|
								                pos++;
							 | 
						|
								                while (pos < text.length && isDigit(text.charCodeAt(pos))) {
							 | 
						|
								                    pos++;
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            else {
							 | 
						|
								                scanError = 3 /* UnexpectedEndOfNumber */;
							 | 
						|
								                return text.substring(start, pos);
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								        var end = pos;
							 | 
						|
								        if (pos < text.length && (text.charCodeAt(pos) === 69 /* E */ || text.charCodeAt(pos) === 101 /* e */)) {
							 | 
						|
								            pos++;
							 | 
						|
								            if (pos < text.length && text.charCodeAt(pos) === 43 /* plus */ || text.charCodeAt(pos) === 45 /* minus */) {
							 | 
						|
								                pos++;
							 | 
						|
								            }
							 | 
						|
								            if (pos < text.length && isDigit(text.charCodeAt(pos))) {
							 | 
						|
								                pos++;
							 | 
						|
								                while (pos < text.length && isDigit(text.charCodeAt(pos))) {
							 | 
						|
								                    pos++;
							 | 
						|
								                }
							 | 
						|
								                end = pos;
							 | 
						|
								            }
							 | 
						|
								            else {
							 | 
						|
								                scanError = 3 /* UnexpectedEndOfNumber */;
							 | 
						|
								            }
							 | 
						|
								        }
							 | 
						|
								        return text.substring(start, end);
							 | 
						|
								    }
							 | 
						|
								    function scanString() {
							 | 
						|
								        var result = '', start = pos;
							 | 
						|
								        while (true) {
							 | 
						|
								            if (pos >= len) {
							 | 
						|
								                result += text.substring(start, pos);
							 | 
						|
								                scanError = 2 /* UnexpectedEndOfString */;
							 | 
						|
								                break;
							 | 
						|
								            }
							 | 
						|
								            var ch = text.charCodeAt(pos);
							 | 
						|
								            if (ch === 34 /* doubleQuote */) {
							 | 
						|
								                result += text.substring(start, pos);
							 | 
						|
								                pos++;
							 | 
						|
								                break;
							 | 
						|
								            }
							 | 
						|
								            if (ch === 92 /* backslash */) {
							 | 
						|
								                result += text.substring(start, pos);
							 | 
						|
								                pos++;
							 | 
						|
								                if (pos >= len) {
							 | 
						|
								                    scanError = 2 /* UnexpectedEndOfString */;
							 | 
						|
								                    break;
							 | 
						|
								                }
							 | 
						|
								                var ch2 = text.charCodeAt(pos++);
							 | 
						|
								                switch (ch2) {
							 | 
						|
								                    case 34 /* doubleQuote */:
							 | 
						|
								                        result += '\"';
							 | 
						|
								                        break;
							 | 
						|
								                    case 92 /* backslash */:
							 | 
						|
								                        result += '\\';
							 | 
						|
								                        break;
							 | 
						|
								                    case 47 /* slash */:
							 | 
						|
								                        result += '/';
							 | 
						|
								                        break;
							 | 
						|
								                    case 98 /* b */:
							 | 
						|
								                        result += '\b';
							 | 
						|
								                        break;
							 | 
						|
								                    case 102 /* f */:
							 | 
						|
								                        result += '\f';
							 | 
						|
								                        break;
							 | 
						|
								                    case 110 /* n */:
							 | 
						|
								                        result += '\n';
							 | 
						|
								                        break;
							 | 
						|
								                    case 114 /* r */:
							 | 
						|
								                        result += '\r';
							 | 
						|
								                        break;
							 | 
						|
								                    case 116 /* t */:
							 | 
						|
								                        result += '\t';
							 | 
						|
								                        break;
							 | 
						|
								                    case 117 /* u */:
							 | 
						|
								                        var ch3 = scanHexDigits(4, true);
							 | 
						|
								                        if (ch3 >= 0) {
							 | 
						|
								                            result += String.fromCharCode(ch3);
							 | 
						|
								                        }
							 | 
						|
								                        else {
							 | 
						|
								                            scanError = 4 /* InvalidUnicode */;
							 | 
						|
								                        }
							 | 
						|
								                        break;
							 | 
						|
								                    default:
							 | 
						|
								                        scanError = 5 /* InvalidEscapeCharacter */;
							 | 
						|
								                }
							 | 
						|
								                start = pos;
							 | 
						|
								                continue;
							 | 
						|
								            }
							 | 
						|
								            if (ch >= 0 && ch <= 0x1f) {
							 | 
						|
								                if (isLineBreak(ch)) {
							 | 
						|
								                    result += text.substring(start, pos);
							 | 
						|
								                    scanError = 2 /* UnexpectedEndOfString */;
							 | 
						|
								                    break;
							 | 
						|
								                }
							 | 
						|
								                else {
							 | 
						|
								                    scanError = 6 /* InvalidCharacter */;
							 | 
						|
								                    // mark as error but continue with string
							 | 
						|
								                }
							 | 
						|
								            }
							 | 
						|
								            pos++;
							 | 
						|
								        }
							 | 
						|
								        return result;
							 | 
						|
								    }
							 | 
						|
								    function scanNext() {
							 | 
						|
								        value = '';
							 | 
						|
								        scanError = 0 /* None */;
							 | 
						|
								        tokenOffset = pos;
							 | 
						|
								        lineStartOffset = lineNumber;
							 | 
						|
								        prevTokenLineStartOffset = tokenLineStartOffset;
							 | 
						|
								        if (pos >= len) {
							 | 
						|
								            // at the end
							 | 
						|
								            tokenOffset = len;
							 | 
						|
								            return token = 17 /* EOF */;
							 | 
						|
								        }
							 | 
						|
								        var code = text.charCodeAt(pos);
							 | 
						|
								        // trivia: whitespace
							 | 
						|
								        if (isWhiteSpace(code)) {
							 | 
						|
								            do {
							 | 
						|
								                pos++;
							 | 
						|
								                value += String.fromCharCode(code);
							 | 
						|
								                code = text.charCodeAt(pos);
							 | 
						|
								            } while (isWhiteSpace(code));
							 | 
						|
								            return token = 15 /* Trivia */;
							 | 
						|
								        }
							 | 
						|
								        // trivia: newlines
							 | 
						|
								        if (isLineBreak(code)) {
							 | 
						|
								            pos++;
							 | 
						|
								            value += String.fromCharCode(code);
							 | 
						|
								            if (code === 13 /* carriageReturn */ && text.charCodeAt(pos) === 10 /* lineFeed */) {
							 | 
						|
								                pos++;
							 | 
						|
								                value += '\n';
							 | 
						|
								            }
							 | 
						|
								            lineNumber++;
							 | 
						|
								            tokenLineStartOffset = pos;
							 | 
						|
								            return token = 14 /* LineBreakTrivia */;
							 | 
						|
								        }
							 | 
						|
								        switch (code) {
							 | 
						|
								            // tokens: []{}:,
							 | 
						|
								            case 123 /* openBrace */:
							 | 
						|
								                pos++;
							 | 
						|
								                return token = 1 /* OpenBraceToken */;
							 | 
						|
								            case 125 /* closeBrace */:
							 | 
						|
								                pos++;
							 | 
						|
								                return token = 2 /* CloseBraceToken */;
							 | 
						|
								            case 91 /* openBracket */:
							 | 
						|
								                pos++;
							 | 
						|
								                return token = 3 /* OpenBracketToken */;
							 | 
						|
								            case 93 /* closeBracket */:
							 | 
						|
								                pos++;
							 | 
						|
								                return token = 4 /* CloseBracketToken */;
							 | 
						|
								            case 58 /* colon */:
							 | 
						|
								                pos++;
							 | 
						|
								                return token = 6 /* ColonToken */;
							 | 
						|
								            case 44 /* comma */:
							 | 
						|
								                pos++;
							 | 
						|
								                return token = 5 /* CommaToken */;
							 | 
						|
								            // strings
							 | 
						|
								            case 34 /* doubleQuote */:
							 | 
						|
								                pos++;
							 | 
						|
								                value = scanString();
							 | 
						|
								                return token = 10 /* StringLiteral */;
							 | 
						|
								            // comments
							 | 
						|
								            case 47 /* slash */:
							 | 
						|
								                var start = pos - 1;
							 | 
						|
								                // Single-line comment
							 | 
						|
								                if (text.charCodeAt(pos + 1) === 47 /* slash */) {
							 | 
						|
								                    pos += 2;
							 | 
						|
								                    while (pos < len) {
							 | 
						|
								                        if (isLineBreak(text.charCodeAt(pos))) {
							 | 
						|
								                            break;
							 | 
						|
								                        }
							 | 
						|
								                        pos++;
							 | 
						|
								                    }
							 | 
						|
								                    value = text.substring(start, pos);
							 | 
						|
								                    return token = 12 /* LineCommentTrivia */;
							 | 
						|
								                }
							 | 
						|
								                // Multi-line comment
							 | 
						|
								                if (text.charCodeAt(pos + 1) === 42 /* asterisk */) {
							 | 
						|
								                    pos += 2;
							 | 
						|
								                    var safeLength = len - 1; // For lookahead.
							 | 
						|
								                    var commentClosed = false;
							 | 
						|
								                    while (pos < safeLength) {
							 | 
						|
								                        var ch = text.charCodeAt(pos);
							 | 
						|
								                        if (ch === 42 /* asterisk */ && text.charCodeAt(pos + 1) === 47 /* slash */) {
							 | 
						|
								                            pos += 2;
							 | 
						|
								                            commentClosed = true;
							 | 
						|
								                            break;
							 | 
						|
								                        }
							 | 
						|
								                        pos++;
							 | 
						|
								                        if (isLineBreak(ch)) {
							 | 
						|
								                            if (ch === 13 /* carriageReturn */ && text.charCodeAt(pos) === 10 /* lineFeed */) {
							 | 
						|
								                                pos++;
							 | 
						|
								                            }
							 | 
						|
								                            lineNumber++;
							 | 
						|
								                            tokenLineStartOffset = pos;
							 | 
						|
								                        }
							 | 
						|
								                    }
							 | 
						|
								                    if (!commentClosed) {
							 | 
						|
								                        pos++;
							 | 
						|
								                        scanError = 1 /* UnexpectedEndOfComment */;
							 | 
						|
								                    }
							 | 
						|
								                    value = text.substring(start, pos);
							 | 
						|
								                    return token = 13 /* BlockCommentTrivia */;
							 | 
						|
								                }
							 | 
						|
								                // just a single slash
							 | 
						|
								                value += String.fromCharCode(code);
							 | 
						|
								                pos++;
							 | 
						|
								                return token = 16 /* Unknown */;
							 | 
						|
								            // numbers
							 | 
						|
								            case 45 /* minus */:
							 | 
						|
								                value += String.fromCharCode(code);
							 | 
						|
								                pos++;
							 | 
						|
								                if (pos === len || !isDigit(text.charCodeAt(pos))) {
							 | 
						|
								                    return token = 16 /* Unknown */;
							 | 
						|
								                }
							 | 
						|
								            // found a minus, followed by a number so
							 | 
						|
								            // we fall through to proceed with scanning
							 | 
						|
								            // numbers
							 | 
						|
								            case 48 /* _0 */:
							 | 
						|
								            case 49 /* _1 */:
							 | 
						|
								            case 50 /* _2 */:
							 | 
						|
								            case 51 /* _3 */:
							 | 
						|
								            case 52 /* _4 */:
							 | 
						|
								            case 53 /* _5 */:
							 | 
						|
								            case 54 /* _6 */:
							 | 
						|
								            case 55 /* _7 */:
							 | 
						|
								            case 56 /* _8 */:
							 | 
						|
								            case 57 /* _9 */:
							 | 
						|
								                value += scanNumber();
							 | 
						|
								                return token = 11 /* NumericLiteral */;
							 | 
						|
								            // literals and unknown symbols
							 | 
						|
								            default:
							 | 
						|
								                // is a literal? Read the full word.
							 | 
						|
								                while (pos < len && isUnknownContentCharacter(code)) {
							 | 
						|
								                    pos++;
							 | 
						|
								                    code = text.charCodeAt(pos);
							 | 
						|
								                }
							 | 
						|
								                if (tokenOffset !== pos) {
							 | 
						|
								                    value = text.substring(tokenOffset, pos);
							 | 
						|
								                    // keywords: true, false, null
							 | 
						|
								                    switch (value) {
							 | 
						|
								                        case 'true': return token = 8 /* TrueKeyword */;
							 | 
						|
								                        case 'false': return token = 9 /* FalseKeyword */;
							 | 
						|
								                        case 'null': return token = 7 /* NullKeyword */;
							 | 
						|
								                    }
							 | 
						|
								                    return token = 16 /* Unknown */;
							 | 
						|
								                }
							 | 
						|
								                // some
							 | 
						|
								                value += String.fromCharCode(code);
							 | 
						|
								                pos++;
							 | 
						|
								                return token = 16 /* Unknown */;
							 | 
						|
								        }
							 | 
						|
								    }
							 | 
						|
								    function isUnknownContentCharacter(code) {
							 | 
						|
								        if (isWhiteSpace(code) || isLineBreak(code)) {
							 | 
						|
								            return false;
							 | 
						|
								        }
							 | 
						|
								        switch (code) {
							 | 
						|
								            case 125 /* closeBrace */:
							 | 
						|
								            case 93 /* closeBracket */:
							 | 
						|
								            case 123 /* openBrace */:
							 | 
						|
								            case 91 /* openBracket */:
							 | 
						|
								            case 34 /* doubleQuote */:
							 | 
						|
								            case 58 /* colon */:
							 | 
						|
								            case 44 /* comma */:
							 | 
						|
								            case 47 /* slash */:
							 | 
						|
								                return false;
							 | 
						|
								        }
							 | 
						|
								        return true;
							 | 
						|
								    }
							 | 
						|
								    function scanNextNonTrivia() {
							 | 
						|
								        var result;
							 | 
						|
								        do {
							 | 
						|
								            result = scanNext();
							 | 
						|
								        } while (result >= 12 /* LineCommentTrivia */ && result <= 15 /* Trivia */);
							 | 
						|
								        return result;
							 | 
						|
								    }
							 | 
						|
								    return {
							 | 
						|
								        setPosition: setPosition,
							 | 
						|
								        getPosition: function () { return pos; },
							 | 
						|
								        scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
							 | 
						|
								        getToken: function () { return token; },
							 | 
						|
								        getTokenValue: function () { return value; },
							 | 
						|
								        getTokenOffset: function () { return tokenOffset; },
							 | 
						|
								        getTokenLength: function () { return pos - tokenOffset; },
							 | 
						|
								        getTokenStartLine: function () { return lineStartOffset; },
							 | 
						|
								        getTokenStartCharacter: function () { return tokenOffset - prevTokenLineStartOffset; },
							 | 
						|
								        getTokenError: function () { return scanError; },
							 | 
						|
								    };
							 | 
						|
								}
							 | 
						|
								function isWhiteSpace(ch) {
							 | 
						|
								    return ch === 32 /* space */ || ch === 9 /* tab */ || ch === 11 /* verticalTab */ || ch === 12 /* formFeed */ ||
							 | 
						|
								        ch === 160 /* nonBreakingSpace */ || ch === 5760 /* ogham */ || ch >= 8192 /* enQuad */ && ch <= 8203 /* zeroWidthSpace */ ||
							 | 
						|
								        ch === 8239 /* narrowNoBreakSpace */ || ch === 8287 /* mathematicalSpace */ || ch === 12288 /* ideographicSpace */ || ch === 65279 /* byteOrderMark */;
							 | 
						|
								}
							 | 
						|
								function isLineBreak(ch) {
							 | 
						|
								    return ch === 10 /* lineFeed */ || ch === 13 /* carriageReturn */ || ch === 8232 /* lineSeparator */ || ch === 8233 /* paragraphSeparator */;
							 | 
						|
								}
							 | 
						|
								function isDigit(ch) {
							 | 
						|
								    return ch >= 48 /* _0 */ && ch <= 57 /* _9 */;
							 | 
						|
								}
							 |