support package name as a resolved value

This commit is contained in:
Dave Holoway
2020-06-19 11:08:02 +01:00
parent 82e660eabc
commit a034a90735
9 changed files with 89 additions and 27 deletions

View File

@@ -37,7 +37,11 @@ class TokenList {
markEnd() { markEnd() {
let i = this.idx; let i = this.idx;
while (this.tokens[--i].kind === 'wsc') { } while (this.tokens[--i].kind === 'wsc') { }
return this.tokens.slice(this.marks.shift(), i + 1); const range = [this.marks.shift(), i + 1];
if (range[1] <= range[0]) {
range[1] = range[0] + 1;
}
return this.tokens.slice(range[0], range[1]);
} }
/** /**

View File

@@ -61,6 +61,10 @@ class AnyValue extends Expression {
this.label = label; this.label = label;
this.type = AnyType.Instance; this.type = AnyType.Instance;
} }
resolveExpression() {
return this.type;
}
} }
/** /**
@@ -100,6 +104,21 @@ class TypeIdentType {
} }
} }
/**
* Custom type used to represent package name expressions
*
* eg. `java`
*/
class PackageNameType {
/**
* @param {string} package_name
*/
constructor(package_name) {
this.package_name = package_name;
}
}
/** /**
* Custom type used to represent an array literal * Custom type used to represent an array literal
* *
@@ -131,7 +150,7 @@ class MultiValueType {
/** /**
* @typedef {import('./expressiontypes/literals/Number').NumberLiteral} NumberLiteral * @typedef {import('./expressiontypes/literals/Number').NumberLiteral} NumberLiteral
* @typedef {JavaType|MethodType|LambdaType|ArrayValueType|TypeIdentType|MultiValueType|NumberLiteral} ResolvedValue * @typedef {JavaType|MethodType|LambdaType|ArrayValueType|TypeIdentType|PackageNameType|MultiValueType|NumberLiteral} ResolvedValue
**/ **/
exports.AnyMethod = AnyMethod; exports.AnyMethod = AnyMethod;
@@ -141,4 +160,5 @@ exports.ArrayValueType = ArrayValueType;
exports.LambdaType = LambdaType; exports.LambdaType = LambdaType;
exports.MethodType = MethodType; exports.MethodType = MethodType;
exports.MultiValueType = MultiValueType; exports.MultiValueType = MultiValueType;
exports.PackageNameType = PackageNameType;
exports.TypeIdentType = TypeIdentType; exports.TypeIdentType = TypeIdentType;

View File

@@ -4,7 +4,7 @@
*/ */
const { JavaType, CEIType, ArrayType, Method } = require('java-mti'); const { JavaType, CEIType, ArrayType, Method } = require('java-mti');
const { Token } = require('./tokenizer'); const { Token } = require('./tokenizer');
const { AnyType, MethodType, TypeIdentType } = require('./anys'); const { AnyType, MethodType, PackageNameType, TypeIdentType } = require('./anys');
const ParseProblem = require('./parsetypes/parse-problem'); const ParseProblem = require('./parsetypes/parse-problem');
@@ -40,10 +40,12 @@ class ResolvedIdent {
if (this.types[0]) { if (this.types[0]) {
return new TypeIdentType(this.types[0]); return new TypeIdentType(this.types[0]);
} }
if (this.package_name) {
return new PackageNameType(this.package_name);
}
ri.problems.push(ParseProblem.Error(this.tokens, `Unresolved identifier: ${this.source}`)); ri.problems.push(ParseProblem.Error(this.tokens, `Unresolved identifier: ${this.source}`));
return AnyType.Instance; return AnyType.Instance;
} }
} }
class Local { class Local {
@@ -98,7 +100,13 @@ class MethodDeclarations {
} }
popScope() { popScope() {
[this.locals, this.labels, this.types] = this._scopeStack.pop(); const prev = {
locals: this.locals,
labels: this.labels,
types: this.types,
};
([this.locals, this.labels, this.types] = this._scopeStack.pop());
return prev;
} }
} }

View File

