mirror of
https://github.com/BorysLevytskyi/BitwiseCmd.git
synced 2025-12-10 23:12:09 +01:00
Introduce BoundedNumber
This commit is contained in:
@@ -2,30 +2,37 @@ import calc from './calc';
|
||||
import { BitwiseOperationExpression, ScalarValue, BitwiseOperator } from '../expression/expression';
|
||||
import { INT32_MAX_VALUE } from './const';
|
||||
import exp from 'constants';
|
||||
import { asBoundedNumber } from './types';
|
||||
|
||||
describe('calc.flipBit', () => {
|
||||
it('calculates flipped bit 32-bit number', () => {
|
||||
expect(calc.flipBit(0, 31)).toBe(1);
|
||||
expect(calc.flipBit(1, 31)).toBe(0);
|
||||
expect(calc.flipBit(-1, 31)).toBe(-2);
|
||||
expect(calc.flipBit(2147483647, 0)).toBe(-1);
|
||||
expect(calc.flipBit(-1, 0)).toBe(2147483647);
|
||||
expect(calc.flipBit(2147483647, 30)).toBe(2147483645);
|
||||
expect(calc.flipBit(0, 31).value).toBe(1);
|
||||
expect(calc.flipBit(1, 31).value).toBe(0);
|
||||
expect(calc.flipBit(-1, 31).value).toBe(-2);
|
||||
expect(calc.flipBit(2147483647, 0).value).toBe(-1);
|
||||
expect(calc.flipBit(-1, 0).value).toBe(2147483647);
|
||||
expect(calc.flipBit(2147483647, 30).value).toBe(2147483645);
|
||||
});
|
||||
|
||||
it('caulate flipped bit 64-bit nubmer', () => {
|
||||
const int64max = BigInt("9223372036854775807");
|
||||
expect(calc.flipBit(BigInt(int64max), 0)).toBe(BigInt(-1));
|
||||
expect(calc.flipBit(BigInt(int64max), 0).value.toString()).toBe("-1");
|
||||
});
|
||||
|
||||
it('calculates flipped bit', () => {
|
||||
expect(calc.flipBit(0, 31)).toBe(1);
|
||||
expect(calc.flipBit(1, 31)).toBe(0);
|
||||
expect(calc.flipBit(-1, 31)).toBe(-2);
|
||||
expect(calc.flipBit(2147483647, 0)).toBe(-1);
|
||||
expect(calc.flipBit(-1, 0)).toBe(2147483647);
|
||||
expect(calc.flipBit(2147483647, 30)).toBe(2147483645);
|
||||
expect(calc.flipBit(0, 31).value).toBe(1);
|
||||
expect(calc.flipBit(1, 31).value).toBe(0);
|
||||
expect(calc.flipBit(-1, 31).value).toBe(-2);
|
||||
expect(calc.flipBit(2147483647, 0).value).toBe(-1);
|
||||
expect(calc.flipBit(-1, 0).value).toBe(2147483647);
|
||||
expect(calc.flipBit(2147483647, 30).value).toBe(2147483645);
|
||||
});
|
||||
|
||||
|
||||
it('calcualte 31th bit in 64-bit int', () => {
|
||||
expect(calc.flipBit(calc.promoteTo64Bit(-1), 31).value.toString()).toBe("8589934591");
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('calc.numberOfBitsDisplayed', () => {
|
||||
@@ -59,8 +66,8 @@ describe('calc.applyTwosComplement', () => {
|
||||
describe('calc.rshift', () => {
|
||||
|
||||
it('produces number when given number and vice vers', () => {
|
||||
const number = calc.rshift(1, 1, 32);
|
||||
const bigInt = calc.rshift(BigInt(1), 1, 32);
|
||||
const number = calc.rshift({value:1, maxBitSize:32}, 1).value;
|
||||
const bigInt = calc.rshift({value:BigInt(1), maxBitSize:32}, 1).value;
|
||||
|
||||
expect(typeof number).toBe('number');
|
||||
expect(number).toBe(2);
|
||||
@@ -69,27 +76,32 @@ describe('calc.rshift', () => {
|
||||
expect(bigInt.toString()).toBe('2');
|
||||
});
|
||||
|
||||
it('supports scalar values', () => {
|
||||
const operand = new ScalarValue(1);
|
||||
expect(calc.rshift(operand, 1).value).toBe(2);
|
||||
});
|
||||
|
||||
it("respects bit size", () => {
|
||||
expect(calc.rshift(BigInt("0b0100"), 2, 4).toString()).toBe("0");
|
||||
expect(calc.rshift({value: BigInt("0b0100"), maxBitSize: 4}, 2).value.toString()).toBe("0");
|
||||
});
|
||||
|
||||
it('transitions number to negative', ()=> {
|
||||
// 4-bit space
|
||||
expect(calc.rshift(BigInt("0b0100"), 1, 4).toString()).toBe("-8");
|
||||
expect(calc.rshift({value: BigInt("0b0100"), maxBitSize: 4}, 1).value.toString()).toBe("-8");
|
||||
|
||||
// 5-bit space
|
||||
expect(calc.rshift(BigInt("0b00100"), 1, 5).toString()).toBe("8");
|
||||
expect(calc.rshift(BigInt("0b01000"), 1, 5).toString()).toBe("-16");
|
||||
expect(calc.rshift({value: BigInt("0b00100"), maxBitSize: 5}, 1).value.toString()).toBe("8");
|
||||
expect(calc.rshift({value: BigInt("0b01000"), maxBitSize: 5}, 1).value.toString()).toBe("-16");
|
||||
|
||||
// 32-bit
|
||||
expect(calc.rshift(BigInt("2147483647"), 1, 32).toString()).toBe("-2");
|
||||
expect(calc.rshift(BigInt("2147483647"), 2, 32).toString()).toBe("-4");
|
||||
expect(calc.rshift({value: BigInt("2147483647"), maxBitSize: 32}, 1).value.toString()).toBe("-2");
|
||||
expect(calc.rshift({value: BigInt("2147483647"), maxBitSize: 32}, 2).value.toString()).toBe("-4");
|
||||
|
||||
// 64-bit
|
||||
expect(calc.rshift(BigInt("9223372036854775807"), 1, 64).toString()).toBe("-2");
|
||||
expect(calc.rshift(BigInt("9223372036854775807"), 2, 64).toString()).toBe("-4");
|
||||
expect(calc.rshift(BigInt("2147483647"), 1, 64).toString()).toBe("4294967294");
|
||||
expect(calc.rshift(BigInt("2147483647"), 2, 64).toString()).toBe("8589934588");
|
||||
expect(calc.rshift({value: BigInt("9223372036854775807"), maxBitSize: 64}, 1).value.toString()).toBe("-2");
|
||||
expect(calc.rshift({value: BigInt("9223372036854775807"), maxBitSize: 64}, 2).value.toString()).toBe("-4");
|
||||
expect(calc.rshift({value: BigInt("2147483647"), maxBitSize: 64}, 1).value.toString()).toBe("4294967294");
|
||||
expect(calc.rshift({value: BigInt("2147483647"), maxBitSize: 64}, 2).value.toString()).toBe("8589934588");
|
||||
|
||||
});
|
||||
});
|
||||
@@ -97,21 +109,19 @@ describe('calc.rshift', () => {
|
||||
describe("calc misc", () => {
|
||||
|
||||
|
||||
it('calcualte 31th bit in 64-bit int', () => {
|
||||
expect(calc.flipBit(calc.promoteToBigInt(-1), 31).toString()).toBe("8589934591");
|
||||
it('promoteTo64Bit', () => {
|
||||
expect(calc.promoteTo64Bit(-1).value.toString(2)).toBe("11111111111111111111111111111111");
|
||||
});
|
||||
|
||||
it('promotes to BigInt with the same bits', () => {
|
||||
expect(calc.promoteToBigInt(-1).toString(2)).toBe("11111111111111111111111111111111");
|
||||
});
|
||||
it('binaryRepresentation', () => {
|
||||
expect(calc.binaryRepresentation(asBoundedNumber(2147483647))).toBe("1111111111111111111111111111111");
|
||||
expect(calc.binaryRepresentation(new ScalarValue(2147483647))).toBe("1111111111111111111111111111111");
|
||||
})
|
||||
|
||||
|
||||
});
|
||||
|
||||
describe("bitwise ", () => {
|
||||
|
||||
|
||||
|
||||
it("NOT same as in node", () => {
|
||||
|
||||
for(var i = -100; i<100;i++) {
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { type } from "os";
|
||||
import { Expression } from "../expression/expression-interfaces";
|
||||
import formatter from "./formatter";
|
||||
import { JsNumber } from "./types";
|
||||
import { BoundedNumber, JsNumber, maxBitSize, asBoundedNumber } from "./types";
|
||||
import { asIntN } from "./utils";
|
||||
|
||||
export default {
|
||||
abs (num : JsNumber) : JsNumber {
|
||||
return num >= 0 ? num : -num;
|
||||
abs (num : BoundedNumber) : BoundedNumber {
|
||||
return asBoundedNumber(num.value >= 0 ? num.value : -num.value);
|
||||
},
|
||||
|
||||
maxBitSize(num : JsNumber) : number {
|
||||
return typeof num == "bigint" ? 64 : 32;
|
||||
return maxBitSize(num);
|
||||
},
|
||||
|
||||
numberOfBitsDisplayed: function (num: JsNumber) : number {
|
||||
@@ -33,11 +31,12 @@ export default {
|
||||
return Math.max.apply(null, counts);
|
||||
},
|
||||
|
||||
flipBit: function(num: JsNumber, index: number): JsNumber {
|
||||
flipBit: function(num: BoundedNumber | JsNumber, index: number): BoundedNumber {
|
||||
|
||||
const is64bit = typeof num == 'bigint';
|
||||
const size = typeof num == "bigint" ? 64 : 32;
|
||||
const bin = formatter.bin(num).padStart(size, '0');
|
||||
num = asBoundedNumber(num);
|
||||
const is64bit = num.maxBitSize == 64;
|
||||
const size = num.maxBitSize;
|
||||
const bin = formatter.bin(num.value).padStart(size, '0');
|
||||
const staysNegative = (bin[0] == "1" && index > 0);
|
||||
const becomesNegative = (bin[0] == "0" && index == 0);
|
||||
|
||||
@@ -49,12 +48,13 @@ export default {
|
||||
m=-1;
|
||||
}
|
||||
|
||||
return is64bit ? BigInt("0b"+ flipped)*BigInt(m) : parseInt(flipped, 2)*m;
|
||||
const n : JsNumber = is64bit ? BigInt("0b"+ flipped)*BigInt(m) : parseInt(flipped, 2)*m;
|
||||
return asBoundedNumber(n);
|
||||
},
|
||||
|
||||
promoteToBigInt(number: number) {
|
||||
const bin = formatter.bin(number);
|
||||
return BigInt("0b" + bin);
|
||||
promoteTo64Bit(number: number) : BoundedNumber {
|
||||
const bin = this.binaryRepresentation(asBoundedNumber(number));
|
||||
return asBoundedNumber(BigInt("0b" + bin));
|
||||
},
|
||||
|
||||
applyTwosComplement: (bin:string):string => {
|
||||
@@ -80,35 +80,38 @@ export default {
|
||||
return bin.split('').map(b => b=="1"?"0":"1").join("");
|
||||
},
|
||||
|
||||
binaryRepresentation(num : JsNumber, bitSize?: number) : string {
|
||||
|
||||
bitSize = bitSize || typeof num == "bigint" ? 64 : 32;
|
||||
const bin = this.abs(num).toString(2);
|
||||
binaryRepresentation(num: BoundedNumber) : string {
|
||||
|
||||
const bitSize = num.maxBitSize;
|
||||
const bin = this.abs(num).value.toString(2);
|
||||
|
||||
if(bin.length > bitSize!)
|
||||
throw new Error(`Binary represenation '${bin}' is bigger than the given bit size ${bitSize}`)
|
||||
|
||||
return num < 0
|
||||
const r = num.value < 0
|
||||
? this.applyTwosComplement(bin.padStart(bitSize, '0'))
|
||||
: bin;
|
||||
|
||||
return r;
|
||||
},
|
||||
|
||||
rshift (num: JsNumber, numBytes : JsNumber, bitSize: number) : JsNumber {
|
||||
rshift (num: BoundedNumber, numBytes : JsNumber) : BoundedNumber {
|
||||
|
||||
const bytes = asIntN(numBytes);
|
||||
|
||||
let bin = this.binaryRepresentation(num, bitSize).padStart(bitSize, '0');
|
||||
let bin = this.binaryRepresentation(num).padStart(num.maxBitSize, '0');
|
||||
|
||||
bin = bin.substring(bytes) + "0".repeat(bytes);
|
||||
|
||||
let m = BigInt(1);
|
||||
|
||||
|
||||
if(bin['0'] == '1') {
|
||||
bin = this.applyTwosComplement(bin);
|
||||
m = BigInt(-1);
|
||||
}
|
||||
|
||||
|
||||
const result = BigInt("0b" + bin) * m;
|
||||
return typeof num == "bigint" ? result : asIntN(result);
|
||||
return asBoundedNumber(typeof num.value == "bigint" ? result : asIntN(result));
|
||||
},
|
||||
|
||||
bitwise: {
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
import { BoundFunction } from "@testing-library/react";
|
||||
import calc from "./calc";
|
||||
import { JsNumber } from "./types";
|
||||
import { BoundedNumber, JsNumber, isBoundedNumber, asBoundedNumber } from "./types";
|
||||
export type NumberBase = 'dec' | 'hex' | 'bin';
|
||||
|
||||
const formatter = {
|
||||
numberToString: function(num: JsNumber, base: NumberBase) : string {
|
||||
numberToString: function(num: BoundedNumber | JsNumber, base: NumberBase) : string {
|
||||
|
||||
num = asBoundedNumber(num);
|
||||
|
||||
switch(base) {
|
||||
case 'hex':
|
||||
var hexVal = calc.abs(num).toString(16);
|
||||
return num >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
||||
var hexVal = calc.abs(num).value.toString(16);
|
||||
return num.value >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
||||
case 'bin':
|
||||
return calc.binaryRepresentation(num);
|
||||
case 'dec':
|
||||
return num.toString(10);
|
||||
return num.value.toString(10);
|
||||
default:
|
||||
throw new Error("Unexpected kind: " + base)
|
||||
}
|
||||
|
||||
@@ -1 +1,27 @@
|
||||
export type JsNumber = number | bigint;
|
||||
import { type } from "os";
|
||||
|
||||
export type JsNumber = number | bigint;
|
||||
export type BoundedNumber = {
|
||||
value: JsNumber,
|
||||
maxBitSize: number
|
||||
}
|
||||
|
||||
export function asBoundedNumber(num: JsNumber | BoundedNumber): BoundedNumber {
|
||||
|
||||
if(isBoundedNumber(num))
|
||||
return num;
|
||||
|
||||
if(typeof num == "number" && isNaN(num)) {
|
||||
throw new Error("Cannot create BoundedNumber from NaN");
|
||||
}
|
||||
|
||||
return {value:num, maxBitSize: maxBitSize(num)};
|
||||
}
|
||||
|
||||
export function isBoundedNumber(num: JsNumber | BoundedNumber): num is BoundedNumber {
|
||||
return (<BoundedNumber>num).maxBitSize !== undefined;
|
||||
}
|
||||
|
||||
export function maxBitSize(num : JsNumber) : number {
|
||||
return typeof num == "bigint" ? 64 : 32;
|
||||
};
|
||||
@@ -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}`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
});
|
||||
});
|
||||
@@ -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);
|
||||
|
||||
@@ -62,7 +62,6 @@ const shellModule = {
|
||||
handle: (s: CommandInput) => {
|
||||
|
||||
const executeCommand = (c: string) => {
|
||||
console.log(c);
|
||||
|
||||
if(c.length === 0) {
|
||||
return "Default comand: " + localStorage.getItem(STARTUP_COMMAND_KEY);
|
||||
|
||||
@@ -49,7 +49,9 @@ function getStartupCommands(appState : AppState) : string[] {
|
||||
}
|
||||
|
||||
if(hashArgs.length > 0) {
|
||||
startupCommands = hashArgs;
|
||||
|
||||
if(hashArgs.indexOf('empty')==-1)
|
||||
startupCommands = hashArgs;
|
||||
}
|
||||
|
||||
log.debug('Executing startup commands', startupCommands);
|
||||
|
||||
Reference in New Issue
Block a user