add support for parsing enum values

This commit is contained in:
Dave Holoway
2020-06-16 13:57:08 +01:00
parent b050f3a82d
commit f74f4b26c6
2 changed files with 107 additions and 1 deletions

View File

@@ -628,6 +628,17 @@ function sourceType(modifiers, tokens, scope_or_pkgname, typeKind, owner, import
type.implements_types = typeIdentList(tokens, type, imports, typemap); type.implements_types = typeIdentList(tokens, type, imports, typemap);
} }
tokens.expectValue('{'); tokens.expectValue('{');
if (type.typeKind === 'enum') {
enumValueList(type, tokens, imports, typemap);
// if there are any declarations following the enum values, the values must be terminated by a semicolon
switch(tokens.current.value) {
case '}':
break;
default:
semicolon(tokens);
break;
}
}
if (!tokens.isValue('}')) { if (!tokens.isValue('}')) {
typeBody(type, tokens, owner, imports, typemap); typeBody(type, tokens, owner, imports, typemap);
tokens.expectValue('}'); tokens.expectValue('}');
@@ -932,6 +943,50 @@ function parameterDeclaration(type_vars, owner, tokens, imports, typemap) {
return new SourceParameter(modifiers, type_ident, varargs, name_token); return new SourceParameter(modifiers, type_ident, varargs, name_token);
} }
/**
* @param {SourceType} type
* @param {TokenList} tokens
* @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap
*/
function enumValueList(type, tokens, imports, typemap) {
for (;;) {
const ident = tokens.getIfKind('ident');
if (!ident) {
addproblem(tokens, ParseProblem.Error(tokens.current, `Identifier expected`));
}
let ctr_args = [];
if (tokens.isValue('(')) {
if (!tokens.isValue(')')) {
ctr_args = expressionList(tokens, new MethodDeclarations(), type, imports, typemap);
tokens.expectValue(')');
}
}
let anonymousEnumType = null;
if (tokens.isValue('{')) {
// anonymous enum type - just skip for now
for (let balance = 1;;) {
if (tokens.isValue('{')) {
balance++;
} else if (tokens.isValue('}')) {
if (--balance === 0) {
break;
}
} else tokens.inc();
}
}
type.addEnumValue(ident, ctr_args, anonymousEnumType);
if (tokens.isValue(',')) {
continue;
}
if (tokens.current.kind === 'ident') {
addproblem(tokens, ParseProblem.Error(tokens.current, `Missing comma`));
continue;
}
break;
}
}
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {MethodDeclarations} mdecls * @param {MethodDeclarations} mdecls
@@ -1948,7 +2003,7 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) {
if (local || param) { if (local || param) {
matches.variables = [local || param]; matches.variables = [local || param];
} else { } else {
// is it a field or method in the current type (or any of the superclasses) // is it a field, method or enum value in the current type (or any of the superclasses)
const scoped_type = scope instanceof SourceType ? scope : scope.owner; const scoped_type = scope instanceof SourceType ? scope : scope.owner;
const types = getTypeInheritanceList(scoped_type); const types = getTypeInheritanceList(scoped_type);
const method_sigs = new Set(); const method_sigs = new Set();
@@ -1957,6 +2012,12 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) {
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 = [field];
return;
}
const enumValue = (type instanceof SourceType) && type.enumValues.find(e => e.ident.value === ident);
if (enumValue) {
matches.variables = [enumValue];
return;
} }
} }
matches.methods = matches.methods.concat( matches.methods = matches.methods.concat(

View File

@@ -1,6 +1,10 @@
const { CEIType, JavaType, PrimitiveType, Field, Method, MethodBase, Constructor, Parameter, TypeVariable } = require('java-mti'); const { CEIType, JavaType, PrimitiveType, Field, Method, MethodBase, Constructor, Parameter, TypeVariable } = require('java-mti');
const { Token } = require('./tokenizer'); const { Token } = require('./tokenizer');
/**
* @typedef {import('./body-types').ResolvedIdent} ResolvedIdent
*/
/** /**
* @param {SourceType|SourceMethod|SourceConstructor|SourceInitialiser|string} scope_or_package_name * @param {SourceType|SourceMethod|SourceConstructor|SourceInitialiser|string} scope_or_package_name
* @param {string} name * @param {string} name
@@ -56,6 +60,18 @@ class SourceType extends CEIType {
this.fields = []; this.fields = [];
/** @type {SourceInitialiser[]} */ /** @type {SourceInitialiser[]} */
this.initers = []; this.initers = [];
/** @type {SourceEnumValue[]} */
this.enumValues = [];
}
/**
*
* @param {Token} ident
* @param {ResolvedIdent[]} ctr_args
* @param {SourceType} anonymousType
*/
addEnumValue(ident, ctr_args, anonymousType) {
this.enumValues.push(new SourceEnumValue(this, ident, ctr_args, anonymousType));
} }
/** /**
@@ -89,6 +105,35 @@ class SourceType extends CEIType {
} }
} }
class SourceEnumValue extends Field {
/**
* @param {SourceType} owner
* @param {Token} ident
* @param {ResolvedIdent[]} ctr_args
* @param {SourceType} anonymousType
*/
constructor(owner, ident, ctr_args, anonymousType) {
super(['public','static','final'], '');
this.owner = owner;
this.ident = ident;
this.value = ctr_args;
this.anonymousType = anonymousType;
}
get label() {
// don't include the implicit modifiers in the label
return `${this.owner.simpleTypeName} ${this.name}`;
}
get name() {
return this.ident.value;
}
get type() {
return this.owner;
}
}
class SourceTypeIdent { class SourceTypeIdent {
/** /**
* @param {Token[]} tokens * @param {Token[]} tokens