From 4f62b5a06ef8f98f1af3d72ea89cedf7417b8499 Mon Sep 17 00:00:00 2001 From: Dave Holoway Date: Wed, 17 Jun 2020 13:00:04 +0100 Subject: [PATCH] remove Value class, add NewExpression and separate out Any classes --- langserver/java/anys.js | 57 ++++++++ langserver/java/body-parser3.js | 56 +++----- langserver/java/body-types.js | 128 +----------------- langserver/java/expressiontypes/Expression.js | 3 +- .../java/expressiontypes/NewExpression.js | 35 +++++ .../java/expressiontypes/literals/Instance.js | 19 +++ langserver/java/typeident.js | 2 +- langserver/java/validation/bad-extends.js | 2 +- langserver/java/validation/bad-implements.js | 2 +- 9 files changed, 139 insertions(+), 165 deletions(-) create mode 100644 langserver/java/anys.js create mode 100644 langserver/java/expressiontypes/NewExpression.js create mode 100644 langserver/java/expressiontypes/literals/Instance.js diff --git a/langserver/java/anys.js b/langserver/java/anys.js new file mode 100644 index 0000000..ec7a13f --- /dev/null +++ b/langserver/java/anys.js @@ -0,0 +1,57 @@ +const { JavaType, Method } = require('java-mti'); +const { Expression } = require('./expressiontypes/Expression'); + +/** + * AnyType is a special type that's used to fill in types that are missing. + * To prevent cascading errors, AnyType should be fully assign/cast/type-compatible + * with any other type + */ +class AnyType extends JavaType { + /** + * + * @param {String} label + */ + constructor(label) { + super("class", [], ''); + super.simpleTypeName = label || ''; + } + + static Instance = new AnyType(''); + + get rawTypeSignature() { + return 'U'; + } + + get typeSignature() { + return 'U'; + } +} + +class AnyMethod extends Method { + /** + * @param {string} name + */ + constructor(name) { + super(null, name, [], ''); + } + + get returnType() { + return AnyType.Instance; + } +} + +class AnyValue extends Expression { + /** + * + * @param {String} label + */ + constructor(label) { + super(); + this.label = label; + this.type = AnyType.Instance; + } +} + +exports.AnyMethod = AnyMethod; +exports.AnyType = AnyType; +exports.AnyValue = AnyValue; diff --git a/langserver/java/body-parser3.js b/langserver/java/body-parser3.js index 79a0003..3a3e286 100644 --- a/langserver/java/body-parser3.js +++ b/langserver/java/body-parser3.js @@ -4,7 +4,7 @@ * * Each token also contains detailed state information used for completion suggestions. */ -const { JavaType, CEIType, PrimitiveType, ArrayType, UnresolvedType, NullType, TypeVariable, Field, Method } = require('java-mti'); +const { JavaType, CEIType, PrimitiveType, ArrayType, UnresolvedType, TypeVariable, Field, Method } = require('java-mti'); const { SourceType, SourceTypeIdent, SourceField, SourceMethod, SourceConstructor, SourceInitialiser, SourceParameter, SourceAnnotation, SourceUnit, SourcePackage, SourceImport } = require('./source-types2'); const ResolvedImport = require('./parsetypes/resolved-import'); @@ -13,7 +13,8 @@ const { tokenize, Token } = require('./tokenizer'); const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver'); const { genericTypeArgs, typeIdent, typeIdentList } = require('./typeident'); const { TokenList } = require("./TokenList"); -const { AnyMethod, AnyType, AnyValue, Label, Local, MethodDeclarations, ResolvedIdent, Value, } = require("./body-types"); +const { AnyMethod, AnyType, AnyValue } = require("./anys"); +const { Label, Local, MethodDeclarations, ResolvedIdent } = require("./body-types"); const { resolveImports, resolveSingleImport } = require('../java/import-resolver'); const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression"); @@ -26,11 +27,13 @@ const { IncDecExpression } = require("./expressiontypes/IncDecExpression"); const { LambdaExpression } = require("./expressiontypes/LambdaExpression"); const { MemberExpression } = require("./expressiontypes/MemberExpression"); const { MethodCallExpression } = require("./expressiontypes/MethodCallExpression"); +const { NewArray, NewObject } = require("./expressiontypes/NewExpression"); const { TernaryOpExpression } = require("./expressiontypes/TernaryOpExpression"); const { ThisMemberExpression } = require("./expressiontypes/ThisMemberExpression"); const { BooleanLiteral } = require('./expressiontypes/literals/Boolean'); const { CharacterLiteral } = require('./expressiontypes/literals/Character'); +const { InstanceLiteral } = require('./expressiontypes/literals/Instance'); const { NumberLiteral } = require('./expressiontypes/literals/Number'); const { NullLiteral } = require('./expressiontypes/literals/Null'); const { StringLiteral } = require('./expressiontypes/literals/String'); @@ -1533,11 +1536,8 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) { case 'object-literal': // this, super or null const scoped_type = scope instanceof SourceType ? scope : scope.owner; - if (tokens.current.value === 'this') { - matches = new ResolvedIdent(tokens.current.value, [new Value(tokens.current.value, scoped_type)]); - } else if (tokens.current.value === 'super') { - 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)]); + if (tokens.current.value === 'this' || tokens.current.value === 'super') { + matches = new ResolvedIdent(tokens.current.value, [new InstanceLiteral(tokens.current, scoped_type)]); } else { matches = new ResolvedIdent(tokens.current.value, [new NullLiteral(tokens.current)]); } @@ -1606,13 +1606,9 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) { */ function newTerm(tokens, mdecls, scope, imports, typemap) { tokens.expectValue('new'); - const type_start_token = tokens.idx; const { resolved: ctr_type } = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers:true, type_vars:[]}); - if (ctr_type instanceof AnyType) { - const toks = tokens.tokens.slice(type_start_token, tokens.idx); - addproblem(tokens, ParseProblem.Error(toks, `Unresolved type: '${toks.map(t => t.source).join('')}'`)); - } let match = new ResolvedIdent(`new ${ctr_type.simpleTypeName}`, [], [], [ctr_type]); + let ctr_args = [], type_body = null; switch(tokens.current.value) { case '[': match = arrayQualifiers(match, tokens, mdecls, scope, imports, typemap); @@ -1621,36 +1617,24 @@ function newTerm(tokens, mdecls, scope, imports, typemap) { // array init rootTerm(tokens, mdecls, scope, imports, typemap); } - return new ResolvedIdent(match.source, [new Value(match.source, match.types[0])]); + return new ResolvedIdent(match.source, [new NewArray(ctr_type, match)]); case '(': - match = methodCallQualifier(match, tokens, mdecls, scope, imports, typemap); + tokens.inc(); + if (!tokens.isValue(')')) { + ctr_args = expressionList(tokens, mdecls, scope, imports, typemap); + tokens.expectValue(')'); + } // @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`)); - } + type_body = skipBody(tokens); } - return match; + break; + default: + addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected')); + break; } - - addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected')); - return new ResolvedIdent(match.source, [new Value(match.source, ctr_type)]); + return new ResolvedIdent(match.source, [new NewObject(ctr_type, ctr_args, type_body)]); } /** diff --git a/langserver/java/body-types.js b/langserver/java/body-types.js index adb1454..dbb9906 100644 --- a/langserver/java/body-types.js +++ b/langserver/java/body-types.js @@ -1,10 +1,13 @@ +/** + * @typedef {import('./expressiontypes/Expression').Expression} Expression + */ const { JavaType, ArrayType, Method, Parameter, Field } = require('java-mti'); const { Token } = require('./tokenizer'); class ResolvedIdent { /** * @param {string} ident - * @param {(Local|Parameter|Field|ArrayElement|ValueBase)[]} variables + * @param {(Local|Parameter|Field|Expression)[]} variables * @param {Method[]} methods * @param {JavaType[]} types * @param {string} package_name @@ -20,45 +23,6 @@ class ResolvedIdent { } } -/** - * AnyType is a special type that's used to fill in types that are missing. - * To prevent cascading errors, AnyType should be fully assign/cast/type-compatible - * with any other type - */ -class AnyType extends JavaType { - /** - * - * @param {String} label - */ - constructor(label) { - super("class", [], ''); - super.simpleTypeName = label || ''; - } - - static Instance = new AnyType(''); - - get rawTypeSignature() { - return 'U'; - } - - get typeSignature() { - return 'U'; - } -} - -class AnyMethod extends Method { - /** - * @param {string} name - */ - constructor(name) { - super(null, name, [], ''); - } - - get returnType() { - return AnyType.Instance; - } -} - class Local { /** * @param {Token[]} modifiers @@ -115,91 +79,7 @@ class MethodDeclarations { } } -class ArrayElement { - /** - * - * @param {Local|Parameter|Field|ArrayElement|Value} array_variable - * @param {ResolvedIdent} index - */ - constructor(array_variable, index) { - this.array_variable = array_variable; - this.index = index; - if (!(this.array_variable.type instanceof ArrayType)) { - throw new Error('Array element cannot be created from non-array type'); - } - this.name = `${array_variable.name}[${index.source}]`; - /** @type {JavaType} */ - this.type = this.array_variable.type.elementType; - } -} - -class ValueBase {} - -class Value extends ValueBase { - /** - * @param {string} name - * @param {JavaType} type - */ - constructor(name, type) { - super(); - this.name = name; - this.type = type; - } -} - -class AnyValue extends Value { - constructor(name) { - super(name, AnyType.Instance); - } -} - -class MethodCall extends Value { - /** - * @param {string} name - * @param {ResolvedIdent} instance - * @param {Method} method - */ - constructor(name, instance, method) { - super(name, method.returnType); - this.instance = instance; - this.method = method; - } -} - -class ConstructorCall extends Value { - /** - * @param {string} name - * @param {JavaType} type - */ - constructor(name, type) { - super(name, type); - } -} - -class TernaryValue extends Value { - /** - * @param {string} name - * @param {JavaType} true_type - * @param {Token} colon - * @param {Value} false_value - */ - constructor(name, true_type, colon, false_value) { - super(name, true_type); - this.colon = colon; - this.falseValue = false_value; - } -} - -exports.AnyMethod = AnyMethod; -exports.AnyType = AnyType; -exports.AnyValue = AnyValue; -exports.ArrayElement = ArrayElement; -exports.ConstructorCall = ConstructorCall; exports.Label = Label; exports.Local = Local; -exports.MethodCall = MethodCall; exports.MethodDeclarations = MethodDeclarations; exports.ResolvedIdent = ResolvedIdent; -exports.TernaryValue = TernaryValue; -exports.Value = Value; -exports.ValueBase = ValueBase; diff --git a/langserver/java/expressiontypes/Expression.js b/langserver/java/expressiontypes/Expression.js index d24c04f..1bb8bb7 100644 --- a/langserver/java/expressiontypes/Expression.js +++ b/langserver/java/expressiontypes/Expression.js @@ -1,6 +1,5 @@ -const { ValueBase } = require("../body-types"); -class Expression extends ValueBase { +class Expression { } exports.Expression = Expression; diff --git a/langserver/java/expressiontypes/NewExpression.js b/langserver/java/expressiontypes/NewExpression.js new file mode 100644 index 0000000..7b1b9e7 --- /dev/null +++ b/langserver/java/expressiontypes/NewExpression.js @@ -0,0 +1,35 @@ +/** + * @typedef {import('../tokenizer').Token} Token + * @typedef {import('../body-types').ResolvedIdent} ResolvedIdent + * @typedef {import('java-mti').JavaType} JavaType + */ +const { Expression } = require("./Expression"); + +class NewArray extends Expression { + /** + * @param {JavaType} element_type + * @param {ResolvedIdent} dimensions + */ + constructor(element_type, dimensions) { + super(); + this.element_type = element_type; + this.dimensions = dimensions; + } +} + +class NewObject extends Expression { + /** + * @param {JavaType} object_type + * @param {ResolvedIdent[]} ctr_args + * @param {Token[]} type_body + */ + constructor(object_type, ctr_args, type_body) { + super(); + this.element_type = object_type; + this.ctr_args = ctr_args; + this.type_body = type_body; + } +} + +exports.NewArray = NewArray; +exports.NewObject = NewObject; diff --git a/langserver/java/expressiontypes/literals/Instance.js b/langserver/java/expressiontypes/literals/Instance.js new file mode 100644 index 0000000..d970730 --- /dev/null +++ b/langserver/java/expressiontypes/literals/Instance.js @@ -0,0 +1,19 @@ +/** + * @typedef {import('../../tokenizer').Token} Token + * @typedef {import('java-mti').CEIType} CEIType + */ +const { LiteralValue } = require('./LiteralValue'); + +class InstanceLiteral extends LiteralValue { + /** + * + * @param {Token} token 'this' or 'super' token + * @param {CEIType} scoped_type + */ + constructor(token, scoped_type) { + super(token); + this.scoped_type = scoped_type; + } +} + +exports.InstanceLiteral = InstanceLiteral; diff --git a/langserver/java/typeident.js b/langserver/java/typeident.js index bf4bbb9..6103b77 100644 --- a/langserver/java/typeident.js +++ b/langserver/java/typeident.js @@ -3,7 +3,7 @@ const { SourceTypeIdent, SourceMethod, SourceConstructor, SourceInitialiser } = const ResolvedImport = require('./parsetypes/resolved-import'); const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver'); const { Token } = require('./tokenizer'); -const { AnyType } = require("./body-types"); +const { AnyType } = require("./anys"); /** * @typedef {SourceMethod|SourceConstructor|SourceInitialiser} SourceMC diff --git a/langserver/java/validation/bad-extends.js b/langserver/java/validation/bad-extends.js index c71601a..36a8c50 100644 --- a/langserver/java/validation/bad-extends.js +++ b/langserver/java/validation/bad-extends.js @@ -1,6 +1,6 @@ const { SourceType } = require('../source-type'); const ParseProblem = require('../parsetypes/parse-problem'); -const { AnyType } = require('../body-types'); +const { AnyType } = require('../anys'); /** * @param {SourceType} source_type diff --git a/langserver/java/validation/bad-implements.js b/langserver/java/validation/bad-implements.js index d7182d0..b702cf3 100644 --- a/langserver/java/validation/bad-implements.js +++ b/langserver/java/validation/bad-implements.js @@ -1,6 +1,6 @@ const ParseProblem = require('../parsetypes/parse-problem'); const {SourceType} = require('../source-type'); -const { AnyType } = require('../body-types'); +const { AnyType } = require('../anys'); const { UnresolvedType } = require('java-mti'); /**