refactor new term qualifiers

This commit is contained in:
Dave Holoway
2020-06-10 23:41:41 +01:00
parent cd725638cd
commit 90c537b82d
2 changed files with 104 additions and 63 deletions

View File

@@ -10,7 +10,7 @@ const ResolvedImport = require('./parsetypes/resolved-import');
const ParseProblem = require('./parsetypes/parse-problem'); const ParseProblem = require('./parsetypes/parse-problem');
const { getOperatorType, Token } = require('./tokenizer'); const { getOperatorType, Token } = require('./tokenizer');
const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver'); const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver');
const { genericTypeArgs } = require('./typeident'); const { genericTypeArgs, typeIdent } = require('./typeident');
const { TokenList } = require("./TokenList"); const { TokenList } = require("./TokenList");
const { AnyMethod, AnyType, AnyValue, ArrayElement, ArrayLiteral, ConstructorCall, LiteralNumber, LiteralValue, Local, MethodCall, ResolvedIdent, TernaryValue, Value } = require("./body-types"); const { AnyMethod, AnyType, AnyValue, ArrayElement, ArrayLiteral, ConstructorCall, LiteralNumber, LiteralValue, Local, MethodCall, ResolvedIdent, TernaryValue, Value } = require("./body-types");
@@ -1680,43 +1680,7 @@ function rootTerm(tokens, locals, method, imports, typemap) {
tokens.inc(); tokens.inc();
return qualifiedTerm(tokens, locals, method, imports, typemap); return qualifiedTerm(tokens, locals, method, imports, typemap);
case 'new-operator': case 'new-operator':
tokens.inc(); return newTerm(tokens, locals, method, imports, typemap);
const ctr = qualifiedTerm(tokens, locals, method, imports, typemap);
let new_ident = `new ${ctr.source}`;
if (ctr.types[0] instanceof ArrayType) {
if (tokens.current.value === '{') {
// array init
rootTerm(tokens, locals, method, imports, typemap);
}
return new ResolvedIdent(new_ident, [new Value(new_ident, ctr.types[0])]);
}
if (ctr.variables[0] instanceof ConstructorCall) {
const ctr_type = ctr.variables[0].type;
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
for (let balance = 0;;) {
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 new ResolvedIdent(new_ident, [new Value(new_ident, ctr.variables[0].type)]);
}
addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected'));
return new ResolvedIdent(new_ident);
case 'open-bracket': case 'open-bracket':
tokens.inc(); tokens.inc();
matches = expression(tokens, locals, method, imports, typemap); matches = expression(tokens, locals, method, imports, typemap);
@@ -1759,6 +1723,57 @@ function rootTerm(tokens, locals, method, imports, typemap) {
return matches; return matches;
} }
/**
* @param {TokenList} tokens
* @param {Local[]} locals
* @param {SourceMC} method
* @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap
*/
function newTerm(tokens, locals, method, imports, typemap) {
tokens.expectValue('new');
const ctr_type = typeIdent(tokens, method, imports, typemap, false);
let match = new ResolvedIdent(`new ${ctr_type.simpleTypeName}`, [], [], [ctr_type]);
switch(tokens.current.value) {
case '[':
match = arrayQualifiers(match, tokens, locals, method, imports, typemap);
// @ts-ignore
if (tokens.current.value === '{') {
// array init
rootTerm(tokens, locals, method, imports, typemap);
}
return new ResolvedIdent(match.source, [new Value(match.source, match.types[0])]);
case '(':
match = methodCallQualifier(match, tokens, locals, method, imports, typemap);
// @ts-ignore
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
for (let balance = 0;;) {
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;
}
addproblem(tokens, ParseProblem.Error(tokens.current, 'Constructor expression expected'));
return new ResolvedIdent(match.source, [new Value(match.source, ctr_type)]);
}
/** /**
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {Local[]} locals * @param {Local[]} locals
@@ -1955,33 +1970,14 @@ function qualifiers(matches, tokens, locals, method, imports, typemap) {
for (;;) { for (;;) {
switch (tokens.current.value) { switch (tokens.current.value) {
case '.': case '.':
tokens.inc(); matches = dottedIdent(matches, tokens, typemap);
matches = parseDottedIdent(matches, tokens, typemap);
break; break;
case '[': case '[':
let open_array = tokens.current; matches = arrayQualifiers(matches, tokens, locals, method, imports, typemap);
if (tokens.inc().value === ']') {
// array type
tokens.inc();
matches = arrayTypeExpression(matches);
} else {
// array index
const index = arrayIndexOrDimension(tokens, locals, method, imports, typemap);
matches = arrayElementOrConstructor(tokens, open_array, matches, index);
// @ts-ignore
tokens.expectValue(']');
}
break; break;
case '(': case '(':
// method or constructor call // method or constructor call
let args = []; matches = methodCallQualifier(matches, tokens, locals, method, imports, typemap);
if (tokens.inc().value === ')') {
tokens.inc();
} else {
args = expressionList(tokens, locals, method, imports, typemap);
tokens.expectValue(')');
}
matches = methodCallExpression(tokens, matches, args, typemap);
break; break;
case '<': case '<':
// generic type arguments - since this can be confused with less-than, only parse // generic type arguments - since this can be confused with less-than, only parse
@@ -1998,6 +1994,49 @@ function qualifiers(matches, tokens, locals, method, imports, typemap) {
} }
} }
/**
* @param {ResolvedIdent} matches
* @param {TokenList} tokens
* @param {Local[]} locals
* @param {SourceMC} method
* @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap
*/
function arrayQualifiers(matches, tokens, locals, method, imports, typemap) {
while (tokens.isValue('[')) {
let open_array = tokens.current;
if (tokens.isValue(']')) {
// array type
matches = arrayTypeExpression(matches);
} else {
// array index
const index = arrayIndexOrDimension(tokens, locals, method, imports, typemap);
matches = arrayElementOrConstructor(tokens, open_array, matches, index);
// @ts-ignore
tokens.expectValue(']');
}
}
return matches;
}
/**
* @param {ResolvedIdent} matches
* @param {TokenList} tokens
* @param {Local[]} locals
* @param {SourceMC} method
* @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap
*/
function methodCallQualifier(matches, tokens, locals, method, imports, typemap) {
let args = [];
tokens.expectValue('(');
if (!tokens.isValue(')')) {
args = expressionList(tokens, locals, method, imports, typemap);
tokens.expectValue(')');
}
return methodCallExpression(tokens, matches, args, typemap);
}
/** /**
* @param {ResolvedIdent} matches * @param {ResolvedIdent} matches
*/ */
@@ -2012,7 +2051,8 @@ function arrayTypeExpression(matches) {
* @param {TokenList} tokens * @param {TokenList} tokens
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
*/ */
function parseDottedIdent(matches, tokens, typemap) { function dottedIdent(matches, tokens, typemap) {
tokens.expectValue('.');
let variables = [], let variables = [],
methods = [], methods = [],
types = [], types = [],

View File

@@ -32,8 +32,9 @@ function typeIdentList(tokens, scope, imports, typemap) {
* @param {CEIType|MethodBase} scope * @param {CEIType|MethodBase} scope
* @param {ResolvedImport[]} imports * @param {ResolvedImport[]} imports
* @param {Map<string,JavaType>} typemap * @param {Map<string,JavaType>} typemap
* @param {boolean} allow_array_qualifiers
*/ */
function typeIdent(tokens, scope, imports, typemap) { function typeIdent(tokens, scope, imports, typemap, allow_array_qualifiers = true) {
/** @type {JavaType[]} */ /** @type {JavaType[]} */
let types = [], package_name = ''; let types = [], package_name = '';
switch(tokens.current.kind) { switch(tokens.current.kind) {
@@ -58,7 +59,7 @@ function typeIdent(tokens, scope, imports, typemap) {
tokens.inc(); tokens.inc();
} else if (tokens.isValue('<')) { } else if (tokens.isValue('<')) {
genericTypeArgs(tokens, types, scope, imports, typemap); genericTypeArgs(tokens, types, scope, imports, typemap);
} else if (tokens.isValue('[')) { } else if (allow_array_qualifiers && tokens.isValue('[')) {
let arrdims = 0; let arrdims = 0;
for(;;) { for(;;) {
arrdims++; arrdims++;