refactor to allow expressions to have a type scope

This commit is contained in:
Dave Holoway
2020-06-13 16:47:59 +01:00
parent da8d37aafd
commit 18d56e0bc0

View File

@@ -18,6 +18,7 @@ const { AnyMethod, AnyType, AnyValue, ArrayElement, ArrayLiteral, ConstructorCal
/** /**
* @typedef {SourceMethod|SourceConstructor|SourceInitialiser} SourceMC * @typedef {SourceMethod|SourceConstructor|SourceInitialiser} SourceMC
* @typedef {SourceType|SourceMC} Scope
*/ */
@@ -105,7 +106,7 @@ function statement(tokens, mdecls, method, imports, typemap) {
tokens.inc(); tokens.inc();
continue; continue;
case 'type-kw': case 'type-kw':
localType(modifiers.splice(0,1e9), tokens, mdecls, method, imports, typemap); sourceType(modifiers.splice(0,1e9), tokens, mdecls, method, imports, typemap);
continue; continue;
} }
break; break;
@@ -239,13 +240,13 @@ class AssertStatement extends Statement {
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function localType(modifiers, tokens, mdecls, method, imports, typemap) { function sourceType(modifiers, tokens, mdecls, scope, imports, typemap) {
// local types are inner types with number-prefixed names, eg. Type$1Inner const scoped_type = scope instanceof SourceType ? scope : scope.owner;
const type = typeDeclaration(method.owner.packageName, method, modifiers, tokens.current, tokens, imports, typemap); const type = typeDeclaration(scoped_type.packageName, scope, modifiers, tokens.current, tokens, imports, typemap);
mdecls.types.push(type); mdecls.types.push(type);
if (tokens.isValue('extends')) { if (tokens.isValue('extends')) {
const extends_types = typeIdentList(tokens, type, imports, typemap); const extends_types = typeIdentList(tokens, type, imports, typemap);
@@ -255,18 +256,18 @@ function localType(modifiers, tokens, mdecls, method, imports, typemap) {
} }
tokens.expectValue('{'); tokens.expectValue('{');
if (!tokens.isValue('}')) { if (!tokens.isValue('}')) {
typeBody(type, tokens, method, imports, typemap); typeBody(type, tokens, scope, imports, typemap);
} }
} }
/** /**
* @param {SourceType} type * @param {SourceType} type
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function typeBody(type, tokens, method, imports, typemap) { function typeBody(type, tokens, scope, imports, typemap) {
while (!tokens.isValue('}')) { while (!tokens.isValue('}')) {
let modifiers = [], annotations = []; let modifiers = [], annotations = [];
while (tokens.current.kind === 'modifier') { while (tokens.current.kind === 'modifier') {
@@ -279,7 +280,7 @@ function typeBody(type, tokens, method, imports, typemap) {
fmc(modifiers, annotations, [], type, tokens, imports, typemap); fmc(modifiers, annotations, [], type, tokens, imports, typemap);
continue; continue;
case 'type-kw': case 'type-kw':
localType(modifiers, tokens, new MethodDeclarations(), method, imports, typemap); sourceType(modifiers, tokens, new MethodDeclarations(), scope, imports, typemap);
continue; continue;
} }
switch(tokens.current.value) { switch(tokens.current.value) {
@@ -364,10 +365,9 @@ function annotation(tokens, scope, imports, typemap) {
/** /**
* @param {string} package_name * @param {string} package_name
* @param {SourceType | SourceMC} scope * @param {Scope} scope
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {CEIType | SourceMC} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
@@ -377,7 +377,7 @@ function annotationTypeDeclaration(package_name, scope, modifiers, tokens, impor
/** /**
* @param {string} package_name * @param {string} package_name
* @param {SourceType | SourceMC} scope * @param {Scope} scope
* @param {Token[]} modifiers * @param {Token[]} modifiers
* @param {Token} kind_token * @param {Token} kind_token
* @param {TokenList} tokens * @param {TokenList} tokens
@@ -402,7 +402,7 @@ function typeDeclaration(package_name, scope, modifiers, kind_token, tokens, imp
/** /**
* @param {CEIType} owner * @param {CEIType} owner
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {CEIType | SourceMC} scope * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
@@ -665,13 +665,13 @@ function statementKeyword(tokens, mdecls, method, imports, typemap) {
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function bracketedTest(tokens, mdecls, method, imports, typemap) { function bracketedTest(tokens, mdecls, scope, imports, typemap) {
tokens.expectValue('('); tokens.expectValue('(');
const e = expression(tokens, mdecls, method, imports, typemap); const e = expression(tokens, mdecls, scope, imports, typemap);
if (e.variables[0] && !isTypeAssignable(PrimitiveType.map.Z, e.variables[0].type)) { if (e.variables[0] && !isTypeAssignable(PrimitiveType.map.Z, e.variables[0].type)) {
addproblem(tokens, ParseProblem.Error(tokens.current, `Boolean expression expected, but type '${e.variables[0].type.fullyDottedTypeName}' found`)); addproblem(tokens, ParseProblem.Error(tokens.current, `Boolean expression expected, but type '${e.variables[0].type.fullyDottedTypeName}' found`));
} }
@@ -1074,14 +1074,14 @@ function checkThrowExpression(tokens, throw_expression, typemap) {
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
* @returns {Local[]} * @returns {Local[]}
*/ */
function var_decl(mods, tokens, mdecls, method, imports, typemap) { function var_decl(mods, tokens, mdecls, scope, imports, typemap) {
const type = typeIdent(tokens, method, imports, typemap); const type = typeIdent(tokens, scope, imports, typemap);
return var_ident_list(mods, type, null, tokens, mdecls, method, imports, typemap) return var_ident_list(mods, type, null, tokens, mdecls, scope, imports, typemap)
} }
/** /**
@@ -1091,11 +1091,11 @@ function var_decl(mods, tokens, mdecls, method, imports, typemap) {
* @param {Token} first_ident * @param {Token} first_ident
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function var_ident_list(mods, type, first_ident, tokens, mdecls, method, imports, typemap) { function var_ident_list(mods, type, first_ident, tokens, mdecls, scope, imports, typemap) {
checkLocalModifiers(tokens, mods); checkLocalModifiers(tokens, mods);
const new_locals = []; const new_locals = [];
for (;;) { for (;;) {
@@ -1117,7 +1117,7 @@ function var_ident_list(mods, type, first_ident, tokens, mdecls, method, imports
} }
let init = null, op = tokens.current; let init = null, op = tokens.current;
if (tokens.isValue('=')) { if (tokens.isValue('=')) {
init = expression(tokens, mdecls, method, imports, typemap); init = expression(tokens, mdecls, scope, imports, typemap);
} }
// only add the local if we have a name // only add the local if we have a name
if (name) { if (name) {
@@ -1138,19 +1138,19 @@ function var_ident_list(mods, type, first_ident, tokens, mdecls, method, imports
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
* @returns {ResolvedIdent|Local[]} * @returns {ResolvedIdent|Local[]}
*/ */
function expression_or_var_decl(tokens, mdecls, method, imports, typemap) { function expression_or_var_decl(tokens, mdecls, scope, imports, typemap) {
/** @type {ResolvedIdent} */ /** @type {ResolvedIdent} */
let matches = expression(tokens, mdecls, method, imports, typemap); let matches = expression(tokens, mdecls, scope, imports, typemap);
// if theres at least one type followed by an ident, we assume a variable declaration // if theres at least one type followed by an ident, we assume a variable declaration
if (matches.types[0] && tokens.current.kind === 'ident') { if (matches.types[0] && tokens.current.kind === 'ident') {
return var_ident_list([], matches.types[0], null, tokens, mdecls, method, imports, typemap); return var_ident_list([], matches.types[0], null, tokens, mdecls, scope, imports, typemap);
} }
return matches; return matches;
@@ -1159,20 +1159,20 @@ function expression_or_var_decl(tokens, mdecls, method, imports, typemap) {
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
* @returns {ResolvedIdent[]|Local[]} * @returns {ResolvedIdent[]|Local[]}
*/ */
function expression_list_or_var_decl(tokens, mdecls, method, imports, typemap) { function expression_list_or_var_decl(tokens, mdecls, scope, imports, typemap) {
let e = expression_or_var_decl(tokens, mdecls, method, imports, typemap); let e = expression_or_var_decl(tokens, mdecls, scope, imports, typemap);
if (Array.isArray(e)) { if (Array.isArray(e)) {
// local var decl // local var decl
return e; return e;
} }
const expressions = [e]; const expressions = [e];
while (tokens.isValue(',')) { while (tokens.isValue(',')) {
e = expression(tokens, mdecls, method, imports, typemap); e = expression(tokens, mdecls, scope, imports, typemap);
expressions.push(e); expressions.push(e);
} }
return expressions; return expressions;
@@ -1215,13 +1215,13 @@ const operator_precedences = {
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function expression(tokens, mdecls, method, imports, typemap, precedence_stack = [13]) { function expression(tokens, mdecls, scope, imports, typemap, precedence_stack = [13]) {
/** @type {ResolvedIdent} */ /** @type {ResolvedIdent} */
let matches = qualifiedTerm(tokens, mdecls, method, imports, typemap); let matches = qualifiedTerm(tokens, mdecls, scope, imports, typemap);
for(;;) { for(;;) {
if (!/^(assignment|equality|comparison|bitwise|shift|logical|muldiv|plumin|instanceof)-operator/.test(tokens.current.kind) && !/\?/.test(tokens.current.value)) { if (!/^(assignment|equality|comparison|bitwise|shift|logical|muldiv|plumin|instanceof)-operator/.test(tokens.current.kind) && !/\?/.test(tokens.current.value)) {
@@ -1239,12 +1239,12 @@ function expression(tokens, mdecls, method, imports, typemap, precedence_stack =
} }
tokens.inc(); tokens.inc();
// higher or equal precendence with rtl evaluation // higher or equal precendence with rtl evaluation
const rhs = expression(tokens, mdecls, method, imports, typemap, [operator_precedence, ...precedence_stack]); const rhs = expression(tokens, mdecls, scope, imports, typemap, [operator_precedence, ...precedence_stack]);
if (binary_operator.value === '?') { if (binary_operator.value === '?') {
const colon = tokens.current; const colon = tokens.current;
tokens.expectValue(':'); tokens.expectValue(':');
const falseStatement = expression(tokens, mdecls, method, imports, typemap, [operator_precedence, ...precedence_stack]); const falseStatement = expression(tokens, mdecls, scope, imports, typemap, [operator_precedence, ...precedence_stack]);
matches = resolveTernaryExpression(tokens, matches, colon, rhs, falseStatement); matches = resolveTernaryExpression(tokens, matches, colon, rhs, falseStatement);
} else { } else {
matches = resolveBinaryOpExpression(tokens, matches, binary_operator, rhs); matches = resolveBinaryOpExpression(tokens, matches, binary_operator, rhs);
@@ -1905,12 +1905,12 @@ function resolveMath(tokens, ident, lhs, op, rhs) {
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function qualifiedTerm(tokens, mdecls, method, imports, typemap) { function qualifiedTerm(tokens, mdecls, scope, imports, typemap) {
let matches = rootTerm(tokens, mdecls, method, imports, typemap); let matches = rootTerm(tokens, mdecls, scope, imports, typemap);
if (tokens.current.kind === 'inc-operator') { if (tokens.current.kind === 'inc-operator') {
// postfix inc/dec - only applies to assignable number variables and no qualifiers are allowed to follow // postfix inc/dec - only applies to assignable number variables and no qualifiers are allowed to follow
const postfix_operator = tokens.current; const postfix_operator = tokens.current;
@@ -1921,7 +1921,7 @@ function qualifiedTerm(tokens, mdecls, method, imports, typemap) {
} }
return new ResolvedIdent(`${matches.source}${postfix_operator.value}`, vars); return new ResolvedIdent(`${matches.source}${postfix_operator.value}`, vars);
} }
matches = qualifiers(matches, tokens, mdecls, method, imports, typemap); matches = qualifiers(matches, tokens, mdecls, scope, imports, typemap);
return matches; return matches;
} }
@@ -1964,17 +1964,17 @@ function isCastExpression(token, matches) {
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
* @returns {ResolvedIdent} * @returns {ResolvedIdent}
*/ */
function rootTerm(tokens, mdecls, method, imports, typemap) { function rootTerm(tokens, mdecls, scope, imports, typemap) {
/** @type {ResolvedIdent} */ /** @type {ResolvedIdent} */
let matches; let matches;
switch(tokens.current.kind) { switch(tokens.current.kind) {
case 'ident': case 'ident':
matches = resolveIdentifier(tokens, mdecls, method, imports, typemap); matches = resolveIdentifier(tokens, mdecls, scope, imports, typemap);
break; break;
case 'primitive-type': case 'primitive-type':
matches = new ResolvedIdent(tokens.current.value, [], [], [PrimitiveType.fromName(tokens.current.value)]); matches = new ResolvedIdent(tokens.current.value, [], [], [PrimitiveType.fromName(tokens.current.value)]);
@@ -1990,10 +1990,11 @@ function rootTerm(tokens, mdecls, method, imports, typemap) {
break; break;
case 'object-literal': case 'object-literal':
// this, super or null // this, super or null
const scoped_type = scope instanceof SourceType ? scope : scope.owner;
if (tokens.current.value === 'this') { if (tokens.current.value === 'this') {
matches = new ResolvedIdent(tokens.current.value, [new Value(tokens.current.value, method.owner)]); matches = new ResolvedIdent(tokens.current.value, [new Value(tokens.current.value, scoped_type)]);
} else if (tokens.current.value === 'super') { } else if (tokens.current.value === 'super') {
const supertype = method.owner.supers.find(s => s.typeKind === 'class') || typemap.get('java/lang/Object'); const supertype = scoped_type.supers.find(s => s.typeKind === 'class') || typemap.get('java/lang/Object');
matches = new ResolvedIdent(tokens.current.value, [new Value(tokens.current.value, supertype)]); matches = new ResolvedIdent(tokens.current.value, [new Value(tokens.current.value, supertype)]);
} else { } else {
matches = new ResolvedIdent(tokens.current.value, [new LiteralValue(tokens.current.value, new NullType())]); matches = new ResolvedIdent(tokens.current.value, [new LiteralValue(tokens.current.value, new NullType())]);
@@ -2005,7 +2006,7 @@ function rootTerm(tokens, mdecls, method, imports, typemap) {
case 'inc-operator': case 'inc-operator':
let incop = tokens.current; let incop = tokens.current;
tokens.inc(); tokens.inc();
matches = qualifiedTerm(tokens, mdecls, method, imports, typemap); matches = qualifiedTerm(tokens, mdecls, scope, imports, typemap);
const inc_ident = `${incop.value}${matches.source}`; const inc_ident = `${incop.value}${matches.source}`;
if (!matches.variables[0]) { if (!matches.variables[0]) {
return new ResolvedIdent(inc_ident); return new ResolvedIdent(inc_ident);
@@ -2017,12 +2018,12 @@ function rootTerm(tokens, mdecls, method, imports, typemap) {
case 'plumin-operator': case 'plumin-operator':
case 'unary-operator': case 'unary-operator':
tokens.inc(); tokens.inc();
return qualifiedTerm(tokens, mdecls, method, imports, typemap); return qualifiedTerm(tokens, mdecls, scope, imports, typemap);
case 'new-operator': case 'new-operator':
return newTerm(tokens, mdecls, method, imports, typemap); return newTerm(tokens, mdecls, scope, imports, typemap);
case 'open-bracket': case 'open-bracket':
tokens.inc(); tokens.inc();
matches = expression(tokens, mdecls, method, imports, typemap); matches = expression(tokens, mdecls, scope, imports, typemap);
const close_bracket = tokens.current; const close_bracket = tokens.current;
tokens.expectValue(')'); tokens.expectValue(')');
if (isCastExpression(tokens.current, matches)) { if (isCastExpression(tokens.current, matches)) {
@@ -2031,7 +2032,7 @@ function rootTerm(tokens, mdecls, method, imports, typemap) {
if (!type) { if (!type) {
addproblem(tokens, ParseProblem.Error(close_bracket, 'Type expected')); addproblem(tokens, ParseProblem.Error(close_bracket, 'Type expected'));
} }
const cast_matches = qualifiedTerm(tokens, mdecls, method, imports, typemap) const cast_matches = qualifiedTerm(tokens, mdecls, scope, imports, typemap)
// cast any variables as values with the new type // cast any variables as values with the new type
const vars = cast_matches.variables.map(v => { const vars = cast_matches.variables.map(v => {
if (type && !isTypeCastable(v.type, type)) { if (type && !isTypeCastable(v.type, type)) {
@@ -2049,7 +2050,7 @@ function rootTerm(tokens, mdecls, method, imports, typemap) {
// array initer // array initer
let elements = []; let elements = [];
if (!tokens.isValue('}')) { if (!tokens.isValue('}')) {
elements = expressionList(tokens, mdecls, method, imports, typemap); elements = expressionList(tokens, mdecls, scope, imports, typemap);
tokens.expectValue('}'); tokens.expectValue('}');
} }
const ident = `{${elements.map(e => e.source).join(',')}}`; const ident = `{${elements.map(e => e.source).join(',')}}`;
@@ -2065,14 +2066,14 @@ function rootTerm(tokens, mdecls, method, imports, typemap) {
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function newTerm(tokens, mdecls, method, imports, typemap) { function newTerm(tokens, mdecls, scope, imports, typemap) {
tokens.expectValue('new'); tokens.expectValue('new');
const type_start_token = tokens.idx; const type_start_token = tokens.idx;
const ctr_type = typeIdent(tokens, method, imports, typemap, false); const ctr_type = typeIdent(tokens, scope, imports, typemap, false);
if (ctr_type instanceof AnyType) { if (ctr_type instanceof AnyType) {
const toks = tokens.tokens.slice(type_start_token, tokens.idx); const toks = tokens.tokens.slice(type_start_token, tokens.idx);
addproblem(tokens, ParseProblem.Error(toks, `Unresolved type: '${toks.map(t => t.source).join('')}'`)); addproblem(tokens, ParseProblem.Error(toks, `Unresolved type: '${toks.map(t => t.source).join('')}'`));
@@ -2080,15 +2081,15 @@ function newTerm(tokens, mdecls, method, imports, typemap) {
let match = new ResolvedIdent(`new ${ctr_type.simpleTypeName}`, [], [], [ctr_type]); let match = new ResolvedIdent(`new ${ctr_type.simpleTypeName}`, [], [], [ctr_type]);
switch(tokens.current.value) { switch(tokens.current.value) {
case '[': case '[':
match = arrayQualifiers(match, tokens, mdecls, method, imports, typemap); match = arrayQualifiers(match, tokens, mdecls, scope, imports, typemap);
// @ts-ignore // @ts-ignore
if (tokens.current.value === '{') { if (tokens.current.value === '{') {
// array init // array init
rootTerm(tokens, mdecls, method, imports, typemap); rootTerm(tokens, mdecls, scope, imports, typemap);
} }
return new ResolvedIdent(match.source, [new Value(match.source, match.types[0])]); return new ResolvedIdent(match.source, [new Value(match.source, match.types[0])]);
case '(': case '(':
match = methodCallQualifier(match, tokens, mdecls, method, imports, typemap); match = methodCallQualifier(match, tokens, mdecls, scope, imports, typemap);
// @ts-ignore // @ts-ignore
if (tokens.current.value === '{') { if (tokens.current.value === '{') {
// final types cannot be inherited // final types cannot be inherited
@@ -2121,16 +2122,16 @@ function newTerm(tokens, mdecls, method, imports, typemap) {
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function expressionList(tokens, mdecls, method, imports, typemap) { function expressionList(tokens, mdecls, scope, imports, typemap) {
let e = expression(tokens, mdecls, method, imports, typemap); let e = expression(tokens, mdecls, scope, imports, typemap);
const expressions = [e]; const expressions = [e];
while (tokens.current.value === ',') { while (tokens.current.value === ',') {
tokens.inc(); tokens.inc();
e = expression(tokens, mdecls, method, imports, typemap); e = expression(tokens, mdecls, scope, imports, typemap);
expressions.push(e); expressions.push(e);
} }
return expressions; return expressions;
@@ -2139,12 +2140,12 @@ function expressionList(tokens, mdecls, method, imports, typemap) {
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function arrayIndexOrDimension(tokens, mdecls, method, imports, typemap) { function arrayIndexOrDimension(tokens, mdecls, scope, imports, typemap) {
let e = expression(tokens, mdecls, method, imports, typemap); let e = expression(tokens, mdecls, scope, imports, typemap);
// the value must be a integer-compatible // the value must be a integer-compatible
const values = e.variables.map(v => new Value(v.name, v.type)).filter(v => /^[BIS]$/.test(v.type.typeSignature)); const values = e.variables.map(v => new Value(v.name, v.type)).filter(v => /^[BIS]$/.test(v.type.typeSignature));
if (!values[0]) { if (!values[0]) {
@@ -2310,22 +2311,22 @@ function getTypeInheritanceList(type) {
* @param {ResolvedIdent} matches * @param {ResolvedIdent} matches
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function qualifiers(matches, tokens, mdecls, method, imports, typemap) { function qualifiers(matches, tokens, mdecls, scope, imports, typemap) {
for (;;) { for (;;) {
switch (tokens.current.value) { switch (tokens.current.value) {
case '.': case '.':
matches = dottedIdent(matches, tokens, typemap); matches = dottedIdent(matches, tokens, typemap);
break; break;
case '[': case '[':
matches = arrayQualifiers(matches, tokens, mdecls, method, imports, typemap); matches = arrayQualifiers(matches, tokens, mdecls, scope, imports, typemap);
break; break;
case '(': case '(':
// method or constructor call // method or constructor call
matches = methodCallQualifier(matches, tokens, mdecls, method, imports, typemap); matches = methodCallQualifier(matches, tokens, mdecls, scope, imports, typemap);
break; break;
case '<': case '<':
// generic type arguments - since this can be confused with less-than, only parse // generic type arguments - since this can be confused with less-than, only parse
@@ -2334,7 +2335,7 @@ function qualifiers(matches, tokens, mdecls, method, imports, typemap) {
return matches; return matches;
} }
tokens.inc(); tokens.inc();
genericTypeArgs(tokens, matches.types, method, imports, typemap); genericTypeArgs(tokens, matches.types, scope, imports, typemap);
break; break;
default: default:
return matches; return matches;
@@ -2346,11 +2347,11 @@ function qualifiers(matches, tokens, mdecls, method, imports, typemap) {
* @param {ResolvedIdent} matches * @param {ResolvedIdent} matches
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function arrayQualifiers(matches, tokens, mdecls, method, imports, typemap) { function arrayQualifiers(matches, tokens, mdecls, scope, imports, typemap) {
while (tokens.isValue('[')) { while (tokens.isValue('[')) {
let open_array = tokens.current; let open_array = tokens.current;
if (tokens.isValue(']')) { if (tokens.isValue(']')) {
@@ -2358,7 +2359,7 @@ function arrayQualifiers(matches, tokens, mdecls, method, imports, typemap) {
matches = arrayTypeExpression(matches); matches = arrayTypeExpression(matches);
} else { } else {
// array index // array index
const index = arrayIndexOrDimension(tokens, mdecls, method, imports, typemap); const index = arrayIndexOrDimension(tokens, mdecls, scope, imports, typemap);
matches = arrayElementOrConstructor(tokens, open_array, matches, index); matches = arrayElementOrConstructor(tokens, open_array, matches, index);
// @ts-ignore // @ts-ignore
tokens.expectValue(']'); tokens.expectValue(']');
@@ -2371,15 +2372,15 @@ function arrayQualifiers(matches, tokens, mdecls, method, imports, typemap) {
* @param {ResolvedIdent} matches * @param {ResolvedIdent} matches
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function methodCallQualifier(matches, tokens, mdecls, method, imports, typemap) { function methodCallQualifier(matches, tokens, mdecls, scope, imports, typemap) {
let args = []; let args = [];
tokens.expectValue('('); tokens.expectValue('(');
if (!tokens.isValue(')')) { if (!tokens.isValue(')')) {
args = expressionList(tokens, mdecls, method, imports, typemap); args = expressionList(tokens, mdecls, scope, imports, typemap);
tokens.expectValue(')'); tokens.expectValue(')');
} }
return methodCallExpression(tokens, matches, args, typemap); return methodCallExpression(tokens, matches, args, typemap);
@@ -2494,13 +2495,13 @@ function dottedIdent(matches, tokens, typemap) {
* *
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function resolveIdentifier(tokens, mdecls, method, imports, typemap) { function resolveIdentifier(tokens, mdecls, scope, imports, typemap) {
const ident = tokens.current.value; const ident = tokens.current.value;
const matches = findIdentifier(ident, mdecls, method, imports, typemap); const matches = findIdentifier(ident, mdecls, scope, imports, typemap);
checkIdentifierFound(tokens, ident, matches); checkIdentifierFound(tokens, ident, matches);
return matches; return matches;
} }
@@ -2522,21 +2523,22 @@ function checkIdentifierFound(tokens, ident, matches) {
/** /**
* @param {string} ident * @param {string} ident
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
* @param {SourceMC} method * @param {Scope} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<String,JavaType>} typemap * @param {Map<String,JavaType>} typemap
*/ */
function findIdentifier(ident, mdecls, method, imports, typemap) { function findIdentifier(ident, mdecls, scope, imports, typemap) {
const matches = new ResolvedIdent(ident); const matches = new ResolvedIdent(ident);
// is it a local or parameter - note that locals must be ordered innermost-scope-first // is it a local or parameter - note that locals must be ordered innermost-scope-first
const local = mdecls.locals.find(local => local.name === ident); const local = mdecls.locals.find(local => local.name === ident);
const param = method.parameters.find(p => p.name === ident); let param = !(scope instanceof SourceType) && scope.parameters.find(p => p.name === ident);
if (local || param) { if (local || param) {
matches.variables = [local || param]; matches.variables = [local || param];
} else { } else {
// is it a field or method in the current type (or any of the superclasses) // is it a field or method in the current type (or any of the superclasses)
const types = getTypeInheritanceList(method.owner); const scoped_type = scope instanceof SourceType ? scope : scope.owner;
const types = getTypeInheritanceList(scoped_type);
const method_sigs = new Set(); const method_sigs = new Set();
types.forEach(type => { types.forEach(type => {
if (!matches.variables[0]) { if (!matches.variables[0]) {
@@ -2561,7 +2563,7 @@ function findIdentifier(ident, mdecls, method, imports, typemap) {
if (type) { if (type) {
matches.types = [type]; matches.types = [type];
} else { } else {
const { types, package_name } = resolveTypeOrPackage(ident, method, imports, typemap); const { types, package_name } = resolveTypeOrPackage(ident, scope, imports, typemap);
matches.types = types; matches.types = types;
matches.package_name = package_name; matches.package_name = package_name;
} }