From f74f4b26c67b05db7eee2e6657e87f4c2a739a21 Mon Sep 17 00:00:00 2001 From: Dave Holoway Date: Tue, 16 Jun 2020 13:57:08 +0100 Subject: [PATCH] add support for parsing enum values --- langserver/java/body-parser3.js | 63 +++++++++++++++++++++++++++++++- langserver/java/source-types2.js | 45 +++++++++++++++++++++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/langserver/java/body-parser3.js b/langserver/java/body-parser3.js index 32a466d..1720d4b 100644 --- a/langserver/java/body-parser3.js +++ b/langserver/java/body-parser3.js @@ -628,6 +628,17 @@ function sourceType(modifiers, tokens, scope_or_pkgname, typeKind, owner, import type.implements_types = typeIdentList(tokens, type, imports, typemap); } 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('}')) { typeBody(type, tokens, owner, imports, typemap); tokens.expectValue('}'); @@ -932,6 +943,50 @@ function parameterDeclaration(type_vars, owner, tokens, imports, typemap) { return new SourceParameter(modifiers, type_ident, varargs, name_token); } +/** +* @param {SourceType} type +* @param {TokenList} tokens +* @param {ResolvedImport[]} imports +* @param {Map} 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 {MethodDeclarations} mdecls @@ -1948,7 +2003,7 @@ function findIdentifier(ident, mdecls, scope, imports, typemap) { if (local || param) { matches.variables = [local || param]; } 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 types = getTypeInheritanceList(scoped_type); 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); if (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( diff --git a/langserver/java/source-types2.js b/langserver/java/source-types2.js index a6690c1..0574bef 100644 --- a/langserver/java/source-types2.js +++ b/langserver/java/source-types2.js @@ -1,6 +1,10 @@ const { CEIType, JavaType, PrimitiveType, Field, Method, MethodBase, Constructor, Parameter, TypeVariable } = require('java-mti'); const { Token } = require('./tokenizer'); +/** + * @typedef {import('./body-types').ResolvedIdent} ResolvedIdent + */ + /** * @param {SourceType|SourceMethod|SourceConstructor|SourceInitialiser|string} scope_or_package_name * @param {string} name @@ -56,6 +60,18 @@ class SourceType extends CEIType { this.fields = []; /** @type {SourceInitialiser[]} */ 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 { /** * @param {Token[]} tokens