Files
android-dev-ext/langserver/java/TokenList.js
2020-06-29 16:24:01 +01:00

176 lines
4.2 KiB
JavaScript

/**
* @typedef {import('./tokenizer').Token} Token
*/
const ParseProblem = require('./parsetypes/parse-problem');
/**
*
* @param {TokenList} tokens
* @param {ParseProblem} problem
*/
function addproblem(tokens, problem) {
tokens.problems.push(problem);
}
class TokenList {
/**
* @param {Token[]} tokens
*/
constructor(tokens) {
this.tokens = tokens;
this.idx = -1;
/** @type {Token} */
this.current = null;
this.inc();
/** @type {ParseProblem[]} */
this.problems = [];
this.marks = [];
this.last_mlc = '';
}
/**
* Returns and consumes the current token
*/
consume() {
const tok = this.current;
this.inc();
return tok;
}
inc() {
for (; ;) {
this.current = this.tokens[this.idx += 1];
if (!this.current || this.current.kind !== 'wsc') {
return this.current;
}
const wsc = this.current.value;
if (wsc.startsWith('/*')) {
this.last_mlc = wsc;
}
}
}
clearMLC() {
this.last_mlc = '';
}
getLastMLC() {
const s = this.last_mlc;
this.last_mlc = '';
return s;
}
mark() {
this.marks.unshift(this.idx);
}
/**
* Returns the array of tokens from the last mark() point, trimming any trailing whitespace tokens
*/
markEnd() {
let i = this.idx;
while (this.tokens[--i].kind === 'wsc') { }
const range = [this.marks.shift(), i + 1];
if (range[1] <= range[0]) {
range[1] = range[0] + 1;
}
return this.tokens.slice(range[0], range[1]);
}
/**
* Token lookahead. The current token is unaffected by this method.
* @param {number} n number of tokens to look ahead
*/
peek(n) {
let token, idx = this.idx;
while (--n >= 0) {
for (; ;) {
token = this.tokens[idx += 1];
if (!token || token.kind !== 'wsc') {
break;
}
}
}
return token;
}
/**
* Check if the current token matches the specified kind, returns and consumes it
* @param {string} kind
*/
getIfKind(kind) {
const token = this.current;
if (token && token.kind === kind) {
this.inc();
return token;
}
return null;
}
/**
* Check if the current token matches the specified value, returns and consumes it
* @param {string} value
*/
getIfValue(value) {
const token = this.current;
if (token && token.value === value) {
this.inc();
return token;
}
return null;
}
/**
* Check if the current token matches the specified value and consumes it
* @param {string} value
*/
isValue(value) {
return this.getIfValue(value) !== null;
}
/**
* Check if the current token matches the specified kind and consumes it
* @param {string} kind
*/
isKind(kind) {
return this.getIfKind(kind) !== null;
}
/**
* Check if the current token matches the specified value and consumes it or reports an error
* @param {string} value
*/
expectValue(value) {
if (this.isValue(value)) {
return true;
}
const token = this.current || this.tokens[this.tokens.length - 1];
addproblem(this, ParseProblem.Error(token, `${value} expected`));
return false;
}
get previous() {
for (let idx = this.idx - 1; idx >= 0; idx--) {
if (idx <= 0) {
return this.tokens[0];
}
if (this.tokens[idx].kind !== 'wsc') {
return this.tokens[idx];
}
}
}
/**
* @param {number} start
* @param {number} delete_count
* @param {...Token} insert
*/
splice(start, delete_count, ...insert) {
this.tokens.splice(start, delete_count, ...insert);
this.current = this.tokens[this.idx];
}
}
exports.TokenList = TokenList;
exports.addproblem = addproblem;