implement resolveExpression

This commit is contained in:
Dave Holoway
2020-06-18 11:46:42 +01:00
parent 13cdd1e0bc
commit 2da127edc0
26 changed files with 435 additions and 33 deletions

View File

@@ -1,7 +1,10 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
*/
const { Expression } = require("./Expression");
const { ArrayType } = require('java-mti');
const { AnyType } = require('../anys');
class ArrayIndexExpression extends Expression {
/**
@@ -17,6 +20,17 @@ class ArrayIndexExpression extends Expression {
tokens() {
return [...this.instance.tokens, ...this.index.tokens];
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
const instance_type = this.instance.resolveExpression(ri);
if (instance_type instanceof ArrayType) {
return instance_type.elementType;
}
return AnyType.Instance;
}
}
exports.ArrayIndexExpression = ArrayIndexExpression;

View File

@@ -1,8 +1,10 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
* @typedef {import('../tokenizer').Token} Token
*/
const { Expression } = require("./Expression");
const { ArrayValueType } = require('../anys');
class ArrayValueExpression extends Expression {
/**
@@ -18,6 +20,13 @@ class ArrayValueExpression extends Expression {
tokens() {
return this.open;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
return new ArrayValueType(this.elements.map(e => e.resolveExpression(ri)));
}
}
exports.ArrayValueExpression = ArrayValueExpression;

View File

@@ -1,8 +1,10 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
* @typedef {import('../tokenizer').Token} Token
*/
const { Expression } = require("./Expression");
const { JavaType, PrimitiveType } = require('java-mti');
class BinaryOpExpression extends Expression {
/**
@@ -17,6 +19,52 @@ class BinaryOpExpression extends Expression {
this.rhs = rhs;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
const operator = this.op.value;
const lhstype = this.lhs.resolveExpression(ri);
const rhstype = this.rhs.resolveExpression(ri);
if (operator === '+') {
const lhstypesig = lhstype instanceof JavaType && lhstype.typeSignature,
rhstypesig = rhstype instanceof JavaType && rhstype.typeSignature;
if (lhstypesig === 'Ljava/lang/String;') {
return lhstype;
}
if (lhstypesig === 'D' || rhstypesig === 'D') {
return PrimitiveType.map.D;
}
if (lhstypesig === 'F' || rhstypesig === 'F') {
return PrimitiveType.map.F;
}
if (lhstypesig === 'J' || rhstypesig === 'J') {
return PrimitiveType.map.J;
}
return PrimitiveType.map.I;
}
if (/^([*/%&|^+-]?=|<<=|>>>?=)$/.test(operator)) {
// result of assignments are lhs
return lhstype;
}
if (/^[*/%-]$/.test(operator)) {
// math operators
return PrimitiveType.map.I;
}
if (/^(<<|>>>?)$/.test(operator)) {
// shift operators
return PrimitiveType.map.I;
}
if (/^[&|^]$/.test(operator)) {
// bitwise or logical operators
return lhstype === PrimitiveType.map.Z ? lhstype : PrimitiveType.map.I;
}
if (operator === 'instanceof') {
}
// logical/comparison operators
return PrimitiveType.map.Z;
}
tokens() {
return [...this.lhs.tokens, this.op, ...this.rhs.tokens];
}

View File

@@ -1,5 +1,6 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
*/
const { Expression } = require("./Expression");
@@ -12,6 +13,13 @@ class BracketedExpression extends Expression {
this.expression = expression;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
return this.expression.resolveExpression(ri);
}
tokens() {
return this.expression.tokens;
}

View File

