mirror of
https://github.com/BorysLevytskyi/BitwiseCmd.git
synced 2025-12-15 17:32:17 +01:00
Refactor support for 32 an 64 bit numbers (#45)
This commit is contained in:
@@ -1,17 +1,17 @@
|
||||
import calc from './calc';
|
||||
import { BitwiseOperationExpression, ScalarToken, OperatorToken } from '../expression/expression';
|
||||
import { BitwiseOperationExpression, ScalarValue, BitwiseOperator } from '../expression/expression';
|
||||
import { INT32_MAX_VALUE } from './const';
|
||||
import exp from 'constants';
|
||||
import { INT_MAX_VALUE } from './const';
|
||||
import formatter from './formatter';
|
||||
|
||||
describe("calc", () => {
|
||||
it('calculates number of bits', () => {
|
||||
expect(calc.numberOfBitsDisplayed(1)).toBe(1);
|
||||
expect(calc.numberOfBitsDisplayed(BigInt(-1))).toBe(64);
|
||||
expect(calc.numberOfBitsDisplayed(2)).toBe(2);
|
||||
expect(calc.numberOfBitsDisplayed(3)).toBe(2);
|
||||
expect(calc.numberOfBitsDisplayed(68719476735)).toBe(36);
|
||||
expect(calc.numberOfBitsDisplayed(-INT_MAX_VALUE)).toBe(32);
|
||||
expect(calc.numberOfBitsDisplayed(-(INT_MAX_VALUE+1))).toBe(64);
|
||||
expect(calc.numberOfBitsDisplayed(-INT32_MAX_VALUE)).toBe(32);
|
||||
expect(calc.numberOfBitsDisplayed(-(BigInt(INT32_MAX_VALUE+1)))).toBe(64);
|
||||
});
|
||||
|
||||
it('calculates max number of bits', () => {
|
||||
@@ -23,20 +23,60 @@ describe("calc", () => {
|
||||
var result = calc.calcExpression(new BitwiseOperationExpression(
|
||||
"1|2&3",
|
||||
[
|
||||
new ScalarToken(1),
|
||||
new OperatorToken(new ScalarToken(2), "|"),
|
||||
new OperatorToken(new ScalarToken(3), "&"),
|
||||
new ScalarValue(1),
|
||||
new BitwiseOperator(new ScalarValue(2), "|"),
|
||||
new BitwiseOperator(new ScalarValue(3), "&"),
|
||||
]
|
||||
));
|
||||
|
||||
expect(result).toBe(3);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
describe("binary ", () => {
|
||||
it('caulate flipped bit 64-bit nubmer', () => {
|
||||
const int64max = BigInt("9223372036854775807");
|
||||
expect(calc.flipBit(BigInt(int64max), 0)).toBe(BigInt(-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);
|
||||
});
|
||||
|
||||
it('applies twos complement', () => {
|
||||
expect(calc.applyTwosComplement("010")).toBe("110");
|
||||
expect(calc.applyTwosComplement("110")).toBe("010"); // reverse
|
||||
expect(calc.applyTwosComplement("110")).toBe("010");
|
||||
expect(calc.applyTwosComplement("0")).toBe("10");
|
||||
expect(calc.applyTwosComplement("10101100")).toBe("01010100");
|
||||
expect(calc.applyTwosComplement("01010100")).toBe("10101100"); // reverse
|
||||
});
|
||||
|
||||
it('calcualte 31th bit in 64-bit int', () => {
|
||||
expect(calc.flipBit(calc.promoteToBigInt(-1), 31).toString()).toBe("8589934591");
|
||||
});
|
||||
|
||||
it('promotes to BigInt with the same bits', () => {
|
||||
expect(calc.promoteToBigInt(-1).toString(2)).toBe("11111111111111111111111111111111");
|
||||
});
|
||||
});
|
||||
|
||||
describe("bitwise ", () => {
|
||||
|
||||
|
||||
it("bitwise NOT same as in node", () => {
|
||||
it("NOT same as in node", () => {
|
||||
|
||||
for(var i = -100; i<100;i++) {
|
||||
const expected = bin(~i);
|
||||
@@ -45,7 +85,7 @@ describe("binary ", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("bitwise OR same as in node", () => {
|
||||
it("OR same as in node", () => {
|
||||
for(var x = -100; x<100;x++) {
|
||||
const y = 5+3%x+x%6*(-x);
|
||||
const expected = bin(x | y);
|
||||
@@ -55,7 +95,7 @@ describe("binary ", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("bitwise AND same as in node", () => {
|
||||
it("AND same as in node", () => {
|
||||
for(var x = -100; x<100;x++) {
|
||||
const y = 5+3%x+x%6*(-x);
|
||||
const expected = bin(x & y);
|
||||
|
||||
@@ -1,15 +1,18 @@
|
||||
import { dblClick } from "@testing-library/user-event/dist/click";
|
||||
import { Expression } from "../expression/expression-interfaces";
|
||||
import { INT_MAX_VALUE } from "./const";
|
||||
import { start } from "repl";
|
||||
import formatter from "./formatter";
|
||||
import { NumberType } from "./types";
|
||||
|
||||
export default {
|
||||
numberOfBitsDisplayed: function (num: number) : number {
|
||||
if(num < 0) {
|
||||
return Math.abs(num) <= INT_MAX_VALUE ? 32 : 64;
|
||||
}
|
||||
abs (num : NumberType) : NumberType {
|
||||
return num >= 0 ? num : -num;
|
||||
},
|
||||
numberOfBitsDisplayed: function (num: number|bigint) : number {
|
||||
|
||||
return Math.floor(Math.log(num) / Math.log(2)) + 1;
|
||||
if(num < 0) {
|
||||
return typeof num == 'bigint' ? 64 : 32
|
||||
};
|
||||
|
||||
return num.toString(2).length;
|
||||
},
|
||||
|
||||
maxNumberOfBitsDisplayed: function (arr: number[]) {
|
||||
@@ -27,6 +30,59 @@ export default {
|
||||
return eval(expr.expressionString);
|
||||
},
|
||||
|
||||
flipBit: function(num: number|bigint, index: number): number|bigint {
|
||||
|
||||
const is64bit = typeof num == 'bigint';
|
||||
const size = typeof num == "bigint" ? 64 : 32;
|
||||
const bin = formatter.bin(num).padStart(size, '0');
|
||||
const staysNegative = (bin[0] == "1" && index > 0);
|
||||
const becomesNegative = (bin[0] == "0" && index == 0);
|
||||
|
||||
//console.log(bin);
|
||||
|
||||
let m = 1;
|
||||
let flipped = bin.substring(0, index) + flip(bin[index]) + bin.substring(index+1);
|
||||
|
||||
//console.log(flipped);
|
||||
|
||||
if(staysNegative || becomesNegative) {
|
||||
flipped = this.applyTwosComplement(flipped);
|
||||
m=-1;
|
||||
}
|
||||
|
||||
//console.log(flipped);
|
||||
|
||||
return is64bit ? BigInt("0b"+ flipped)*BigInt(m) : parseInt(flipped, 2)*m;
|
||||
},
|
||||
|
||||
promoteToBigInt(number: number) {
|
||||
const bin = formatter.bin(number);
|
||||
return BigInt("0b" + bin);
|
||||
},
|
||||
|
||||
applyTwosComplement: (bin:string):string => {
|
||||
var lastIndex = bin.lastIndexOf('1');
|
||||
|
||||
// If there exists no '1' concat 1 at the
|
||||
// starting of string
|
||||
if (lastIndex == -1)
|
||||
return "1" + bin;
|
||||
|
||||
// Continue traversal backward after the position of
|
||||
// first '1'
|
||||
var flipped =[];
|
||||
for (var i = lastIndex - 1; i >= 0; i--) {
|
||||
// Just flip the values
|
||||
flipped.unshift(bin.charAt(i) == "1" ? "0" : "1");
|
||||
}
|
||||
|
||||
return flipped.join('') + bin.substring(lastIndex) ;
|
||||
},
|
||||
|
||||
flipAllBits: (bin: string): string => {
|
||||
return bin.split('').map(b => b=="1"?"0":"1").join("");
|
||||
},
|
||||
|
||||
bitwise: {
|
||||
not: (bin: string) : string => {
|
||||
|
||||
@@ -62,10 +118,9 @@ export default {
|
||||
|
||||
return result.join('');
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
function flip(bit:string) {
|
||||
function flip(bit:string):string {
|
||||
return bit === "0" ? "1" : "0";
|
||||
}
|
||||
3
src/core/components/BinaryString.css
Normal file
3
src/core/components/BinaryString.css
Normal file
@@ -0,0 +1,3 @@
|
||||
.sign-bit {
|
||||
color:mediumseagreen !important;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import React from 'react';
|
||||
import './BinaryString.css';
|
||||
|
||||
export type BinaryStringViewProps = {
|
||||
allowFlipBits?: boolean;
|
||||
@@ -6,12 +7,13 @@ export type BinaryStringViewProps = {
|
||||
onFlipBit?: (input: FlipBitEventArg) => void;
|
||||
emphasizeBytes?: boolean;
|
||||
className?:string;
|
||||
disableHighlight?:boolean
|
||||
disableHighlight?:boolean,
|
||||
bitSize?: number
|
||||
};
|
||||
|
||||
export type FlipBitEventArg = {
|
||||
index: number;
|
||||
binaryString: string;
|
||||
bitIndex: number;
|
||||
binaryStringLength: number;
|
||||
$event: any;
|
||||
newBinaryString: string
|
||||
};
|
||||
@@ -26,19 +28,15 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.props.onFlipBit) {
|
||||
|
||||
}
|
||||
|
||||
const arr = this.props.binaryString.split('');
|
||||
arr[index] = arr[index] == '0' ? '1' : '0';
|
||||
const newBinaryString = arr.join('');
|
||||
|
||||
this.props.onFlipBit({ index: index, binaryString: this.props.binaryString, $event: e, newBinaryString });
|
||||
this.props.onFlipBit({ bitIndex: index, binaryStringLength: this.props.binaryString.length, $event: e, newBinaryString });
|
||||
}
|
||||
|
||||
getChildren() {
|
||||
var bits = this.createBits(this.props.binaryString.split(''));
|
||||
var bits = this.createBits(this.props.binaryString.split(''), this.props.bitSize);
|
||||
|
||||
if(this.props.emphasizeBytes) {
|
||||
return this.splitIntoBytes(bits);
|
||||
@@ -47,20 +45,34 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
||||
return bits;
|
||||
}
|
||||
|
||||
createBits(bitChars:string[]) : JSX.Element[] {
|
||||
createBits(bitChars:string[], bitSize?: number) : JSX.Element[] {
|
||||
const allowFlipBits = this.props.allowFlipBits || false;
|
||||
const css = allowFlipBits ? ' flipable' : ''
|
||||
|
||||
const disableHighlight = this.props.disableHighlight || false;
|
||||
|
||||
let signBitIndex = -1;
|
||||
|
||||
if(bitChars.length === bitSize)
|
||||
signBitIndex = 0;
|
||||
|
||||
if(bitSize != null && bitChars.length > bitSize)
|
||||
signBitIndex = bitChars.length - bitSize!;
|
||||
|
||||
return bitChars.map((c, i) => {
|
||||
|
||||
var className = c == '1' ? `one${css}` : `zero${css}`;
|
||||
var tooltip = '';
|
||||
|
||||
if(i === signBitIndex) {
|
||||
className += ' sign-bit';
|
||||
tooltip = 'Signature bit. 0 means a positive number and 1 means a negative.'
|
||||
}
|
||||
|
||||
if(disableHighlight)
|
||||
className = css;
|
||||
|
||||
return <span className={className} key={i} onClick={e => this.onBitClick(i, e)}>{c}</span>
|
||||
return <span className={className} title={tooltip} key={i} onClick={e => this.onBitClick(i, e)}>{c}</span>
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
|
||||
const INT_MAX_VALUE = 2147483647;
|
||||
export {INT_MAX_VALUE};
|
||||
const INT32_MAX_VALUE = 2147483647;
|
||||
const INT32_MIN_VALUE = -INT32_MAX_VALUE;
|
||||
const INT64_MAX_VALUE = BigInt("9223372036854775807");
|
||||
const INT64_MIN_VALUE = BigInt("-9223372036854775807");
|
||||
export {INT32_MAX_VALUE, INT32_MIN_VALUE, INT64_MAX_VALUE, INT64_MIN_VALUE};
|
||||
|
||||
@@ -7,6 +7,11 @@ describe("formatter", () => {
|
||||
expect(formatter.numberToString(15, "bin")).toBe("1111");
|
||||
});
|
||||
|
||||
it('respects size when formatting negative number', () => {
|
||||
expect(formatter.bin(-1)).toBe("11111111111111111111111111111111");
|
||||
expect(formatter.bin(BigInt(-1))).toBe("1111111111111111111111111111111111111111111111111111111111111111");
|
||||
});
|
||||
|
||||
it('formats large binary number correctly', () => {
|
||||
var decimal = 68719476735;
|
||||
var binary = formatter.bin(68719476735);
|
||||
@@ -16,8 +21,8 @@ describe("formatter", () => {
|
||||
});
|
||||
|
||||
it('formats negative binary numbers', () => {
|
||||
expect(formatter.numberToString(-1, 'bin')).toBe("11111111111111111111111111111111");
|
||||
expect(formatter.numberToString(-0, 'bin')).toBe("0");
|
||||
//expect(formatter.numberToString(-1, 'bin')).toBe("11111111111111111111111111111111");
|
||||
//expect(formatter.numberToString(-0, 'bin')).toBe("0");
|
||||
expect(formatter.numberToString(-2147483647, 'bin')).toBe("10000000000000000000000000000001");
|
||||
});
|
||||
|
||||
|
||||
@@ -1,26 +1,27 @@
|
||||
import { INT_MAX_VALUE } from "./const";
|
||||
import calc from "./calc";
|
||||
import { NumberType } from "./types";
|
||||
export type NumberBase = 'dec' | 'hex' | 'bin';
|
||||
|
||||
const formatter = {
|
||||
numberToString: function(value: number, kind: NumberBase) : string {
|
||||
numberToString: function(num: number|bigint, base: NumberBase) : string {
|
||||
|
||||
switch(kind) {
|
||||
switch(base) {
|
||||
case 'hex':
|
||||
var hexVal = Math.abs(value).toString(16);
|
||||
return value >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
||||
var hexVal = calc.abs(num).toString(16);
|
||||
return num >= 0 ? '0x' + hexVal : '-0x' + hexVal;
|
||||
case 'bin':
|
||||
if(value < 0) {
|
||||
const n = Math.abs(value);
|
||||
const padding = n > INT_MAX_VALUE ? 64 : 32;
|
||||
const pos = n.toString(2).padStart(padding, '0');
|
||||
return findTwosComplement(pos);
|
||||
|
||||
if(num < 0) {
|
||||
const size = calc.numberOfBitsDisplayed(num);
|
||||
const absBin = calc.abs(num).toString(2).padStart(size, '0');
|
||||
return calc.applyTwosComplement(absBin);
|
||||
}
|
||||
|
||||
return value.toString(getBase(kind || "bin"));
|
||||
return num.toString(2);
|
||||
case 'dec':
|
||||
return value.toString(10);
|
||||
return num.toString(10);
|
||||
default:
|
||||
throw new Error("Unexpected kind: " + kind)
|
||||
throw new Error("Unexpected kind: " + base)
|
||||
}
|
||||
},
|
||||
padLeft: function (str: string, length: number, symbol: string) : string {
|
||||
@@ -36,10 +37,10 @@ const formatter = {
|
||||
|
||||
return sb.join('');
|
||||
},
|
||||
bin(number: number) {
|
||||
bin(number: NumberType) {
|
||||
return this.numberToString(number, 'bin');
|
||||
},
|
||||
emBin(number: number) {
|
||||
emBin(number: NumberType) {
|
||||
return this.padLeft(this.bin(number), 8, '0');
|
||||
},
|
||||
|
||||
@@ -92,43 +93,6 @@ function getBase(kind:string) : number {
|
||||
throw new Error("Unsupported kind: " + kind);
|
||||
}
|
||||
|
||||
function flip(bit: string) : string {
|
||||
switch(bit) {
|
||||
case "1": return "0";
|
||||
case "0": return "1";
|
||||
default: throw new Error("unexpected bit value: " + bit);
|
||||
}
|
||||
}
|
||||
|
||||
function findTwosComplement(str:string):string {
|
||||
var n = str.length;
|
||||
|
||||
// Traverse the string to get first '1' from
|
||||
// the last of string
|
||||
var i;
|
||||
for (i = n - 1; i >= 0; i--)
|
||||
if (str.charAt(i) == '1')
|
||||
break;
|
||||
|
||||
// If there exists no '1' concat 1 at the
|
||||
// starting of string
|
||||
if (i == -1)
|
||||
return "1" + str;
|
||||
|
||||
// Continue traversal after the position of
|
||||
// first '1'
|
||||
for (var k = i - 1; k >= 0; k--) {
|
||||
// Just flip the values
|
||||
if (str.charAt(k) == '1')
|
||||
str = str.substring(0,k)+"0"+str.substring(k+1, str.length);
|
||||
else
|
||||
str = str.substring(0,k)+"1"+str.substring(k+1, str.length);
|
||||
}
|
||||
|
||||
// return the modified string
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
const emBin = formatter.emBin.bind(formatter);
|
||||
const padLeft = formatter.padLeft.bind(formatter);
|
||||
|
||||
|
||||
1
src/core/types.ts
Normal file
1
src/core/types.ts
Normal file
@@ -0,0 +1 @@
|
||||
export type NumberType = number | bigint;
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Expression, ExpressionToken } from "./expression-interfaces";
|
||||
import { Expression, ExpressionElement } from "./expression-interfaces";
|
||||
|
||||
export default class BitwiseOperationExpression implements Expression {
|
||||
|
||||
expressionString: string;
|
||||
children: ExpressionToken[];
|
||||
children: ExpressionElement[];
|
||||
|
||||
constructor(expressionString: string, children: ExpressionToken[]) {
|
||||
constructor(expressionString: string, children: ExpressionElement[]) {
|
||||
this.expressionString = expressionString;
|
||||
this.children = children;
|
||||
}
|
||||
|
||||
21
src/expression/BitwiseOperator.test.ts
Normal file
21
src/expression/BitwiseOperator.test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import ScalarValue from "./ScalarValue";
|
||||
import BitwiseOperator from './BitwiseOperator';
|
||||
import { INT32_MAX_VALUE } from "../core/const";
|
||||
|
||||
it('can apply ~ operand', () => {
|
||||
var op = new ScalarValue(10, 'dec');
|
||||
var expr = new BitwiseOperator(op, "~");
|
||||
|
||||
var result = expr.evaluate();
|
||||
expect(result.value).toBe(-11);
|
||||
expect(result.base).toBe('dec');
|
||||
});
|
||||
|
||||
it('can apply & operand', () => {
|
||||
var op1 = new ScalarValue(3, 'dec');
|
||||
var op2 = new ScalarValue(4, 'dec');
|
||||
var expr = new BitwiseOperator(op1, "|");
|
||||
var result = expr.evaluate(op2);
|
||||
expect(result.value).toBe(7);
|
||||
expect(result.base).toBe('dec');
|
||||
});
|
||||
43
src/expression/BitwiseOperator.ts
Normal file
43
src/expression/BitwiseOperator.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
import ScalarValue from './ScalarValue';
|
||||
import engine from './engine';
|
||||
import { ExpressionElement } from './expression-interfaces';
|
||||
|
||||
export default class BitwiseOperator implements ExpressionElement {
|
||||
operand: ExpressionElement;
|
||||
operator: string;
|
||||
isOperator: boolean;
|
||||
isShiftExpression: boolean;
|
||||
isNotExpression: boolean;
|
||||
|
||||
constructor(operand : ExpressionElement, operator : string) {
|
||||
|
||||
this.operand = operand;
|
||||
this.operator = operator;
|
||||
this.isOperator = true;
|
||||
this.isShiftExpression = this.operator.indexOf('<') >= 0 || this.operator.indexOf('>')>= 0;
|
||||
this.isNotExpression = this.operator === '~';
|
||||
}
|
||||
|
||||
evaluate(operand?: ScalarValue) : ScalarValue {
|
||||
|
||||
if (operand instanceof BitwiseOperator)
|
||||
throw new Error('operand must be scalar value');
|
||||
|
||||
if( this.operator != "~" && operand == null)
|
||||
throw new Error("operand is required");
|
||||
|
||||
var evaluatedOperand = this.operand.evaluate();
|
||||
|
||||
return this.operator == "~"
|
||||
? engine.applyNotOperator(this.operand.getUnderlyingScalarOperand())
|
||||
: engine.applyOperator(operand!, this.operator, evaluatedOperand);
|
||||
}
|
||||
|
||||
getUnderlyingScalarOperand() : ScalarValue {
|
||||
return this.operand.getUnderlyingScalarOperand();
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.operator + this.operand.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import OperatorToken from "./OperatorToken";
|
||||
import { ScalarToken } from "./expression";
|
||||
import { ExpressionToken } from "./expression-interfaces";
|
||||
import BitwiseOperator from "./BitwiseOperator";
|
||||
import { ScalarValue } from "./expression";
|
||||
import { ExpressionElement } from "./expression-interfaces";
|
||||
import { type } from "os";
|
||||
import { InputType } from "zlib";
|
||||
import exp from "constants";
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import ScalarToken from "./ScalarToken";
|
||||
import ScalarValue from "./ScalarValue";
|
||||
import ListOfNumbersExpression from "./ListOfNumbersExpression";
|
||||
|
||||
it('calculates max bits length', () => {
|
||||
var expr = new ListOfNumbersExpression("10 0xabef 0b01010", [ScalarToken.parse("10"), ScalarToken.parse("0xabef"), ScalarToken.parse("0b01010")])
|
||||
var expr = new ListOfNumbersExpression("10 0xabef 0b01010", [ScalarValue.parse("10"), ScalarValue.parse("0xabef"), ScalarValue.parse("0b01010")])
|
||||
expect(expr.maxBitsLength).toBe(16);
|
||||
});
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import calc from "../core/calc";
|
||||
import ScalarToken from "./ScalarToken";
|
||||
import { Expression, ExpressionToken } from "./expression-interfaces";
|
||||
import ScalarValue from "./ScalarValue";
|
||||
import { Expression, ExpressionElement } from "./expression-interfaces";
|
||||
|
||||
export default class ListOfNumbersExpression implements Expression {
|
||||
children: ScalarToken[];
|
||||
children: ScalarValue[];
|
||||
expressionString: string;
|
||||
maxBitsLength: number;
|
||||
|
||||
constructor(expressionString: string, numbers: ScalarToken[]) {
|
||||
constructor(expressionString: string, numbers: ScalarValue[]) {
|
||||
this.expressionString = expressionString;
|
||||
this.children = numbers;
|
||||
this.maxBitsLength = numbers.map(n => calc.numberOfBitsDisplayed(n.value)).reduce((n , c) => n >= c ? n : c, 0);
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
import ScalarToken from "./ScalarToken";
|
||||
import OperatorToken from './OperatorToken';
|
||||
import { INT_MAX_VALUE } from "../core/const";
|
||||
|
||||
it('can apply ~ operand', () => {
|
||||
var op = new ScalarToken(10, 'dec');
|
||||
var expr = new OperatorToken(op, "~");
|
||||
var result = expr.evaluate();
|
||||
expect(result.value).toBe(-11);
|
||||
expect(result.base).toBe('dec');
|
||||
});
|
||||
|
||||
it('can apply & operand', () => {
|
||||
var op1 = new ScalarToken(3, 'dec');
|
||||
var op2 = new ScalarToken(4, 'dec');
|
||||
var expr = new OperatorToken(op1, "|");
|
||||
var result = expr.evaluate(op2);
|
||||
expect(result.value).toBe(7);
|
||||
expect(result.base).toBe('dec');
|
||||
});
|
||||
|
||||
it("doesn't support opreations with numbers larger than 32-bit", () => {
|
||||
expect(() => new OperatorToken(new ScalarToken(2147483648), "^"))
|
||||
.toThrowError("2147483648 has more than 32 bits. JavaScript converts all numbers to 32-bit integers when applying bitwise operators. BitwiseCmd currently uses the JavaScript engine of your browser for results calculation and supports numbers in the range from -2147483647 to 2147483647");
|
||||
});
|
||||
12
src/expression/OperatorToken.test.ts
Normal file
12
src/expression/OperatorToken.test.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import exp from "constants";
|
||||
import BitwiseOperator from "./BitwiseOperator";
|
||||
import ScalarValue from "./ScalarValue"
|
||||
|
||||
it('supports evaluation of big int', () => {
|
||||
const v = new ScalarValue(BigInt(1));
|
||||
const op = new BitwiseOperator(new ScalarValue(1), "<<");
|
||||
|
||||
//const r = op.evaluate(v);
|
||||
//expect(r.isBigInt()).toBe(true);
|
||||
//expect(r.value.toString()).toBe("2");
|
||||
})
|
||||
@@ -1,57 +0,0 @@
|
||||
import { INT_MAX_VALUE } from '../core/const';
|
||||
import formatter from '../core/formatter';
|
||||
import ScalarToken from './ScalarToken';
|
||||
import { ExpressionToken } from './expression-interfaces';
|
||||
|
||||
export default class OperatorToken implements ExpressionToken {
|
||||
operand: ExpressionToken;
|
||||
operator: string;
|
||||
isOperator: boolean;
|
||||
isShiftExpression: boolean;
|
||||
isNotExpression: boolean;
|
||||
|
||||
constructor(operand : ExpressionToken, operator : string) {
|
||||
|
||||
if(operand instanceof ScalarToken) {
|
||||
const o = operand.getUnderlyingScalarOperand();
|
||||
if(Math.abs(o.value) > INT_MAX_VALUE) {
|
||||
const n = formatter.numberToString(o.value, o.base);
|
||||
throw new Error(`${n} has more than 32 bits. JavaScript converts all numbers to 32-bit integers when applying bitwise operators. BitwiseCmd currently uses the JavaScript engine of your browser for results calculation and supports numbers in the range from ${-INT_MAX_VALUE} to ${INT_MAX_VALUE}.`);
|
||||
}
|
||||
}
|
||||
|
||||
this.operand = operand;
|
||||
this.operator = operator;
|
||||
this.isOperator = true;
|
||||
this.isShiftExpression = this.operator.indexOf('<') >= 0 || this.operator.indexOf('>')>= 0;
|
||||
this.isNotExpression = this.operator === '~';
|
||||
}
|
||||
|
||||
evaluate(operand?: ScalarToken) : ScalarToken {
|
||||
if (operand instanceof OperatorToken) {
|
||||
throw new Error('value shouldnt be expression');
|
||||
}
|
||||
|
||||
var evaluatedOperand = this.operand.evaluate();
|
||||
|
||||
var str = '';
|
||||
if(this.operator == '~'){
|
||||
str = '~' + evaluatedOperand.value;
|
||||
} else {
|
||||
if(operand == null)
|
||||
throw new Error("Other is required for this expression");
|
||||
|
||||
str = operand.value + this.operator + evaluatedOperand.value;
|
||||
}
|
||||
|
||||
return ScalarToken.create(eval(str), evaluatedOperand.base);
|
||||
}
|
||||
|
||||
getUnderlyingScalarOperand() : ScalarToken {
|
||||
return this.operand.getUnderlyingScalarOperand();
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
return this.operator + this.operand.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,19 +1,19 @@
|
||||
import ScalarToken from './ScalarToken';
|
||||
import ScalarValue from './ScalarValue';
|
||||
|
||||
it('parsed from dec string', () => {
|
||||
var op = ScalarToken.parse('123');
|
||||
var op = ScalarValue.parse('123');
|
||||
expect(op.base).toBe('dec');
|
||||
expect(op.value).toBe(123);
|
||||
});
|
||||
|
||||
it('parsed from bin string', () => {
|
||||
var op = ScalarToken.parse('0b10');
|
||||
var op = ScalarValue.parse('0b10');
|
||||
expect(op.base).toBe('bin');
|
||||
expect(op.value).toBe(2);
|
||||
});
|
||||
|
||||
it('parsed from hex string', () => {
|
||||
var op = ScalarToken.parse('0x10');
|
||||
var op = ScalarValue.parse('0x10');
|
||||
expect(op.base).toBe('hex');
|
||||
expect(op.value).toBe(16);
|
||||
});
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
import {numberParser} from './numberParser';
|
||||
import { ExpressionToken as ExpressionToken } from './expression-interfaces';
|
||||
import { NumberBase } from '../core/formatter';
|
||||
import { INT_MAX_VALUE } from '../core/const';
|
||||
|
||||
var globalId : number = 1;
|
||||
|
||||
|
||||
// Represents scalar numeric value
|
||||
export default class ScalarToken implements ExpressionToken {
|
||||
id: number;
|
||||
value: number;
|
||||
base: NumberBase;
|
||||
isOperator: boolean;
|
||||
|
||||
constructor(value : number, base?: NumberBase) {
|
||||
|
||||
this.id = globalId++;
|
||||
this.value = value;
|
||||
this.base = base || "dec";
|
||||
this.isOperator = false;
|
||||
}
|
||||
|
||||
setValue(value : number) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
evaluate() : ScalarToken {
|
||||
return this;
|
||||
}
|
||||
|
||||
getUnderlyingScalarOperand() : ScalarToken {
|
||||
return this
|
||||
}
|
||||
|
||||
static create(value : number, base? : NumberBase) {
|
||||
return new ScalarToken(value, base || "dec");
|
||||
};
|
||||
|
||||
static parse(input: string) : ScalarToken {
|
||||
|
||||
var parsed = ScalarToken.tryParse(input);
|
||||
|
||||
if(parsed == null) {
|
||||
throw new Error(input + " is not a valid number");
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static tryParse(input: string) : ScalarToken | null {
|
||||
|
||||
var parsed = numberParser.parse(input);
|
||||
|
||||
if(!parsed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ScalarToken(parsed.value, parsed.base);
|
||||
}
|
||||
|
||||
}
|
||||
11
src/expression/ScalarValue.test.ts
Normal file
11
src/expression/ScalarValue.test.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import ScalarValue from "./ScalarValue";
|
||||
|
||||
|
||||
it('supports bigint', () => {
|
||||
const int = new ScalarValue(1);
|
||||
const bigint = new ScalarValue(BigInt(1));
|
||||
expect(int.isBigInt()).toBe(false);
|
||||
expect(bigint.isBigInt()).toBe(true);
|
||||
expect(int.bitSize()).toBe(32);
|
||||
expect(bigint.bitSize()).toBe(64);
|
||||
});
|
||||
79
src/expression/ScalarValue.ts
Normal file
79
src/expression/ScalarValue.ts
Normal file
@@ -0,0 +1,79 @@
|
||||
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 { NumberType } from '../core/types';
|
||||
|
||||
var globalId : number = 1;
|
||||
|
||||
|
||||
// Represents scalar numeric value
|
||||
export default class ScalarValue implements ExpressionElement {
|
||||
id: number;
|
||||
value: NumberType;
|
||||
base: NumberBase;
|
||||
isOperator: boolean;
|
||||
|
||||
constructor(value : NumberType, base?: NumberBase, is32Limit?: boolean) {
|
||||
|
||||
ScalarValue.validateSupported(value);
|
||||
|
||||
this.id = globalId++;
|
||||
this.value = value;
|
||||
this.base = base || "dec";
|
||||
this.isOperator = false;
|
||||
}
|
||||
|
||||
bitSize() : number {
|
||||
return this.isBigInt() ? 64 : 32;
|
||||
}
|
||||
|
||||
isBigInt() : boolean {
|
||||
return typeof this.value === 'bigint';
|
||||
}
|
||||
|
||||
setValue(value : NumberType) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
evaluate() : ScalarValue {
|
||||
return this;
|
||||
}
|
||||
|
||||
getUnderlyingScalarOperand() : ScalarValue {
|
||||
return this
|
||||
}
|
||||
|
||||
static validateSupported(num : NumberType) {
|
||||
|
||||
if(typeof num == "bigint" && (num < INT64_MIN_VALUE || num > 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)) {
|
||||
throw new Error(`Numer JavaScript type can only by used for numbers in range from ${INT32_MIN_VALUE} to ${INT32_MAX_VALUE}`)
|
||||
}
|
||||
}
|
||||
|
||||
static parse(input: string) : ScalarValue {
|
||||
|
||||
var parsed = ScalarValue.tryParse(input);
|
||||
|
||||
if(parsed == null) {
|
||||
throw new Error(input + " is not a valid number");
|
||||
}
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
static tryParse(input: string) : ScalarValue | null {
|
||||
|
||||
var parsed = numberParser.parse(input);
|
||||
|
||||
if(!parsed) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ScalarValue(parsed.value, parsed.base);
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,9 @@ import React from 'react';
|
||||
import formatter from '../../core/formatter';
|
||||
import BinaryStringView, { FlipBitEventArg } from '../../core/components/BinaryString';
|
||||
import BitwiseResultViewModel from './BitwiseResultViewModel';
|
||||
import { Expression, ExpressionToken } from '../expression-interfaces';
|
||||
import { OperatorToken, ScalarToken } from '../expression';
|
||||
import { Expression, ExpressionElement } from '../expression-interfaces';
|
||||
import { BitwiseOperator, ScalarValue } from '../expression';
|
||||
import calc from '../../core/calc';
|
||||
|
||||
type BitwiseResultViewProps = {
|
||||
expression: Expression;
|
||||
@@ -15,12 +16,29 @@ type BitwiseResultViewState = {
|
||||
}
|
||||
|
||||
export default class BitwiseResultView extends React.Component<BitwiseResultViewProps, BitwiseResultViewState> {
|
||||
maxSeenLengthNumberOfBits: number;
|
||||
|
||||
constructor(props: BitwiseResultViewProps) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
this.maxSeenLengthNumberOfBits = 0;
|
||||
}
|
||||
|
||||
render() {
|
||||
var rows = this.getRows();
|
||||
|
||||
let model : BitwiseResultViewModel | null = null
|
||||
|
||||
try
|
||||
{
|
||||
model = BitwiseResultViewModel.createModel(this.props.expression, this.props.emphasizeBytes);
|
||||
}
|
||||
catch(err) {
|
||||
const text = (err as any).message;
|
||||
return <div className='error'>Error: {text}</div>
|
||||
}
|
||||
|
||||
|
||||
var rows = this.getRows(model!);
|
||||
|
||||
return <table className="expression">
|
||||
<tbody>
|
||||
@@ -29,18 +47,20 @@ export default class BitwiseResultView extends React.Component<BitwiseResultView
|
||||
</table>
|
||||
}
|
||||
|
||||
getRows() : JSX.Element[] {
|
||||
var model = BitwiseResultViewModel.createModel(this.props.expression, this.props.emphasizeBytes);
|
||||
getRows(model: BitwiseResultViewModel): JSX.Element[] {
|
||||
|
||||
this.maxSeenLengthNumberOfBits = Math.max(model.maxNumberOfBits, this.maxSeenLengthNumberOfBits);
|
||||
|
||||
return model.items.map((itm, i) =>
|
||||
<ExpressionRow
|
||||
key={i}
|
||||
sign={itm.sign}
|
||||
css={itm.css}
|
||||
bitSize={itm.bitSize}
|
||||
allowFlipBits={itm.allowFlipBits}
|
||||
expressionItem={itm.expression}
|
||||
emphasizeBytes={this.props.emphasizeBytes}
|
||||
maxNumberOfBits={model.maxNumberOfBits}
|
||||
maxNumberOfBits={this.maxSeenLengthNumberOfBits}
|
||||
onBitFlipped={() => this.onBitFlipped()} />);
|
||||
}
|
||||
|
||||
@@ -53,10 +73,11 @@ export default class BitwiseResultView extends React.Component<BitwiseResultView
|
||||
type ExpressionRowProps = {
|
||||
sign: string,
|
||||
css: string,
|
||||
bitSize: number,
|
||||
maxNumberOfBits: number,
|
||||
emphasizeBytes: boolean,
|
||||
allowFlipBits: boolean,
|
||||
expressionItem: ExpressionToken,
|
||||
expressionItem: ExpressionElement,
|
||||
onBitFlipped: any
|
||||
}
|
||||
|
||||
@@ -67,6 +88,7 @@ class ExpressionRow extends React.Component<ExpressionRowProps> {
|
||||
}
|
||||
render() {
|
||||
const { sign, css, maxNumberOfBits, emphasizeBytes, allowFlipBits } = this.props;
|
||||
const maxBits = Math.max()
|
||||
|
||||
return <tr className={"row-with-bits " + css}>
|
||||
<td className="sign">{sign}</td>
|
||||
@@ -76,9 +98,11 @@ class ExpressionRow extends React.Component<ExpressionRowProps> {
|
||||
emphasizeBytes={emphasizeBytes}
|
||||
binaryString={formatter.padLeft(this.getBinaryString(), maxNumberOfBits, '0')}
|
||||
allowFlipBits={allowFlipBits}
|
||||
bitSize={this.props.bitSize}
|
||||
onFlipBit={args => this.flipBit(args)} />
|
||||
</td>
|
||||
<td className="other">{this.getAlternative()}</td>
|
||||
<td className="info" data-test-name='ignore'>{this.getInfo(maxNumberOfBits)}</td>
|
||||
</tr>;;
|
||||
}
|
||||
|
||||
@@ -92,7 +116,7 @@ class ExpressionRow extends React.Component<ExpressionRowProps> {
|
||||
// For expressions like |~2
|
||||
// TODO: find a better way...
|
||||
if (this.props.expressionItem.isOperator) {
|
||||
const ex = this.props.expressionItem as OperatorToken;
|
||||
const ex = this.props.expressionItem as BitwiseOperator;
|
||||
return ex.operator + this.getLabelString(ex.getUnderlyingScalarOperand());
|
||||
}
|
||||
|
||||
@@ -102,7 +126,7 @@ class ExpressionRow extends React.Component<ExpressionRowProps> {
|
||||
getAlternative() {
|
||||
|
||||
if (this.props.expressionItem.isOperator) {
|
||||
const ex = this.props.expressionItem as OperatorToken;
|
||||
const ex = this.props.expressionItem as BitwiseOperator;
|
||||
const res = ex.evaluate();
|
||||
|
||||
return formatter.numberToString(res.value, res.base);
|
||||
@@ -113,22 +137,45 @@ class ExpressionRow extends React.Component<ExpressionRowProps> {
|
||||
return formatter.numberToString(v.value, altBase);
|
||||
}
|
||||
|
||||
getLabelString (op: ScalarToken) : string {
|
||||
getLabelString(op: ScalarValue): string {
|
||||
return formatter.numberToString(op.value, op.base == 'bin' ? 'dec' : op.base);
|
||||
}
|
||||
|
||||
flipBit(args: FlipBitEventArg) {
|
||||
|
||||
const op = this.props.expressionItem.getUnderlyingScalarOperand();
|
||||
const { index, binaryString } = args;
|
||||
const { bitIndex: index, binaryStringLength: totalLength } = args;
|
||||
|
||||
var arr = binaryString.split('');
|
||||
arr[index] = arr[index] == '0' ? '1' : '0';
|
||||
var bin = arr.join('');
|
||||
if(totalLength > op.bitSize() && (totalLength - index) > op.bitSize()) {
|
||||
op.setValue(calc.promoteToBigInt(op.value as number));
|
||||
}
|
||||
|
||||
var newValue = parseInt(bin, 2);
|
||||
console.log(op.bitSize());
|
||||
const pad = op.bitSize() - totalLength;
|
||||
console.log(pad + index);
|
||||
const newValue = calc.flipBit(op.value, pad + index);
|
||||
op.setValue(newValue);
|
||||
|
||||
this.props.onBitFlipped();
|
||||
}
|
||||
|
||||
getInfo(maxNumberOfBits:number) {
|
||||
var op = this.props.expressionItem.getUnderlyingScalarOperand();
|
||||
|
||||
if (op.isBigInt())
|
||||
{
|
||||
const title = `BigInt JavaScript type is used to reprsent this number. All bitwise operations that involve this number have their operands converted to BigInt. BitwiseCmd treats this number as 64-bit number.`;
|
||||
|
||||
return <span title={title} style={{cursor:"help"}}>(64-bit BigInt)</span>;
|
||||
}
|
||||
|
||||
if(op.bitSize() == 32 && maxNumberOfBits >= 32)
|
||||
{
|
||||
const title = "BitwiseCmd treats this number as 32-bit integer. First bit is a sign bit. Try clicking on the first bit and see what will happen.";
|
||||
|
||||
return <span title={title} style={{cursor:"help"}}>(32-bit Number)</span>;
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,8 @@
|
||||
import { ScalarToken, ListOfNumbersExpression, BitwiseOperationExpression, OperatorToken } from '../expression';
|
||||
import { ExpressionToken, Expression } from '../expression-interfaces';
|
||||
import { ScalarValue, ListOfNumbersExpression, BitwiseOperationExpression, BitwiseOperator } from '../expression';
|
||||
import { ExpressionElement, Expression } from '../expression-interfaces';
|
||||
import calc from '../../core/calc';
|
||||
import formatter from '../../core/formatter';
|
||||
import exp from 'constants';
|
||||
|
||||
type Config = {
|
||||
emphasizeBytes: boolean;
|
||||
@@ -11,9 +12,10 @@ type Config = {
|
||||
type ExpressionRowModel = {
|
||||
sign: string;
|
||||
css: string;
|
||||
expression: ExpressionToken;
|
||||
expression: ExpressionElement;
|
||||
allowFlipBits: boolean;
|
||||
label: string;
|
||||
bitSize: number;
|
||||
}
|
||||
|
||||
export default class BitwiseResultViewModel {
|
||||
@@ -43,17 +45,17 @@ export default class BitwiseResultViewModel {
|
||||
i = 0, len = expr.children.length,
|
||||
ex, m = new BitwiseResultViewModel(config);
|
||||
|
||||
var prev : ScalarToken | null = null;
|
||||
var prev : ScalarValue | null = null;
|
||||
|
||||
for (;i<len;i++) {
|
||||
ex = expr.children[i];
|
||||
if(ex instanceof ScalarToken) {
|
||||
if(ex instanceof ScalarValue) {
|
||||
m.addScalarRow(ex);
|
||||
prev = ex;
|
||||
continue;
|
||||
}
|
||||
|
||||
var eo = ex as OperatorToken;
|
||||
var eo = ex as BitwiseOperator;
|
||||
|
||||
// If it a single NOT expression
|
||||
if(eo.isNotExpression) {
|
||||
@@ -63,11 +65,11 @@ export default class BitwiseResultViewModel {
|
||||
prev = notResult;
|
||||
}
|
||||
else if(eo.isShiftExpression){
|
||||
prev = eo.evaluate(prev as ScalarToken);
|
||||
prev = eo.evaluate(prev as ScalarValue);
|
||||
m.addShiftExpressionResultRow(eo, prev);
|
||||
} else {
|
||||
|
||||
prev = eo.evaluate(prev as ScalarToken);
|
||||
prev = eo.evaluate(prev as ScalarValue);
|
||||
m.addOperatorRow(eo);
|
||||
m.addExpressionResultRow(prev);
|
||||
}
|
||||
@@ -77,7 +79,7 @@ export default class BitwiseResultViewModel {
|
||||
return m;
|
||||
};
|
||||
|
||||
addScalarRow(expr: ScalarToken) {
|
||||
addScalarRow(expr: ScalarValue) {
|
||||
const bits = calc.numberOfBitsDisplayed(expr.value);
|
||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||
this.items.push({
|
||||
@@ -85,14 +87,16 @@ export default class BitwiseResultViewModel {
|
||||
css: '',
|
||||
expression: expr,
|
||||
allowFlipBits: this.allowFlipBits,
|
||||
label: ''
|
||||
label: '',
|
||||
bitSize: expr.bitSize(),
|
||||
});
|
||||
};
|
||||
|
||||
addOperatorRow(expr: OperatorToken) {
|
||||
addOperatorRow(expr: BitwiseOperator) {
|
||||
|
||||
const resultNumber = expr.isNotExpression ? expr.evaluate() : expr.getUnderlyingScalarOperand();
|
||||
const bits = calc.numberOfBitsDisplayed(resultNumber.value);
|
||||
|
||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||
|
||||
this.items.push({
|
||||
@@ -100,11 +104,12 @@ export default class BitwiseResultViewModel {
|
||||
css: '',
|
||||
label: this.getLabel(resultNumber),
|
||||
expression: expr.operand,
|
||||
allowFlipBits: this.allowFlipBits
|
||||
allowFlipBits: this.allowFlipBits,
|
||||
bitSize: resultNumber.bitSize()
|
||||
});
|
||||
};
|
||||
|
||||
addShiftExpressionResultRow(expr : OperatorToken, resultExpr : ScalarToken) {
|
||||
addShiftExpressionResultRow(expr : BitwiseOperator, resultExpr : ScalarValue) {
|
||||
const bits = calc.numberOfBitsDisplayed(resultExpr.value);
|
||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||
const child = expr.operand.getUnderlyingScalarOperand();
|
||||
@@ -113,11 +118,12 @@ export default class BitwiseResultViewModel {
|
||||
css: 'expression-result',
|
||||
expression: resultExpr,
|
||||
allowFlipBits: false,
|
||||
label: ''
|
||||
label: '',
|
||||
bitSize: resultExpr.bitSize()
|
||||
});
|
||||
};
|
||||
|
||||
addExpressionResultRow(expr : ScalarToken) {
|
||||
addExpressionResultRow(expr : ScalarValue) {
|
||||
const bits = calc.numberOfBitsDisplayed(expr.value);
|
||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||
this.items.push({
|
||||
@@ -126,10 +132,11 @@ export default class BitwiseResultViewModel {
|
||||
expression: expr,
|
||||
allowFlipBits: false,
|
||||
label: '',
|
||||
bitSize: expr.bitSize()
|
||||
});
|
||||
};
|
||||
|
||||
getLabel (op: ScalarToken) : string {
|
||||
getLabel (op: ScalarValue) : string {
|
||||
|
||||
return formatter.numberToString(op.value, op.base === 'bin' ? 'dec' : op.base)
|
||||
}
|
||||
|
||||
41
src/expression/engine.test.ts
Normal file
41
src/expression/engine.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import ScalarValue from "./ScalarValue";
|
||||
import engine from "./engine";
|
||||
|
||||
describe('evaluate', () => {
|
||||
|
||||
it('treats differently big ints and regular numbers', () => {
|
||||
const bigResult = engine.applyOperator(new ScalarValue(BigInt(2147483647)), "<<", new ScalarValue(BigInt(1)));
|
||||
const result = engine.applyOperator(new ScalarValue(2147483647), "<<", new ScalarValue(1));
|
||||
|
||||
expect(bigResult.value.toString()).toBe("4294967294");
|
||||
expect(result.value.toString()).toBe("-2");
|
||||
});
|
||||
|
||||
it("can execute all operators without errors", () => {
|
||||
|
||||
const operators = [">>", "<<", "|", "&", "^"];
|
||||
|
||||
// >>> not supported by BigInt
|
||||
expect(() => engine.applyOperator(new ScalarValue(1), ">>>", new ScalarValue(2))).not.toThrow();
|
||||
|
||||
operators.forEach(o => {
|
||||
expect(() => engine.applyOperator(new ScalarValue(BigInt(1)), o, new ScalarValue(BigInt(2)))).not.toThrow();
|
||||
expect(() => engine.applyOperator(new ScalarValue(1), o, new ScalarValue(2))).not.toThrow();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it('promotes either of operands to BigInt if the other one is', () => {
|
||||
const bint = new ScalarValue(BigInt(1));
|
||||
const int = new ScalarValue(1);
|
||||
|
||||
const rshift = engine.applyOperator(bint, ">>", int);
|
||||
expect(rshift.isBigInt()).toBe(true);
|
||||
expect(rshift.value.toString()).toBe('0');
|
||||
|
||||
const lshift = engine.applyOperator(int, "<<", bint);
|
||||
expect(lshift.isBigInt()).toBe(true);
|
||||
expect(lshift.value.toString()).toBe('2');
|
||||
});
|
||||
|
||||
});
|
||||
40
src/expression/engine.ts
Normal file
40
src/expression/engine.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import { NumberType } from "../core/types";
|
||||
import ScalarValue from "./ScalarValue";
|
||||
|
||||
const engine = {
|
||||
applyNotOperator(operand: ScalarValue) : ScalarValue {
|
||||
return new ScalarValue(~operand.value, operand.base);
|
||||
},
|
||||
applyOperator(op1 : ScalarValue, operator: string, op2 : ScalarValue) : ScalarValue {
|
||||
|
||||
const result = evalute(op1.value, operator, op2.value);
|
||||
|
||||
return new ScalarValue(result, op2.base);
|
||||
}
|
||||
};
|
||||
|
||||
function evalute(op1 : NumberType, operator: string, op2 : NumberType) : NumberType{
|
||||
const a = equalizeType(op2, op1) as any;
|
||||
const b = equalizeType(op1, op2) as any;
|
||||
|
||||
switch(operator) {
|
||||
case ">>": return (a >> b) as (NumberType);
|
||||
case ">>>": return (a >>> b) as (NumberType);
|
||||
case "<<": return (a << b) as (NumberType);
|
||||
case "&": return (b & a) as (NumberType);
|
||||
case "|": return (b | a) as (NumberType);
|
||||
case "^": return (b ^ a) as (NumberType);
|
||||
default: throw new Error(operator + " operator is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
function equalizeType(source : NumberType, dest : NumberType) : NumberType {
|
||||
|
||||
return typeof source == 'bigint' && typeof dest != 'bigint'
|
||||
? BigInt(dest)
|
||||
: dest;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export default engine;
|
||||
@@ -1,14 +1,14 @@
|
||||
import { ScalarToken } from "./expression";
|
||||
import { ScalarValue } from "./expression";
|
||||
|
||||
export interface Expression
|
||||
{
|
||||
expressionString: string;
|
||||
}
|
||||
|
||||
export interface ExpressionToken
|
||||
export interface ExpressionElement
|
||||
{
|
||||
isOperator: boolean;
|
||||
getUnderlyingScalarOperand: () => ScalarToken;
|
||||
evaluate(operand? : ScalarToken): ScalarToken;
|
||||
getUnderlyingScalarOperand: () => ScalarValue;
|
||||
evaluate(operand? : ScalarValue): ScalarValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { parser, ListOfNumbersExpression, BitwiseOperationExpression, ScalarToken, OperatorToken } from "./expression";
|
||||
import { parser, ListOfNumbersExpression, BitwiseOperationExpression, ScalarValue, BitwiseOperator } from "./expression";
|
||||
|
||||
describe("expression parser", () => {
|
||||
|
||||
@@ -25,8 +25,8 @@ describe("expression parser", () => {
|
||||
expect(actual).toBeInstanceOf(ListOfNumbersExpression);
|
||||
|
||||
const expr = actual as ListOfNumbersExpression;
|
||||
expect(expr.children[0].getUnderlyingScalarOperand().value).toBe(305419896);
|
||||
expect(expr.children[1].getUnderlyingScalarOperand().value).toBe(2863311360);
|
||||
expect(expr.children[0].getUnderlyingScalarOperand().value.toString()).toBe('305419896');
|
||||
expect(expr.children[1].getUnderlyingScalarOperand().value.toString()).toBe('2863311360');
|
||||
})
|
||||
|
||||
it("pares multiple operand expression", () => {
|
||||
@@ -36,17 +36,17 @@ describe("expression parser", () => {
|
||||
const first = result.children[0];
|
||||
const second = result.children[1];
|
||||
|
||||
expect(first).toBeInstanceOf(ScalarToken);
|
||||
expect(first).toBeInstanceOf(ScalarValue);
|
||||
|
||||
expect((first as ScalarToken).value).toBe(1);
|
||||
expect((first as ScalarValue).value).toBe(1);
|
||||
|
||||
expect(second).toBeInstanceOf(OperatorToken);
|
||||
var secondOp = second as OperatorToken;
|
||||
expect(second).toBeInstanceOf(BitwiseOperator);
|
||||
var secondOp = second as BitwiseOperator;
|
||||
expect(secondOp.operator).toBe("^");
|
||||
|
||||
expect(secondOp.operand).toBeInstanceOf(ScalarToken);
|
||||
var childOp = secondOp.operand as ScalarToken;
|
||||
expect(childOp.value).toBe(2);
|
||||
expect(secondOp.operand).toBeInstanceOf(ScalarValue);
|
||||
var childOp = secondOp.operand as ScalarValue;
|
||||
expect(childOp.value.toString()).toBe('2');
|
||||
});
|
||||
|
||||
it("bug", () => {
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
import ScalarToken from './ScalarToken';
|
||||
import OperatorToken from './OperatorToken'
|
||||
import ScalarValue from './ScalarValue';
|
||||
import BitwiseOperator from './BitwiseOperator'
|
||||
import ListOfNumbersExpression from './ListOfNumbersExpression';
|
||||
import BitwiseOperationExpression from './BitwiseOperationExpression';
|
||||
import { Expression, ExpressionToken } from './expression-interfaces';
|
||||
import { NumberBase } from '../core/formatter';
|
||||
import { Expression, ExpressionElement } from './expression-interfaces';
|
||||
|
||||
export { default as ScalarToken } from './ScalarToken';
|
||||
export { default as OperatorToken } from './OperatorToken';
|
||||
export { default as ScalarValue } from './ScalarValue';
|
||||
export { default as BitwiseOperator } from './BitwiseOperator';
|
||||
export { default as ListOfNumbersExpression } from './ListOfNumbersExpression';
|
||||
export { default as BitwiseOperationExpression } from './BitwiseOperationExpression';
|
||||
|
||||
@@ -47,14 +46,6 @@ class ExpressionParser {
|
||||
return null;
|
||||
};
|
||||
|
||||
parseOperand (input : string) : ScalarToken {
|
||||
return ScalarToken.parse(input);
|
||||
};
|
||||
|
||||
createOperand (number : number, base : NumberBase) : ScalarToken {
|
||||
return ScalarToken.create(number, base);
|
||||
};
|
||||
|
||||
addFactory (factory: IExpressionParserFactory) {
|
||||
this.factories.push(factory);
|
||||
}
|
||||
@@ -70,7 +61,7 @@ class ListOfNumbersExpressionFactory implements IExpressionParserFactory
|
||||
|
||||
return input.split(' ')
|
||||
.filter(p => p.length > 0)
|
||||
.map(p => ScalarToken.tryParse(p))
|
||||
.map(p => ScalarValue.tryParse(p))
|
||||
.filter(n => n == null)
|
||||
.length == 0;
|
||||
};
|
||||
@@ -79,7 +70,7 @@ class ListOfNumbersExpressionFactory implements IExpressionParserFactory
|
||||
|
||||
const numbers = input.split(' ')
|
||||
.filter(p => p.length > 0)
|
||||
.map(m => ScalarToken.parse(m));
|
||||
.map(m => ScalarValue.parse(m));
|
||||
|
||||
return new ListOfNumbersExpression(input, numbers);
|
||||
}
|
||||
@@ -90,8 +81,8 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
regex: RegExp;
|
||||
|
||||
constructor() {
|
||||
this.fullRegex = /^((<<|>>|>>>|\||\&|\^)?(~?-?([b,x,a-f,0-9]+)))+$/;
|
||||
this.regex = /(<<|>>|>>>|\||\&|\^)?(~?-?(?:[b,x,a-f,0-9]+))/g;
|
||||
this.fullRegex = /^((<<|>>|>>>|\||\&|\^)?(~?-?([b,x,l,L,a-f,0-9]+)))+$/;
|
||||
this.regex = /(<<|>>|>>>|\||\&|\^)?(~?-?(?:[b,x,l,L,a-f,0-9]+))/g;
|
||||
}
|
||||
|
||||
canCreate (input: string) : boolean {
|
||||
@@ -101,7 +92,7 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
|
||||
create (input: string) : Expression {
|
||||
var m : RegExpExecArray | null;
|
||||
const operands : ExpressionToken[] = [];
|
||||
const operands : ExpressionElement[] = [];
|
||||
const normalizedString = this.normalizeString(input);
|
||||
|
||||
this.regex.lastIndex = 0;
|
||||
@@ -113,23 +104,23 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
return new BitwiseOperationExpression(normalizedString, operands)
|
||||
};
|
||||
|
||||
parseMatch (m:any): ExpressionToken {
|
||||
parseMatch (m:any): ExpressionElement {
|
||||
var input = m[0],
|
||||
operator = m[1],
|
||||
num = m[2];
|
||||
|
||||
var parsed = null;
|
||||
if(num.indexOf('~') == 0) {
|
||||
parsed = new OperatorToken(ScalarToken.parse(num.substring(1)), '~');
|
||||
parsed = new BitwiseOperator(ScalarValue.parse(num.substring(1)), '~');
|
||||
}
|
||||
else {
|
||||
parsed = ScalarToken.parse(num);
|
||||
parsed = ScalarValue.parse(num);
|
||||
}
|
||||
|
||||
if(operator == null) {
|
||||
return parsed as OperatorToken;
|
||||
return parsed as BitwiseOperator;
|
||||
} else {
|
||||
return new OperatorToken(parsed as ScalarToken, operator);
|
||||
return new BitwiseOperator(parsed as ScalarValue, operator);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
106
src/expression/numberParser.test.ts
Normal file
106
src/expression/numberParser.test.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
import exp from 'constants';
|
||||
import calc from '../core/calc';
|
||||
import {numberParser, ParsedNumber} from './numberParser';
|
||||
|
||||
describe("parser", () => {
|
||||
|
||||
it('parses decimal number', () => {
|
||||
const result = numberParser.parse('10');
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
var number = result as ParsedNumber;
|
||||
expect(number.value).toBe(10);
|
||||
expect(number.base).toBe('dec');
|
||||
expect(number.input).toBe('10');
|
||||
});
|
||||
|
||||
it('parses bigint numbers', () => {
|
||||
const dec = numberParser.parse('10L');
|
||||
expect(dec).not.toBeNull();
|
||||
|
||||
expect(dec?.value).toBe(BigInt(10));
|
||||
expect(typeof dec?.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?.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?.base).toBe('hex');
|
||||
expect(hex?.input).toBe('0xfL');
|
||||
});
|
||||
|
||||
|
||||
it('switches to bigint if value exceeds max safe int', () => {
|
||||
const unsafeInt = BigInt(Number.MAX_SAFE_INTEGER)+BigInt(1);
|
||||
|
||||
const dec = numberParser.parse(unsafeInt.toString());
|
||||
expect(dec?.value).toEqual(unsafeInt);
|
||||
expect(dec?.base).toBe('dec');
|
||||
|
||||
const bin = numberParser.parse("0b" + unsafeInt.toString(2));
|
||||
expect(bin?.value).toEqual(unsafeInt);
|
||||
expect(bin?.base).toEqual('bin');
|
||||
|
||||
const hex = numberParser.parse("0x" + unsafeInt.toString(16));
|
||||
expect(hex?.value).toEqual(unsafeInt);
|
||||
expect(hex?.base).toEqual('hex');
|
||||
});
|
||||
|
||||
it('switches to bigint if value exceeds max safe negative int', () => {
|
||||
const unsafeInt = BigInt(Number.MIN_SAFE_INTEGER)-BigInt(1);
|
||||
|
||||
const dec = numberParser.parse(unsafeInt.toString());
|
||||
expect(dec?.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?.base).toEqual('bin');
|
||||
|
||||
const hex = numberParser.parse("-0x" + unsafeInt.toString(16).replace('-',''));
|
||||
expect(hex?.value.toString()).toEqual(unsafeInt.toString());
|
||||
expect(hex?.base).toEqual('hex');
|
||||
});
|
||||
|
||||
it('parses hex number', () => {
|
||||
const result = numberParser.parse('0xab');
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
var number = result as ParsedNumber;
|
||||
expect(number.value).toBe(171);
|
||||
expect(number.base).toBe('hex');
|
||||
expect(number.input).toBe('0xab');
|
||||
});
|
||||
|
||||
it('parses bin number', () => {
|
||||
var result = numberParser.parse('0b0110');
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
var number = result as ParsedNumber;
|
||||
expect(number.value).toBe(6);
|
||||
expect(number.base).toBe('bin');
|
||||
expect(number.input).toBe('0b0110');
|
||||
});
|
||||
|
||||
it('returns null on bad inputs', () => {
|
||||
expect(numberParser.parse('abc')).toBeNull();
|
||||
expect(numberParser.parse('')).toBeNull();
|
||||
});
|
||||
|
||||
it('parses big int', () => {
|
||||
var v = numberParser.parse('1l')?.value
|
||||
expect(typeof v).toBe("bigint");
|
||||
expect(v?.toString()).toBe("1");
|
||||
})
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
import {numberParser, ParsedNumber} from './numberParser';
|
||||
|
||||
describe("parser", () => {
|
||||
it('parses decimal number', () => {
|
||||
const result = numberParser.parse('10');
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
var number = result as ParsedNumber;
|
||||
expect(number.value).toBe(10);
|
||||
expect(number.base).toBe('dec');
|
||||
expect(number.input).toBe('10');
|
||||
});
|
||||
|
||||
it('parses hex number', () => {
|
||||
const result = numberParser.parse('0xab');
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
var number = result as ParsedNumber;
|
||||
expect(number.value).toBe(171);
|
||||
expect(number.base).toBe('hex');
|
||||
expect(number.input).toBe('0xab');
|
||||
});
|
||||
|
||||
it('parses bin number', () => {
|
||||
var result = numberParser.parse('0b0110');
|
||||
expect(result).not.toBeNull();
|
||||
|
||||
var number = result as ParsedNumber;
|
||||
expect(number.value).toBe(6);
|
||||
expect(number.base).toBe('bin');
|
||||
expect(number.input).toBe('0b0110');
|
||||
});
|
||||
|
||||
it('returns null on bad inputs', () => {
|
||||
expect(numberParser.parse('abc')).toBeNull();
|
||||
expect(numberParser.parse('')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -1,27 +1,27 @@
|
||||
import { INT32_MAX_VALUE, INT32_MIN_VALUE } from "../core/const";
|
||||
import { NumberBase } from "../core/formatter";
|
||||
import { NumberType } from "../core/types";
|
||||
|
||||
const decimalRegex = /^-?\d+$/;
|
||||
const hexRegex = /^-?0x[0-9,a-f]+$/i;
|
||||
const binRegex = /^-?0b[0-1]+$/i;
|
||||
const operatorRegex = /^<<|>>|<<<|\&|\|\^|~$/;
|
||||
const decimalRegex = /^-?\d+[l,L]?$/;
|
||||
const hexRegex = /^-?0x[0-9,a-f]+[l,L]?$/i;
|
||||
const binRegex = /^-?0b[0-1]+[l,L]?$/i;
|
||||
|
||||
interface ParserConfig {
|
||||
regex: RegExp,
|
||||
radix: number,
|
||||
base: NumberBase,
|
||||
prefix: string|RegExp
|
||||
parse: (input: string) => NumberType
|
||||
}
|
||||
|
||||
export interface ParsedNumber {
|
||||
value: number;
|
||||
value: number|bigint;
|
||||
base: NumberBase;
|
||||
input: string;
|
||||
}
|
||||
|
||||
var knownParsers : ParserConfig[] = [
|
||||
{ regex: decimalRegex, radix: 10, base: 'dec', prefix: '^$' },
|
||||
{ regex: hexRegex, radix: 16, base: 'hex', prefix:/0x/i },
|
||||
{ regex: binRegex, radix: 2, base: 'bin', prefix:/0b/i }];
|
||||
{ regex: decimalRegex, base: 'dec', parse:(s) => parseIntSafe(s, 10) },
|
||||
{ regex: hexRegex, base: 'hex', parse:(s) => parseIntSafe(s, 16)},
|
||||
{ regex: binRegex, base: 'bin', parse:(s) => parseIntSafe(s, 2) }];
|
||||
|
||||
|
||||
class NumberParser {
|
||||
@@ -53,7 +53,7 @@ class NumberParser {
|
||||
return null;
|
||||
}
|
||||
|
||||
var value = parseInt(rawInput.replace(parser.prefix, ''), parser.radix);
|
||||
var value = parser.parse(rawInput);
|
||||
|
||||
return {
|
||||
value: value,
|
||||
@@ -63,6 +63,29 @@ class NumberParser {
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_SAFE_INTn = BigInt(INT32_MAX_VALUE);
|
||||
const MIN_SAFE_INTn = BigInt(INT32_MIN_VALUE);
|
||||
|
||||
function parseIntSafe(input : string, radix: number) : NumberType {
|
||||
|
||||
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(bigInt > MAX_SAFE_INTn)
|
||||
return bigInt;
|
||||
|
||||
if(bigInt < MIN_SAFE_INTn)
|
||||
return bigInt;
|
||||
|
||||
return parseInt(input.replace(/0(x|b)/, ''), radix);
|
||||
}
|
||||
|
||||
const numberParser = new NumberParser(knownParsers);
|
||||
|
||||
export {numberParser};
|
||||
@@ -32,6 +32,7 @@ code { font-size: 1.2em; font-weight: bold; }
|
||||
|
||||
.expression .label { font-weight: bold; padding-right: 5px; text-align: right; }
|
||||
.expression .bin { letter-spacing: 3px; }
|
||||
.expression .info { font-size: 0.9em; color: teal; }
|
||||
.expression .byte { margin: 0 3px; }
|
||||
.expression-result td { border-top: dotted 1px gray; }
|
||||
.expression { font-size: 1.5em; font-family: monospace }
|
||||
|
||||
@@ -44,7 +44,7 @@ export default class AppState {
|
||||
this.emphasizeBytes = persistData.emphasizeBytes || true;
|
||||
this.persistedVersion = persistData.version || 0.1;
|
||||
this.wasOldVersion = persistData.version != null && this.version > this.persistedVersion;
|
||||
this.debugMode = env !== 'prod' || persistData.debugMode === true;
|
||||
this.debugMode = persistData.debugMode === true;
|
||||
this.pageVisitsCount = persistData.pageVisistsCount || 0;
|
||||
this.donationClicked = persistData.donationClicked;
|
||||
}
|
||||
|
||||
@@ -28,7 +28,6 @@ export default class AppRoot extends React.Component<AppRootProps, AppRootState>
|
||||
}
|
||||
|
||||
refresh() {
|
||||
console.log('refresh');
|
||||
this.setState(this.props.appState);
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import HelpResultView from './components/HelpResultView';
|
||||
import TextResultView from './components/TextResultView';
|
||||
import WhatsnewResultView from './components/WhatsNewResultView';
|
||||
import {STARTUP_COMMAND_KEY} from './startup';
|
||||
import { INT32_MAX_VALUE, INT64_MAX_VALUE } from '../core/const';
|
||||
|
||||
const shellModule = {
|
||||
setup: function(appState: AppState, cmd: CmdShell) {
|
||||
@@ -31,6 +32,10 @@ const shellModule = {
|
||||
appState.toggleDebugMode();
|
||||
appState.addCommandResult(c.input, () => <TextResultView text={`Debug Mode: ${appState.debugMode}`}/>);
|
||||
});
|
||||
cmd.command("-max", (c:CommandInput) => {
|
||||
const text = `Int32 ${INT32_MAX_VALUE}\nInt64 ${INT64_MAX_VALUE}`
|
||||
appState.addCommandResult(c.input, () => <TextResultView text={text} />)
|
||||
})
|
||||
|
||||
cmd.command("donate", (c:CommandInput) => {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user