From bd5b748a64e16381eadf4ea4232e8b4ee1aa7e09 Mon Sep 17 00:00:00 2001 From: Borys Levytskyi Date: Sat, 6 May 2023 14:31:07 +0300 Subject: [PATCH] Better support for 64 bit numbers (#44) --- .vscode/launch.json | 2 +- src/core/calc.test.ts | 21 +-- src/core/calc.ts | 9 +- src/core/const.ts | 4 + src/core/formatter.test.ts | 18 ++- src/core/formatter.ts | 73 ++++++++- src/expression/BitwiseOperationExpression.ts | 8 +- src/expression/ExpressionOperand.test.ts | 19 --- src/expression/ExpressionParser.test.ts | 8 +- .../ListOfNumbersExpression.test.ts | 4 +- src/expression/ListOfNumbersExpression.ts | 11 +- src/expression/OperatorExpression.test.ts | 19 +++ ...essionOperand.ts => OperatorExpression.ts} | 24 +-- src/expression/ScalarExpression.test.ts | 19 +++ src/expression/ScalarExpression.ts | 61 ++++++++ src/expression/ScalarOperand.test.ts | 26 ---- src/expression/ScalarOperand.ts | 141 ------------------ .../components/BitwiseExpressionModel.ts | 86 ++++++----- .../BitwiseOperationExpressionView.tsx | 42 +++--- src/expression/expression-interfaces.ts | 13 +- src/expression/expression.test.ts | 26 ++-- src/expression/expression.ts | 35 ++--- src/expression/numberParser.ts | 2 +- src/networking/components/IpAddressView.tsx | 2 +- src/shell/components/HelpResultView.tsx | 8 +- 25 files changed, 336 insertions(+), 345 deletions(-) create mode 100644 src/core/const.ts delete mode 100644 src/expression/ExpressionOperand.test.ts create mode 100644 src/expression/OperatorExpression.test.ts rename src/expression/{ExpressionOperand.ts => OperatorExpression.ts} (59%) create mode 100644 src/expression/ScalarExpression.test.ts create mode 100644 src/expression/ScalarExpression.ts delete mode 100644 src/expression/ScalarOperand.test.ts delete mode 100644 src/expression/ScalarOperand.ts diff --git a/.vscode/launch.json b/.vscode/launch.json index 562cc64..0bba95a 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,7 +4,7 @@ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 "version": "0.2.0", "configurations": [ - { + { "type": "pwa-chrome", "request": "launch", "name": "Launch Chrome against localhost", diff --git a/src/core/calc.test.ts b/src/core/calc.test.ts index 05c892c..b521467 100644 --- a/src/core/calc.test.ts +++ b/src/core/calc.test.ts @@ -1,26 +1,29 @@ import calc from './calc'; -import { BitwiseOperationExpression, ScalarOperand, ExpressionOperand } from '../expression/expression'; +import { BitwiseOperationExpression, ScalarExpression, OperatorExpression } from '../expression/expression'; import exp from 'constants'; +import { INT_MAX_VALUE } from './const'; describe("calc", () => { it('calculates number of bits', () => { - expect(calc.numberOfBits(1)).toBe(1); - expect(calc.numberOfBits(2)).toBe(2); - expect(calc.numberOfBits(3)).toBe(2); - expect(calc.numberOfBits(68719476735)).toBe(36); + expect(calc.numberOfBitsDisplayed(1)).toBe(1); + 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); }); it('calculates max number of bits', () => { - expect(calc.maxNumberOfBits([1, 2, 3, 10])).toBe(4); + expect(calc.maxNumberOfBitsDisplayed([1, 2, 3, 10])).toBe(4); }); it('calculates expression', () => { var result = calc.calcExpression(new BitwiseOperationExpression( "1|2&3", [ - new ScalarOperand(1), - new ExpressionOperand("|2", new ScalarOperand(2), "|"), - new ExpressionOperand("&3", new ScalarOperand(3), "&"), + new ScalarExpression(1), + new OperatorExpression("|2", new ScalarExpression(2), "|"), + new OperatorExpression("&3", new ScalarExpression(3), "&"), ] )); diff --git a/src/core/calc.ts b/src/core/calc.ts index 4e717cc..e7ef6e8 100644 --- a/src/core/calc.ts +++ b/src/core/calc.ts @@ -1,20 +1,21 @@ import { ExpressionInput } from "../expression/expression-interfaces"; +import { INT_MAX_VALUE } from "./const"; export default { - numberOfBits: function (num: number) : number { + numberOfBitsDisplayed: function (num: number) : number { if(num < 0) { - return 32; + return Math.abs(num) <= INT_MAX_VALUE ? 32 : 64; } return Math.floor(Math.log(num) / Math.log(2)) + 1; }, - maxNumberOfBits: function (arr: number[]) { + maxNumberOfBitsDisplayed: function (arr: number[]) { var counts = [], num; for (var i = 0; i < arr.length; i++) { num = arr[i]; - counts.push(this.numberOfBits(num)); + counts.push(this.numberOfBitsDisplayed(num)); } return Math.max.apply(null, counts); diff --git a/src/core/const.ts b/src/core/const.ts new file mode 100644 index 0000000..a5b1ef5 --- /dev/null +++ b/src/core/const.ts @@ -0,0 +1,4 @@ + +const INT_MAX_VALUE = 2147483647; + +export {INT_MAX_VALUE}; \ No newline at end of file diff --git a/src/core/formatter.test.ts b/src/core/formatter.test.ts index 9d08afa..f25c532 100644 --- a/src/core/formatter.test.ts +++ b/src/core/formatter.test.ts @@ -2,18 +2,24 @@ import formatter from './formatter'; describe("formatter", () => { it('formats string', () => { - expect(formatter.formatString(15, "dec")).toBe("15"); - expect(formatter.formatString(15, "hex")).toBe("f"); - expect(formatter.formatString(15, "bin")).toBe("1111"); + expect(formatter.numberToString(15, "dec")).toBe("15"); + expect(formatter.numberToString(15, "hex")).toBe("0xf"); + expect(formatter.numberToString(15, "bin")).toBe("1111"); }); it('formats large binary number correctly', () => { var decimal = 68719476735; var binary = formatter.bin(68719476735); - var hex = formatter.formatString(decimal, 'hex'); + var hex = formatter.numberToString(decimal, 'hex'); expect(binary).toBe('111111111111111111111111111111111111'); - expect(hex).toBe('fffffffff'); - }) + expect(hex).toBe('0xfffffffff'); + }); + + it('formats negative binary numbers', () => { + expect(formatter.numberToString(-1, 'bin')).toBe("11111111111111111111111111111111"); + expect(formatter.numberToString(-0, 'bin')).toBe("0"); + expect(formatter.numberToString(-2147483647, 'bin')).toBe("10000000000000000000000000000001"); + }); it('pads left', () => { expect(formatter.padLeft("1", 3, " ")).toBe(" 1"); diff --git a/src/core/formatter.ts b/src/core/formatter.ts index 2e5cd04..189e73a 100644 --- a/src/core/formatter.ts +++ b/src/core/formatter.ts @@ -1,6 +1,27 @@ +import { INT_MAX_VALUE } from "./const"; +export type NumberBase = 'dec' | 'hex' | 'bin'; + const formatter = { - formatString: function(num: number, kind: string) : string { - return num.toString(getBase(kind || "bin")); + numberToString: function(value: number, kind: NumberBase) : string { + + switch(kind) { + case 'hex': + var hexVal = Math.abs(value).toString(16); + return value >= 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); + } + + return value.toString(getBase(kind || "bin")); + case 'dec': + return value.toString(10); + default: + throw new Error("Unexpected kind: " + kind) + } }, padLeft: function (str: string, length: number, symbol: string) : string { var sb = Array.prototype.slice.call(str), symbol = symbol || "0"; @@ -16,7 +37,7 @@ const formatter = { return sb.join(''); }, bin(number: number) { - return this.formatString(number, 'bin'); + return this.numberToString(number, 'bin'); }, emBin(number: number) { return this.padLeft(this.bin(number), 8, '0'); @@ -49,6 +70,15 @@ const formatter = { if(tmp.length > 0) res.push(tmp.join('')); return { vpc: res[0], subnet: res[1], hosts: res[2]}; + }, + getAlternativeBase: (base: NumberBase) : NumberBase => { + switch(base) { + case 'dec': + case 'bin': + return 'hex'; + case 'hex': return 'dec'; + default : throw new Error(base + " kind doesn't have opposite kind") + } } }; @@ -62,6 +92,43 @@ 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); diff --git a/src/expression/BitwiseOperationExpression.ts b/src/expression/BitwiseOperationExpression.ts index 96e5119..95b0785 100644 --- a/src/expression/BitwiseOperationExpression.ts +++ b/src/expression/BitwiseOperationExpression.ts @@ -1,12 +1,12 @@ -import { ExpressionInput, ExpressionInputItem } from "./expression-interfaces"; +import { ExpressionInput, Expression } from "./expression-interfaces"; export default class BitwiseOperationExpression implements ExpressionInput { expressionString: string; - expressionItems: ExpressionInputItem[]; + children: Expression[]; - constructor(expressionString: string, expressions: ExpressionInputItem[]) { + constructor(expressionString: string, children: Expression[]) { this.expressionString = expressionString; - this.expressionItems = expressions; + this.children = children; } } \ No newline at end of file diff --git a/src/expression/ExpressionOperand.test.ts b/src/expression/ExpressionOperand.test.ts deleted file mode 100644 index 8346c91..0000000 --- a/src/expression/ExpressionOperand.test.ts +++ /dev/null @@ -1,19 +0,0 @@ -import ScalarOperand from "./ScalarOperand"; -import ExpressionOperand from './ExpressionOperand'; - -it('can apply ~ operand', () => { - var op = new ScalarOperand(10, 'dec'); - var expr = new ExpressionOperand("~10", op, "~"); - var result = expr.evaluate(); - expect(result.value).toBe(-11); - expect(result.base).toBe('dec'); -}); - -it('can apply & operand', () => { - var op1 = new ScalarOperand(3, 'dec'); - var op2 = new ScalarOperand(4, 'dec'); - var expr = new ExpressionOperand("|3", op1, "|"); - var result = expr.evaluate(op2); - expect(result.value).toBe(7); - expect(result.base).toBe('dec'); -}); \ No newline at end of file diff --git a/src/expression/ExpressionParser.test.ts b/src/expression/ExpressionParser.test.ts index ce3edfa..851cacf 100644 --- a/src/expression/ExpressionParser.test.ts +++ b/src/expression/ExpressionParser.test.ts @@ -1,7 +1,6 @@ -import { Expression, NumericLiteral } from "typescript"; -import ExpressionOperand from "./ExpressionOperand"; -import { ScalarOperand } from "./expression"; -import { ExpressionInputItem } from "./expression-interfaces"; +import OperatorExpression from "./OperatorExpression"; +import { ScalarExpression } from "./expression"; +import { Expression } from "./expression-interfaces"; import { type } from "os"; import { InputType } from "zlib"; import exp from "constants"; @@ -17,7 +16,6 @@ type TokenDef = { const decimalRegex = /^-?\d+/; const hexRegex = /^-?0x[0-9,a-f]+/i; const binRegex = /^-?0b[0-1]+/i; -const operatorRegex = /^<<|>>|<<<|\&|\|\^|~/; type Token = { value: string, diff --git a/src/expression/ListOfNumbersExpression.test.ts b/src/expression/ListOfNumbersExpression.test.ts index 546e297..71c0a2a 100644 --- a/src/expression/ListOfNumbersExpression.test.ts +++ b/src/expression/ListOfNumbersExpression.test.ts @@ -1,7 +1,7 @@ -import ScalarOperand from "./ScalarOperand"; +import ScalarExpression from "./ScalarExpression"; import ListOfNumbersExpression from "./ListOfNumbersExpression"; it('calculates max bits length', () => { - var expr = new ListOfNumbersExpression("10 0xabef 0b01010", [ScalarOperand.parse("10"), ScalarOperand.parse("0xabef"), ScalarOperand.parse("0b01010")]) + var expr = new ListOfNumbersExpression("10 0xabef 0b01010", [ScalarExpression.parse("10"), ScalarExpression.parse("0xabef"), ScalarExpression.parse("0b01010")]) expect(expr.maxBitsLength).toBe(16); }); diff --git a/src/expression/ListOfNumbersExpression.ts b/src/expression/ListOfNumbersExpression.ts index 775cf75..5d90564 100644 --- a/src/expression/ListOfNumbersExpression.ts +++ b/src/expression/ListOfNumbersExpression.ts @@ -1,15 +1,16 @@ -import ScalarOperand from "./ScalarOperand"; -import { ExpressionInput, ExpressionInputItem } from "./expression-interfaces"; +import calc from "../core/calc"; +import ScalarExpression from "./ScalarExpression"; +import { ExpressionInput, Expression } from "./expression-interfaces"; export default class ListOfNumbersExpression implements ExpressionInput { - numbers: ScalarOperand[]; + numbers: ScalarExpression[]; expressionString: string; maxBitsLength: number; - constructor(expressionString: string, numbers: ScalarOperand[]) { + constructor(expressionString: string, numbers: ScalarExpression[]) { this.expressionString = expressionString; this.numbers = numbers; - this.maxBitsLength = numbers.map(n => n.lengthInBits).reduce((n , c) => n >= c ? n : c, 0); + this.maxBitsLength = numbers.map(n => calc.numberOfBitsDisplayed(n.value)).reduce((n , c) => n >= c ? n : c, 0); } toString() { diff --git a/src/expression/OperatorExpression.test.ts b/src/expression/OperatorExpression.test.ts new file mode 100644 index 0000000..2dda211 --- /dev/null +++ b/src/expression/OperatorExpression.test.ts @@ -0,0 +1,19 @@ +import ScalarExpression from "./ScalarExpression"; +import OperatorExpression from './OperatorExpression'; + +it('can apply ~ operand', () => { + var op = new ScalarExpression(10, 'dec'); + var expr = new OperatorExpression("~10", op, "~"); + var result = expr.evaluate(); + expect(result.value).toBe(-11); + expect(result.base).toBe('dec'); +}); + +it('can apply & operand', () => { + var op1 = new ScalarExpression(3, 'dec'); + var op2 = new ScalarExpression(4, 'dec'); + var expr = new OperatorExpression("|3", op1, "|"); + var result = expr.evaluate(op2); + expect(result.value).toBe(7); + expect(result.base).toBe('dec'); +}); \ No newline at end of file diff --git a/src/expression/ExpressionOperand.ts b/src/expression/OperatorExpression.ts similarity index 59% rename from src/expression/ExpressionOperand.ts rename to src/expression/OperatorExpression.ts index 0fb250b..2df3bd8 100644 --- a/src/expression/ExpressionOperand.ts +++ b/src/expression/OperatorExpression.ts @@ -1,25 +1,25 @@ -import ScalarOperand from './ScalarOperand'; -import { ExpressionInputItem } from './expression-interfaces'; +import ScalarExpression from './ScalarExpression'; +import { Expression } from './expression-interfaces'; -export default class ExpressionOperand implements ExpressionInputItem { +export default class OperatorExpression implements Expression { expressionString: string; - operand: ExpressionInputItem; + operand: Expression; sign: string; - isExpression: boolean; + isOperator: boolean; isShiftExpression: boolean; isNotExpression: boolean; - constructor(expressionString : string, operand : ExpressionInputItem, sign : string) { + constructor(expressionString : string, operand : Expression, sign : string) { this.expressionString = expressionString; this.operand = operand; this.sign = sign; - this.isExpression = true; + this.isOperator = true; this.isShiftExpression = this.sign.indexOf('<') >= 0 || this.sign.indexOf('>')>= 0; this.isNotExpression = this.sign === '~'; } - evaluate(operand?: ScalarOperand) : ScalarOperand { - if (operand instanceof ExpressionOperand) { + evaluate(operand?: ScalarExpression) : ScalarExpression { + if (operand instanceof OperatorExpression) { throw new Error('value shouldnt be expression'); } @@ -35,11 +35,11 @@ export default class ExpressionOperand implements ExpressionInputItem { str = operand.value + this.sign + evaluatedOperand.value; } - return ScalarOperand.create(eval(str), evaluatedOperand.base); + return ScalarExpression.create(eval(str), evaluatedOperand.base); } - getUnderlyingOperand() : ScalarOperand { - return this.operand.getUnderlyingOperand(); + getUnderlyingScalarOperand() : ScalarExpression { + return this.operand.getUnderlyingScalarOperand(); } toString(): string { diff --git a/src/expression/ScalarExpression.test.ts b/src/expression/ScalarExpression.test.ts new file mode 100644 index 0000000..d8b2f12 --- /dev/null +++ b/src/expression/ScalarExpression.test.ts @@ -0,0 +1,19 @@ +import ScalarExpression from './ScalarExpression'; + +it('parsed from dec string', () => { + var op = ScalarExpression.parse('123'); + expect(op.base).toBe('dec'); + expect(op.value).toBe(123); +}); + +it('parsed from bin string', () => { + var op = ScalarExpression.parse('0b10'); + expect(op.base).toBe('bin'); + expect(op.value).toBe(2); +}); + +it('parsed from hex string', () => { + var op = ScalarExpression.parse('0x10'); + expect(op.base).toBe('hex'); + expect(op.value).toBe(16); +}); diff --git a/src/expression/ScalarExpression.ts b/src/expression/ScalarExpression.ts new file mode 100644 index 0000000..256c7ce --- /dev/null +++ b/src/expression/ScalarExpression.ts @@ -0,0 +1,61 @@ +import {numberParser} from './numberParser'; +import { Expression as Expression } from './expression-interfaces'; +import { NumberBase } from '../core/formatter'; + +var globalId : number = 1; + + +// Represents scalar numeric value +export default class ScalarExpression implements Expression { + 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() : ScalarExpression { + return this; + } + + getUnderlyingScalarOperand() : ScalarExpression { + return this + } + + static create(value : number, base? : NumberBase) { + return new ScalarExpression(value, base || "dec"); + }; + + static parse(input: string) : ScalarExpression { + + var parsed = ScalarExpression.tryParse(input); + + if(parsed == null) { + throw new Error(input + " is not a valid number"); + } + + return parsed; + } + + static tryParse(input: string) : ScalarExpression | null { + + var parsed = numberParser.parse(input); + + if(!parsed) { + return null; + } + + return new ScalarExpression(parsed.value, parsed.base); + } + +} \ No newline at end of file diff --git a/src/expression/ScalarOperand.test.ts b/src/expression/ScalarOperand.test.ts deleted file mode 100644 index 154befe..0000000 --- a/src/expression/ScalarOperand.test.ts +++ /dev/null @@ -1,26 +0,0 @@ -import ScalarOperand, { INT_MAX_VALUE } from './ScalarOperand'; - -it('parsed from string', () => { - var op = ScalarOperand.parse('123'); - expect(op.base).toBe('dec'); - expect(op.value).toBe(123); -}); - -it('can get other kind', () => { - var op = new ScalarOperand(10, 'dec'); - expect(op.getOtherBase('hex')).toBe('dec'); - expect(op.getOtherBase('bin')).toBe('hex'); -}); - -it('negtive value binary string', () => { - expect(ScalarOperand.toBaseString(-1, 'bin')).toBe('11111111111111111111111111111111'); -}); - -it('64 bit operand binary string', () => { - expect(ScalarOperand.toBaseString(68719476735, 'bin')).toBe('111111111111111111111111111111111111'); -}); - -it('throws on negative 64 bit numbers', () => { - var bigN = -(INT_MAX_VALUE+1); - expect(() => new ScalarOperand(bigN)).toThrowError("BitwiseCmd currently doesn't support 64 bit negative numbers such as " + bigN); -}) \ No newline at end of file diff --git a/src/expression/ScalarOperand.ts b/src/expression/ScalarOperand.ts deleted file mode 100644 index 5761575..0000000 --- a/src/expression/ScalarOperand.ts +++ /dev/null @@ -1,141 +0,0 @@ -import {numberParser} from './numberParser'; -import { ExpressionInputItem, NumberBase } from './expression-interfaces'; - -var globalId : number = 1; - -const INT_MAX_VALUE = 2147483647; - -export {INT_MAX_VALUE}; - -// Represents scalar numeric value -export default class ScalarOperand implements ExpressionInputItem { - id: number; - value: number; - base: NumberBase; - lengthInBits: number; - isExpression: boolean; - - constructor(value : number, base?: NumberBase) { - this.id = globalId++; - this.value = value; - this.base = base || "dec"; - this.lengthInBits = ScalarOperand.getBitLength(this.value); - this.isExpression = false; - - if(value < 0 && !ScalarOperand.is32Bit(value)) - throw new Error("BitwiseCmd currently doesn't support 64 bit negative numbers such as " + value); - } - - getLengthInBits() { - if(this.value < 0) { - return 32; - } - return Math.floor(Math.log(this.value) / Math.log(2)) + 1; - }; - - getOtherBase(kind?: NumberBase) : NumberBase { - switch(kind || this.base) { - case 'dec': - case 'bin': - return 'hex'; - case 'hex': return 'dec'; - default : throw new Error(kind + " kind doesn't have opposite kind") - } - }; - - toString(base?: NumberBase) : string { - return ScalarOperand.toBaseString(this.value, base || this.base); - } - - toOtherKindString() : string { - return this.toString(this.getOtherBase()); - } - - toDecimalString() { - return this.toString('dec'); - } - - toHexString() { - return this.toString('hex'); - } - - toBinaryString() : string { - return this.toString('bin'); - } - - setValue(value : number) { - this.value = value; - this.lengthInBits = ScalarOperand.getBitLength(value); - } - - evaluate() : ScalarOperand { - return this; - } - - getUnderlyingOperand() : ScalarOperand { - return this - } - - static getBitLength(num : number) { - return Math.floor(Math.log(num) / Math.log(2)) + 1; - } - - static getBase(kind : string){ - switch (kind){ - case 'bin': return 2; - case 'hex': return 16; - case 'dec': return 10; - } - }; - - static create(value : number, base? : NumberBase) { - return new ScalarOperand(value, base || "dec"); - }; - - static parse(input: string) : ScalarOperand { - - var parsed = ScalarOperand.tryParse(input); - - if(parsed == null) { - throw new Error(input + " is not a valid number"); - } - - return parsed; - } - - static tryParse(input: string) : ScalarOperand | null { - - var parsed = numberParser.parse(input); - - if(!parsed) { - return null; - } - - return new ScalarOperand(parsed.value, parsed.base); - } - - static toBaseString(value : number, base : NumberBase) : string { - switch(base) { - case 'hex': - var hexVal = Math.abs(value).toString(16); - return value >= 0 ? '0x' + hexVal : '-0x' + hexVal; - case 'bin': - // >>> 0 is used to get an actual bit representation of the negative numbers - // https://stackoverflow.com/questions/5747123/strange-javascript-operator-expr-0 - const is32Bit = ScalarOperand.is32Bit(value); - return (is32Bit && value < 0 ? (value >>> 0) : value).toString(2); - case 'dec': - return value.toString(10); - default: - throw new Error("Unexpected kind: " + base) - } - }; - - static toHexString (hex : string) { - return hex.indexOf('-') === 0 ? '-0x' + hex.substr(1) : '0x' + hex; - }; - - static is32Bit(n: number) { - return Math.abs(n) <= INT_MAX_VALUE - } -} \ No newline at end of file diff --git a/src/expression/components/BitwiseExpressionModel.ts b/src/expression/components/BitwiseExpressionModel.ts index 31f0a6e..706fd1e 100644 --- a/src/expression/components/BitwiseExpressionModel.ts +++ b/src/expression/components/BitwiseExpressionModel.ts @@ -1,5 +1,7 @@ -import { ScalarOperand, ListOfNumbersExpression, BitwiseOperationExpression, ExpressionOperand } from '../expression'; -import { ExpressionInputItem, ExpressionInput } from '../expression-interfaces'; +import { ScalarExpression, ListOfNumbersExpression, BitwiseOperationExpression, OperatorExpression } from '../expression'; +import { Expression, ExpressionInput } from '../expression-interfaces'; +import calc from '../../core/calc'; +import formatter from '../../core/formatter'; type Config = { emphasizeBytes: boolean; @@ -9,7 +11,7 @@ type Config = { type ExpressionItemModel = { sign: string; css: string; - expressionItem: ExpressionInputItem; + expression: Expression; allowFlipBits: boolean; label: string; } @@ -30,28 +32,28 @@ export default class BitwiseExpressionViewModel { static buildListOfNumbers(expr : ListOfNumbersExpression, config : Config) { var model = new BitwiseExpressionViewModel(config); - expr.numbers.forEach(op => model.addOperandRow(op)); - model.maxNumberOfBits = BitwiseExpressionViewModel.getNumberOfBits(model.maxNumberOfBits, model.emphasizeBytes); + expr.numbers.forEach(op => model.addScalarRow(op)); + model.maxNumberOfBits = BitwiseExpressionViewModel.applyEmphasizeBytes(model.maxNumberOfBits, model.emphasizeBytes); return model; } static buildMultiple (expr : BitwiseOperationExpression, config : Config) { - var op = expr.expressionItems[0], - i = 0, len = expr.expressionItems.length, + var op = expr.children[0], + i = 0, len = expr.children.length, ex, m = new BitwiseExpressionViewModel(config); - var prev : ScalarOperand | null = null; + var prev : ScalarExpression | null = null; for (;i this.onBitFlipped()} />); @@ -59,7 +59,7 @@ type ExpressionRowProps = { maxNumberOfBits: number, emphasizeBytes: boolean, allowFlipBits: boolean, - expressionItem: ExpressionInputItem, + expressionItem: Expression, onBitFlipped: any } @@ -81,46 +81,48 @@ class ExpressionRow extends React.Component { allowFlipBits={allowFlipBits} onFlipBit={args => this.flipBit(args)}/> - {this.getOther()} + {this.getAlternative()} ;; } getBinaryString() : string { - var binary = this.props.expressionItem.evaluate().toBinaryString(); - return binary; + var v = this.props.expressionItem.evaluate(); + return formatter.numberToString(v.value, 'bin'); } getLabel(): string { // For expressions like |~2 // TODO: find a better way... - if(this.props.expressionItem.isExpression) { - const ex = this.props.expressionItem as ExpressionOperand; - return ex.sign + this.getLabelString(ex.getUnderlyingOperand()); + if(this.props.expressionItem.isOperator) { + const ex = this.props.expressionItem as OperatorExpression; + return ex.sign + this.getLabelString(ex.getUnderlyingScalarOperand()); } - return this.getLabelString(this.props.expressionItem.getUnderlyingOperand()); + return this.getLabelString(this.props.expressionItem.getUnderlyingScalarOperand()); } - getOther() { + getAlternative() { - if(this.props.expressionItem.isExpression) { - const ex = this.props.expressionItem as ExpressionOperand; - const op = ex.evaluate(); + if(this.props.expressionItem.isOperator) { + const ex = this.props.expressionItem as OperatorExpression; + const res = ex.evaluate(); - return op.toString(); + return formatter.numberToString(res.value, res.base); } - return this.props.expressionItem.evaluate().toOtherKindString(); + const v = this.props.expressionItem.evaluate(); + const altBase = formatter.getAlternativeBase(v.base); + return formatter.numberToString(v.value, altBase); } - getLabelString (op: ScalarOperand) : string { - return op.toString(op.base == 'bin' ? 'dec' : op.base); + getLabelString (op: ScalarExpression) : string { + return formatter.numberToString(op.value, op.base == 'bin' ? 'dec' : op.base); } flipBit(args: FlipBitEventArg) { - const op = this.props.expressionItem.getUnderlyingOperand(); + const op = this.props.expressionItem.getUnderlyingScalarOperand(); const { index, binaryString } = args; var arr = binaryString.split(''); diff --git a/src/expression/expression-interfaces.ts b/src/expression/expression-interfaces.ts index 106dba0..f3b65f9 100644 --- a/src/expression/expression-interfaces.ts +++ b/src/expression/expression-interfaces.ts @@ -1,17 +1,14 @@ -import { ScalarOperand } from "./expression"; +import { ScalarExpression } from "./expression"; export interface ExpressionInput { expressionString: string; } -export interface ExpressionInputItem +export interface Expression { - isExpression: boolean; - getUnderlyingOperand: () => ScalarOperand; - evaluate(operand? : ScalarOperand): ScalarOperand; + isOperator: boolean; + getUnderlyingScalarOperand: () => ScalarExpression; + evaluate(operand? : ScalarExpression): ScalarExpression; } -export type NumberBase = 'dec' | 'hex' | 'bin'; - - diff --git a/src/expression/expression.test.ts b/src/expression/expression.test.ts index c88dc08..98c8781 100644 --- a/src/expression/expression.test.ts +++ b/src/expression/expression.test.ts @@ -1,5 +1,5 @@ import { OperationCanceledException } from "typescript"; -import { parser, ListOfNumbersExpression, BitwiseOperationExpression, ScalarOperand, ExpressionOperand } from "./expression"; +import { parser, ListOfNumbersExpression, BitwiseOperationExpression, ScalarExpression, OperatorExpression } from "./expression"; describe("expression parser", () => { @@ -26,32 +26,32 @@ describe("expression parser", () => { expect(actual).toBeInstanceOf(BitwiseOperationExpression); const expr = actual as BitwiseOperationExpression; - expect(expr.expressionItems[0].getUnderlyingOperand().value).toBe(305419896); - expect(expr.expressionItems[1].getUnderlyingOperand().value).toBe(2863311360); + expect(expr.children[0].getUnderlyingScalarOperand().value).toBe(305419896); + expect(expr.children[1].getUnderlyingScalarOperand().value).toBe(2863311360); }) it("pares multiple operand expression", () => { const result = parser.parse("1^2") as BitwiseOperationExpression; - expect(result.expressionItems.length).toBe(2); + expect(result.children.length).toBe(2); - const first = result.expressionItems[0]; - const second = result.expressionItems[1]; + const first = result.children[0]; + const second = result.children[1]; - expect(first).toBeInstanceOf(ScalarOperand); + expect(first).toBeInstanceOf(ScalarExpression); - expect((first as ScalarOperand).value).toBe(1); + expect((first as ScalarExpression).value).toBe(1); - expect(second).toBeInstanceOf(ExpressionOperand); - var secondOp = second as ExpressionOperand; + expect(second).toBeInstanceOf(OperatorExpression); + var secondOp = second as OperatorExpression; expect(secondOp.sign).toBe("^"); - expect(secondOp.operand).toBeInstanceOf(ScalarOperand); - var childOp = secondOp.operand as ScalarOperand; + expect(secondOp.operand).toBeInstanceOf(ScalarExpression); + var childOp = secondOp.operand as ScalarExpression; expect(childOp.value).toBe(2); }); it("bug", () => { var result = parser.parse("1|~2") as BitwiseOperationExpression; - expect(result.expressionItems.length).toBe(2); + expect(result.children.length).toBe(2); }); }) \ No newline at end of file diff --git a/src/expression/expression.ts b/src/expression/expression.ts index 4897697..62080f4 100644 --- a/src/expression/expression.ts +++ b/src/expression/expression.ts @@ -1,11 +1,12 @@ -import ScalarOperand from './ScalarOperand'; -import ExpressionOperand from './ExpressionOperand' +import ScalarExpression from './ScalarExpression'; +import OperatorExpression from './OperatorExpression' import ListOfNumbersExpression from './ListOfNumbersExpression'; import BitwiseOperationExpression from './BitwiseOperationExpression'; -import { ExpressionInput, ExpressionInputItem, NumberBase } from './expression-interfaces'; +import { ExpressionInput, Expression } from './expression-interfaces'; +import { NumberBase } from '../core/formatter'; -export { default as ScalarOperand } from './ScalarOperand'; -export { default as ExpressionOperand } from './ExpressionOperand'; +export { default as ScalarExpression } from './ScalarExpression'; +export { default as OperatorExpression } from './OperatorExpression'; export { default as ListOfNumbersExpression } from './ListOfNumbersExpression'; export { default as BitwiseOperationExpression } from './BitwiseOperationExpression'; @@ -46,12 +47,12 @@ class ExpressionParser { return null; }; - parseOperand (input : string) : ScalarOperand { - return ScalarOperand.parse(input); + parseOperand (input : string) : ScalarExpression { + return ScalarExpression.parse(input); }; - createOperand (number : number, base : NumberBase) : ScalarOperand { - return ScalarOperand.create(number, base); + createOperand (number : number, base : NumberBase) : ScalarExpression { + return ScalarExpression.create(number, base); }; addFactory (factory: IExpressionParserFactory) { @@ -69,7 +70,7 @@ class ListOfNumbersExpressionFactory implements IExpressionParserFactory return input.split(' ') .filter(p => p.length > 0) - .map(p => ScalarOperand.tryParse(p)) + .map(p => ScalarExpression.tryParse(p)) .filter(n => n == null) .length == 0; }; @@ -78,7 +79,7 @@ class ListOfNumbersExpressionFactory implements IExpressionParserFactory const numbers = input.split(' ') .filter(p => p.length > 0) - .map(m => ScalarOperand.parse(m)); + .map(m => ScalarExpression.parse(m)); return new ListOfNumbersExpression(input, numbers); } @@ -99,7 +100,7 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory { }; create (input: string) : ExpressionInput { - var m, operands : ExpressionInputItem[] = [], + var m, operands : Expression[] = [], normalizedString = this.normalizeString(input); while ((m = this.regex.exec(normalizedString)) != null) { @@ -109,23 +110,23 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory { return new BitwiseOperationExpression(normalizedString, operands) }; - parseMatch (m:any): ExpressionInputItem { + parseMatch (m:any): Expression { var input = m[0], sign = m[1], num = m[2]; var parsed = null; if(num.indexOf('~') == 0) { - parsed = new ExpressionOperand(num, ScalarOperand.parse(num.substring(1)), '~'); + parsed = new OperatorExpression(num, ScalarExpression.parse(num.substring(1)), '~'); } else { - parsed = ScalarOperand.parse(num); + parsed = ScalarExpression.parse(num); } if(sign == null) { - return parsed as ExpressionOperand; + return parsed as OperatorExpression; } else { - return new ExpressionOperand(input, parsed as ScalarOperand, sign); + return new OperatorExpression(input, parsed as ScalarExpression, sign); } }; diff --git a/src/expression/numberParser.ts b/src/expression/numberParser.ts index 1f0af70..13c3d9b 100644 --- a/src/expression/numberParser.ts +++ b/src/expression/numberParser.ts @@ -1,4 +1,4 @@ -import { NumberBase } from "./expression-interfaces"; +import { NumberBase } from "../core/formatter"; const decimalRegex = /^-?\d+$/; const hexRegex = /^-?0x[0-9,a-f]+$/i; diff --git a/src/networking/components/IpAddressView.tsx b/src/networking/components/IpAddressView.tsx index e87ab1c..68df2c3 100644 --- a/src/networking/components/IpAddressView.tsx +++ b/src/networking/components/IpAddressView.tsx @@ -44,7 +44,7 @@ export class IpAddressView extends React.Component }; function fmt(num: number) : string { - return formatter.padLeft(formatter.formatString(num, 'bin'), 8, '0'); + return formatter.padLeft(formatter.numberToString(num, 'bin'), 8, '0'); } export default IpAddressView; \ No newline at end of file diff --git a/src/shell/components/HelpResultView.tsx b/src/shell/components/HelpResultView.tsx index 5fe13d8..1ce38a6 100644 --- a/src/shell/components/HelpResultView.tsx +++ b/src/shell/components/HelpResultView.tsx @@ -1,8 +1,6 @@ import React from 'react'; import CommandLink from '../../core/components/CommandLink'; import './HelpResultView.css'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faInfoCircle } from '@fortawesome/free-solid-svg-icons'; function HelpResultView() { @@ -14,11 +12,7 @@ function HelpResultView() {
  • — type bitwise expression to see the result in binary
  • — type one or more numbers to see their binary representations
  • -
-

- - Negative 64-bit numbers (lower than int32 min value of -2,147,483,647) are not supported. -

+
IP Address & Networking Commands