@@ -1,7 +1,9 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
*/
const { Expression } = require("./Expression");
const { AnyType, TypeIdentType } = require('../anys');
class CastExpression extends Expression {
/**
@@ -14,6 +16,17 @@ class CastExpression extends Expression {
this.expression = expression;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
const cast_type = this.castType.resolveExpression(ri);
if (cast_type instanceof TypeIdentType) {
return cast_type.type;
}
return AnyType.Instance;
}
tokens() {
return [...this.castType.tokens, ...this.expression.tokens];
}

View File

@@ -1,8 +1,10 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../tokenizer').Token} Token
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
*/
const { Expression } = require("./Expression");
const { AnyType } = require('../anys');
class ClassMemberExpression extends Expression {
/**
@@ -15,6 +17,15 @@ class ClassMemberExpression extends Expression {
this.classToken = class_token;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
const classType = ri.typemap.get('java/lang/Class');
const type = this.instance.types[0];
return classType.specialise([type || AnyType.Instance]);
}
tokens() {
return this.classToken;
}

View File

@@ -1,8 +1,21 @@
/**
* @typedef {import('java-mti').JavaType} JavaType
* @typedef {import('java-mti').CEIType} CEIType
* @typedef {import('../tokenizer').Token} Token
*/
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
* @typedef {import('../anys').ResolvedType} ResolvedType
*/
class Expression {
/**
* @param {ResolveInfo} ri
* @returns {ResolvedType}
*/
resolveExpression(ri) {
throw new Error('Expression.resolveType');
}
/** @returns {Token|Token[]} */
tokens() {
throw new Error('Expression.tokens');

View File

@@ -1,8 +1,11 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
* @typedef {import('../tokenizer').Token} Token
*/
const { Expression } = require("./Expression");
const { PrimitiveType } = require('java-mti');
const { AnyType } = require('../anys');
class IncDecExpression extends Expression {
/**
@@ -17,6 +20,19 @@ class IncDecExpression extends Expression {
this.which = which;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
const type = this.expr.resolveExpression(ri);
if (type instanceof PrimitiveType) {
if (/^[BSIJFD]$/.test(type.typeSignature)) {
return type;
}
}
return AnyType.Instance;
}
tokens() {
return this.operator;
}

View File

@@ -1,8 +1,10 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
*/
const { Expression } = require("./Expression");
const { Block } = require('../statementtypes/Block');
const { LambdaType } = require('../anys');
class LambdaExpression extends Expression {
/**
@@ -16,6 +18,13 @@ class LambdaExpression extends Expression {
this.body = body;
}
/**
* @param {ResolveInfo} ri
*/
resolveType(ri) {
return new LambdaType();
}
tokens() {
if (this.body instanceof Block) {
return this.body.open;

View File

@@ -1,8 +1,12 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
* @typedef {import('../tokenizer').Token} Token
*/
const { Expression } = require("./Expression");
const { CEIType } = require('java-mti');
const { AnyType, MethodType } = require('../anys');
const { getTypeInheritanceList } = require('../expression-resolver');
class MemberExpression extends Expression {
/**
@@ -16,6 +20,34 @@ class MemberExpression extends Expression {
this.member = member;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
const type = this.instance.resolveExpression(ri);
if (!(type instanceof CEIType)) {
return AnyType.Instance;
}
const ident = this.member.value;
const field = type.fields.find(f => f.name === ident);
if (field) {
return field.type;
}
let methods = new Map();
getTypeInheritanceList(type).forEach(type => {
type.methods.forEach(m => {
let msig;
if (m.name === ident && !methods.has(msig = m.methodSignature)) {
methods.set(msig, m);
}
})
});
if (methods.size > 0) {
return new MethodType([...methods.values()]);
}
return AnyType.Instance;
}
tokens() {
return this.member;
}

View File

@@ -1,7 +1,9 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
*/
const { Expression } = require("./Expression");
const { AnyType, MethodType } = require('../anys');
class MethodCallExpression extends Expression {
/**
@@ -14,6 +16,18 @@ class MethodCallExpression extends Expression {
this.args = args;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
const type = this.instance.resolveExpression(ri);
if (!(type instanceof MethodType)) {
return AnyType.Instance;
}
const arg_types = this.args.map(arg => arg.resolveExpression(ri));
return type.methods[0].returnType;
}
tokens() {
return this.instance.tokens;
}

View File

@@ -1,10 +1,12 @@
/**
* @typedef {import('../tokenizer').Token} Token
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
* @typedef {import('../source-types').SourceTypeIdent} SourceTypeIdent
* @typedef {import('java-mti').JavaType} JavaType
*/
const { Expression } = require("./Expression");
const { ArrayType } = require('java-mti');
class NewArray extends Expression {
/**
@@ -17,6 +19,14 @@ class NewArray extends Expression {
this.new_token = new_token;
this.element_type = element_type;
this.dimensions = dimensions;
this.array_type = new ArrayType(element_type.resolved, 1);
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
return this.array_type;
}
tokens() {
@@ -39,6 +49,13 @@ class NewObject extends Expression {
this.type_body = type_body;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
return this.object_type.resolved;
}
tokens() {
return [this.new_token, ...this.object_type.tokens];
}

View File

@@ -1,7 +1,9 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
*/
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
*/
const { Expression } = require("./Expression");
const { MultiValueType } = require('../anys');
class TernaryOpExpression extends Expression {
/**
@@ -16,6 +18,15 @@ class TernaryOpExpression extends Expression {
this.falseExpression = falseExpression;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
const ttype = this.truthExpression.resolveExpression(ri);
const ftype = this.falseExpression.resolveExpression(ri);
return new MultiValueType(ttype, ftype);
}
tokens() {
return [...this.test.tokens, ...this.truthExpression.tokens, ...this.falseExpression.tokens];
}

View File

@@ -1,8 +1,10 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
* @typedef {import('../tokenizer').Token} Token
*/
const { Expression } = require("./Expression");
const { AnyType, TypeIdentType } = require('../anys');
class ThisMemberExpression extends Expression {
/**
@@ -15,6 +17,18 @@ class ThisMemberExpression extends Expression {
this.thisToken = this_token;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
// instance should be a type identifier
const typeident = this.instance.resolveExpression(ri);
if (typeident instanceof TypeIdentType) {
return typeident.type;
}
return AnyType.Instance;
}
tokens() {
return this.thisToken;
}

View File

@@ -1,6 +1,7 @@
/**
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
* @typedef {import('../body-types').Local} Local
* @typedef {import('../body-types').ResolveInfo} ResolveInfo
* @typedef {import('../tokenizer').Token} Token
* @typedef {import('java-mti').Field} Field
* @typedef {import('java-mti').Parameter} Parameter
@@ -20,6 +21,13 @@ class Variable extends Expression {
this.type = this.variable.type;
}
/**
* @param {ResolveInfo} ri
*/
resolveType(ri) {
return this.type;
}
tokens() {
return this.name_token;
}

View File

@@ -10,8 +10,7 @@ class BooleanLiteral extends LiteralValue {
* @param {Token} token
*/
constructor(token) {
super(token);
this.type = PrimitiveType.map.Z;
super(token, PrimitiveType.map.Z);
}
}

View File

@@ -10,8 +10,7 @@ class CharacterLiteral extends LiteralValue {
* @param {Token} token
*/
constructor(token) {
super(token);
this.type = PrimitiveType.map.C;
super(token, PrimitiveType.map.C);
}
}

View File

@@ -1,4 +1,5 @@
/**
* @typedef {import('../../body-types').ResolveInfo} ResolveInfo
* @typedef {import('../../tokenizer').Token} Token
* @typedef {import('java-mti').CEIType} CEIType
*/
@@ -11,9 +12,19 @@ class InstanceLiteral extends LiteralValue {
* @param {CEIType} scoped_type
*/
constructor(token, scoped_type) {
super(token);
super(token, null);
this.scoped_type = scoped_type;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
if (this.token.value === 'this') {
return this.scoped_type;
}
return this.scoped_type.supers.find(t => t.typeKind === 'class') || ri.typemap.get('java/lang/Object');
}
}
exports.InstanceLiteral = InstanceLiteral;

View File

@@ -1,4 +1,6 @@
/**
* @typedef {import('java-mti').JavaType} JavaType
* @typedef {import('../../body-types').ResolveInfo} ResolveInfo
* @typedef {import('../../tokenizer').Token} Token
*/
const { Expression } = require('../Expression');
@@ -6,10 +8,19 @@ const { Expression } = require('../Expression');
class LiteralValue extends Expression {
/**
* @param {Token} token
* @param {JavaType} known_type
*/
constructor(token) {
constructor(token, known_type) {
super();
this.token = token;
this.type = known_type;
}
/**
* @param {ResolveInfo} ri
*/
resolveExpression(ri) {
return this.type;
}
tokens() {

View File

@@ -10,8 +10,7 @@ class NullLiteral extends LiteralValue {
* @param {Token} token
*/
constructor(token) {
super(token);
this.type = new NullType();
super(token, new NullType());
}
}

View File

@@ -18,9 +18,8 @@ class NumberLiteral extends LiteralValue {
* @param {PrimitiveType} default_type
*/
constructor(value, kind, default_type) {
super(value);
super(value, default_type);
this.numberKind = kind;
this.type = default_type;
}
static shift(a, b, op) {

View File

@@ -11,8 +11,7 @@ class StringLiteral extends LiteralValue {
* @param {CEIType} string_type
*/
constructor(token, string_type) {
super(token);
this.type = string_type;
super(token, string_type);
}
}