@@ -13,7 +13,7 @@ class Expression {
* @returns {ResolvedValue} * @returns {ResolvedValue}
*/ */
resolveExpression(ri) { resolveExpression(ri) {
throw new Error('Expression.resolveType'); throw new Error('Expression.resolveExpression');
} }
/** @returns {Token|Token[]} */ /** @returns {Token|Token[]} */

View File

@@ -21,7 +21,7 @@ class LambdaExpression extends Expression {
/** /**
* @param {ResolveInfo} ri * @param {ResolveInfo} ri
*/ */
resolveType(ri) { resolveExpression(ri) {
return new LambdaType(); return new LambdaType();
} }

View File

@@ -5,8 +5,9 @@
*/ */
const { Expression } = require("./Expression"); const { Expression } = require("./Expression");
const { CEIType } = require('java-mti'); const { CEIType } = require('java-mti');
const { AnyType, MethodType, TypeIdentType } = require('../anys'); const { AnyType, MethodType, PackageNameType, TypeIdentType } = require('../anys');
const { getTypeInheritanceList } = require('../expression-resolver'); const { getTypeInheritanceList } = require('../expression-resolver');
const { resolveNextPackage } = require('../type-resolver');
const ParseProblem = require('../parsetypes/parse-problem'); const ParseProblem = require('../parsetypes/parse-problem');
class MemberExpression extends Expression { class MemberExpression extends Expression {
@@ -25,25 +26,36 @@ class MemberExpression extends Expression {
* @param {ResolveInfo} ri * @param {ResolveInfo} ri
*/ */
resolveExpression(ri) { resolveExpression(ri) {
let type = this.instance.resolveExpression(ri); let instance = this.instance.resolveExpression(ri);
if (type instanceof TypeIdentType) { if (instance instanceof TypeIdentType) {
// static member // static member
type = type.type; instance = instance.type;
} }
if (type instanceof AnyType) { if (instance instanceof AnyType) {
return type; return instance;
} }
const ident = this.member.value; const ident = this.member.value;
if (!(type instanceof CEIType)) {
if (instance instanceof PackageNameType) {
const { sub_package_name, type } = resolveNextPackage(instance.package_name, ident, ri.typemap);
if (!type && !sub_package_name) {
ri.problems.push(ParseProblem.Error(this.member, `Unresolved identifier: '${ident}'`));
}
return type ? new TypeIdentType(type)
: sub_package_name ? new PackageNameType(sub_package_name)
: AnyType.Instance;
}
if (!(instance instanceof CEIType)) {
ri.problems.push(ParseProblem.Error(this.member, `Unresolved member: '${ident}'`)); ri.problems.push(ParseProblem.Error(this.member, `Unresolved member: '${ident}'`));
return AnyType.Instance; return AnyType.Instance;
} }
const field = type.fields.find(f => f.name === ident); const field = instance.fields.find(f => f.name === ident);
if (field) { if (field) {
return field.type; return field.type;
} }
let methods = new Map(); let methods = new Map();
getTypeInheritanceList(type).forEach(type => { getTypeInheritanceList(instance).forEach(type => {
type.methods.forEach(m => { type.methods.forEach(m => {
let msig; let msig;
if (m.name === ident && !methods.has(msig = m.methodSignature)) { if (m.name === ident && !methods.has(msig = m.methodSignature)) {
@@ -54,7 +66,7 @@ class MemberExpression extends Expression {
if (methods.size > 0) { if (methods.size > 0) {
return new MethodType([...methods.values()]); return new MethodType([...methods.values()]);
} }
ri.problems.push(ParseProblem.Error(this.member, `Unresolved member: '${ident}' in type '${type.fullyDottedRawName}'`)); ri.problems.push(ParseProblem.Error(this.member, `Unresolved member: '${ident}' in type '${instance.fullyDottedRawName}'`));
return AnyType.Instance; return AnyType.Instance;
} }

View File

@@ -24,7 +24,7 @@ class Variable extends Expression {
/** /**
* @param {ResolveInfo} ri * @param {ResolveInfo} ri
*/ */
resolveType(ri) { resolveExpression(ri) {
return this.type; return this.type;
} }

View File

@@ -13,6 +13,7 @@ class InstanceLiteral extends LiteralValue {
*/ */
constructor(token, scoped_type) { constructor(token, scoped_type) {
super(token, null); super(token, null);
this.token = token;
this.scoped_type = scoped_type; this.scoped_type = scoped_type;
} }

View File

@@ -364,16 +364,11 @@ function resolveNextTypeOrPackage(ident, outer_types, outer_package_name, typema
}) })
if (outer_package_name) { if (outer_package_name) {
const type_match = `${outer_package_name}/${ident}`; const { type, sub_package_name } = resolveNextPackage(outer_package_name, ident, typemap);
if (typemap.has(type_match)) { if (type) {
// it matches a type types.push(type);
types.push(typemap.get(type_match));
}
const package_match = type_match + '/';
if ([...typemap.keys()].find(fqn => fqn.startsWith(package_match))) {
// it matches a sub-package
package_name = type_match;
} }
package_name = sub_package_name;
} }
return { return {
@@ -382,6 +377,27 @@ function resolveNextTypeOrPackage(ident, outer_types, outer_package_name, typema
} }
} }
/**
*
* @param {string} package_name
* @param {string} ident
* @param {TypeMap} typemap
*/
function resolveNextPackage(package_name, ident, typemap) {
let type = null, sub_package_name = '';
const qualified_name = `${package_name}/${ident}`;
type = typemap.get(qualified_name) || null;
const package_match = qualified_name + '/';
if ([...typemap.keys()].find(fqn => fqn.startsWith(package_match))) {
// it matches a sub-package
sub_package_name = qualified_name;
}
return {
type,
sub_package_name
}
}
module.exports = { module.exports = {
parse_type, parse_type,
resolveType, resolveType,
@@ -390,4 +406,5 @@ module.exports = {
ResolvedType, ResolvedType,
resolveTypeOrPackage, resolveTypeOrPackage,
resolveNextTypeOrPackage, resolveNextTypeOrPackage,
resolveNextPackage,
} }