remove Value class, add NewExpression and separate out Any classes

This commit is contained in:
Dave Holoway
2020-06-17 13:00:04 +01:00
parent b9fd805a6d
commit 4f62b5a06e
9 changed files with 139 additions and 165 deletions

57
langserver/java/anys.js Normal file
View File

@@ -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 || '<unknown type>';
}
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;

View File

@@ -4,7 +4,7 @@
* *
* Each token also contains detailed state information used for completion suggestions. * 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, const { SourceType, SourceTypeIdent, SourceField, SourceMethod, SourceConstructor, SourceInitialiser, SourceParameter, SourceAnnotation,
SourceUnit, SourcePackage, SourceImport } = require('./source-types2'); SourceUnit, SourcePackage, SourceImport } = require('./source-types2');
const ResolvedImport = require('./parsetypes/resolved-import'); const ResolvedImport = require('./parsetypes/resolved-import');
@@ -13,7 +13,8 @@ const { tokenize, Token } = require('./tokenizer');
const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver'); const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver');
const { genericTypeArgs, typeIdent, typeIdentList } = require('./typeident'); const { genericTypeArgs, typeIdent, typeIdentList } = require('./typeident');
const { TokenList } = require("./TokenList"); 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 { resolveImports, resolveSingleImport } = require('../java/import-resolver');
const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression"); const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression");
@@ -26,11 +27,13 @@ const { IncDecExpression } = require("./expressiontypes/IncDecExpression");
const { LambdaExpression } = require("./expressiontypes/LambdaExpression"); const { LambdaExpression } = require("./expressiontypes/LambdaExpression");
const { MemberExpression } = require("./expressiontypes/MemberExpression"); const { MemberExpression } = require("./expressiontypes/MemberExpression");
const { MethodCallExpression } = require("./expressiontypes/MethodCallExpression"); const { MethodCallExpression } = require("./expressiontypes/MethodCallExpression");
const { NewArray, NewObject } = require("./expressiontypes/NewExpression");
const { TernaryOpExpression } = require("./expressiontypes/TernaryOpExpression"); const { TernaryOpExpression } = require("./expressiontypes/TernaryOpExpression");
const { ThisMemberExpression } = require("./expressiontypes/ThisMemberExpression"); const { ThisMemberExpression } = require("./expressiontypes/ThisMemberExpression");
const { BooleanLiteral } = require('./expressiontypes/literals/Boolean'); const { BooleanLiteral } = require('./expressiontypes/literals/Boolean');
const { CharacterLiteral } = require('./expressiontypes/literals/Character'); const { CharacterLiteral } = require('./expressiontypes/literals/Character');
const { InstanceLiteral } = require('./expressiontypes/literals/Instance');
const { NumberLiteral } = require('./expressiontypes/literals/Number'); const { NumberLiteral } = require('./expressiontypes/literals/Number');
const { NullLiteral } = require('./expressiontypes/literals/Null'); const { NullLiteral } = require('./expressiontypes/literals/Null');
const { StringLiteral } = require('./expressiontypes/literals/String'); const { StringLiteral } = require('./expressiontypes/literals/String');
@@ -1533,11 +1536,8 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
case 'object-literal': case 'object-literal':
// this, super or null // this, super or null
const scoped_type = scope instanceof SourceType ? scope : scope.owner; const scoped_type = scope instanceof SourceType ? scope : scope.owner;
if (tokens.current.value === 'this') { if (tokens.current.value === 'this' || tokens.current.value === 'super') {
matches = new ResolvedIdent(tokens.current.value, [new Value(tokens.current.value, scoped_type)]); matches = new ResolvedIdent(tokens.current.value, [new InstanceLiteral(tokens.current, 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)]);
} else { } else {
matches = new ResolvedIdent(tokens.current.value, [new NullLiteral(tokens.current)]); 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) { function newTerm(tokens, mdecls, scope, imports, typemap) {
tokens.expectValue('new'); tokens.expectValue('new');
const type_start_token = tokens.idx;
const { resolved: ctr_type } = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers:true, type_vars:[]}); 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 match = new ResolvedIdent(`new ${ctr_type.simpleTypeName}`, [], [], [ctr_type]);
let ctr_args = [], type_body = null;
switch(tokens.current.value) { switch(tokens.current.value) {
case '[': case '[':
match = arrayQualifiers(match, tokens, mdecls, scope, imports, typemap); match = arrayQualifiers(match, tokens, mdecls, scope, imports, typemap);
@@ -1621,36 +1617,24 @@ function newTerm(tokens, mdecls, scope, imports, typemap) {
// array init // array init
rootTerm(tokens, mdecls, scope, 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 NewArray(ctr_type, match)]);
case '(': 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 // @ts-ignore
if (tokens.current.value === '{') { 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 // anonymous type - just skip for now
for (let balance = 0;;) { type_body = skipBody(tokens);
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; break;
default:
addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected'));
break;
} }
return new ResolvedIdent(match.source, [new NewObject(ctr_type, ctr_args, type_body)]);
addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected'));
return new ResolvedIdent(match.source, [new Value(match.source, ctr_type)]);
} }
/** /**

View File

@@ -1,10 +1,13 @@
/**
* @typedef {import('./expressiontypes/Expression').Expression} Expression
*/
const { JavaType, ArrayType, Method, Parameter, Field } = require('java-mti'); const { JavaType, ArrayType, Method, Parameter, Field } = require('java-mti');
const { Token } = require('./tokenizer'); const { Token } = require('./tokenizer');
class ResolvedIdent { class ResolvedIdent {
/** /**
* @param {string} ident * @param {string} ident
* @param {(Local|Parameter|Field|ArrayElement|ValueBase)[]} variables * @param {(Local|Parameter|Field|Expression)[]} variables
* @param {Method[]} methods * @param {Method[]} methods
* @param {JavaType[]} types * @param {JavaType[]} types
* @param {string} package_name * @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 || '<unknown type>';
}
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 { class Local {
/** /**
* @param {Token[]} modifiers * @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.Label = Label;
exports.Local = Local; exports.Local = Local;
exports.MethodCall = MethodCall;
exports.MethodDeclarations = MethodDeclarations; exports.MethodDeclarations = MethodDeclarations;
exports.ResolvedIdent = ResolvedIdent; exports.ResolvedIdent = ResolvedIdent;
exports.TernaryValue = TernaryValue;
exports.Value = Value;
exports.ValueBase = ValueBase;

View File

@@ -1,6 +1,5 @@
const { ValueBase } = require("../body-types");
class Expression extends ValueBase { class Expression {
} }
exports.Expression = Expression; exports.Expression = Expression;

View File

@@ -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;

View File

@@ -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;

View File

@@ -3,7 +3,7 @@ const { SourceTypeIdent, SourceMethod, SourceConstructor, SourceInitialiser } =
const ResolvedImport = require('./parsetypes/resolved-import'); const ResolvedImport = require('./parsetypes/resolved-import');
const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver'); const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver');
const { Token } = require('./tokenizer'); const { Token } = require('./tokenizer');
const { AnyType } = require("./body-types"); const { AnyType } = require("./anys");
/** /**
* @typedef {SourceMethod|SourceConstructor|SourceInitialiser} SourceMC * @typedef {SourceMethod|SourceConstructor|SourceInitialiser} SourceMC

View File

@@ -1,6 +1,6 @@
const { SourceType } = require('../source-type'); const { SourceType } = require('../source-type');
const ParseProblem = require('../parsetypes/parse-problem'); const ParseProblem = require('../parsetypes/parse-problem');
const { AnyType } = require('../body-types'); const { AnyType } = require('../anys');
/** /**
* @param {SourceType} source_type * @param {SourceType} source_type

View File

@@ -1,6 +1,6 @@
const ParseProblem = require('../parsetypes/parse-problem'); const ParseProblem = require('../parsetypes/parse-problem');
const {SourceType} = require('../source-type'); const {SourceType} = require('../source-type');
const { AnyType } = require('../body-types'); const { AnyType } = require('../anys');
const { UnresolvedType } = require('java-mti'); const { UnresolvedType } = require('java-mti');
/** /**