mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 09:59:25 +00:00
add checks for unary operators
This commit is contained in:
96
langserver/java/expressiontypes/UnaryOpExpression.js
Normal file
96
langserver/java/expressiontypes/UnaryOpExpression.js
Normal file
@@ -0,0 +1,96 @@
|
||||
/**
|
||||
* @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');
|
||||
const ParseProblem = require('../parsetypes/parse-problem');
|
||||
const { AnyType } = require('../anys');
|
||||
const { NumberLiteral } = require('./literals/Number');
|
||||
|
||||
class UnaryOpExpression extends Expression {
|
||||
/**
|
||||
* @param {ResolvedIdent} expression
|
||||
* @param {Token} op
|
||||
*/
|
||||
constructor(expression, op) {
|
||||
super();
|
||||
this.expression = expression;
|
||||
this.op = op;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {ResolveInfo} ri
|
||||
*/
|
||||
resolveExpression(ri) {
|
||||
const operator = this.op.value;
|
||||
const value = this.expression.resolveExpression(ri);
|
||||
|
||||
if (value instanceof AnyType) {
|
||||
return AnyType.Instance;
|
||||
}
|
||||
|
||||
if (value instanceof NumberLiteral) {
|
||||
if (/^[+-]$/.test(operator)) {
|
||||
return NumberLiteral[operator](value);
|
||||
}
|
||||
if (/^[!~]$/.test(operator) && value.type.typeSignature === 'I') {
|
||||
return NumberLiteral[operator](value);
|
||||
}
|
||||
}
|
||||
|
||||
const type = value instanceof JavaType ? value : value instanceof NumberLiteral ? value.type : null;
|
||||
|
||||
if (!type) {
|
||||
ri.problems.push(ParseProblem.Error(this.expression.tokens, `Expression expected`));
|
||||
return AnyType.Instance;
|
||||
}
|
||||
|
||||
return checkOperator(operator, ri, this.op, type);
|
||||
}
|
||||
|
||||
tokens() {
|
||||
return [this.op, ...this.expression.tokens];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} operator
|
||||
* @param {ResolveInfo} ri
|
||||
* @param {Token} operator_token
|
||||
* @param {JavaType} type
|
||||
*/
|
||||
function checkOperator(operator, ri, operator_token, type) {
|
||||
|
||||
let is_valid = false;
|
||||
/** @type {JavaType} */
|
||||
let return_type = AnyType.Instance;
|
||||
|
||||
if (/^[+-]$/.test(operator)) {
|
||||
// math operators - must be numeric
|
||||
is_valid = /^[BSIJFDC]$/.test(type.typeSignature);
|
||||
return_type = type;
|
||||
}
|
||||
|
||||
if (/^~$/.test(operator)) {
|
||||
// bitwise invert operator - must be integral
|
||||
is_valid = /^[BSIJC]$/.test(type.typeSignature);
|
||||
return_type = PrimitiveType.map.I;
|
||||
}
|
||||
|
||||
if (/^!$/.test(operator)) {
|
||||
// logical not operator - must be boolean
|
||||
is_valid = /^Z$/.test(type.typeSignature);
|
||||
return_type = PrimitiveType.map.Z;
|
||||
}
|
||||
|
||||
if (!is_valid) {
|
||||
ri.problems.push(ParseProblem.Error(operator_token, `Operator '${operator_token.value}' is not valid for type '${type.fullyDottedTypeName}'`));
|
||||
}
|
||||
|
||||
return return_type;
|
||||
}
|
||||
|
||||
exports.UnaryOpExpression = UnaryOpExpression;
|
||||
@@ -77,6 +77,32 @@ class NumberLiteral extends LiteralValue {
|
||||
return NumberLiteral.calc(a, b, 'int-number-literal', type, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NumberLiteral} a
|
||||
* @param {string} opvalue
|
||||
* @param {(a) => Number} op
|
||||
*/
|
||||
static unary(a, opvalue, op) {
|
||||
if (opvalue === '-') {
|
||||
const ai = a.toNumber();
|
||||
if (ai === null) {
|
||||
return null;
|
||||
}
|
||||
const val = op(ai);
|
||||
const type = PrimitiveType.map[a.type.typeSignature];
|
||||
const toks = a.tokens();
|
||||
return new NumberLiteral(Array.isArray(toks) ? toks : [toks], 'int-number-literal', type, val.toString());
|
||||
}
|
||||
const ai = a.toInt();
|
||||
if (ai === null) {
|
||||
return null;
|
||||
}
|
||||
const val = op(ai);
|
||||
const type = /J/.test(a.type.typeSignature) ? PrimitiveType.map.J : PrimitiveType.map.I;
|
||||
const toks = a.tokens();
|
||||
return new NumberLiteral(Array.isArray(toks) ? toks : [toks], 'int-number-literal', type, val.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {NumberLiteral} a
|
||||
* @param {NumberLiteral} b
|
||||
@@ -99,8 +125,15 @@ class NumberLiteral extends LiteralValue {
|
||||
return NumberLiteral.calc(a, b, 'int-number-literal', type, val);
|
||||
}
|
||||
|
||||
static '+'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a + b) }
|
||||
static '-'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a - b) }
|
||||
static '~'(value) { return NumberLiteral.unary(value, '~', (a) => ~a) }
|
||||
static '+'(lhs, rhs) { return !rhs
|
||||
? lhs // unary e.g +5
|
||||
: NumberLiteral.math(lhs, rhs, (a,b) => a + b)
|
||||
}
|
||||
static '-'(lhs, rhs) { return !rhs
|
||||
? NumberLiteral.unary(lhs, '-', (a) => -a)
|
||||
: NumberLiteral.math(lhs, rhs, (a,b) => a - b)
|
||||
}
|
||||
static '*'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a * b) }
|
||||
static '/'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a / b) }
|
||||
static '%'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a % b) }
|
||||
|
||||
Reference in New Issue
Block a user