From 90c537b82de700ccf6eac2fe80ce35545311d3ce Mon Sep 17 00:00:00 2001 From: Dave Holoway Date: Wed, 10 Jun 2020 23:41:41 +0100 Subject: [PATCH] refactor new term qualifiers --- langserver/java/body-parser3.js | 162 ++++++++++++++++++++------------ langserver/java/typeident.js | 5 +- 2 files changed, 104 insertions(+), 63 deletions(-) diff --git a/langserver/java/body-parser3.js b/langserver/java/body-parser3.js index 3153f35..67ea8f4 100644 --- a/langserver/java/body-parser3.js +++ b/langserver/java/body-parser3.js @@ -10,7 +10,7 @@ const ResolvedImport = require('./parsetypes/resolved-import'); const ParseProblem = require('./parsetypes/parse-problem'); const { getOperatorType, Token } = require('./tokenizer'); const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver'); -const { genericTypeArgs } = require('./typeident'); +const { genericTypeArgs, typeIdent } = require('./typeident'); const { TokenList } = require("./TokenList"); const { AnyMethod, AnyType, AnyValue, ArrayElement, ArrayLiteral, ConstructorCall, LiteralNumber, LiteralValue, Local, MethodCall, ResolvedIdent, TernaryValue, Value } = require("./body-types"); @@ -1680,43 +1680,7 @@ function rootTerm(tokens, locals, method, imports, typemap) { tokens.inc(); return qualifiedTerm(tokens, locals, method, imports, typemap); case 'new-operator': - tokens.inc(); - const ctr = qualifiedTerm(tokens, locals, method, imports, typemap); - let new_ident = `new ${ctr.source}`; - if (ctr.types[0] instanceof ArrayType) { - if (tokens.current.value === '{') { - // array init - rootTerm(tokens, locals, method, imports, typemap); - } - return new ResolvedIdent(new_ident, [new Value(new_ident, ctr.types[0])]); - } - if (ctr.variables[0] instanceof ConstructorCall) { - const ctr_type = ctr.variables[0].type; - if (tokens.current.value === '{') { - // final types cannot be inherited - if (ctr_type.modifiers.includes('final') ) { - addproblem(tokens, ParseProblem.Error(tokens.current, `Type '${ctr_type.fullyDottedTypeName}' is declared final and cannot be inherited from.`)); - } - // anonymous type - just skip for now - for (let balance = 0;;) { - if (tokens.isValue('{')) { - balance++; - } else if (tokens.isValue('}')) { - if (--balance === 0) { - break; - } - } else tokens.inc(); - } - } else { - // abstract and interface types must have a type body - if (ctr_type.typeKind === 'interface' || ctr_type.modifiers.includes('abstract') ) { - addproblem(tokens, ParseProblem.Error(tokens.current, `Type '${ctr_type.fullyDottedTypeName}' is abstract and cannot be instantiated without a body`)); - } - } - return new ResolvedIdent(new_ident, [new Value(new_ident, ctr.variables[0].type)]); - } - addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected')); - return new ResolvedIdent(new_ident); + return newTerm(tokens, locals, method, imports, typemap); case 'open-bracket': tokens.inc(); matches = expression(tokens, locals, method, imports, typemap); @@ -1759,6 +1723,57 @@ function rootTerm(tokens, locals, method, imports, typemap) { return matches; } +/** + * @param {TokenList} tokens + * @param {Local[]} locals + * @param {SourceMC} method + * @param {ResolvedImport[]} imports + * @param {Map} typemap + */ +function newTerm(tokens, locals, method, imports, typemap) { + tokens.expectValue('new'); + const ctr_type = typeIdent(tokens, method, imports, typemap, false); + let match = new ResolvedIdent(`new ${ctr_type.simpleTypeName}`, [], [], [ctr_type]); + switch(tokens.current.value) { + case '[': + match = arrayQualifiers(match, tokens, locals, method, imports, typemap); + // @ts-ignore + if (tokens.current.value === '{') { + // array init + rootTerm(tokens, locals, method, imports, typemap); + } + return new ResolvedIdent(match.source, [new Value(match.source, match.types[0])]); + case '(': + match = methodCallQualifier(match, tokens, locals, method, imports, typemap); + // @ts-ignore + if (tokens.current.value === '{') { + // final types cannot be inherited + if (ctr_type.modifiers.includes('final') ) { + addproblem(tokens, ParseProblem.Error(tokens.current, `Type '${ctr_type.fullyDottedTypeName}' is declared final and cannot be inherited from.`)); + } + // anonymous type - just skip for now + for (let balance = 0;;) { + if (tokens.isValue('{')) { + balance++; + } else if (tokens.isValue('}')) { + if (--balance === 0) { + break; + } + } else tokens.inc(); + } + } else { + // abstract and interface types must have a type body + if (ctr_type.typeKind === 'interface' || ctr_type.modifiers.includes('abstract') ) { + addproblem(tokens, ParseProblem.Error(tokens.current, `Type '${ctr_type.fullyDottedTypeName}' is abstract and cannot be instantiated without a body`)); + } + } + return match; + } + + addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected')); + return new ResolvedIdent(match.source, [new Value(match.source, ctr_type)]); +} + /** * @param {TokenList} tokens * @param {Local[]} locals @@ -1955,33 +1970,14 @@ function qualifiers(matches, tokens, locals, method, imports, typemap) { for (;;) { switch (tokens.current.value) { case '.': - tokens.inc(); - matches = parseDottedIdent(matches, tokens, typemap); + matches = dottedIdent(matches, tokens, typemap); break; case '[': - let open_array = tokens.current; - if (tokens.inc().value === ']') { - // array type - tokens.inc(); - matches = arrayTypeExpression(matches); - } else { - // array index - const index = arrayIndexOrDimension(tokens, locals, method, imports, typemap); - matches = arrayElementOrConstructor(tokens, open_array, matches, index); - // @ts-ignore - tokens.expectValue(']'); - } + matches = arrayQualifiers(matches, tokens, locals, method, imports, typemap); break; case '(': // method or constructor call - let args = []; - if (tokens.inc().value === ')') { - tokens.inc(); - } else { - args = expressionList(tokens, locals, method, imports, typemap); - tokens.expectValue(')'); - } - matches = methodCallExpression(tokens, matches, args, typemap); + matches = methodCallQualifier(matches, tokens, locals, method, imports, typemap); break; case '<': // generic type arguments - since this can be confused with less-than, only parse @@ -1998,6 +1994,49 @@ function qualifiers(matches, tokens, locals, method, imports, typemap) { } } +/** + * @param {ResolvedIdent} matches + * @param {TokenList} tokens + * @param {Local[]} locals + * @param {SourceMC} method + * @param {ResolvedImport[]} imports + * @param {Map} typemap + */ +function arrayQualifiers(matches, tokens, locals, method, imports, typemap) { + while (tokens.isValue('[')) { + let open_array = tokens.current; + if (tokens.isValue(']')) { + // array type + matches = arrayTypeExpression(matches); + } else { + // array index + const index = arrayIndexOrDimension(tokens, locals, method, imports, typemap); + matches = arrayElementOrConstructor(tokens, open_array, matches, index); + // @ts-ignore + tokens.expectValue(']'); + } + } + return matches; +} + +/** + * @param {ResolvedIdent} matches + * @param {TokenList} tokens + * @param {Local[]} locals + * @param {SourceMC} method + * @param {ResolvedImport[]} imports + * @param {Map} typemap + */ +function methodCallQualifier(matches, tokens, locals, method, imports, typemap) { + let args = []; + tokens.expectValue('('); + if (!tokens.isValue(')')) { + args = expressionList(tokens, locals, method, imports, typemap); + tokens.expectValue(')'); + } + return methodCallExpression(tokens, matches, args, typemap); +} + /** * @param {ResolvedIdent} matches */ @@ -2012,7 +2051,8 @@ function arrayTypeExpression(matches) { * @param {TokenList} tokens * @param {Map} typemap */ -function parseDottedIdent(matches, tokens, typemap) { +function dottedIdent(matches, tokens, typemap) { + tokens.expectValue('.'); let variables = [], methods = [], types = [], diff --git a/langserver/java/typeident.js b/langserver/java/typeident.js index 5fcffe1..626f511 100644 --- a/langserver/java/typeident.js +++ b/langserver/java/typeident.js @@ -32,8 +32,9 @@ function typeIdentList(tokens, scope, imports, typemap) { * @param {CEIType|MethodBase} scope * @param {ResolvedImport[]} imports * @param {Map} typemap + * @param {boolean} allow_array_qualifiers */ -function typeIdent(tokens, scope, imports, typemap) { +function typeIdent(tokens, scope, imports, typemap, allow_array_qualifiers = true) { /** @type {JavaType[]} */ let types = [], package_name = ''; switch(tokens.current.kind) { @@ -58,7 +59,7 @@ function typeIdent(tokens, scope, imports, typemap) { tokens.inc(); } else if (tokens.isValue('<')) { genericTypeArgs(tokens, types, scope, imports, typemap); - } else if (tokens.isValue('[')) { + } else if (allow_array_qualifiers && tokens.isValue('[')) { let arrdims = 0; for(;;) { arrdims++;