| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 | var constants = require('../tokenizer/const');var TYPE = constants.TYPE;var NAME = constants.NAME;var utils = require('../tokenizer/utils');var cmpStr = utils.cmpStr;var EOF = TYPE.EOF;var WHITESPACE = TYPE.WhiteSpace;var COMMENT = TYPE.Comment;var OFFSET_MASK = 0x00FFFFFF;var TYPE_SHIFT = 24;var TokenStream = function() {    this.offsetAndType = null;    this.balance = null;    this.reset();};TokenStream.prototype = {    reset: function() {        this.eof = false;        this.tokenIndex = -1;        this.tokenType = 0;        this.tokenStart = this.firstCharOffset;        this.tokenEnd = this.firstCharOffset;    },    lookupType: function(offset) {        offset += this.tokenIndex;        if (offset < this.tokenCount) {            return this.offsetAndType[offset] >> TYPE_SHIFT;        }        return EOF;    },    lookupOffset: function(offset) {        offset += this.tokenIndex;        if (offset < this.tokenCount) {            return this.offsetAndType[offset - 1] & OFFSET_MASK;        }        return this.source.length;    },    lookupValue: function(offset, referenceStr) {        offset += this.tokenIndex;        if (offset < this.tokenCount) {            return cmpStr(                this.source,                this.offsetAndType[offset - 1] & OFFSET_MASK,                this.offsetAndType[offset] & OFFSET_MASK,                referenceStr            );        }        return false;    },    getTokenStart: function(tokenIndex) {        if (tokenIndex === this.tokenIndex) {            return this.tokenStart;        }        if (tokenIndex > 0) {            return tokenIndex < this.tokenCount                ? this.offsetAndType[tokenIndex - 1] & OFFSET_MASK                : this.offsetAndType[this.tokenCount] & OFFSET_MASK;        }        return this.firstCharOffset;    },    // TODO: -> skipUntilBalanced    getRawLength: function(startToken, mode) {        var cursor = startToken;        var balanceEnd;        var offset = this.offsetAndType[Math.max(cursor - 1, 0)] & OFFSET_MASK;        var type;        loop:        for (; cursor < this.tokenCount; cursor++) {            balanceEnd = this.balance[cursor];            // stop scanning on balance edge that points to offset before start token            if (balanceEnd < startToken) {                break loop;            }            type = this.offsetAndType[cursor] >> TYPE_SHIFT;            // check token is stop type            switch (mode(type, this.source, offset)) {                case 1:                    break loop;                case 2:                    cursor++;                    break loop;                default:                    // fast forward to the end of balanced block                    if (this.balance[balanceEnd] === cursor) {                        cursor = balanceEnd;                    }                    offset = this.offsetAndType[cursor] & OFFSET_MASK;            }        }        return cursor - this.tokenIndex;    },    isBalanceEdge: function(pos) {        return this.balance[this.tokenIndex] < pos;    },    isDelim: function(code, offset) {        if (offset) {            return (                this.lookupType(offset) === TYPE.Delim &&                this.source.charCodeAt(this.lookupOffset(offset)) === code            );        }        return (            this.tokenType === TYPE.Delim &&            this.source.charCodeAt(this.tokenStart) === code        );    },    getTokenValue: function() {        return this.source.substring(this.tokenStart, this.tokenEnd);    },    getTokenLength: function() {        return this.tokenEnd - this.tokenStart;    },    substrToCursor: function(start) {        return this.source.substring(start, this.tokenStart);    },    skipWS: function() {        for (var i = this.tokenIndex, skipTokenCount = 0; i < this.tokenCount; i++, skipTokenCount++) {            if ((this.offsetAndType[i] >> TYPE_SHIFT) !== WHITESPACE) {                break;            }        }        if (skipTokenCount > 0) {            this.skip(skipTokenCount);        }    },    skipSC: function() {        while (this.tokenType === WHITESPACE || this.tokenType === COMMENT) {            this.next();        }    },    skip: function(tokenCount) {        var next = this.tokenIndex + tokenCount;        if (next < this.tokenCount) {            this.tokenIndex = next;            this.tokenStart = this.offsetAndType[next - 1] & OFFSET_MASK;            next = this.offsetAndType[next];            this.tokenType = next >> TYPE_SHIFT;            this.tokenEnd = next & OFFSET_MASK;        } else {            this.tokenIndex = this.tokenCount;            this.next();        }    },    next: function() {        var next = this.tokenIndex + 1;        if (next < this.tokenCount) {            this.tokenIndex = next;            this.tokenStart = this.tokenEnd;            next = this.offsetAndType[next];            this.tokenType = next >> TYPE_SHIFT;            this.tokenEnd = next & OFFSET_MASK;        } else {            this.tokenIndex = this.tokenCount;            this.eof = true;            this.tokenType = EOF;            this.tokenStart = this.tokenEnd = this.source.length;        }    },    forEachToken(fn) {        for (var i = 0, offset = this.firstCharOffset; i < this.tokenCount; i++) {            var start = offset;            var item = this.offsetAndType[i];            var end = item & OFFSET_MASK;            var type = item >> TYPE_SHIFT;            offset = end;            fn(type, start, end, i);        }    },    dump() {        var tokens = new Array(this.tokenCount);        this.forEachToken((type, start, end, index) => {            tokens[index] = {                idx: index,                type: NAME[type],                chunk: this.source.substring(start, end),                balance: this.balance[index]            };        });        return tokens;    }};module.exports = TokenStream;
 |