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

@@ -32,6 +32,7 @@ const { MethodCallExpression } = require("./expressiontypes/MethodCallExpression
const { NewArray, NewObject } = require("./expressiontypes/NewExpression"); const { NewArray, NewObject } = require("./expressiontypes/NewExpression");
const { TernaryOpExpression } = require("./expressiontypes/TernaryOpExpression"); const { TernaryOpExpression } = require("./expressiontypes/TernaryOpExpression");
const { ThisMemberExpression } = require("./expressiontypes/ThisMemberExpression"); const { ThisMemberExpression } = require("./expressiontypes/ThisMemberExpression");
const { UnaryOpExpression } = require("./expressiontypes/UnaryOpExpression");
const { Variable } = require("./expressiontypes/Variable"); const { Variable } = require("./expressiontypes/Variable");
const { BooleanLiteral } = require('./expressiontypes/literals/Boolean'); const { BooleanLiteral } = require('./expressiontypes/literals/Boolean');
@@ -1512,13 +1513,17 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
matches = new ResolvedIdent(tokens.current, [NumberLiteral.from(tokens.current)]); matches = new ResolvedIdent(tokens.current, [NumberLiteral.from(tokens.current)]);
break; break;
case 'inc-operator': case 'inc-operator':
let incop = tokens.getIfKind('inc-operator'); let incop = tokens.consume();
matches = qualifiedTerm(tokens, mdecls, scope, imports, typemap); matches = qualifiedTerm(tokens, mdecls, scope, imports, typemap);
return new ResolvedIdent(`${incop.value}${matches.source}`, [new IncDecExpression(matches, incop, 'prefix')]) return new ResolvedIdent(`${incop.value}${matches.source}`, [new IncDecExpression(matches, incop, 'prefix')])
case 'plumin-operator': case 'plumin-operator':
case 'unary-operator': case 'unary-operator':
tokens.inc(); let unaryop = tokens.consume();
return qualifiedTerm(tokens, mdecls, scope, imports, typemap); matches = qualifiedTerm(tokens, mdecls, scope, imports, typemap);
let unary_value = matches.variables[0] instanceof NumberLiteral
? NumberLiteral[unaryop.value](matches.variables[0])
: new UnaryOpExpression(matches, unaryop);
return new ResolvedIdent(`${unaryop.value}${matches.source}`, [unary_value])
case 'new-operator': case 'new-operator':
return newTerm(tokens, mdecls, scope, imports, typemap); return newTerm(tokens, mdecls, scope, imports, typemap);
case 'open-bracket': case 'open-bracket':

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); 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} a
* @param {NumberLiteral} b * @param {NumberLiteral} b
@@ -99,8 +125,15 @@ class NumberLiteral extends LiteralValue {
return NumberLiteral.calc(a, b, 'int-number-literal', type, val); return NumberLiteral.calc(a, b, 'int-number-literal', type, val);
} }
static '+'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a + b) } static '~'(value) { return NumberLiteral.unary(value, '~', (a) => ~a) }
static '-'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a - b) } 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) } 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) }