Introduce BoundedNumber

This commit is contained in:
BorysLevytskyi
2023-05-08 18:38:16 +02:00
parent 3ee06ac9c0
commit 960ad50fb9
11 changed files with 167 additions and 113 deletions

View File

@@ -2,7 +2,8 @@ import {numberParser} from './numberParser';
import { ExpressionElement as ExpressionElement } from './expression-interfaces';
import { NumberBase } from '../core/formatter';
import { INT32_MAX_VALUE, INT32_MIN_VALUE, INT64_MAX_VALUE, INT64_MIN_VALUE } from '../core/const';
import { JsNumber } from '../core/types';
import { BoundedNumber, JsNumber, isBoundedNumber, asBoundedNumber } from '../core/types';
import calc from '../core/calc';
var globalId : number = 1;
@@ -13,15 +14,22 @@ export default class ScalarValue implements ExpressionElement {
value: JsNumber;
base: NumberBase;
isOperator: boolean;
maxBitSize: number;
constructor(value : JsNumber, base?: NumberBase, is32Limit?: boolean) {
constructor(value : BoundedNumber | JsNumber, base?: NumberBase) {
if(!isBoundedNumber(value))
value = asBoundedNumber(value);
ScalarValue.validateSupported(value);
this.id = globalId++;
this.value = value;
this.value = 0;
this.maxBitSize = 0;
this.base = base || "dec";
this.isOperator = false;
this.setValue(value);
}
bitSize() : number {
@@ -32,8 +40,9 @@ export default class ScalarValue implements ExpressionElement {
return typeof this.value === 'bigint';
}
setValue(value : JsNumber) {
this.value = value;
setValue(value : BoundedNumber) {
this.value = value.value;
this.maxBitSize = value.maxBitSize;
}
evaluate() : ScalarValue {
@@ -44,13 +53,13 @@ export default class ScalarValue implements ExpressionElement {
return this
}
static validateSupported(num : JsNumber) {
static validateSupported(num : BoundedNumber) {
if(typeof num == "bigint" && (num < INT64_MIN_VALUE || num > INT64_MAX_VALUE)) {
if(typeof num.value == "bigint" && (num.value < INT64_MIN_VALUE || num.value > INT64_MAX_VALUE)) {
throw new Error(`64-bit numbers are supported in range from ${INT64_MIN_VALUE} to ${INT64_MAX_VALUE}`);
}
if(typeof num == "number" && (num < INT32_MIN_VALUE || num > INT32_MAX_VALUE)) {
if(typeof num.value == "number" && (num.value < INT32_MIN_VALUE || num.value > INT32_MAX_VALUE)) {
throw new Error(`Numer JavaScript type can only by used for numbers in range from ${INT32_MIN_VALUE} to ${INT32_MAX_VALUE}`)
}
}

View File

@@ -147,13 +147,11 @@ class ExpressionRow extends React.Component<ExpressionRowProps> {
const { bitIndex: index, binaryStringLength: totalLength } = args;
if(totalLength > op.bitSize() && (totalLength - index) > op.bitSize()) {
op.setValue(calc.promoteToBigInt(op.value as number));
op.setValue(calc.promoteTo64Bit(op.value as number));
}
console.log(op.bitSize());
const pad = op.bitSize() - totalLength;
console.log(pad + index);
const newValue = calc.flipBit(op.value, pad + index);
const newValue = calc.flipBit(op, pad + index);
op.setValue(newValue);
this.props.onBitFlipped();
}

View File

@@ -1,5 +1,5 @@
import calc from "../core/calc";
import { JsNumber } from "../core/types";
import { JsNumber, asBoundedNumber } from "../core/types";
import ScalarValue from "./ScalarValue";
const engine = {
@@ -15,13 +15,16 @@ const engine = {
};
function evalute(op1 : JsNumber, operator: string, op2 : JsNumber) : JsNumber{
const a = equalizeType(op2, op1) as any;
const b = equalizeType(op1, op2) as any;
const o1 = equalizeType(op2, op1);
const o2 = equalizeType(op1, op2);
const a = o1 as any;
const b = o2 as any;
switch(operator) {
case ">>": return (a >> b) as (JsNumber);
case ">>>": return (a >>> b) as (JsNumber);
case "<<": return calc.rshift(a, b, calc.maxBitSize(a));
case "<<": return calc.rshift(asBoundedNumber(o1), o2).value;
case "&": return (b & a) as (JsNumber);
case "|": return (b | a) as (JsNumber);
case "^": return (b ^ a) as (JsNumber);

View File

@@ -1,5 +1,3 @@
import exp from 'constants';
import calc from '../core/calc';
import {numberParser, ParsedNumber} from './numberParser';
describe("parser", () => {
@@ -9,7 +7,7 @@ describe("parser", () => {
expect(result).not.toBeNull();
var number = result as ParsedNumber;
expect(number.value).toBe(10);
expect(number.value.value).toBe(10);
expect(number.base).toBe('dec');
expect(number.input).toBe('10');
});
@@ -18,24 +16,24 @@ describe("parser", () => {
const dec = numberParser.parse('10L');
expect(dec).not.toBeNull();
expect(dec?.value).toBe(BigInt(10));
expect(typeof dec?.value).toBe("bigint");
expect(dec?.value.value).toBe(BigInt(10));
expect(typeof dec?.value.value).toBe("bigint");
expect(dec?.base).toBe('dec');
expect(dec?.input).toBe('10L');
const bin = numberParser.parse('0b10l');
expect(bin).not.toBeNull();
expect(bin?.value).toBe(BigInt(2));
expect(typeof bin?.value).toBe("bigint");
expect(bin?.value.value).toBe(BigInt(2));
expect(typeof bin?.value.value).toBe("bigint");
expect(bin?.base).toBe('bin');
expect(bin?.input).toBe('0b10l');
const hex = numberParser.parse('0xfL');
expect(hex).not.toBeNull();
expect(hex?.value.toString()).toBe(BigInt(15).toString());
expect(typeof hex?.value).toBe("bigint");
expect(hex?.value.value.toString()).toBe(BigInt(15).toString());
expect(typeof hex?.value.value).toBe("bigint");
expect(hex?.base).toBe('hex');
expect(hex?.input).toBe('0xfL');
});
@@ -45,15 +43,15 @@ describe("parser", () => {
const unsafeInt = BigInt(Number.MAX_SAFE_INTEGER)+BigInt(1);
const dec = numberParser.parse(unsafeInt.toString());
expect(dec?.value).toEqual(unsafeInt);
expect(dec?.value.value).toEqual(unsafeInt);
expect(dec?.base).toBe('dec');
const bin = numberParser.parse("0b" + unsafeInt.toString(2));
expect(bin?.value).toEqual(unsafeInt);
expect(bin?.value.value).toEqual(unsafeInt);
expect(bin?.base).toEqual('bin');
const hex = numberParser.parse("0x" + unsafeInt.toString(16));
expect(hex?.value).toEqual(unsafeInt);
expect(hex?.value.value).toEqual(unsafeInt);
expect(hex?.base).toEqual('hex');
});
@@ -61,15 +59,15 @@ describe("parser", () => {
const unsafeInt = BigInt(Number.MIN_SAFE_INTEGER)-BigInt(1);
const dec = numberParser.parse(unsafeInt.toString());
expect(dec?.value.toString()).toEqual(unsafeInt.toString());
expect(dec?.value.value.toString()).toEqual(unsafeInt.toString());
expect(dec?.base).toBe('dec');
const bin = numberParser.parse("-0b" + unsafeInt.toString(2).replace('-', ''));
expect(bin?.value.toString()).toEqual(unsafeInt.toString());
expect(bin?.value.value.toString()).toEqual(unsafeInt.toString());
expect(bin?.base).toEqual('bin');
const hex = numberParser.parse("-0x" + unsafeInt.toString(16).replace('-',''));
expect(hex?.value.toString()).toEqual(unsafeInt.toString());
expect(hex?.value.value.toString()).toEqual(unsafeInt.toString());
expect(hex?.base).toEqual('hex');
});
@@ -78,7 +76,7 @@ describe("parser", () => {
expect(result).not.toBeNull();
var number = result as ParsedNumber;
expect(number.value).toBe(171);
expect(number.value.value).toBe(171);
expect(number.base).toBe('hex');
expect(number.input).toBe('0xab');
});
@@ -88,7 +86,7 @@ describe("parser", () => {
expect(result).not.toBeNull();
var number = result as ParsedNumber;
expect(number.value).toBe(6);
expect(number.value.value).toBe(6);
expect(number.base).toBe('bin');
expect(number.input).toBe('0b0110');
});
@@ -100,7 +98,7 @@ describe("parser", () => {
it('parses big int', () => {
var v = numberParser.parse('1l')?.value
expect(typeof v).toBe("bigint");
expect(v?.toString()).toBe("1");
})
expect(typeof v?.value).toBe("bigint");
expect(v?.value.toString()).toBe("1");
});
});

View File

@@ -1,19 +1,22 @@
import { INT32_MAX_VALUE, INT32_MIN_VALUE } from "../core/const";
import { NumberBase } from "../core/formatter";
import { JsNumber } from "../core/types";
import { BoundedNumber, asBoundedNumber } from "../core/types";
const decimalRegex = /^-?\d+[l,L]?$/;
const hexRegex = /^-?0x[0-9,a-f]+[l,L]?$/i;
const binRegex = /^-?0b[0-1]+[l,L]?$/i;
// byte -i8 or b
// single - i16 or s
const decimalRegex = /^-?\d+[l,L,S,s,B,b]?$/;
const hexRegex = /^-?0x[0-9,a-f]+[l,L,S,s,B,b]?$/i;
const binRegex = /^-?0b[0-1]+[l,L,S,s,B,b]?$/i;
interface ParserConfig {
regex: RegExp,
base: NumberBase,
parse: (input: string) => JsNumber
parse: (input: string) => BoundedNumber
}
export interface ParsedNumber {
value: JsNumber;
value: BoundedNumber;
base: NumberBase;
input: string;
}
@@ -66,24 +69,24 @@ class NumberParser {
const MAX_SAFE_INTn = BigInt(INT32_MAX_VALUE);
const MIN_SAFE_INTn = BigInt(INT32_MIN_VALUE);
function parseIntSafe(input : string, radix: number) : JsNumber {
function parseIntSafe(input : string, radix: number) : BoundedNumber {
const bigIntStr = input.replace('-', '').replace('l', '').replace('L', '');
const bigIntStr = input.replace('-', '').replace('l', '').replace('L', '');
let bigInt = BigInt(bigIntStr);
const isNegative = input.startsWith('-');
const isBigInt = input.toLowerCase().endsWith('l');
if(isNegative) bigInt *= BigInt(-1);
if(isBigInt) return bigInt;
if(isBigInt) return asBoundedNumber(bigInt);
if(bigInt > MAX_SAFE_INTn)
return bigInt;
return asBoundedNumber(bigInt);
if(bigInt < MIN_SAFE_INTn)
return bigInt;
return asBoundedNumber(bigInt);
return parseInt(input.replace(/0(x|b)/, ''), radix);
return asBoundedNumber(parseInt(input.replace(/0(x|b)/, ''), radix));
}
const numberParser = new NumberParser(knownParsers);