mirror of
https://github.com/adelphes/android-dev-ext.git
synced 2025-12-23 09:59:25 +00:00
extract literals into separate files
This commit is contained in:
@@ -13,11 +13,11 @@ const { tokenize, Token } = require('./tokenizer');
|
|||||||
const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver');
|
const { resolveTypeOrPackage, resolveNextTypeOrPackage } = require('./type-resolver');
|
||||||
const { genericTypeArgs, typeIdent, typeIdentList } = require('./typeident');
|
const { genericTypeArgs, typeIdent, typeIdentList } = require('./typeident');
|
||||||
const { TokenList } = require("./TokenList");
|
const { TokenList } = require("./TokenList");
|
||||||
const { AnyMethod, AnyType, AnyValue, ArrayLiteral, Label, LiteralNumber, LiteralValue, Local,
|
const { AnyMethod, AnyType, AnyValue, Label, Local, MethodDeclarations, ResolvedIdent, Value, } = require("./body-types");
|
||||||
MethodDeclarations, ResolvedIdent, Value, } = require("./body-types");
|
|
||||||
const { resolveImports, resolveSingleImport } = require('../java/import-resolver');
|
const { resolveImports, resolveSingleImport } = require('../java/import-resolver');
|
||||||
|
|
||||||
const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression");
|
const { ArrayIndexExpression } = require("./expressiontypes/ArrayIndexExpression");
|
||||||
|
const { ArrayValueExpression } = require("./expressiontypes/ArrayValueExpression");
|
||||||
const { BinaryOpExpression } = require("./expressiontypes/BinaryOpExpression");
|
const { BinaryOpExpression } = require("./expressiontypes/BinaryOpExpression");
|
||||||
const { BracketedExpression } = require("./expressiontypes/BracketedExpression");
|
const { BracketedExpression } = require("./expressiontypes/BracketedExpression");
|
||||||
const { CastExpression } = require("./expressiontypes/CastExpression");
|
const { CastExpression } = require("./expressiontypes/CastExpression");
|
||||||
@@ -29,6 +29,12 @@ const { MethodCallExpression } = require("./expressiontypes/MethodCallExpression
|
|||||||
const { TernaryOpExpression } = require("./expressiontypes/TernaryOpExpression");
|
const { TernaryOpExpression } = require("./expressiontypes/TernaryOpExpression");
|
||||||
const { ThisMemberExpression } = require("./expressiontypes/ThisMemberExpression");
|
const { ThisMemberExpression } = require("./expressiontypes/ThisMemberExpression");
|
||||||
|
|
||||||
|
const { BooleanLiteral } = require('./expressiontypes/literals/Boolean');
|
||||||
|
const { CharacterLiteral } = require('./expressiontypes/literals/Character');
|
||||||
|
const { NumberLiteral } = require('./expressiontypes/literals/Number');
|
||||||
|
const { NullLiteral } = require('./expressiontypes/literals/Null');
|
||||||
|
const { StringLiteral } = require('./expressiontypes/literals/String');
|
||||||
|
|
||||||
const { AssertStatement } = require("./statementtypes/AssertStatement");
|
const { AssertStatement } = require("./statementtypes/AssertStatement");
|
||||||
const { Block } = require("./statementtypes/Block");
|
const { Block } = require("./statementtypes/Block");
|
||||||
const { BreakStatement } = require("./statementtypes/BreakStatement");
|
const { BreakStatement } = require("./statementtypes/BreakStatement");
|
||||||
@@ -1516,13 +1522,13 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
|
|||||||
matches = new ResolvedIdent(tokens.current.value, [], [], [PrimitiveType.fromName(tokens.current.value)]);
|
matches = new ResolvedIdent(tokens.current.value, [], [], [PrimitiveType.fromName(tokens.current.value)]);
|
||||||
break;
|
break;
|
||||||
case 'string-literal':
|
case 'string-literal':
|
||||||
matches = new ResolvedIdent(tokens.current.value, [new LiteralValue(tokens.current.value, typemap.get('java/lang/String'))]);
|
matches = new ResolvedIdent(tokens.current.value, [new StringLiteral(tokens.current, typemap.get('java/lang/String'))]);
|
||||||
break;
|
break;
|
||||||
case 'char-literal':
|
case 'char-literal':
|
||||||
matches = new ResolvedIdent(tokens.current.value, [new LiteralValue(tokens.current.value, PrimitiveType.map.C)]);
|
matches = new ResolvedIdent(tokens.current.value, [new CharacterLiteral(tokens.current)]);
|
||||||
break;
|
break;
|
||||||
case 'boolean-literal':
|
case 'boolean-literal':
|
||||||
matches = new ResolvedIdent(tokens.current.value, [new LiteralValue(tokens.current.value, PrimitiveType.map.Z)]);
|
matches = new ResolvedIdent(tokens.current.value, [new BooleanLiteral(tokens.current)]);
|
||||||
break;
|
break;
|
||||||
case 'object-literal':
|
case 'object-literal':
|
||||||
// this, super or null
|
// this, super or null
|
||||||
@@ -1533,11 +1539,11 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
|
|||||||
const supertype = scoped_type.supers.find(s => s.typeKind === 'class') || typemap.get('java/lang/Object');
|
const supertype = scoped_type.supers.find(s => s.typeKind === 'class') || typemap.get('java/lang/Object');
|
||||||
matches = new ResolvedIdent(tokens.current.value, [new Value(tokens.current.value, supertype)]);
|
matches = new ResolvedIdent(tokens.current.value, [new Value(tokens.current.value, supertype)]);
|
||||||
} else {
|
} else {
|
||||||
matches = new ResolvedIdent(tokens.current.value, [new LiteralValue(tokens.current.value, new NullType())]);
|
matches = new ResolvedIdent(tokens.current.value, [new NullLiteral(tokens.current)]);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case /number-literal/.test(tokens.current.kind) && tokens.current.kind:
|
case /number-literal/.test(tokens.current.kind) && tokens.current.kind:
|
||||||
matches = new ResolvedIdent(tokens.current.value, [LiteralNumber.from(tokens.current)]);
|
matches = new ResolvedIdent(tokens.current.value, [NumberLiteral.from(tokens.current)]);
|
||||||
break;
|
break;
|
||||||
case 'inc-operator':
|
case 'inc-operator':
|
||||||
let incop = tokens.getIfKind('inc-operator');
|
let incop = tokens.getIfKind('inc-operator');
|
||||||
@@ -1582,7 +1588,7 @@ function rootTerm(tokens, mdecls, scope, imports, typemap) {
|
|||||||
tokens.expectValue('}');
|
tokens.expectValue('}');
|
||||||
}
|
}
|
||||||
const ident = `{${elements.map(e => e.source).join(',')}}`;
|
const ident = `{${elements.map(e => e.source).join(',')}}`;
|
||||||
return new ResolvedIdent(ident, [new ArrayLiteral(ident, elements)]);
|
return new ResolvedIdent(ident, [new ArrayValueExpression(elements)]);
|
||||||
default:
|
default:
|
||||||
addproblem(tokens, ParseProblem.Error(tokens.current, 'Expression expected'));
|
addproblem(tokens, ParseProblem.Error(tokens.current, 'Expression expected'));
|
||||||
return new ResolvedIdent('');
|
return new ResolvedIdent('');
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const { JavaType, ArrayType, PrimitiveType, Method, Parameter, Field } = require('java-mti');
|
const { JavaType, ArrayType, Method, Parameter, Field } = require('java-mti');
|
||||||
const { Token } = require('./tokenizer');
|
const { Token } = require('./tokenizer');
|
||||||
|
|
||||||
class ResolvedIdent {
|
class ResolvedIdent {
|
||||||
@@ -145,22 +145,6 @@ class Value extends ValueBase {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {string} ident
|
|
||||||
* @param {ResolvedIdent} lhs
|
|
||||||
* @param {ResolvedIdent} rhs
|
|
||||||
* @param {JavaType} type
|
|
||||||
*/
|
|
||||||
static build(ident, lhs, rhs, type) {
|
|
||||||
if (!lhs.variables[0] || !rhs.variables[0]) {
|
|
||||||
return new Value(ident, type);
|
|
||||||
}
|
|
||||||
if (lhs.variables[0] instanceof LiteralValue && rhs.variables && rhs.variables[0] instanceof LiteralValue) {
|
|
||||||
new LiteralValue(ident, type);
|
|
||||||
}
|
|
||||||
return new Value(ident, type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class AnyValue extends Value {
|
class AnyValue extends Value {
|
||||||
@@ -169,167 +153,6 @@ class AnyValue extends Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class LiteralValue extends Value { }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* LiteralNumberType is a value representing literal numbers (like 0, 5.3, -0.1e+12, etc).
|
|
||||||
*
|
|
||||||
* It's used to allow literal numbers to be type-assignable to variables with different primitive types.
|
|
||||||
* For example, 200 is type-assignable to short, int, long, float and double, but not byte.
|
|
||||||
*/
|
|
||||||
class LiteralNumber extends LiteralValue {
|
|
||||||
/**
|
|
||||||
* @param {string} value
|
|
||||||
* @param {string} kind
|
|
||||||
* @param {PrimitiveType} default_type
|
|
||||||
*/
|
|
||||||
constructor(value, kind, default_type) {
|
|
||||||
super(value, default_type);
|
|
||||||
this.numberValue = value;
|
|
||||||
this.numberKind = kind;
|
|
||||||
}
|
|
||||||
|
|
||||||
static shift(a, b, op) {
|
|
||||||
const ai = a.toInt(), bi = b.toInt();
|
|
||||||
if (ai === null || bi === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const val = op(ai, bi);
|
|
||||||
const type = a.type.typeSignature === 'J' ? PrimitiveType.map.J : PrimitiveType.map.I;
|
|
||||||
return new LiteralNumber(val.toString(), 'int-number-literal', type);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bitwise(a, b, op) {
|
|
||||||
const ai = a.toInt(), bi = b.toInt();
|
|
||||||
if (ai === null || bi === null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
const val = op(ai, bi);
|
|
||||||
const typekey = a.type.typeSignature+ b.type.typeSignature;
|
|
||||||
let type = /J/.test(typekey) ? PrimitiveType.map.J : PrimitiveType.map.I;
|
|
||||||
return new LiteralNumber(val.toString(), 'int-number-literal', type);
|
|
||||||
}
|
|
||||||
|
|
||||||
static math(a, b, op, divmod) {
|
|
||||||
const ai = a.toNumber(), bi = b.toNumber();
|
|
||||||
if (bi === 0 && divmod) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let val = op(ai, bi);
|
|
||||||
const typekey = a.type.typeSignature+ b.type.typeSignature;
|
|
||||||
if (!/[FD]/.test(typekey) && divmod) {
|
|
||||||
val = Math.trunc(val);
|
|
||||||
}
|
|
||||||
let type;
|
|
||||||
if (/^(D|F[^D]|J[^FD])/.test(typekey)) {
|
|
||||||
type = a.type;
|
|
||||||
} else {
|
|
||||||
type = b.type;
|
|
||||||
}
|
|
||||||
return new LiteralNumber(val.toString(), 'int-number-literal', type);
|
|
||||||
}
|
|
||||||
|
|
||||||
static '+'(lhs, rhs) { return LiteralNumber.math(lhs, rhs, (a,b) => a + b) }
|
|
||||||
static '-'(lhs, rhs) { return LiteralNumber.math(lhs, rhs, (a,b) => a - b) }
|
|
||||||
static '*'(lhs, rhs) { return LiteralNumber.math(lhs, rhs, (a,b) => a * b) }
|
|
||||||
static '/'(lhs, rhs) { return LiteralNumber.math(lhs, rhs, (a,b) => a / b, true) }
|
|
||||||
static '%'(lhs, rhs) { return LiteralNumber.math(lhs, rhs, (a,b) => a % b, true) }
|
|
||||||
static '&'(lhs, rhs) { return LiteralNumber.bitwise(lhs, rhs, (a,b) => a & b) }
|
|
||||||
static '|'(lhs, rhs) { return LiteralNumber.bitwise(lhs, rhs, (a,b) => a | b) }
|
|
||||||
static '^'(lhs, rhs) { return LiteralNumber.bitwise(lhs, rhs, (a,b) => a ^ b) }
|
|
||||||
static '>>'(lhs, rhs) { return LiteralNumber.shift(lhs, rhs, (a,b) => a >> b) }
|
|
||||||
static '>>>'(lhs, rhs) { return LiteralNumber.shift(lhs, rhs, (a,b) => {
|
|
||||||
// unsigned shift (>>>) is not supported by bigints
|
|
||||||
// @ts-ignore
|
|
||||||
return (a >> b) & ~(-1n << (64n - b));
|
|
||||||
}) }
|
|
||||||
static '<<'(lhs, rhs) { return LiteralNumber.shift(lhs, rhs, (a,b) => a << b) }
|
|
||||||
|
|
||||||
toInt() {
|
|
||||||
switch (this.numberKind) {
|
|
||||||
case 'hex-number-literal':
|
|
||||||
case 'int-number-literal':
|
|
||||||
// unlike parseInt, BigInt doesn't like invalid characters, so
|
|
||||||
// ensure we strip any trailing long specifier
|
|
||||||
return BigInt(this.name.match(/(.+?)[lL]?$/)[1]);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
toNumber() {
|
|
||||||
return parseFloat(this.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {JavaType} type
|
|
||||||
*/
|
|
||||||
isCompatibleWith(type) {
|
|
||||||
if (this.type === type) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
switch(this.type.simpleTypeName) {
|
|
||||||
case 'double':
|
|
||||||
return /^([D]|Ljava\/lang\/(Double);)$/.test(type.typeSignature);
|
|
||||||
case 'float':
|
|
||||||
return /^([FD]|Ljava\/lang\/(Float|Double);)$/.test(type.typeSignature);
|
|
||||||
}
|
|
||||||
// all integral types are all compatible with long, float and double variables
|
|
||||||
if (/^([JFD]|Ljava\/lang\/(Long|Float|Double);)$/.test(type.typeSignature)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// the desintation type must be a number primitive or one of the corresponding boxed classes
|
|
||||||
if (!/^([BSIJFDC]|Ljava\/lang\/(Byte|Short|Integer|Long|Float|Double|Character);)$/.test(type.typeSignature)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
let number = 0;
|
|
||||||
if (this.numberKind === 'hex-number-literal') {
|
|
||||||
if (this.numberValue !== '0x') {
|
|
||||||
const non_leading_zero_digits = this.numberValue.match(/0x0*(.+)/)[1];
|
|
||||||
number = non_leading_zero_digits.length > 8 ? Number.MAX_SAFE_INTEGER : parseInt(non_leading_zero_digits, 16);
|
|
||||||
}
|
|
||||||
} else if (this.numberKind === 'int-number-literal') {
|
|
||||||
const non_leading_zero_digits = this.numberValue.match(/0*(.+)/)[1];
|
|
||||||
number = non_leading_zero_digits.length > 10 ? Number.MAX_SAFE_INTEGER : parseInt(non_leading_zero_digits, 10);
|
|
||||||
}
|
|
||||||
if (number >= -128 && number <= 127) {
|
|
||||||
return true; // byte values are compatible with all other numbers
|
|
||||||
}
|
|
||||||
if (number >= -32768 && number <= 32767) {
|
|
||||||
return !/^([B]|Ljava\/lang\/(Byte);)$/.test(type.typeSignature); // anything except byte
|
|
||||||
}
|
|
||||||
return !/^([BSC]|Ljava\/lang\/(Byte|Short|Character);)$/.test(type.typeSignature); // anything except byte, short and character
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {Token} token
|
|
||||||
*/
|
|
||||||
static from(token) {
|
|
||||||
function suffix(which) {
|
|
||||||
switch(which.indexOf(token.value.slice(-1))) {
|
|
||||||
case 0:
|
|
||||||
case 1:
|
|
||||||
return PrimitiveType.map.F;
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
return PrimitiveType.map.D;
|
|
||||||
case 4:
|
|
||||||
case 5:
|
|
||||||
return PrimitiveType.map.J;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
switch(token.kind) {
|
|
||||||
case 'dec-exp-number-literal':
|
|
||||||
case 'dec-number-literal':
|
|
||||||
return new LiteralNumber(token.value, token.kind, suffix('FfDdLl') || PrimitiveType.map.D);
|
|
||||||
case 'hex-number-literal':
|
|
||||||
return new LiteralNumber(token.value, token.kind, suffix(' Ll') || PrimitiveType.map.I);
|
|
||||||
case 'int-number-literal':
|
|
||||||
default:
|
|
||||||
return new LiteralNumber(token.value, token.kind, suffix('FfDdLl') || PrimitiveType.map.I);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MethodCall extends Value {
|
class MethodCall extends Value {
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
@@ -353,17 +176,6 @@ class ConstructorCall extends Value {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ArrayLiteral extends LiteralValue {
|
|
||||||
/**
|
|
||||||
* @param {string} name
|
|
||||||
* @param {ResolvedIdent[]} elements
|
|
||||||
*/
|
|
||||||
constructor(name, elements) {
|
|
||||||
super(name, null);
|
|
||||||
this.elements = elements;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class TernaryValue extends Value {
|
class TernaryValue extends Value {
|
||||||
/**
|
/**
|
||||||
* @param {string} name
|
* @param {string} name
|
||||||
@@ -382,11 +194,8 @@ exports.AnyMethod = AnyMethod;
|
|||||||
exports.AnyType = AnyType;
|
exports.AnyType = AnyType;
|
||||||
exports.AnyValue = AnyValue;
|
exports.AnyValue = AnyValue;
|
||||||
exports.ArrayElement = ArrayElement;
|
exports.ArrayElement = ArrayElement;
|
||||||
exports.ArrayLiteral = ArrayLiteral;
|
|
||||||
exports.ConstructorCall = ConstructorCall;
|
exports.ConstructorCall = ConstructorCall;
|
||||||
exports.Label = Label;
|
exports.Label = Label;
|
||||||
exports.LiteralNumber = LiteralNumber;
|
|
||||||
exports.LiteralValue = LiteralValue;
|
|
||||||
exports.Local = Local;
|
exports.Local = Local;
|
||||||
exports.MethodCall = MethodCall;
|
exports.MethodCall = MethodCall;
|
||||||
exports.MethodDeclarations = MethodDeclarations;
|
exports.MethodDeclarations = MethodDeclarations;
|
||||||
|
|||||||
16
langserver/java/expressiontypes/ArrayValueExpression.js
Normal file
16
langserver/java/expressiontypes/ArrayValueExpression.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('../body-types').ResolvedIdent} ResolvedIdent
|
||||||
|
*/
|
||||||
|
const { Expression } = require("./Expression");
|
||||||
|
|
||||||
|
class ArrayValueExpression extends Expression {
|
||||||
|
/**
|
||||||
|
* @param {ResolvedIdent[]} elements
|
||||||
|
*/
|
||||||
|
constructor(elements) {
|
||||||
|
super();
|
||||||
|
this.elements = elements;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.ArrayValueExpression = ArrayValueExpression;
|
||||||
18
langserver/java/expressiontypes/literals/Boolean.js
Normal file
18
langserver/java/expressiontypes/literals/Boolean.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('../../tokenizer').Token} Token
|
||||||
|
*/
|
||||||
|
const { LiteralValue } = require('./LiteralValue');
|
||||||
|
const { PrimitiveType } = require('java-mti');
|
||||||
|
|
||||||
|
class BooleanLiteral extends LiteralValue {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Token} token
|
||||||
|
*/
|
||||||
|
constructor(token) {
|
||||||
|
super(token);
|
||||||
|
this.type = PrimitiveType.map.Z;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.BooleanLiteral = BooleanLiteral;
|
||||||
18
langserver/java/expressiontypes/literals/Character.js
Normal file
18
langserver/java/expressiontypes/literals/Character.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('../../tokenizer').Token} Token
|
||||||
|
*/
|
||||||
|
const { LiteralValue } = require('./LiteralValue');
|
||||||
|
const { PrimitiveType } = require('java-mti');
|
||||||
|
|
||||||
|
class CharacterLiteral extends LiteralValue {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Token} token
|
||||||
|
*/
|
||||||
|
constructor(token) {
|
||||||
|
super(token);
|
||||||
|
this.type = PrimitiveType.map.C;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.CharacterLiteral = CharacterLiteral;
|
||||||
16
langserver/java/expressiontypes/literals/LiteralValue.js
Normal file
16
langserver/java/expressiontypes/literals/LiteralValue.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('../../tokenizer').Token} Token
|
||||||
|
*/
|
||||||
|
const { Expression } = require('../Expression');
|
||||||
|
|
||||||
|
class LiteralValue extends Expression {
|
||||||
|
/**
|
||||||
|
* @param {Token} token
|
||||||
|
*/
|
||||||
|
constructor(token) {
|
||||||
|
super();
|
||||||
|
this.token = token;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.LiteralValue = LiteralValue;
|
||||||
18
langserver/java/expressiontypes/literals/Null.js
Normal file
18
langserver/java/expressiontypes/literals/Null.js
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('../../tokenizer').Token} Token
|
||||||
|
*/
|
||||||
|
const { LiteralValue } = require('./LiteralValue');
|
||||||
|
const { NullType } = require('java-mti');
|
||||||
|
|
||||||
|
class NullLiteral extends LiteralValue {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Token} token
|
||||||
|
*/
|
||||||
|
constructor(token) {
|
||||||
|
super(token);
|
||||||
|
this.type = new NullType();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.NullLiteral = NullLiteral;
|
||||||
167
langserver/java/expressiontypes/literals/Number.js
Normal file
167
langserver/java/expressiontypes/literals/Number.js
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('../../tokenizer').Token} Token
|
||||||
|
* @typedef {import('java-mti').JavaType} JavaType
|
||||||
|
*/
|
||||||
|
const { LiteralValue } = require('./LiteralValue');
|
||||||
|
const { PrimitiveType } = require('java-mti');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* NumberLiteral is a value representing literal numbers (like 0, 5.3, -0.1e+12, etc).
|
||||||
|
*
|
||||||
|
* It allows literal numbers to be type-assignable to variables with different primitive types.
|
||||||
|
* For example, 200 is type-assignable to short, int, long, float and double, but not byte.
|
||||||
|
*/
|
||||||
|
class NumberLiteral extends LiteralValue {
|
||||||
|
/**
|
||||||
|
* @param {Token} value
|
||||||
|
* @param {string} kind
|
||||||
|
* @param {PrimitiveType} default_type
|
||||||
|
*/
|
||||||
|
constructor(value, kind, default_type) {
|
||||||
|
super(value);
|
||||||
|
this.numberKind = kind;
|
||||||
|
this.type = default_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static shift(a, b, op) {
|
||||||
|
const ai = a.toInt(), bi = b.toInt();
|
||||||
|
if (ai === null || bi === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const val = op(ai, bi);
|
||||||
|
const type = a.type.typeSignature === 'J' ? PrimitiveType.map.J : PrimitiveType.map.I;
|
||||||
|
return new NumberLiteral(val.toString(), 'int-number-literal', type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bitwise(a, b, op) {
|
||||||
|
const ai = a.toInt(), bi = b.toInt();
|
||||||
|
if (ai === null || bi === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const val = op(ai, bi);
|
||||||
|
const typekey = a.type.typeSignature+ b.type.typeSignature;
|
||||||
|
let type = /J/.test(typekey) ? PrimitiveType.map.J : PrimitiveType.map.I;
|
||||||
|
return new NumberLiteral(val.toString(), 'int-number-literal', type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static math(a, b, op, divmod) {
|
||||||
|
const ai = a.toNumber(), bi = b.toNumber();
|
||||||
|
if (bi === 0 && divmod) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
let val = op(ai, bi);
|
||||||
|
const typekey = a.type.typeSignature+ b.type.typeSignature;
|
||||||
|
if (!/[FD]/.test(typekey) && divmod) {
|
||||||
|
val = Math.trunc(val);
|
||||||
|
}
|
||||||
|
let type;
|
||||||
|
if (/^(D|F[^D]|J[^FD])/.test(typekey)) {
|
||||||
|
type = a.type;
|
||||||
|
} else {
|
||||||
|
type = b.type;
|
||||||
|
}
|
||||||
|
return new NumberLiteral(val.toString(), 'int-number-literal', type);
|
||||||
|
}
|
||||||
|
|
||||||
|
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, true) }
|
||||||
|
static '%'(lhs, rhs) { return NumberLiteral.math(lhs, rhs, (a,b) => a % b, true) }
|
||||||
|
static '&'(lhs, rhs) { return NumberLiteral.bitwise(lhs, rhs, (a,b) => a & b) }
|
||||||
|
static '|'(lhs, rhs) { return NumberLiteral.bitwise(lhs, rhs, (a,b) => a | b) }
|
||||||
|
static '^'(lhs, rhs) { return NumberLiteral.bitwise(lhs, rhs, (a,b) => a ^ b) }
|
||||||
|
static '>>'(lhs, rhs) { return NumberLiteral.shift(lhs, rhs, (a,b) => a >> b) }
|
||||||
|
static '>>>'(lhs, rhs) { return NumberLiteral.shift(lhs, rhs, (a,b) => {
|
||||||
|
// unsigned shift (>>>) is not supported by bigints
|
||||||
|
// @ts-ignore
|
||||||
|
return (a >> b) & ~(-1n << (64n - b));
|
||||||
|
}) }
|
||||||
|
static '<<'(lhs, rhs) { return NumberLiteral.shift(lhs, rhs, (a,b) => a << b) }
|
||||||
|
|
||||||
|
toInt() {
|
||||||
|
switch (this.numberKind) {
|
||||||
|
case 'hex-number-literal':
|
||||||
|
case 'int-number-literal':
|
||||||
|
// unlike parseInt, BigInt doesn't like invalid characters, so
|
||||||
|
// ensure we strip any trailing long specifier
|
||||||
|
return BigInt(this.token.value.match(/(.+?)[lL]?$/)[1]);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
toNumber() {
|
||||||
|
return parseFloat(this.token.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {JavaType} type
|
||||||
|
*/
|
||||||
|
isCompatibleWith(type) {
|
||||||
|
if (this.type === type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
switch(this.type.simpleTypeName) {
|
||||||
|
case 'double':
|
||||||
|
return /^([D]|Ljava\/lang\/(Double);)$/.test(type.typeSignature);
|
||||||
|
case 'float':
|
||||||
|
return /^([FD]|Ljava\/lang\/(Float|Double);)$/.test(type.typeSignature);
|
||||||
|
}
|
||||||
|
// all integral types are all compatible with long, float and double variables
|
||||||
|
if (/^([JFD]|Ljava\/lang\/(Long|Float|Double);)$/.test(type.typeSignature)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// the desintation type must be a number primitive or one of the corresponding boxed classes
|
||||||
|
if (!/^([BSIJFDC]|Ljava\/lang\/(Byte|Short|Integer|Long|Float|Double|Character);)$/.test(type.typeSignature)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let number = 0;
|
||||||
|
if (this.numberKind === 'hex-number-literal') {
|
||||||
|
if (this.token.value !== '0x') {
|
||||||
|
const non_leading_zero_digits = this.token.value.match(/0x0*(.+)/)[1];
|
||||||
|
number = non_leading_zero_digits.length > 8 ? Number.MAX_SAFE_INTEGER : parseInt(non_leading_zero_digits, 16);
|
||||||
|
}
|
||||||
|
} else if (this.numberKind === 'int-number-literal') {
|
||||||
|
const non_leading_zero_digits = this.token.value.match(/0*(.+)/)[1];
|
||||||
|
number = non_leading_zero_digits.length > 10 ? Number.MAX_SAFE_INTEGER : parseInt(non_leading_zero_digits, 10);
|
||||||
|
}
|
||||||
|
if (number >= -128 && number <= 127) {
|
||||||
|
return true; // byte values are compatible with all other numbers
|
||||||
|
}
|
||||||
|
if (number >= -32768 && number <= 32767) {
|
||||||
|
return !/^([B]|Ljava\/lang\/(Byte);)$/.test(type.typeSignature); // anything except byte
|
||||||
|
}
|
||||||
|
return !/^([BSC]|Ljava\/lang\/(Byte|Short|Character);)$/.test(type.typeSignature); // anything except byte, short and character
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Token} token
|
||||||
|
*/
|
||||||
|
static from(token) {
|
||||||
|
function suffix(which) {
|
||||||
|
switch(which.indexOf(token.value.slice(-1))) {
|
||||||
|
case 0:
|
||||||
|
case 1:
|
||||||
|
return PrimitiveType.map.F;
|
||||||
|
case 2:
|
||||||
|
case 3:
|
||||||
|
return PrimitiveType.map.D;
|
||||||
|
case 4:
|
||||||
|
case 5:
|
||||||
|
return PrimitiveType.map.J;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch(token.kind) {
|
||||||
|
case 'dec-exp-number-literal':
|
||||||
|
case 'dec-number-literal':
|
||||||
|
return new NumberLiteral(token, token.kind, suffix('FfDdLl') || PrimitiveType.map.D);
|
||||||
|
case 'hex-number-literal':
|
||||||
|
return new NumberLiteral(token, token.kind, suffix(' Ll') || PrimitiveType.map.I);
|
||||||
|
case 'int-number-literal':
|
||||||
|
default:
|
||||||
|
return new NumberLiteral(token, token.kind, suffix('FfDdLl') || PrimitiveType.map.I);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.NumberLiteral = NumberLiteral;
|
||||||
19
langserver/java/expressiontypes/literals/String.js
Normal file
19
langserver/java/expressiontypes/literals/String.js
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
/**
|
||||||
|
* @typedef {import('../../tokenizer').Token} Token
|
||||||
|
* @typedef {import('java-mti').CEIType} CEIType
|
||||||
|
*/
|
||||||
|
const { LiteralValue } = require('./LiteralValue');
|
||||||
|
|
||||||
|
class StringLiteral extends LiteralValue {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {Token} token
|
||||||
|
* @param {CEIType} string_type
|
||||||
|
*/
|
||||||
|
constructor(token, string_type) {
|
||||||
|
super(token);
|
||||||
|
this.type = string_type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
exports.StringLiteral = StringLiteral;
|
||||||
Reference in New Issue
Block a user