validate array literals

This commit is contained in:
Dave Holoway
2020-06-18 15:45:34 +01:00
parent f05b34171c
commit 1255a15bba
3 changed files with 72 additions and 7 deletions

View File

@@ -1,5 +1,8 @@
const { JavaType, Method } = require('java-mti'); const { JavaType, Method } = require('java-mti');
const { Expression } = require('./expressiontypes/Expression'); const { Expression } = require('./expressiontypes/Expression');
/**
* @typedef {import('./tokenizer').Token} Token
*/
/** /**
* Custom type designed to be used where a type is missing or unresolved. * Custom type designed to be used where a type is missing or unresolved.
@@ -104,10 +107,10 @@ class TypeIdentType {
*/ */
class ArrayValueType { class ArrayValueType {
/** /**
* @param {(ResolvedType)[]} element_types * @param {{tokens:Token[], value: ResolvedType}[]} elements
*/ */
constructor(element_types) { constructor(elements) {
this.element_types = element_types; this.elements = elements;
} }
} }

View File

@@ -1,9 +1,10 @@
/** /**
* @typedef {import('./tokenizer').Token} Token * @typedef {import('./tokenizer').Token} Token
* @typedef {import('./anys').ResolvedType} ResolvedType
*/ */
const ParseProblem = require('./parsetypes/parse-problem'); const ParseProblem = require('./parsetypes/parse-problem');
const { TypeVariable, JavaType, PrimitiveType, NullType, ArrayType, CEIType, WildcardType, TypeVariableType, InferredTypeArgument } = require('java-mti'); const { TypeVariable, JavaType, PrimitiveType, NullType, ArrayType, CEIType, WildcardType, TypeVariableType, InferredTypeArgument } = require('java-mti');
const { AnyType, MultiValueType } = require('./anys'); const { AnyType, ArrayValueType, MultiValueType } = require('./anys');
const { ResolveInfo } = require('./body-types'); const { ResolveInfo } = require('./body-types');
const { LiteralValue } = require('./expressiontypes/literals/LiteralValue'); const { LiteralValue } = require('./expressiontypes/literals/LiteralValue');
const { NumberLiteral } = require('./expressiontypes/literals/Number'); const { NumberLiteral } = require('./expressiontypes/literals/Number');
@@ -24,7 +25,7 @@ function checkAssignment(e, assign_type, typemap, problems) {
} }
if (value instanceof NumberLiteral) { if (value instanceof NumberLiteral) {
if (!value.isCompatibleWith(assign_type)) { if (!value.isCompatibleWith(assign_type)) {
problems.push(ParseProblem.Error(value.token, `Incompatible types: Expression of type '${value.type.fullyDottedTypeName}' cannot be assigned to a variable of type '${assign_type.fullyDottedTypeName}'`)); incompatibleTypesError(assign_type, value.type, () => value.token, problems);
} }
return; return;
} }
@@ -44,21 +45,79 @@ function checkAssignment(e, assign_type, typemap, problems) {
* @param {JavaType} variable_type * @param {JavaType} variable_type
* @param {import('./anys').ResolvedType} value_type * @param {import('./anys').ResolvedType} value_type
* @param {() => Token|Token[]} tokens * @param {() => Token|Token[]} tokens
* @param {ParseProblem[]} problems
*/ */
function checkTypeAssignable(variable_type, value_type, tokens, problems) { function checkTypeAssignable(variable_type, value_type, tokens, problems) {
if (value_type instanceof MultiValueType) { if (value_type instanceof MultiValueType) {
value_type.types.forEach(t => checkTypeAssignable(variable_type, t, tokens, problems)); value_type.types.forEach(t => checkTypeAssignable(variable_type, t, tokens, problems));
return; return;
} }
if (value_type instanceof ArrayValueType) {
checkArrayLiteral(variable_type, value_type, tokens, problems);
return;
}
if (value_type instanceof JavaType) { if (value_type instanceof JavaType) {
if (!isTypeAssignable(variable_type, value_type)) { if (!isTypeAssignable(variable_type, value_type)) {
problems.push(ParseProblem.Error(tokens(), `Incompatible types: Expression of type '${value_type.fullyDottedTypeName}' cannot be assigned to a variable of type '${variable_type.fullyDottedTypeName}'`)); incompatibleTypesError(variable_type, value_type, tokens, problems);
} }
return; return;
} }
problems.push(ParseProblem.Error(tokens(), `Field, variable or method call expected`)); problems.push(ParseProblem.Error(tokens(), `Field, variable or method call expected`));
} }
/**
*
* @param {JavaType} variable_type
* @param {JavaType} value_type
* @param {() => Token|Token[]} tokens
* @param {ParseProblem[]} problems
*/
function incompatibleTypesError(variable_type, value_type, tokens, problems) {
problems.push(ParseProblem.Error(tokens(), `Incompatible types: Expression of type '${value_type.fullyDottedTypeName}' cannot be assigned to a variable of type '${variable_type.fullyDottedTypeName}'`));
}
/**
*
* @param {JavaType} variable_type
* @param {ArrayValueType} value_type
* @param {() => Token|Token[]} tokens
* @param {ParseProblem[]} problems
*/
function checkArrayLiteral(variable_type, value_type, tokens, problems) {
if (!(variable_type instanceof ArrayType)) {
problems.push(ParseProblem.Error(tokens(), `Array expression cannot be assigned to a variable of type '${variable_type.fullyDottedTypeName}'`));
return;
}
if (value_type.elements.length === 0) {
// empty arrays are compatible with all array types
return;
}
const element_type = variable_type.elementType;
value_type.elements.forEach(element => {
checkArrayElement(element_type, element.value, element.tokens);
});
/**
* @param {JavaType} element_type
* @param {ResolvedType} value_type
* @param {Token[]} tokens
*/
function checkArrayElement(element_type, value_type, tokens) {
if (value_type instanceof JavaType) {
if (!isTypeAssignable(element_type, value_type)) {
incompatibleTypesError(element_type, value_type, () => tokens, problems);
}
return;
}
if (value_type instanceof ArrayValueType) {
checkArrayLiteral(element_type, value_type, () => tokens, problems);
return;
}
problems.push(ParseProblem.Error(tokens, `Expression expected`));
}
}
/** /**
* Set of regexes to map source primitives to their destination types. * 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) * eg, long (J) is type-assignable to long, float and double (and their boxed counterparts)

View File

@@ -25,7 +25,10 @@ class ArrayValueExpression extends Expression {
* @param {ResolveInfo} ri * @param {ResolveInfo} ri
*/ */
resolveExpression(ri) { resolveExpression(ri) {
return new ArrayValueType(this.elements.map(e => e.resolveExpression(ri))); return new ArrayValueType(this.elements.map(e => ({
tokens: e.tokens,
value: e.resolveExpression(ri),
})));
} }
} }