mirror of
https://github.com/BorysLevytskyi/BitwiseCmd.git
synced 2025-12-10 06:52:05 +01:00
Allow to switch a number sign (#47)
This commit is contained in:
37
src/core/calc.compatibility.test.ts
Normal file
37
src/core/calc.compatibility.test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import calc from "./calc";
|
||||
|
||||
describe("engine comparison", () => {
|
||||
|
||||
it("NOT same as in node", () => {
|
||||
|
||||
for(var i = -100; i<100;i++) {
|
||||
const expected = bin(~i);
|
||||
const actual = calc.engine.not(bin(i));
|
||||
expect(`${i} is ${actual}`).toBe(`${i} is ${(expected)}`);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
const actual = calc.engine.or(bin(x), bin(y));
|
||||
|
||||
expect(`${x} is ${actual}`).toBe(`${x} is ${(expected)}`);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
const actual = calc.engine.and(bin(x), bin(y));
|
||||
|
||||
expect(`${x} is ${actual}`).toBe(`${x} is ${(expected)}`);
|
||||
}
|
||||
});
|
||||
|
||||
function bin(i: number) {
|
||||
return (i >= 0 ? i : i >>> 0).toString(2).padStart(32, i<0 ? '1' : '0');
|
||||
}
|
||||
});
|
||||
@@ -236,41 +236,4 @@ describe("calc.engine.", () => {
|
||||
expect(calc.engine.applyTwosComplement("10101100")).toBe("01010100");
|
||||
expect(calc.engine.applyTwosComplement("01010100")).toBe("10101100"); // reverse
|
||||
});
|
||||
})
|
||||
|
||||
describe("engine comparison", () => {
|
||||
|
||||
it("NOT same as in node", () => {
|
||||
|
||||
for(var i = -100; i<100;i++) {
|
||||
const expected = bin(~i);
|
||||
const actual = calc.engine.not(bin(i));
|
||||
expect(`${i} is ${actual}`).toBe(`${i} is ${(expected)}`);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
const actual = calc.engine.or(bin(x), bin(y));
|
||||
|
||||
expect(`${x} is ${actual}`).toBe(`${x} is ${(expected)}`);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
const actual = calc.engine.and(bin(x), bin(y));
|
||||
|
||||
expect(`${x} is ${actual}`).toBe(`${x} is ${(expected)}`);
|
||||
}
|
||||
});
|
||||
|
||||
function bin(i: number) {
|
||||
return (i >= 0 ? i : i >>> 0).toString(2).padStart(32, i<0 ? '1' : '0');
|
||||
}
|
||||
|
||||
});
|
||||
@@ -26,6 +26,7 @@ export default {
|
||||
},
|
||||
|
||||
operation (op1: Integer, operator: string, op2 : Integer) : Integer {
|
||||
|
||||
switch(operator) {
|
||||
case ">>": return this.rshift(op1, op2.value);
|
||||
case ">>>": return this.urshift(op1, op2.value);
|
||||
@@ -246,7 +247,7 @@ function nextPowOfTwo(num: number) : number {
|
||||
}
|
||||
|
||||
function equalizeSize(n1: Integer, n2: Integer) : [Integer, Integer] {
|
||||
|
||||
console.log('equalizeSize()', new Error().stack);
|
||||
if(n1.maxBitSize == n2.maxBitSize)
|
||||
{
|
||||
if(n1.signed === n2.signed) return [n1,n2];
|
||||
|
||||
@@ -51,8 +51,6 @@ export default class BinaryStringView extends React.Component<BinaryStringViewPr
|
||||
|
||||
const disableHighlight = this.props.disableHighlight || false;
|
||||
|
||||
let signBitIndex = -1;
|
||||
|
||||
return bitChars.map((c, i) => {
|
||||
|
||||
var className = c == '1' ? `one${css}` : `zero${css}`;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Expression, ExpressionElement } from "./expression-interfaces";
|
||||
|
||||
export default class BitwiseOperationExpression implements Expression {
|
||||
export default class BitwiseOperation implements Expression {
|
||||
|
||||
expressionString: string;
|
||||
children: ExpressionElement[];
|
||||
@@ -1,5 +1,5 @@
|
||||
import BitwiseOperator from "./BitwiseOperator";
|
||||
import { ScalarValue } from "./expression";
|
||||
import Operator from "./Operator";
|
||||
import { Operand } from "./expression";
|
||||
import { ExpressionElement } from "./expression-interfaces";
|
||||
import { type } from "os";
|
||||
import { InputType } from "zlib";
|
||||
|
||||
12
src/expression/ListOfNumbers.test.ts
Normal file
12
src/expression/ListOfNumbers.test.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import Operand from "./Operand";
|
||||
import ListOfNumbers from "./ListOfNumbers";
|
||||
import { numberParser } from "./numberParser";
|
||||
|
||||
it('calculates max bits length', () => {
|
||||
const v1 = new Operand(numberParser.parse("10").value);
|
||||
const v2 = new Operand(numberParser.parse("0xabef").value);
|
||||
const v3 = new Operand(numberParser.parse("0b01010").value);
|
||||
|
||||
var expr = new ListOfNumbers("10 0xabef 0b01010", [v1, v2, v3])
|
||||
expect(expr.maxBitsLength).toBe(16);
|
||||
});
|
||||
@@ -1,13 +1,13 @@
|
||||
import calc from "../core/calc";
|
||||
import ScalarValue from "./ScalarValue";
|
||||
import Operand from "./Operand";
|
||||
import { Expression, ExpressionElement } from "./expression-interfaces";
|
||||
|
||||
export default class ListOfNumbersExpression implements Expression {
|
||||
children: ScalarValue[];
|
||||
export default class ListOfNumbers implements Expression {
|
||||
children: Operand[];
|
||||
expressionString: string;
|
||||
maxBitsLength: number;
|
||||
|
||||
constructor(expressionString: string, numbers: ScalarValue[]) {
|
||||
constructor(expressionString: string, numbers: Operand[]) {
|
||||
this.expressionString = expressionString;
|
||||
this.children = numbers;
|
||||
this.maxBitsLength = numbers.map(n => calc.numberOfBitsDisplayed(n.value)).reduce((n , c) => n >= c ? n : c, 0);
|
||||
@@ -1,12 +0,0 @@
|
||||
import ScalarValue from "./ScalarValue";
|
||||
import ListOfNumbersExpression from "./ListOfNumbersExpression";
|
||||
import { numberParser } from "./numberParser";
|
||||
|
||||
it('calculates max bits length', () => {
|
||||
const v1 = new ScalarValue(numberParser.parse("10").value);
|
||||
const v2 = new ScalarValue(numberParser.parse("0xabef").value);
|
||||
const v3 = new ScalarValue(numberParser.parse("0b01010").value);
|
||||
|
||||
var expr = new ListOfNumbersExpression("10 0xabef 0b01010", [v1, v2, v3])
|
||||
expect(expr.maxBitsLength).toBe(16);
|
||||
});
|
||||
@@ -1,10 +1,10 @@
|
||||
import { INT32_MAX_VALUE } from "../core/const";
|
||||
import ScalarValue from "./ScalarValue";
|
||||
import Operand from "./Operand";
|
||||
|
||||
|
||||
it('converts numbers to bigint', () => {
|
||||
const int32 = new ScalarValue(INT32_MAX_VALUE);
|
||||
const int64 = new ScalarValue(BigInt(INT32_MAX_VALUE+1));
|
||||
const int32 = new Operand(INT32_MAX_VALUE);
|
||||
const int64 = new Operand(BigInt(INT32_MAX_VALUE+1));
|
||||
|
||||
expect(int32.value.maxBitSize).toBe(32);
|
||||
expect(int64.value.maxBitSize).toBe(64);
|
||||
@@ -8,7 +8,7 @@ var globalId : number = 1;
|
||||
|
||||
|
||||
// Represents scalar numeric value
|
||||
export default class ScalarValue implements ExpressionElement {
|
||||
export default class Operand implements ExpressionElement {
|
||||
id: number;
|
||||
value: Integer;
|
||||
base: NumberBase;
|
||||
@@ -20,7 +20,7 @@ export default class ScalarValue implements ExpressionElement {
|
||||
if(!isInteger(value))
|
||||
value = asInteger(value);
|
||||
|
||||
ScalarValue.validateSupported(value);
|
||||
Operand.validateSupported(value);
|
||||
|
||||
this.id = globalId++;
|
||||
this.value = value as Integer;
|
||||
@@ -29,21 +29,21 @@ export default class ScalarValue implements ExpressionElement {
|
||||
this.label = '';
|
||||
}
|
||||
|
||||
setValue(value : Integer) : ScalarValue {
|
||||
setValue(value : Integer) : Operand {
|
||||
this.value = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
setLabel(label : string) : ScalarValue {
|
||||
setLabel(label : string) : Operand {
|
||||
this.label = label;
|
||||
return this;
|
||||
}
|
||||
|
||||
evaluate() : ScalarValue {
|
||||
evaluate() : Operand {
|
||||
return this;
|
||||
}
|
||||
|
||||
getUnderlyingScalarOperand() : ScalarValue {
|
||||
getUnderlyingOperand() : Operand {
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import ScalarValue from "./ScalarValue";
|
||||
import BitwiseOperator from './BitwiseOperator';
|
||||
import Operand from "./Operand";
|
||||
import Operator from './Operator';
|
||||
|
||||
it('can apply ~ operand', () => {
|
||||
var op = new ScalarValue(10, 'dec');
|
||||
var expr = new BitwiseOperator(op, "~");
|
||||
var op = new Operand(10, 'dec');
|
||||
var expr = new Operator(op, "~");
|
||||
|
||||
var result = expr.evaluate();
|
||||
expect(result.value.num()).toBe(-11);
|
||||
@@ -11,9 +11,9 @@ it('can apply ~ operand', () => {
|
||||
});
|
||||
|
||||
it('can apply & operand', () => {
|
||||
var op1 = new ScalarValue(3, 'dec');
|
||||
var op2 = new ScalarValue(4, 'dec');
|
||||
var expr = new BitwiseOperator(op1, "|");
|
||||
var op1 = new Operand(3, 'dec');
|
||||
var op2 = new Operand(4, 'dec');
|
||||
var expr = new Operator(op1, "|");
|
||||
var result = expr.evaluate(op2);
|
||||
expect(result.value.num()).toBe(7);
|
||||
expect(result.base).toBe('dec');
|
||||
@@ -1,8 +1,8 @@
|
||||
import calc from '../core/calc';
|
||||
import ScalarValue from './ScalarValue';
|
||||
import Operand from './Operand';
|
||||
import { ExpressionElement } from './expression-interfaces';
|
||||
|
||||
export default class BitwiseOperator implements ExpressionElement {
|
||||
export default class Operator implements ExpressionElement {
|
||||
operand: ExpressionElement;
|
||||
operator: string;
|
||||
isOperator: boolean;
|
||||
@@ -18,9 +18,9 @@ export default class BitwiseOperator implements ExpressionElement {
|
||||
this.isNotExpression = this.operator === '~';
|
||||
}
|
||||
|
||||
evaluate(operand?: ScalarValue) : ScalarValue {
|
||||
evaluate(operand?: Operand) : Operand {
|
||||
|
||||
if (operand instanceof BitwiseOperator)
|
||||
if (operand instanceof Operator)
|
||||
throw new Error('operand must be scalar value');
|
||||
|
||||
if( this.operator != "~" && operand == null)
|
||||
@@ -29,12 +29,12 @@ export default class BitwiseOperator implements ExpressionElement {
|
||||
var evaluatedOperand = this.operand.evaluate();
|
||||
|
||||
return this.operator == "~"
|
||||
? applyNotOperator(this.operand.getUnderlyingScalarOperand())
|
||||
? applyNotOperator(this.operand.getUnderlyingOperand())
|
||||
: applyOperator(operand!, this.operator, evaluatedOperand);
|
||||
}
|
||||
|
||||
getUnderlyingScalarOperand() : ScalarValue {
|
||||
return this.operand.getUnderlyingScalarOperand();
|
||||
getUnderlyingOperand() : Operand {
|
||||
return this.operand.getUnderlyingOperand();
|
||||
}
|
||||
|
||||
toString(): string {
|
||||
@@ -42,13 +42,14 @@ export default class BitwiseOperator implements ExpressionElement {
|
||||
}
|
||||
}
|
||||
|
||||
function applyNotOperator(operand: ScalarValue) : ScalarValue {
|
||||
return new ScalarValue(calc.not(operand.value), operand.base);
|
||||
function applyNotOperator(operand: Operand) : Operand {
|
||||
return new Operand(calc.not(operand.value), operand.base);
|
||||
}
|
||||
|
||||
function applyOperator(op1 : ScalarValue, operator: string, op2 : ScalarValue) : ScalarValue {
|
||||
function applyOperator(op1 : Operand, operator: string, op2 : Operand) : Operand {
|
||||
|
||||
const isShift = /<|>/.test(operator);
|
||||
|
||||
if(!isShift)
|
||||
{
|
||||
if(op1.value.maxBitSize == op2.value.maxBitSize && op1.value.signed != op2.value.signed)
|
||||
@@ -57,11 +58,13 @@ function applyOperator(op1 : ScalarValue, operator: string, op2 : ScalarValue) :
|
||||
equalizeSize(op1, op2);
|
||||
}
|
||||
|
||||
console.log(op1.value, operator, op2.value);
|
||||
const result = calc.operation(op1.value, operator, op2.value);
|
||||
return new ScalarValue(result, op2.base);
|
||||
console.log('=', result);
|
||||
return new Operand(result, op2.base);
|
||||
}
|
||||
|
||||
function equalizeSize(op1: ScalarValue, op2: ScalarValue) {
|
||||
function equalizeSize(op1: Operand, op2: Operand) {
|
||||
|
||||
const n1 = op1.value;
|
||||
const n2 = op2.value;
|
||||
@@ -3,7 +3,7 @@ import formatter from '../../core/formatter';
|
||||
import BinaryStringView, { FlipBitEventArg } from '../../core/components/BinaryString';
|
||||
import BitwiseResultViewModel from './BitwiseResultViewModel';
|
||||
import { Expression, ExpressionElement } from '../expression-interfaces';
|
||||
import { BitwiseOperator, ScalarValue } from '../expression';
|
||||
import { Operator, Operand, ListOfNumbers } from '../expression';
|
||||
import calc from '../../core/calc';
|
||||
|
||||
type BitwiseResultViewProps = {
|
||||
@@ -27,18 +27,18 @@ export default class BitwiseResultView extends React.Component<BitwiseResultView
|
||||
render() {
|
||||
|
||||
let model : BitwiseResultViewModel | null = null
|
||||
const isListOfNumbers = this.props.expression instanceof ListOfNumbers;
|
||||
|
||||
try
|
||||
{
|
||||
{
|
||||
model = BitwiseResultViewModel.createModel(this.props.expression, this.props.emphasizeBytes);
|
||||
}
|
||||
catch(err) {
|
||||
}
|
||||
catch(err) {
|
||||
const text = (err as any).message;
|
||||
return <div className='error'>Error: {text}</div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var rows = this.getRows(model!);
|
||||
var rows = this.getRows(model!, isListOfNumbers);
|
||||
|
||||
return <table className="expression">
|
||||
<tbody>
|
||||
@@ -47,7 +47,7 @@ export default class BitwiseResultView extends React.Component<BitwiseResultView
|
||||
</table>
|
||||
}
|
||||
|
||||
getRows(model: BitwiseResultViewModel): JSX.Element[] {
|
||||
getRows(model: BitwiseResultViewModel, allowSignChange : boolean): JSX.Element[] {
|
||||
|
||||
this.maxSeenLengthNumberOfBits = Math.max(model.maxNumberOfBits, this.maxSeenLengthNumberOfBits);
|
||||
|
||||
@@ -58,6 +58,7 @@ export default class BitwiseResultView extends React.Component<BitwiseResultView
|
||||
css={itm.css}
|
||||
bitSize={itm.maxBitSize}
|
||||
allowFlipBits={itm.allowFlipBits}
|
||||
allowSignChange={allowSignChange}
|
||||
expressionItem={itm.expression}
|
||||
emphasizeBytes={this.props.emphasizeBytes}
|
||||
maxNumberOfBits={this.maxSeenLengthNumberOfBits}
|
||||
@@ -77,15 +78,20 @@ type ExpressionRowProps = {
|
||||
maxNumberOfBits: number,
|
||||
emphasizeBytes: boolean,
|
||||
allowFlipBits: boolean,
|
||||
allowSignChange: boolean,
|
||||
expressionItem: ExpressionElement,
|
||||
onBitFlipped: any
|
||||
}
|
||||
|
||||
class ExpressionRow extends React.Component<ExpressionRowProps> {
|
||||
|
||||
infoWasShown: boolean = false;
|
||||
|
||||
constructor(props: ExpressionRowProps) {
|
||||
super(props);
|
||||
this.state = { operand: null };
|
||||
}
|
||||
|
||||
render() {
|
||||
const { sign, css, maxNumberOfBits, emphasizeBytes, allowFlipBits } = this.props;
|
||||
const scalar = this.props.expressionItem.evaluate();
|
||||
@@ -113,17 +119,17 @@ 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 BitwiseOperator;
|
||||
return ex.operator + this.getLabelString(ex.getUnderlyingScalarOperand());
|
||||
const ex = this.props.expressionItem as Operator;
|
||||
return ex.operator + this.getLabelString(ex.getUnderlyingOperand());
|
||||
}
|
||||
|
||||
return this.getLabelString(this.props.expressionItem.getUnderlyingScalarOperand());
|
||||
return this.getLabelString(this.props.expressionItem.getUnderlyingOperand());
|
||||
}
|
||||
|
||||
getAlternative() {
|
||||
|
||||
if (this.props.expressionItem.isOperator) {
|
||||
const ex = this.props.expressionItem as BitwiseOperator;
|
||||
const ex = this.props.expressionItem as Operator;
|
||||
const res = ex.evaluate();
|
||||
|
||||
return formatter.numberToString(res.value, res.base);
|
||||
@@ -134,13 +140,13 @@ class ExpressionRow extends React.Component<ExpressionRowProps> {
|
||||
return formatter.numberToString(v.value, altBase);
|
||||
}
|
||||
|
||||
getLabelString(op: ScalarValue): string {
|
||||
getLabelString(op: Operand): string {
|
||||
return formatter.numberToString(op.value, op.base == 'bin' ? 'dec' : op.base);
|
||||
}
|
||||
|
||||
flipBit(args: FlipBitEventArg) {
|
||||
|
||||
const op = this.props.expressionItem.getUnderlyingScalarOperand();
|
||||
const op = this.props.expressionItem.getUnderlyingOperand();
|
||||
const { bitIndex: index, binaryStringLength: totalLength } = args;
|
||||
|
||||
const maxBitSize = op.value.maxBitSize;
|
||||
@@ -155,27 +161,47 @@ class ExpressionRow extends React.Component<ExpressionRowProps> {
|
||||
this.props.onBitFlipped();
|
||||
}
|
||||
|
||||
onChangeSign () {
|
||||
var op = this.props.expressionItem.getUnderlyingOperand();
|
||||
op.setValue(op.value.signed ? op.value.toUnsigned() : op.value.toSigned());
|
||||
this.forceUpdate();
|
||||
}
|
||||
|
||||
getInfo(maxNumberOfBits:number) {
|
||||
var op = this.props.expressionItem.getUnderlyingScalarOperand();
|
||||
|
||||
const op = this.props.expressionItem.getUnderlyingOperand();
|
||||
const allBitsDisplayed = op.value.maxBitSize != 32 || op.value.maxBitSize <= maxNumberOfBits;
|
||||
const { allowSignChange } = this.props;
|
||||
const hasLabel = op.label.length > 0;
|
||||
|
||||
if((op.value.maxBitSize != 32 || op.value.maxBitSize <= maxNumberOfBits) || op.label.length > 0)
|
||||
if(!allBitsDisplayed && !hasLabel && !this.infoWasShown)
|
||||
return null;
|
||||
|
||||
this.infoWasShown = true;
|
||||
|
||||
const children = [];
|
||||
let title = `BitwiseCmd treats this number as ${op.value.maxBitSize}-bit integer`;
|
||||
let text = `${op.value.maxBitSize}-bit `;
|
||||
const signedStr = op.value.signed ? 'signed' : 'unsigned';
|
||||
const signedOther = op.value.signed ? 'usigned' : 'signed';
|
||||
const signedTitle = `Click to change to ${signedOther} preserving the same bits`;
|
||||
|
||||
if(op.label.length > 0)
|
||||
{
|
||||
let title = `BitwiseCmd treats this number as ${op.value.maxBitSize}-bit integer`;
|
||||
let text = `${op.value.maxBitSize}-bit `;
|
||||
|
||||
if(!op.value.signed)
|
||||
text += " unsigned ";
|
||||
|
||||
if(op.label.length > 0)
|
||||
{
|
||||
text += " (converted)";
|
||||
title += ". This number was converted to facilitate bitwise operation with an operand of a different type";
|
||||
}
|
||||
|
||||
return <span title={title} style={{cursor:"help"}}>{text}</span>;
|
||||
text += " (converted)";
|
||||
title += ". This number was converted to facilitate bitwise operation with an operand of a different type";
|
||||
}
|
||||
|
||||
return null;
|
||||
|
||||
children.push(<span title={title} style={{cursor:"help"}}>{text.trim()}</span>);
|
||||
|
||||
if(allBitsDisplayed)
|
||||
{
|
||||
if(allowSignChange)
|
||||
children.push(<button className='accent1' title={signedTitle} onClick={() => this.onChangeSign()}>{signedStr}</button>);
|
||||
else if(!op.value.signed)
|
||||
children.push(<span className='accent1'> {signedStr}</span>)
|
||||
}
|
||||
|
||||
return <React.Fragment>{children}</React.Fragment>
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ScalarValue, ListOfNumbersExpression, BitwiseOperationExpression, BitwiseOperator } from '../expression';
|
||||
import { Operand, ListOfNumbers, BitwiseOperation, Operator } from '../expression';
|
||||
import { ExpressionElement, Expression } from '../expression-interfaces';
|
||||
import calc from '../../core/calc';
|
||||
import formatter from '../../core/formatter';
|
||||
@@ -31,30 +31,30 @@ export default class BitwiseResultViewModel {
|
||||
this.allowFlipBits = allowFlipBits === true;
|
||||
}
|
||||
|
||||
static buildListOfNumbers(expr : ListOfNumbersExpression, config : Config) {
|
||||
static buildListOfNumbers(expr : ListOfNumbers, config : Config) {
|
||||
var model = new BitwiseResultViewModel(config);
|
||||
expr.children.forEach(op => model.addScalarRow(op));
|
||||
model.maxNumberOfBits = BitwiseResultViewModel.applyEmphasizeBytes(model.maxNumberOfBits, model.emphasizeBytes);
|
||||
return model;
|
||||
}
|
||||
|
||||
static buildBitwiseOperation (expr : BitwiseOperationExpression, config : Config) {
|
||||
static buildBitwiseOperation (expr : BitwiseOperation, config : Config) {
|
||||
|
||||
var op = expr.children[0],
|
||||
i = 0, len = expr.children.length,
|
||||
ex, m = new BitwiseResultViewModel(config);
|
||||
|
||||
var prev : ScalarValue | null = null;
|
||||
var prev : Operand | null = null;
|
||||
|
||||
for (;i<len;i++) {
|
||||
ex = expr.children[i];
|
||||
if(ex instanceof ScalarValue) {
|
||||
if(ex instanceof Operand) {
|
||||
m.addScalarRow(ex);
|
||||
prev = ex;
|
||||
continue;
|
||||
}
|
||||
|
||||
var eo = ex as BitwiseOperator;
|
||||
var eo = ex as Operator;
|
||||
|
||||
// If it a single NOT expression
|
||||
if(eo.isNotExpression) {
|
||||
@@ -64,11 +64,11 @@ export default class BitwiseResultViewModel {
|
||||
prev = notResult;
|
||||
}
|
||||
else if(eo.isShiftExpression){
|
||||
prev = eo.evaluate(prev as ScalarValue);
|
||||
prev = eo.evaluate(prev as Operand);
|
||||
m.addShiftExpressionResultRow(eo, prev);
|
||||
} else {
|
||||
|
||||
prev = eo.evaluate(prev as ScalarValue);
|
||||
prev = eo.evaluate(prev as Operand);
|
||||
m.addOperatorRow(eo);
|
||||
m.addExpressionResultRow(prev);
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export default class BitwiseResultViewModel {
|
||||
return m;
|
||||
};
|
||||
|
||||
addScalarRow(expr: ScalarValue) {
|
||||
addScalarRow(expr: Operand) {
|
||||
const bits = calc.numberOfBitsDisplayed(expr.value);
|
||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||
this.items.push({
|
||||
@@ -91,9 +91,9 @@ export default class BitwiseResultViewModel {
|
||||
});
|
||||
};
|
||||
|
||||
addOperatorRow(expr: BitwiseOperator) {
|
||||
addOperatorRow(expr: Operator) {
|
||||
|
||||
const resultNumber = expr.isNotExpression ? expr.evaluate() : expr.getUnderlyingScalarOperand();
|
||||
const resultNumber = expr.isNotExpression ? expr.evaluate() : expr.getUnderlyingOperand();
|
||||
const bits = calc.numberOfBitsDisplayed(resultNumber.value);
|
||||
|
||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||
@@ -108,10 +108,10 @@ export default class BitwiseResultViewModel {
|
||||
});
|
||||
};
|
||||
|
||||
addShiftExpressionResultRow(expr : BitwiseOperator, resultExpr : ScalarValue) {
|
||||
addShiftExpressionResultRow(expr : Operator, resultExpr : Operand) {
|
||||
const bits = calc.numberOfBitsDisplayed(resultExpr.value);
|
||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||
const child = expr.operand.getUnderlyingScalarOperand();
|
||||
const child = expr.operand.getUnderlyingOperand();
|
||||
this.items.push({
|
||||
sign: expr.operator + formatter.numberToString(child.value, child.base),
|
||||
css: 'expression-result',
|
||||
@@ -122,7 +122,7 @@ export default class BitwiseResultViewModel {
|
||||
});
|
||||
};
|
||||
|
||||
addExpressionResultRow(expr : ScalarValue) {
|
||||
addExpressionResultRow(expr : Operand) {
|
||||
const bits = calc.numberOfBitsDisplayed(expr.value);
|
||||
this.maxNumberOfBits = Math.max(bits, this.maxNumberOfBits);
|
||||
this.items.push({
|
||||
@@ -135,7 +135,7 @@ export default class BitwiseResultViewModel {
|
||||
});
|
||||
};
|
||||
|
||||
getLabel (op: ScalarValue) : string {
|
||||
getLabel (op: Operand) : string {
|
||||
|
||||
return formatter.numberToString(op.value, op.base === 'bin' ? 'dec' : op.base)
|
||||
}
|
||||
@@ -155,14 +155,15 @@ export default class BitwiseResultViewModel {
|
||||
};
|
||||
|
||||
static createModel(expr : Expression, emphasizeBytes: boolean) : BitwiseResultViewModel {
|
||||
if(expr instanceof ListOfNumbersExpression) {
|
||||
|
||||
if(expr instanceof ListOfNumbers) {
|
||||
return BitwiseResultViewModel.buildListOfNumbers(expr, {
|
||||
emphasizeBytes: emphasizeBytes,
|
||||
allowFlipBits: true
|
||||
});
|
||||
}
|
||||
|
||||
if(expr instanceof BitwiseOperationExpression) {
|
||||
if(expr instanceof BitwiseOperation) {
|
||||
return BitwiseResultViewModel.buildBitwiseOperation(expr, {
|
||||
emphasizeBytes: emphasizeBytes,
|
||||
allowFlipBits: true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ScalarValue } from "./expression";
|
||||
import { Operand } from "./expression";
|
||||
|
||||
export interface Expression
|
||||
{
|
||||
@@ -8,7 +8,7 @@ export interface Expression
|
||||
export interface ExpressionElement
|
||||
{
|
||||
isOperator: boolean;
|
||||
getUnderlyingScalarOperand: () => ScalarValue;
|
||||
evaluate(operand? : ScalarValue): ScalarValue;
|
||||
getUnderlyingOperand: () => Operand;
|
||||
evaluate(operand? : Operand): Operand;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { parser, ListOfNumbersExpression, BitwiseOperationExpression, ScalarValue, BitwiseOperator } from "./expression";
|
||||
import { parser, ListOfNumbers, BitwiseOperation, Operand, Operator } from "./expression";
|
||||
import { random } from "../core/utils";
|
||||
import { INT32_MAX_VALUE } from "../core/const";
|
||||
|
||||
@@ -6,7 +6,7 @@ describe("expression parser", () => {
|
||||
|
||||
it("parses list of number expression", () => {
|
||||
var result = parser.parse("1 2 3");
|
||||
expect(result).toBeInstanceOf(ListOfNumbersExpression);
|
||||
expect(result).toBeInstanceOf(ListOfNumbers);
|
||||
});
|
||||
|
||||
it("doesn't list of numbers in case of bad numbers", () => {
|
||||
@@ -15,43 +15,43 @@ describe("expression parser", () => {
|
||||
});
|
||||
|
||||
it("pares different operations expressions", () => {
|
||||
expect(parser.parse("~1")).toBeInstanceOf(BitwiseOperationExpression);
|
||||
expect(parser.parse("1^2")).toBeInstanceOf(BitwiseOperationExpression);
|
||||
expect(parser.parse("1|2")).toBeInstanceOf(BitwiseOperationExpression);
|
||||
expect(parser.parse("~1")).toBeInstanceOf(BitwiseOperation);
|
||||
expect(parser.parse("1^2")).toBeInstanceOf(BitwiseOperation);
|
||||
expect(parser.parse("1|2")).toBeInstanceOf(BitwiseOperation);
|
||||
});
|
||||
|
||||
it("parses big binary bitwise expression", () => {
|
||||
const input = "0b00010010001101000101011001111000 0b10101010101010101010101000000000";
|
||||
const actual = parser.parse(input);
|
||||
expect(actual).toBeInstanceOf(ListOfNumbersExpression);
|
||||
expect(actual).toBeInstanceOf(ListOfNumbers);
|
||||
|
||||
const expr = actual as ListOfNumbersExpression;
|
||||
expect(expr.children[0].getUnderlyingScalarOperand().value.toString()).toBe('305419896');
|
||||
expect(expr.children[1].getUnderlyingScalarOperand().value.toString()).toBe('2863311360');
|
||||
const expr = actual as ListOfNumbers;
|
||||
expect(expr.children[0].getUnderlyingOperand().value.toString()).toBe('305419896');
|
||||
expect(expr.children[1].getUnderlyingOperand().value.toString()).toBe('2863311360');
|
||||
})
|
||||
|
||||
it("pares multiple operand expression", () => {
|
||||
const result = parser.parse("1^2") as BitwiseOperationExpression;
|
||||
const result = parser.parse("1^2") as BitwiseOperation;
|
||||
expect(result.children.length).toBe(2);
|
||||
|
||||
const first = result.children[0];
|
||||
const second = result.children[1];
|
||||
|
||||
expect(first).toBeInstanceOf(ScalarValue);
|
||||
expect(first).toBeInstanceOf(Operand);
|
||||
|
||||
expect((first as ScalarValue).value.toString()).toBe("1");
|
||||
expect((first as Operand).value.toString()).toBe("1");
|
||||
|
||||
expect(second).toBeInstanceOf(BitwiseOperator);
|
||||
var secondOp = second as BitwiseOperator;
|
||||
expect(second).toBeInstanceOf(Operator);
|
||||
var secondOp = second as Operator;
|
||||
expect(secondOp.operator).toBe("^");
|
||||
|
||||
expect(secondOp.operand).toBeInstanceOf(ScalarValue);
|
||||
var childOp = secondOp.operand as ScalarValue;
|
||||
expect(secondOp.operand).toBeInstanceOf(Operand);
|
||||
var childOp = secondOp.operand as Operand;
|
||||
expect(childOp.value.toString()).toBe('2');
|
||||
});
|
||||
|
||||
it("bug", () => {
|
||||
var result = parser.parse("1|~2") as BitwiseOperationExpression;
|
||||
var result = parser.parse("1|~2") as BitwiseOperation;
|
||||
expect(result.children.length).toBe(2);
|
||||
});
|
||||
});
|
||||
@@ -104,13 +104,13 @@ describe("comparison with nodejs engine", () => {
|
||||
|
||||
try
|
||||
{
|
||||
const expr = parser.parse(actualInput) as BitwiseOperationExpression;
|
||||
const bo = (expr.children[0] as BitwiseOperator);
|
||||
const expr = parser.parse(actualInput) as BitwiseOperation;
|
||||
const bo = (expr.children[0] as Operator);
|
||||
const res = bo.evaluate();
|
||||
actual = res.value.toString();
|
||||
|
||||
if(actual != expected) {
|
||||
const uop = bo.getUnderlyingScalarOperand();
|
||||
const uop = bo.getUnderlyingOperand();
|
||||
console.log(`Expected:${expectedInput}\nActual:${actualInput}\n${uop.value} ${uop.value.maxBitSize}\n${res.value} ${typeof res.value} ${res.value.maxBitSize}`)
|
||||
}
|
||||
}
|
||||
@@ -151,17 +151,17 @@ describe("comparison with nodejs engine", () => {
|
||||
|
||||
try
|
||||
{
|
||||
var expr = parser.parse(actualInput) as BitwiseOperationExpression;
|
||||
var expr = parser.parse(actualInput) as BitwiseOperation;
|
||||
|
||||
var op1 = expr.children[0] as ScalarValue;
|
||||
var op2 = expr.children[1] as BitwiseOperator;
|
||||
var op1 = expr.children[0] as Operand;
|
||||
var op2 = expr.children[1] as Operator;
|
||||
|
||||
actual = op2.evaluate(op1).value.toString();
|
||||
const equals = actual === expected;
|
||||
|
||||
if(!equals)
|
||||
{
|
||||
console.log(`Expected:${expectedInput}\n$Actual:${actualInput}\nop1:${typeof op1.value}\nop2:${typeof op2.getUnderlyingScalarOperand().value}`);
|
||||
console.log(`Expected:${expectedInput}\n$Actual:${actualInput}\nop1:${typeof op1.value}\nop2:${typeof op2.getUnderlyingOperand().value}`);
|
||||
}
|
||||
}
|
||||
catch(err)
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import ScalarValue from './ScalarValue';
|
||||
import BitwiseOperator from './BitwiseOperator'
|
||||
import ListOfNumbersExpression from './ListOfNumbersExpression';
|
||||
import BitwiseOperationExpression from './BitwiseOperationExpression';
|
||||
import Operand from './Operand';
|
||||
import Operator from './Operator'
|
||||
import ListOfNumbers from './ListOfNumbers';
|
||||
import BitwiseOperation from './BitwiseOperation';
|
||||
import { Expression, ExpressionElement } from './expression-interfaces';
|
||||
import { numberParser, numberRegexString } from './numberParser';
|
||||
import { parse } from 'path';
|
||||
|
||||
export { default as ScalarValue } from './ScalarValue';
|
||||
export { default as BitwiseOperator } from './BitwiseOperator';
|
||||
export { default as ListOfNumbersExpression } from './ListOfNumbersExpression';
|
||||
export { default as BitwiseOperationExpression } from './BitwiseOperationExpression';
|
||||
export { default as Operand } from './Operand';
|
||||
export { default as Operator } from './Operator';
|
||||
export { default as ListOfNumbers } from './ListOfNumbers';
|
||||
export { default as BitwiseOperation } from './BitwiseOperation';
|
||||
|
||||
interface IExpressionParserFactory {
|
||||
canCreate: (input: string) => boolean;
|
||||
@@ -76,7 +75,7 @@ class ListOfNumbersExpressionFactory implements IExpressionParserFactory
|
||||
.filter(p => p.length > 0)
|
||||
.map(m => parseScalarValue(m));
|
||||
|
||||
return new ListOfNumbersExpression(input, numbers);
|
||||
return new ListOfNumbers(input, numbers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +104,7 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
operands.push(this.parseMatch(m));
|
||||
}
|
||||
|
||||
return new BitwiseOperationExpression(normalizedString, operands)
|
||||
return new BitwiseOperation(normalizedString, operands)
|
||||
};
|
||||
|
||||
parseMatch (m:RegExpExecArray): ExpressionElement {
|
||||
@@ -117,16 +116,16 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
var parsed = null;
|
||||
|
||||
if(num.indexOf('~') == 0) {
|
||||
parsed = new BitwiseOperator(parseScalarValue(num.substring(1)), '~');
|
||||
parsed = new Operator(parseScalarValue(num.substring(1)), '~');
|
||||
}
|
||||
else {
|
||||
parsed = parseScalarValue(num);
|
||||
}
|
||||
|
||||
if(operator == null) {
|
||||
return parsed as BitwiseOperator;
|
||||
return parsed as Operator;
|
||||
} else {
|
||||
return new BitwiseOperator(parsed as ScalarValue, operator);
|
||||
return new Operator(parsed as Operand, operator);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -135,9 +134,9 @@ class BitwiseOperationExpressionFactory implements IExpressionParserFactory {
|
||||
};
|
||||
}
|
||||
|
||||
function parseScalarValue(input : string) : ScalarValue {
|
||||
function parseScalarValue(input : string) : Operand {
|
||||
const n = numberParser.parse(input);
|
||||
var sv = new ScalarValue(n.value, n.base);
|
||||
var sv = new Operand(n.value, n.base);
|
||||
if(sv.value.maxBitSize != n.value.maxBitSize) throw new Error("Gotcha!");
|
||||
return sv;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,9 @@ code { font-size: 1.2em; font-weight: bold; }
|
||||
.result .content { padding-left: 10px}
|
||||
.result .cur { margin-right: 5px; }
|
||||
|
||||
.input-string { margin-right: 5px;}
|
||||
.hashLink { text-decoration: none; margin-left: 5px; visibility: hidden; margin-right: 0; padding: 0; text-decoration: none; }
|
||||
a.hashLink { font-size: 1.1em;}
|
||||
.hashLink:hover { text-decoration: underline; margin-left: 5px; background: none; }
|
||||
.result:hover .hashLink { visibility: visible }
|
||||
|
||||
@@ -57,7 +59,8 @@ code { font-size: 1.2em; font-weight: bold; }
|
||||
|
||||
.hex .prefix { display: inline; }
|
||||
|
||||
.indicator { padding: 0px 5px; background: transparent; border: none; cursor: pointer; vertical-align: middle; }
|
||||
.indicator { padding: 0px 5px; background: transparent; border: none; cursor: pointer; vertical-align: middle; margin-left: -3em; color:rgba(0, 0, 0, 0.25) }
|
||||
|
||||
.error { color: maroon; }
|
||||
|
||||
.soft { opacity: 0.7 }
|
||||
@@ -68,9 +71,12 @@ code { font-size: 1.2em; font-weight: bold; }
|
||||
|
||||
.cur { color: lightgray; }
|
||||
|
||||
button { border: none; text-decoration: underline;}
|
||||
|
||||
/* Light */
|
||||
.light { background: #fafafa; }
|
||||
.light a, .light a:visited { color: #222; }
|
||||
.light .header-cmd { color: #919191 }
|
||||
.light a, .light a:visited, .light button { color: #222; }
|
||||
.light .one { color: black; }
|
||||
.light .zero { color: #888; }
|
||||
.light .indicator { color: #ddd; }
|
||||
@@ -91,29 +97,29 @@ code { font-size: 1.2em; font-weight: bold; }
|
||||
.dark { background: #121212; color: white;}
|
||||
.dark .expression { color: white;}
|
||||
.dark .expressionInput { color: white; }
|
||||
.dark a, .dark a:visited { color: white; }
|
||||
.dark a, .dark a:visited, .dark button { color: white; }
|
||||
.dark .indicator { color: #555; }
|
||||
.dark .on { color: white; }
|
||||
.dark .zero { color: #999;}
|
||||
.dark .prefix { color: #999}
|
||||
.dark .other { color: #777;}
|
||||
.dark .hashLink, .dark .hashLink:visited { color: #333 }
|
||||
.dark .hashLink, .dark .hashLink:visited { color: #555 }
|
||||
.dark .hashLink:hover { color: #999 }
|
||||
.dark ul.top-links li:hover { background: #333 }
|
||||
.dark .error { color: #da586d}
|
||||
.dark button.btn { color: white}
|
||||
.dark button.btn:hover { background: #333}
|
||||
.dark button.btn:disabled { color: #999; background-color: inherit; }
|
||||
.dark .accent1 { color:mediumseagreen}ч
|
||||
.dark .accent1 { color:mediumseagreen}
|
||||
|
||||
/*
|
||||
Midnight Theme
|
||||
*/
|
||||
.midnight .header-cmd { color: #85a0ad }
|
||||
.midnight { background: #2c3e50; color: white }
|
||||
.midnight .header-cmd { color: #7ea3b5 !important }
|
||||
.midnight .expression { color: white;}
|
||||
.midnight .expressionInput { color: white;}
|
||||
.midnight a, .dark a:visited { color: white; }
|
||||
.midnight a, .dark a:visited, .midnight button { color: white; }
|
||||
.midnight .indicator { color: #85a0ad; }
|
||||
.midnight .on { color: white; }
|
||||
.midnight .zero { color: #85a0ad;}
|
||||
@@ -177,7 +183,3 @@ button:focus {outline:0;}
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.debug-indicators {
|
||||
left: 1px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import log from 'loglevel';
|
||||
|
||||
const APP_VERSION = 9;
|
||||
export const APP_VERSION = 9;
|
||||
|
||||
export type PersistedAppData = {
|
||||
emphasizeBytes: boolean;
|
||||
uiTheme: string;
|
||||
version: number;
|
||||
version: number | null;
|
||||
debugMode: boolean | null;
|
||||
pageVisistsCount: number;
|
||||
donationClicked: boolean
|
||||
@@ -41,7 +41,7 @@ export default class AppState {
|
||||
this.uiTheme = persistData.uiTheme || 'midnight';
|
||||
this.env = env;
|
||||
|
||||
this.emphasizeBytes = persistData.emphasizeBytes || true;
|
||||
this.emphasizeBytes = !!persistData.emphasizeBytes;
|
||||
this.persistedVersion = persistData.version || 0.1;
|
||||
this.wasOldVersion = persistData.version != null && this.version > this.persistedVersion;
|
||||
this.debugMode = persistData.debugMode === true;
|
||||
|
||||
@@ -1,12 +1,21 @@
|
||||
import AppState, { PersistedAppData } from "./AppState";
|
||||
import AppState, { APP_VERSION, PersistedAppData } from "./AppState";
|
||||
|
||||
const storeKey = 'AppState';
|
||||
|
||||
const DEFAULT_DATA : PersistedAppData = {
|
||||
uiTheme: 'midnight',
|
||||
emphasizeBytes: true,
|
||||
version: APP_VERSION,
|
||||
debugMode: false,
|
||||
pageVisistsCount: 0,
|
||||
donationClicked: false
|
||||
}
|
||||
|
||||
export default {
|
||||
getPersistedData() : PersistedAppData {
|
||||
var json = window.localStorage.getItem(storeKey);
|
||||
if(!json) {
|
||||
return {} as PersistedAppData;
|
||||
return DEFAULT_DATA;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -14,7 +23,7 @@ export default {
|
||||
}
|
||||
catch(ex) {
|
||||
console.error('Failed to parse AppState json. Json Value: \n' + json, ex);
|
||||
return {} as PersistedAppData;;
|
||||
return DEFAULT_DATA;;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
.debug-indicators { position: absolute; top: 1em; left: 1em}
|
||||
.debug-indicators { position: absolute; top: 3.5em; right: 0.1em}
|
||||
.debug-indicators span {display: block;}
|
||||
@@ -21,8 +21,8 @@ const DisplayResultView: React.FunctionComponent<DisplayResultProps> = (props) =
|
||||
|
||||
return <div className="result">
|
||||
<div className="input mono">
|
||||
<span className="cur">
|
||||
></span>{props.input}
|
||||
<span className="cur">></span>
|
||||
<span className="input-string">{props.input}</span>
|
||||
<a className="hashLink" title="Link for this expression" href={window.location.pathname + '#' + props.inputHash}>
|
||||
<FontAwesomeIcon className="icon" icon={faHashtag} size="xs" />
|
||||
</a>
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
.help ul { list-style-type: none; margin: 0; margin-top: 0.2em; padding: 0; }
|
||||
.help ul { list-style-type: none; margin: 0; margin-left: 10px; margin-top: 0.2em; padding: 0; }
|
||||
.help li { padding: 1px;}
|
||||
.help p { margin-top: 0.5em }
|
||||
.help .section {padding: 1em;}
|
||||
.help .panel-container {overflow: hidden;}
|
||||
.help .left-panel {float:left; margin-right: 1em;}
|
||||
.help .left-panel {float:left; margin-right: 1em;}
|
||||
.help .right-panel {float:left; }
|
||||
.help .section-title {font-weight: bold;}
|
||||
|
||||
@media (min-width: 1024px) {
|
||||
.left-panel, .right-panel {
|
||||
width: 45%;
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,7 @@ function TopLinks() {
|
||||
return <ul className="top-links">
|
||||
<li>
|
||||
<a href='https://www.paypal.com/donate/?hosted_button_id=3GREJYC4T5AJ8' onClick={onDonate} target="_blank">
|
||||
<FontAwesomeIcon className='icon' icon={faDonate} size="lg" />donate
|
||||
<FontAwesomeIcon className='icon' icon={faDonate} size="lg" /><span className='link-text'>donate</span>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
|
||||
@@ -53,7 +53,7 @@ function getStartupCommands(appState : AppState) : string[] {
|
||||
startupCommands = hashArgs;
|
||||
}
|
||||
|
||||
log.debug('Executing startup commands', startupCommands);
|
||||
log.debug('Startup commands loaded', startupCommands);
|
||||
|
||||
return startupCommands;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user