mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 01:48:18 +00:00
support token extraction in expressions
This commit is contained in:
@@ -16,6 +16,7 @@ const { TokenList } = require("./TokenList");
|
|||||||
const { AnyMethod, AnyType, AnyValue } = require("./anys");
|
const { AnyMethod, AnyType, AnyValue } = require("./anys");
|
||||||
const { Label, Local, MethodDeclarations, ResolvedIdent } = require("./body-types");
|
const { Label, Local, MethodDeclarations, ResolvedIdent } = require("./body-types");
|
||||||
const { resolveImports, resolveSingleImport } = require('../java/import-resolver');
|
const { resolveImports, resolveSingleImport } = require('../java/import-resolver');
|
||||||
|
const { getTypeInheritanceList } = require('./expression-resolver');
|
||||||
|
|
||||||
const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression");
|
const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression");
|
||||||
const { ArrayValueExpression } = require("./expressiontypes/ArrayValueExpression");
|
const { ArrayValueExpression } = require("./expressiontypes/ArrayValueExpression");
|
||||||
@@ -30,6 +31,7 @@ const { MethodCallExpression } = require("./expressiontypes/MethodCallExpression
|
|||||||
const { NewArray, NewObject } = require("./expressiontypes/NewExpression");
|
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 { Variable } = require("./expressiontypes/Variable");
|
||||||
|
|
||||||
const { BooleanLiteral } = require('./expressiontypes/literals/Boolean');
|
const { BooleanLiteral } = require('./expressiontypes/literals/Boolean');
|
||||||
const { CharacterLiteral } = require('./expressiontypes/literals/Character');
|
const { CharacterLiteral } = require('./expressiontypes/literals/Character');
|
||||||
@@ -843,15 +845,15 @@ function enumValueList(type, tokens, imports, typemap) {
|
|||||||
* @param {Map<string,CEIType>} typemap
|
* @param {Map<string,CEIType>} typemap
|
||||||
*/
|
*/
|
||||||
function statementBlock(tokens, mdecls, method, imports, typemap) {
|
function statementBlock(tokens, mdecls, method, imports, typemap) {
|
||||||
const b = new Block();
|
const block = new Block(tokens.current);
|
||||||
tokens.expectValue('{');
|
tokens.expectValue('{');
|
||||||
mdecls.pushScope();
|
mdecls.pushScope();
|
||||||
while (!tokens.isValue('}')) {
|
while (!tokens.isValue('}')) {
|
||||||
const s = statement(tokens, mdecls, method, imports, typemap);
|
const s = statement(tokens, mdecls, method, imports, typemap);
|
||||||
b.statements.push(s);
|
block.statements.push(s);
|
||||||
}
|
}
|
||||||
mdecls.popScope();
|
mdecls.popScope();
|
||||||
return b;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1538,15 +1540,16 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
|
|||||||
// the result of a bracketed expression is always a value, never a variable
|
// the result of a bracketed expression is always a value, never a variable
|
||||||
// - this prevents things like: (a) = 5;
|
// - this prevents things like: (a) = 5;
|
||||||
return new ResolvedIdent(`(${matches.source})`, [new BracketedExpression(matches)]);
|
return new ResolvedIdent(`(${matches.source})`, [new BracketedExpression(matches)]);
|
||||||
case tokens.isValue('{') && 'symbol':
|
case tokens.current.value === '{' && 'symbol':
|
||||||
// array initer
|
// array initer
|
||||||
let elements = [];
|
let elements = [], open = tokens.current;
|
||||||
|
tokens.expectValue('{');
|
||||||
if (!tokens.isValue('}')) {
|
if (!tokens.isValue('}')) {
|
||||||
elements = expressionList(tokens, mdecls, scope, imports, typemap, { isArrayLiteral:true });
|
elements = expressionList(tokens, mdecls, scope, imports, typemap, { isArrayLiteral:true });
|
||||||
tokens.expectValue('}');
|
tokens.expectValue('}');
|
||||||
}
|
}
|
||||||
const ident = `{${elements.map(e => e.source).join(',')}}`;
|
const ident = `{${elements.map(e => e.source).join(',')}}`;
|
||||||
return new ResolvedIdent(ident, [new ArrayValueExpression(elements)]);
|
return new ResolvedIdent(ident, [new ArrayValueExpression(elements, open)]);
|
||||||
default:
|
default:
|
||||||
addproblem(tokens, ParseProblem.Error(tokens.current, 'Expression expected'));
|
addproblem(tokens, ParseProblem.Error(tokens.current, 'Expression expected'));
|
||||||
return new ResolvedIdent('');
|
return new ResolvedIdent('');
|
||||||
@@ -1563,9 +1566,10 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
|
|||||||
* @param {Map<string,CEIType>} typemap
|
* @param {Map<string,CEIType>} typemap
|
||||||
*/
|
*/
|
||||||
function newTerm(tokens, mdecls, scope, imports, typemap) {
|
function newTerm(tokens, mdecls, scope, imports, typemap) {
|
||||||
|
const new_token = tokens.current;
|
||||||
tokens.expectValue('new');
|
tokens.expectValue('new');
|
||||||
const { resolved: ctr_type } = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers:true, type_vars:[]});
|
const ctr_type = typeIdent(tokens, scope, imports, typemap, {no_array_qualifiers:true, type_vars:[]});
|
||||||
let match = new ResolvedIdent(`new ${ctr_type.simpleTypeName}`, [], [], [ctr_type]);
|
let match = new ResolvedIdent(`new ${ctr_type.resolved.simpleTypeName}`, [], [], [ctr_type.resolved]);
|
||||||
let ctr_args = [], type_body = null;
|
let ctr_args = [], type_body = null;
|
||||||
switch(tokens.current.value) {
|
switch(tokens.current.value) {
|
||||||
case '[':
|
case '[':
|
||||||
@@ -1575,7 +1579,7 @@ 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 NewArray(ctr_type, match)]);
|
return new ResolvedIdent(match.source, [new NewArray(new_token, ctr_type, match)]);
|
||||||
case '(':
|
case '(':
|
||||||
tokens.inc();
|
tokens.inc();
|
||||||
if (!tokens.isValue(')')) {
|
if (!tokens.isValue(')')) {
|
||||||
@@ -1592,7 +1596,7 @@ function newTerm(tokens, mdecls, scope, imports, typemap) {
|
|||||||
addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected'));
|
addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected'));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return new ResolvedIdent(match.source, [new NewObject(ctr_type, ctr_args, type_body)]);
|
return new ResolvedIdent(match.source, [new NewObject(new_token, ctr_type, ctr_args, type_body)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1630,28 +1634,6 @@ function arrayElementOrConstructor(tokens, open_array, instance, index) {
|
|||||||
return new ResolvedIdent(ident, [new ArrayIndexExpression(instance, index)]);
|
return new ResolvedIdent(ident, [new ArrayIndexExpression(instance, index)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {CEIType} type
|
|
||||||
*/
|
|
||||||
function getTypeInheritanceList(type) {
|
|
||||||
const types = {
|
|
||||||
/** @type {JavaType[]} */
|
|
||||||
list: [type],
|
|
||||||
/** @type {Set<JavaType>} */
|
|
||||||
done: new Set(),
|
|
||||||
};
|
|
||||||
for (let type; type = types.list.shift(); ) {
|
|
||||||
if (types.done.has(type)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
types.done.add(type);
|
|
||||||
if (type instanceof CEIType)
|
|
||||||
types.list.push(...type.supers);
|
|
||||||
}
|
|
||||||
return Array.from(types.done);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {ResolvedIdent} matches
|
* @param {ResolvedIdent} matches
|
||||||
* @param {TokenList} tokens
|
* @param {TokenList} tokens
|
||||||
@@ -1807,7 +1789,7 @@ function arrayTypeExpression(matches) {
|
|||||||
*/
|
*/
|
||||||
function resolveIdentifier(tokens, mdecls, scope, imports, typemap) {
|
function resolveIdentifier(tokens, mdecls, scope, imports, typemap) {
|
||||||
const ident = tokens.current.value;
|
const ident = tokens.current.value;
|
||||||
const matches = findIdentifier(ident, mdecls, scope, imports, typemap);
|
const matches = findIdentifier(tokens.current, mdecls, scope, imports, typemap);
|
||||||
checkIdentifierFound(tokens, ident, matches);
|
checkIdentifierFound(tokens, ident, matches);
|
||||||
return matches;
|
return matches;
|
||||||
}
|
}
|
||||||
@@ -1827,20 +1809,22 @@ function checkIdentifierFound(tokens, ident, matches) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {string} ident
|
* @param {Token} token
|
||||||
* @param {MethodDeclarations} mdecls
|
* @param {MethodDeclarations} mdecls
|
||||||
* @param {Scope} scope
|
* @param {Scope} scope
|
||||||
* @param {ResolvedImport[]} imports
|
* @param {ResolvedImport[]} imports
|
||||||
* @param {Map<string,CEIType>} typemap
|
* @param {Map<string,CEIType>} typemap
|
||||||
*/
|
*/
|
||||||
function findIdentifier(ident, mdecls, scope, imports, typemap) {
|
function findIdentifier(token, mdecls, scope, imports, typemap) {
|
||||||
|
const ident = token.value;
|
||||||
const matches = new ResolvedIdent(ident);
|
const matches = new ResolvedIdent(ident);
|
||||||
|
matches.tokens = [token];
|
||||||
|
|
||||||
// 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);
|
||||||
let param = scope && !(scope instanceof SourceType) && scope.parameters.find(p => p.name === ident);
|
let param = scope && !(scope instanceof SourceType) && scope.parameters.find(p => p.name === ident);
|
||||||
if (local || param) {
|
if (local || param) {
|
||||||
matches.variables = [local || param];
|
matches.variables = [new Variable(token, local || param)];
|
||||||
} else if (scope) {
|
} else if (scope) {
|
||||||
// is it a field, method or enum value in the current type (or any of the outer types or superclasses)
|
// is it a field, method or enum value in the current type (or any of the outer types or superclasses)
|
||||||
const scoped_type = scope instanceof SourceType ? scope : scope.owner;
|
const scoped_type = scope instanceof SourceType ? scope : scope.owner;
|
||||||
@@ -1857,12 +1841,12 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) {
|
|||||||
if (!matches.variables[0]) {
|
if (!matches.variables[0]) {
|
||||||
const field = type.fields.find(f => f.name === ident);
|
const field = type.fields.find(f => f.name === ident);
|
||||||
if (field) {
|
if (field) {
|
||||||
matches.variables = [field];
|
matches.variables = [new Variable(token, field)];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const enumValue = (type instanceof SourceType) && type.enumValues.find(e => e.ident.value === ident);
|
const enumValue = (type instanceof SourceType) && type.enumValues.find(e => e.ident.value === ident);
|
||||||
if (enumValue) {
|
if (enumValue) {
|
||||||
matches.variables = [enumValue];
|
matches.variables = [new Variable(token, enumValue)];
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1883,7 +1867,7 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) {
|
|||||||
imp.members.forEach(member => {
|
imp.members.forEach(member => {
|
||||||
if (member.name === ident) {
|
if (member.name === ident) {
|
||||||
if (member instanceof Field) {
|
if (member instanceof Field) {
|
||||||
matches.variables.push(member);
|
matches.variables.push(new Variable(token, member));
|
||||||
} else if (member instanceof Method) {
|
} else if (member instanceof Method) {
|
||||||
matches.methods.push(member);
|
matches.methods.push(member);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ const { Token } = require('./tokenizer');
|
|||||||
class ResolvedIdent {
|
class ResolvedIdent {
|
||||||
/**
|
/**
|
||||||
* @param {string} ident
|
* @param {string} ident
|
||||||
* @param {(Local|Parameter|Field|Expression)[]} variables
|
* @param {Expression[]} variables
|
||||||
* @param {Method[]} methods
|
* @param {Method[]} methods
|
||||||
* @param {JavaType[]} types
|
* @param {JavaType[]} types
|
||||||
* @param {string} package_name
|
* @param {string} package_name
|
||||||
|
|||||||
175
langserver/java/expression-resolver.js
Normal file
175
langserver/java/expression-resolver.js
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('./tokenizer').Token} Token
|
||||||
|
*/
|
||||||
|
const ParseProblem = require('./parsetypes/parse-problem');
|
||||||
|
const { TypeVariable, JavaType, PrimitiveType, NullType, ArrayType, CEIType, WildcardType, TypeVariableType, InferredTypeArgument } = require('java-mti');
|
||||||
|
const { AnyType } = require('./anys');
|
||||||
|
const { Local } = require('./body-types');
|
||||||
|
const { Expression } = require('./expressiontypes/Expression');
|
||||||
|
const { Variable } = require('./expressiontypes/Variable');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {import('./body-types').ResolvedIdent} e
|
||||||
|
* @param {JavaType} assign_type
|
||||||
|
* @param {Map<string,CEIType>} typemap
|
||||||
|
* @param {ParseProblem[]} problems
|
||||||
|
*/
|
||||||
|
function checkAssignment(e, assign_type, typemap, problems) {
|
||||||
|
const value = e.variables[0];
|
||||||
|
if (value instanceof Variable) {
|
||||||
|
checkTypeAssignable(assign_type, value.type, () => value.name_token, problems);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (value instanceof Expression) {
|
||||||
|
const expression_result_type = null;//value.resolveType(typemap);
|
||||||
|
checkTypeAssignable(assign_type, expression_result_type, value.tokens, problems);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {JavaType} variable_type
|
||||||
|
* @param {JavaType} value_type
|
||||||
|
* @param {() => Token|Token[]} tokens
|
||||||
|
*/
|
||||||
|
function checkTypeAssignable(variable_type, value_type, tokens, problems) {
|
||||||
|
if (!isTypeAssignable(variable_type, value_type)) {
|
||||||
|
const t = tokens();
|
||||||
|
problems.push(ParseProblem.Error(t, `Incompatible types: Expression of type '${value_type.fullyDottedTypeName}' cannot be assigned to a variable of type '${variable_type.fullyDottedTypeName}'`));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set of regexes to map source primitives to their destination types.
|
||||||
|
* eg, long (J) is type-assignable to long, float and double (and their boxed counterparts)
|
||||||
|
* Note that void (V) is never type-assignable to anything
|
||||||
|
*/
|
||||||
|
const valid_primitive_types = {
|
||||||
|
// conversions from a primitive to a value
|
||||||
|
from: {
|
||||||
|
B: /^[BSIJFD]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Float|Double);$/,
|
||||||
|
S: /^[SIJFD]$|^Ljava\/lang\/(Short|Integer|Long|Float|Double);$/,
|
||||||
|
I: /^[IJFD]$|^Ljava\/lang\/(Integer|Long|Float|Double);$/,
|
||||||
|
J: /^[JFD]$|^Ljava\/lang\/(Long|Float|Double);$/,
|
||||||
|
F: /^[FD]$|^Ljava\/lang\/(Float|Double);$/,
|
||||||
|
D: /^D$|^Ljava\/lang\/(Double);$/,
|
||||||
|
C: /^[CIJFD]$|^Ljava\/lang\/(Character|Integer|Long|Float|Double);$/,
|
||||||
|
Z: /^Z$|^Ljava\/lang\/(Boolean);$/,
|
||||||
|
V: /$^/, // V.test() always returns false
|
||||||
|
},
|
||||||
|
// conversions to a primitive from a value
|
||||||
|
to: {
|
||||||
|
B: /^[B]$|^Ljava\/lang\/(Byte);$/,
|
||||||
|
S: /^[BS]$|^Ljava\/lang\/(Byte|Short);$/,
|
||||||
|
I: /^[BSIC]$|^Ljava\/lang\/(Byte|Short|Integer|Character);$/,
|
||||||
|
J: /^[BSIJC]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Character);$/,
|
||||||
|
F: /^[BSIJCF]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Character|Float);$/,
|
||||||
|
D: /^[BSIJCFD]$|^Ljava\/lang\/(Byte|Short|Integer|Long|Character|Float|Double);$/,
|
||||||
|
C: /^C$|^Ljava\/lang\/(Character);$/,
|
||||||
|
Z: /^Z$|^Ljava\/lang\/(Boolean);$/,
|
||||||
|
V: /$^/, // V.test() always returns false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if a value of value_type is assignable to a variable of dest_type
|
||||||
|
* @param {JavaType} dest_type
|
||||||
|
* @param {JavaType} value_type
|
||||||
|
*/
|
||||||
|
function isTypeAssignable(dest_type, value_type) {
|
||||||
|
let is_assignable = false;
|
||||||
|
if (dest_type.typeSignature === value_type.typeSignature) {
|
||||||
|
// exact signature match
|
||||||
|
is_assignable = true;
|
||||||
|
} else if (dest_type instanceof AnyType || value_type instanceof AnyType) {
|
||||||
|
// everything is assignable to or from AnyType
|
||||||
|
is_assignable = true;
|
||||||
|
} else if (dest_type.rawTypeSignature === 'Ljava/lang/Object;') {
|
||||||
|
// everything is assignable to Object
|
||||||
|
is_assignable = true;
|
||||||
|
} else if (value_type instanceof PrimitiveType) {
|
||||||
|
// primitive values can only be assigned to wider primitives or their class equivilents
|
||||||
|
is_assignable = valid_primitive_types.from[value_type.typeSignature].test(dest_type.typeSignature);
|
||||||
|
} else if (dest_type instanceof PrimitiveType) {
|
||||||
|
// primitive variables can only be assigned from narrower primitives or their class equivilents
|
||||||
|
is_assignable = valid_primitive_types.to[dest_type.typeSignature].test(value_type.typeSignature);
|
||||||
|
} else if (value_type instanceof NullType) {
|
||||||
|
// null is assignable to any non-primitive
|
||||||
|
is_assignable = !(dest_type instanceof PrimitiveType);
|
||||||
|
} else if (value_type instanceof ArrayType) {
|
||||||
|
// arrays are assignable to other arrays with the same dimensionality and type-assignable bases
|
||||||
|
is_assignable = dest_type instanceof ArrayType
|
||||||
|
&& dest_type.arrdims === value_type.arrdims
|
||||||
|
&& isTypeAssignable(dest_type.base, value_type.base);
|
||||||
|
} else if (value_type instanceof CEIType && dest_type instanceof CEIType) {
|
||||||
|
// class/interfaces types are assignable to any class/interface types in their inheritence tree
|
||||||
|
const valid_types = getTypeInheritanceList(value_type);
|
||||||
|
is_assignable = valid_types.includes(dest_type);
|
||||||
|
if (!is_assignable) {
|
||||||
|
// generic types are also assignable to their raw counterparts
|
||||||
|
const valid_raw_types = valid_types.map(t => t.getRawType());
|
||||||
|
is_assignable = valid_raw_types.includes(dest_type);
|
||||||
|
if (!is_assignable) {
|
||||||
|
// generic types are also assignable to compatible wildcard type bounds
|
||||||
|
const raw_type = valid_raw_types.find(rt => rt.rawTypeSignature === dest_type.rawTypeSignature);
|
||||||
|
if (raw_type instanceof CEIType && raw_type.typeVariables.length === value_type.typeVariables.length) {
|
||||||
|
is_assignable = dest_type.typeVariables.every((dest_tv, idx) => isTypeArgumentCompatible(dest_tv, value_type.typeVariables[idx].type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return is_assignable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {TypeVariable} dest_typevar
|
||||||
|
* @param {JavaType} value_typevar_type
|
||||||
|
*/
|
||||||
|
function isTypeArgumentCompatible(dest_typevar, value_typevar_type) {
|
||||||
|
if (dest_typevar.type instanceof WildcardType) {
|
||||||
|
if (!dest_typevar.type.bound) {
|
||||||
|
// unbounded wildcard types are compatible with everything
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (dest_typevar.type.bound.type === value_typevar_type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
switch (dest_typevar.type.bound.kind) {
|
||||||
|
case 'extends':
|
||||||
|
return isTypeAssignable(dest_typevar.type.bound.type, value_typevar_type);
|
||||||
|
case 'super':;
|
||||||
|
return isTypeAssignable(value_typevar_type, dest_typevar.type.bound.type);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (value_typevar_type instanceof TypeVariableType) {
|
||||||
|
// inferred type arguments of the form `x = List<>` are compatible with every destination type variable
|
||||||
|
return value_typevar_type.typeVariable instanceof InferredTypeArgument;
|
||||||
|
}
|
||||||
|
return dest_typevar.type === value_typevar_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {CEIType} type
|
||||||
|
*/
|
||||||
|
function getTypeInheritanceList(type) {
|
||||||
|
const types = {
|
||||||
|
/** @type {JavaType[]} */
|
||||||
|
list: [type],
|
||||||
|
/** @type {Set<JavaType>} */
|
||||||
|
done: new Set(),
|
||||||
|
};
|
||||||
|
for (let type; type = types.list.shift(); ) {
|
||||||
|
if (types.done.has(type)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
types.done.add(type);
|
||||||
|
if (type instanceof CEIType)
|
||||||
|
types.list.push(...type.supers);
|
||||||
|
}
|
||||||
|
return Array.from(types.done);
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.checkAssignment = checkAssignment;
|
||||||
|
exports.getTypeInheritanceList = getTypeInheritanceList;
|
||||||
@@ -13,6 +13,10 @@ class ArrayIndexExpression extends Expression {
|
|||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.index = index;
|
this.index = index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return [...this.instance.tokens, ...this.index.tokens];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.ArrayIndexExpression = ArrayIndexExpression;
|
exports.ArrayIndexExpression = ArrayIndexExpression;
|
||||||
|
|||||||
@@ -1,15 +1,22 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
|
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
|
||||||
|
* @typedef {import('../tokenizer').Token} Token
|
||||||
*/
|
*/
|
||||||
const { Expression } = require("./Expression");
|
const { Expression } = require("./Expression");
|
||||||
|
|
||||||
class ArrayValueExpression extends Expression {
|
class ArrayValueExpression extends Expression {
|
||||||
/**
|
/**
|
||||||
* @param {ResolvedIdent[]} elements
|
* @param {ResolvedIdent[]} elements
|
||||||
|
* @param {Token} open
|
||||||
*/
|
*/
|
||||||
constructor(elements) {
|
constructor(elements, open) {
|
||||||
super();
|
super();
|
||||||
this.elements = elements;
|
this.elements = elements;
|
||||||
|
this.open = open;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return this.open;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ class BinaryOpExpression extends Expression {
|
|||||||
this.op = op;
|
this.op = op;
|
||||||
this.rhs = rhs;
|
this.rhs = rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return [...this.lhs.tokens, this.op, ...this.rhs.tokens];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.BinaryOpExpression = BinaryOpExpression;
|
exports.BinaryOpExpression = BinaryOpExpression;
|
||||||
|
|||||||
@@ -11,6 +11,10 @@ class BracketedExpression extends Expression {
|
|||||||
super();
|
super();
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return this.expression.tokens;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.BracketedExpression = BracketedExpression;
|
exports.BracketedExpression = BracketedExpression;
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ class CastExpression extends Expression {
|
|||||||
this.castType = castType;
|
this.castType = castType;
|
||||||
this.expression = expression;
|
this.expression = expression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return [...this.castType.tokens, ...this.expression.tokens];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.CastExpression = CastExpression;
|
exports.CastExpression = CastExpression;
|
||||||
|
|||||||
@@ -14,5 +14,9 @@ class ClassMemberExpression extends Expression {
|
|||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.classToken = class_token;
|
this.classToken = class_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return this.classToken;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.ClassMemberExpression = ClassMemberExpression;
|
exports.ClassMemberExpression = ClassMemberExpression;
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('../tokenizer').Token} Token
|
||||||
|
*/
|
||||||
|
|
||||||
class Expression {
|
class Expression {
|
||||||
|
/** @returns {Token|Token[]} */
|
||||||
|
tokens() {
|
||||||
|
throw new Error('Expression.tokens');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.Expression = Expression;
|
exports.Expression = Expression;
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ class IncDecExpression extends Expression {
|
|||||||
this.operator = operator;
|
this.operator = operator;
|
||||||
this.which = which;
|
this.which = which;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return this.operator;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.IncDecExpression = IncDecExpression;
|
exports.IncDecExpression = IncDecExpression;
|
||||||
|
|||||||
@@ -1,19 +1,26 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
|
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
|
||||||
* @typedef {import('../statementtypes/Block').Block} Block
|
|
||||||
*/
|
*/
|
||||||
const { Expression } = require("./Expression");
|
const { Expression } = require("./Expression");
|
||||||
|
const { Block } = require('../statementtypes/Block');
|
||||||
|
|
||||||
class LambdaExpression extends Expression {
|
class LambdaExpression extends Expression {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {*[]} params
|
* @param {*[]} params
|
||||||
* @param {Expression|Block} body
|
* @param {ResolvedIdent|Block} body
|
||||||
*/
|
*/
|
||||||
constructor(params, body) {
|
constructor(params, body) {
|
||||||
super();
|
super();
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
if (this.body instanceof Block) {
|
||||||
|
return this.body.open;
|
||||||
|
}
|
||||||
|
return this.body.tokens;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
exports.LambdaExpression = LambdaExpression;
|
exports.LambdaExpression = LambdaExpression;
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ class MemberExpression extends Expression {
|
|||||||
// member will be null for incomplete expressions
|
// member will be null for incomplete expressions
|
||||||
this.member = member;
|
this.member = member;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return this.member;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.MemberExpression = MemberExpression;
|
exports.MemberExpression = MemberExpression;
|
||||||
|
|||||||
@@ -13,6 +13,10 @@ class MethodCallExpression extends Expression {
|
|||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return this.instance.tokens;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.MethodCallExpression = MethodCallExpression;
|
exports.MethodCallExpression = MethodCallExpression;
|
||||||
|
|||||||
@@ -1,34 +1,47 @@
|
|||||||
/**
|
/**
|
||||||
* @typedef {import('../tokenizer').Token} Token
|
* @typedef {import('../tokenizer').Token} Token
|
||||||
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
|
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
|
||||||
|
* @typedef {import('../source-types').SourceTypeIdent} SourceTypeIdent
|
||||||
* @typedef {import('java-mti').JavaType} JavaType
|
* @typedef {import('java-mti').JavaType} JavaType
|
||||||
*/
|
*/
|
||||||
const { Expression } = require("./Expression");
|
const { Expression } = require("./Expression");
|
||||||
|
|
||||||
class NewArray extends Expression {
|
class NewArray extends Expression {
|
||||||
/**
|
/**
|
||||||
* @param {JavaType} element_type
|
* @param {Token} new_token
|
||||||
|
* @param {SourceTypeIdent} element_type
|
||||||
* @param {ResolvedIdent} dimensions
|
* @param {ResolvedIdent} dimensions
|
||||||
*/
|
*/
|
||||||
constructor(element_type, dimensions) {
|
constructor(new_token, element_type, dimensions) {
|
||||||
super();
|
super();
|
||||||
|
this.new_token = new_token;
|
||||||
this.element_type = element_type;
|
this.element_type = element_type;
|
||||||
this.dimensions = dimensions;
|
this.dimensions = dimensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return [this.new_token, ...this.element_type.tokens, ...this.dimensions.tokens];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class NewObject extends Expression {
|
class NewObject extends Expression {
|
||||||
/**
|
/**
|
||||||
* @param {JavaType} object_type
|
* @param {Token} new_token
|
||||||
|
* @param {SourceTypeIdent} object_type
|
||||||
* @param {ResolvedIdent[]} ctr_args
|
* @param {ResolvedIdent[]} ctr_args
|
||||||
* @param {Token[]} type_body
|
* @param {Token[]} type_body
|
||||||
*/
|
*/
|
||||||
constructor(object_type, ctr_args, type_body) {
|
constructor(new_token, object_type, ctr_args, type_body) {
|
||||||
super();
|
super();
|
||||||
this.element_type = object_type;
|
this.new_token = new_token;
|
||||||
|
this.object_type = object_type;
|
||||||
this.ctr_args = ctr_args;
|
this.ctr_args = ctr_args;
|
||||||
this.type_body = type_body;
|
this.type_body = type_body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return [this.new_token, ...this.object_type.tokens];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.NewArray = NewArray;
|
exports.NewArray = NewArray;
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ class TernaryOpExpression extends Expression {
|
|||||||
this.truthExpression = truthExpression;
|
this.truthExpression = truthExpression;
|
||||||
this.falseExpression = falseExpression;
|
this.falseExpression = falseExpression;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return [...this.test.tokens, ...this.truthExpression.tokens, ...this.falseExpression.tokens];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.TernaryOpExpression = TernaryOpExpression;
|
exports.TernaryOpExpression = TernaryOpExpression;
|
||||||
|
|||||||
@@ -14,6 +14,10 @@ class ThisMemberExpression extends Expression {
|
|||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.thisToken = this_token;
|
this.thisToken = this_token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return this.thisToken;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.ThisMemberExpression = ThisMemberExpression;
|
exports.ThisMemberExpression = ThisMemberExpression;
|
||||||
|
|||||||
28
langserver/java/expressiontypes/Variable.js
Normal file
28
langserver/java/expressiontypes/Variable.js
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
|
||||||
|
* @typedef {import('../body-types').Local} Local
|
||||||
|
* @typedef {import('../tokenizer').Token} Token
|
||||||
|
* @typedef {import('java-mti').Field} Field
|
||||||
|
* @typedef {import('java-mti').Parameter} Parameter
|
||||||
|
* @typedef {import('../source-types').SourceEnumValue} SourceEnumValue
|
||||||
|
*/
|
||||||
|
const { Expression } = require("./Expression");
|
||||||
|
|
||||||
|
class Variable extends Expression {
|
||||||
|
/**
|
||||||
|
* @param {Token} name_token
|
||||||
|
* @param {Local|Parameter|Field|SourceEnumValue} variable
|
||||||
|
*/
|
||||||
|
constructor(name_token, variable) {
|
||||||
|
super();
|
||||||
|
this.name_token = name_token;
|
||||||
|
this.variable = variable;
|
||||||
|
this.type = this.variable.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return this.name_token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.Variable = Variable;
|
||||||
@@ -11,6 +11,10 @@ class LiteralValue extends Expression {
|
|||||||
super();
|
super();
|
||||||
this.token = token;
|
this.token = token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tokens() {
|
||||||
|
return this.token;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.LiteralValue = LiteralValue;
|
exports.LiteralValue = LiteralValue;
|
||||||
|
|||||||
@@ -498,3 +498,4 @@ exports.SourceAnnotation = SourceAnnotation;
|
|||||||
exports.SourceUnit = SourceUnit;
|
exports.SourceUnit = SourceUnit;
|
||||||
exports.SourcePackage = SourcePackage;
|
exports.SourcePackage = SourcePackage;
|
||||||
exports.SourceImport = SourceImport;
|
exports.SourceImport = SourceImport;
|
||||||
|
exports.SourceEnumValue = SourceEnumValue;
|
||||||
|
|||||||
@@ -1,8 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('../tokenizer').Token} Token
|
||||||
|
*/
|
||||||
const { Statement } = require("./Statement");
|
const { Statement } = require("./Statement");
|
||||||
|
|
||||||
class Block extends Statement {
|
class Block extends Statement {
|
||||||
/** @type {Statement[]} */
|
/** @type {Statement[]} */
|
||||||
statements = [];
|
statements = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Token} open
|
||||||
|
*/
|
||||||
|
constructor(open) {
|
||||||
|
super();
|
||||||
|
this.open = open;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.Block = Block;
|
exports.Block = Block;
|
||||||
|
|||||||
Reference in New Issue
Block a user