parse labels and break/continue targets

This commit is contained in:
Dave Holoway
2020-06-10 18:43:43 +01:00
parent 87a2d72ae3
commit cd725638cd
2 changed files with 54 additions and 3 deletions

View File

@@ -25,6 +25,24 @@ class TokenList {
} }
} }
} }
/**
* 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 value and consumes it * Check if the current token matches the specified value and consumes it
* @param {string} value * @param {string} value

View File

@@ -99,8 +99,19 @@ function statement(tokens, locals, method, imports, typemap) {
case 'statement-kw': case 'statement-kw':
s = statementKeyword(tokens, locals, method, imports, typemap); s = statementKeyword(tokens, locals, method, imports, typemap);
return s; return s;
case 'modifier':
case 'ident': case 'ident':
// checking every statement identifier for a possible label is really inefficient, but trying to
// merge this into expression_or_var_decl is worse for now
if (tokens.peek(1).value === ':') {
const label = new Label(tokens.current);
tokens.inc(), tokens.inc();
// ignore and just return the next statement
// - we cannot return the label as a statement because for/if/while check the next statement type
// the labels should be collated and checked for duplicates, etc
return statement(tokens, locals, method, imports, typemap);
}
// fall-through to expression_or_var_decl
case 'modifier':
case 'primitive-type': case 'primitive-type':
s = expression_or_var_decl(tokens, locals, method, imports, typemap); s = expression_or_var_decl(tokens, locals, method, imports, typemap);
if (Array.isArray(s)) { if (Array.isArray(s)) {
@@ -158,8 +169,14 @@ class WhileStatement extends Statement {
test = null; test = null;
statement = null; statement = null;
} }
class BreakStatement extends Statement {} class BreakStatement extends Statement {
class ContinueStatement extends Statement {} /** @type {Token} */
target = null;
}
class ContinueStatement extends Statement {
/** @type {Token} */
target = null;
}
class DoStatement extends Statement { class DoStatement extends Statement {
test = null; test = null;
block = null; block = null;
@@ -193,6 +210,14 @@ class AssertStatement extends Statement {
expression = null; expression = null;
message = null; message = null;
} }
class Label {
/**
* @param {Token} token
*/
constructor(token) {
this.name_token = token;
}
}
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
@@ -253,11 +278,19 @@ function statementKeyword(tokens, locals, method, imports, typemap) {
case 'break': case 'break':
tokens.inc(); tokens.inc();
s = new BreakStatement(); s = new BreakStatement();
if (tokens.current.kind === 'ident') {
s.target = tokens.current;
tokens.inc();
}
semicolon(tokens); semicolon(tokens);
break; break;
case 'continue': case 'continue':
tokens.inc(); tokens.inc();
s = new ContinueStatement(); s = new ContinueStatement();
if (tokens.current.kind === 'ident') {
s.target = tokens.current;
tokens.inc();
}
semicolon(tokens); semicolon(tokens);
break; break;
case 'switch': case 'switch':