add checks for unary operators

This commit is contained in:
Dave Holoway
2020-06-22 11:09:50 +01:00
parent 491086a750
commit df210b4659
3 changed files with 139 additions and 5 deletions

View 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;

View File

@@ -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) }