Initial support of multi-operand expressions

This commit is contained in:
BorysLevytskyi
2015-12-03 19:51:27 +02:00
parent b7b72161eb
commit ded459b575
7 changed files with 197 additions and 37 deletions

View File

@@ -146,6 +146,18 @@
</table> </table>
</script> </script>
<script data-template="bitwiseExpressionView" data-compiled="" type="text/template">
<table class="expression">
{each itm in m.items}
<tr class="{itm.css}">
<td class="label">{itm.label}</td>
<td class="bin">{itm.bin.padLeft(m.maxNumberOfBits, '0')}</td>
<td class="other">{itm.other}</td>
</tr>
{/}
</table>
</script>
<script data-template="notExpressionView" data-compiled="" type="text/template"> <script data-template="notExpressionView" data-compiled="" type="text/template">
<table class="expression"> <table class="expression">
<tr> <tr>

View File

@@ -4,28 +4,25 @@ app.set('expression', function() {
var expression = { var expression = {
factories:[], factories:[],
canParse: function(string) { canParse: function(string) {
var trimmed = string.replace(/^\s+|\s+$/, '');
var i = this.factories.length-1; var i = this.factories.length-1;
for(;i>=0;i--) { for(;i>=0;i--) {
if(this.factories[i].regex.test(string)){ if(this.factories[i].canCreate(trimmed) === true){
return true; return true;
} }
} }
return false; return false;
}, },
parse: function(string) { parse: function(string) {
var trimmed = string.replace(/^\s+|\s+$/, ''); var trimmed = string.replace(/^\s+|\s+$/, '');
var i = 0, l = this.factories.length, factory, matches; var i = 0, l = this.factories.length, factory, matches;
for(;i<l;i++) { for(;i<l;i++) {
factory = this.factories[i]; factory = this.factories[i];
matches = factory.regex.exec(trimmed);
if(matches == null){ if(factory.canCreate(trimmed) == true){
continue; return factory.create(trimmed);
} }
return factory.create(matches);
} }
return null; return null;
@@ -34,26 +31,26 @@ app.set('expression', function() {
return new Operand(input); return new Operand(input);
}, },
createOperand: function(number, kind) { createOperand: function(number, kind) {
var str = number.toString(getBase(kind)); return Operand.create(number, kind);
if(kind == 'hex') {
str = toHex(str);
}
return new Operand(str);
}, },
addFactory: function(factory) { addFactory: function(factory) {
this.factories.push(factory); this.factories.push(factory);
}, },
TwoOperandExpression: TwoOperandExpression, TwoOperandExpression: TwoOperandExpression,
SingleOperandExpression: SingleOperandExpression, SingleOperandExpression: SingleOperandExpression,
ListOfNumbersExpression: ListOfNumbersExpression ListOfNumbersExpression: ListOfNumbersExpression,
MultipleOperandsExpression: MultipleOperandsExpression
}; };
// List of numbers // List of numbers
expression.addFactory({ expression.addFactory({
regex: /^(-?(?:\d+|0x[\d,a-f]+)\s?)+$/, regex: /^(-?(?:\d+|0x[\d,a-f]+)\s?)+$/,
create: function (matches) { canCreate: function(string) {
var numbers = [], return this.regex.test(string);
},
create: function (string) {
var matches = this.regex.exec(string),
numbers = [],
input = matches.input; input = matches.input;
input.split(' ').forEach(function(n){ input.split(' ').forEach(function(n){
@@ -69,8 +66,13 @@ app.set('expression', function() {
// Not Expression // Not Expression
expression.addFactory({ expression.addFactory({
regex: /^(~)(-?(?:\d+|0x[\d,a-f]+))$/, regex: /^(~)(-?(?:\d+|0x[\d,a-f]+))$/,
create: function (matches) { canCreate: function(string) {
var operand = new Operand(matches[2]) return this.regex.test(string);
},
create: function (string) {
var matches = this.regex.exec(string),
operand = new Operand(matches[2]);
return new SingleOperandExpression(matches.input, operand, matches[1]); return new SingleOperandExpression(matches.input, operand, matches[1]);
} }
}); });
@@ -78,9 +80,12 @@ app.set('expression', function() {
// Two operands expression // Two operands expression
expression.addFactory({ expression.addFactory({
regex: /^(-?(?:\d+|0x[\d,a-f]+))\s*(<<|>>|>>>|\||\&|\^)\s*(-?(?:\d+|0x[\d,a-f]+))$/, regex: /^(-?(?:\d+|0x[\d,a-f]+))\s*(<<|>>|>>>|\||\&|\^)\s*(-?(?:\d+|0x[\d,a-f]+))$/,
create: function (matches) { canCreate: function(string) {
return this.regex.test(string);
var operand1 = new Operand(matches[1]), },
create: function (string) {
var matches = this.regex.exec(string),
operand1 = new Operand(matches[1]),
operand2 = new Operand(matches[3]), operand2 = new Operand(matches[3]),
sign = matches[2], sign = matches[2],
expressionString = matches.input; expressionString = matches.input;
@@ -89,22 +94,49 @@ app.set('expression', function() {
} }
}); });
function getBase(kind) { // Multiple operands expression
switch (kind){ expression.addFactory({
case 'bin': return 2; fullRegex: /^((?:\s*)(<<|>>|>>>|\||\&|\^)?(?:\s*)(-?(?:\d+|0x[\d,a-f]+)))+$/,
case 'hex': return 16; regex: /(?:\s*)(<<|>>|>>>|\||\&|\^)?(?:\s*)(-?(?:\d+|0x[\d,a-f]+))/g,
case 'dec': return 10; canCreate: function(string) {
} this.fullRegex.lastIndex = 0;
return this.fullRegex.test(string);
},
create: function (string) {
var m = null, operands = [];
while ((m = this.regex.exec(string)) != null) {
operands.push(this.parseMatch(m));
} }
function toHex(hex) { //matches.input.replace(this.regex, function(inpt, sign, num) {
return hex.indexOf('-') == 0 ? '-0x' + hex.substr(1) : '0x' + hex; // if(sign == null) {
// operands.push(new Operand(num));
// } else {
// operands.push(new SingleOperandExpression(inpt, new Operand(num), sign))
// }
//
//});
return new MultipleOperandsExpression(string, operands)
},
parseMatch: function (m) {
var input = m[0],
sign = m[1],
num = m[2];
if(sign == null) {
return new Operand(num);
} else {
return new SingleOperandExpression(input, new Operand(num), sign);
} }
}
});
function Operand(input) { function Operand(input) {
this.input = input; this.input = input;
this.value = parseInt(input); this.value = parseInt(input);
this.hex = toHex(this.value.toString(16)); this.hex = Operand.toHexString(this.value.toString(16));
this.dec = this.value.toString(10); this.dec = this.value.toString(10);
// >>> 0 makes negative numbers like -1 to be displayed as '11111111111111111111111111111111' in binary instead of -1 // >>> 0 makes negative numbers like -1 to be displayed as '11111111111111111111111111111111' in binary instead of -1
this.bin = this.value < 0 ? (this.value >>> 0).toString(2) : this.value.toString(2); this.bin = this.value < 0 ? (this.value >>> 0).toString(2) : this.value.toString(2);
@@ -112,12 +144,51 @@ app.set('expression', function() {
this.other = this.kind == 'dec' ? this.hex : this.dec; this.other = this.kind == 'dec' ? this.hex : this.dec;
} }
Operand.toHexString = function (hex) {
return hex.indexOf('-') == 0 ? '-0x' + hex.substr(1) : '0x' + hex;
};
Operand.create = function(number, kind) {
var str = number.toString(Operand.getBase(kind));
if(kind == 'hex') {
str = Operand.toHexString(str);
}
return new Operand(str);
};
Operand.prototype.getLengthInBits = function() {
if(this.value < 0) {
return 32;
}
return Math.floor(Math.log(this.value) / Math.log(2)) + 1;
};
Operand.getBase = function(kind){
switch (kind){
case 'bin': return 2;
case 'hex': return 16;
case 'dec': return 10;
}
};
function SingleOperandExpression(expressionString, operand, sign) { function SingleOperandExpression(expressionString, operand, sign) {
this.expressionString = expressionString; this.expressionString = expressionString;
this.operand1 = operand; this.operand1 = operand;
this.sign = sign; this.sign = sign;
} }
SingleOperandExpression.prototype.apply = function (value) {
var str = '';
if(this.sign == '~'){
str = '~' + this.operand1.value;
} else {
str = value + this.sign + this.operand1.value
}
return Operand.create(eval(str), this.operand1.kind);
};
function TwoOperandExpression(expressionString, operand1, operand2, sign) { function TwoOperandExpression(expressionString, operand1, operand2, sign) {
this.expressionString = expressionString; this.expressionString = expressionString;
this.operand1 = operand1; this.operand1 = operand1;
@@ -125,6 +196,11 @@ app.set('expression', function() {
this.sign = sign; this.sign = sign;
} }
function MultipleOperandsExpression(expressionString, expressions) {
this.expressionString = expressionString;
this.expressions = expressions;
}
function ListOfNumbersExpression(expressionString, numbers) { function ListOfNumbersExpression(expressionString, numbers) {
this.expressionString = expressionString; this.expressionString = expressionString;
this.numbers = numbers; this.numbers = numbers;
@@ -141,5 +217,13 @@ app.set('expression', function() {
//SingleOperandExpression.prototype = Expression.prototype; //SingleOperandExpression.prototype = Expression.prototype;
//ListOfNumbersExpression.prototype = Expression.prototype; //ListOfNumbersExpression.prototype = Expression.prototype;
Operand.prototype.toString = function () {
return this.input;
};
SingleOperandExpression.prototype.toString = function() {
return this.sign + this.operand1.toString();
};
return expression; return expression;
}); });

View File

@@ -6,11 +6,7 @@ app.set("formatter", function() {
return { return {
formatString: function(num, kind) { formatString: function(num, kind) {
kind = kind || "bin"; return num.toString(getBase(kind || "bin"));
var convertedString = num.toString(getBase(kind));
return convertedString;
}, },
padLeft: function (str, length, symbol) { padLeft: function (str, length, symbol) {
var sb = Array.prototype.slice.call(str), symbol = symbol || "0"; var sb = Array.prototype.slice.call(str), symbol = symbol || "0";

View File

@@ -58,6 +58,10 @@ app.run(function() {
return new app.models.BitwiseNumbers(expr); return new app.models.BitwiseNumbers(expr);
} }
if(expr instanceof expression.MultipleOperandsExpression) {
return new app.models.BitwiseExpression(expr);
}
return new app.models.ErrorResult('Cannot create model for expression: ' + expr.toString()); return new app.models.ErrorResult('Cannot create model for expression: ' + expr.toString());
} }
}); });

View File

@@ -46,6 +46,13 @@ app.compose(function () {
} }
}); });
app.modelView(app.models.BitwiseExpression, {
renderView: function(model) {
var template = app.template('bitwiseExpressionView');
return colorizeBits(template.render(model));
}
});
app.modelView(app.models.BitwiseNumbers, { app.modelView(app.models.BitwiseNumbers, {
renderView: function(model) { renderView: function(model) {
model.bitsSize = getBinaryLength(model.numbers); model.bitsSize = getBinaryLength(model.numbers);
@@ -88,6 +95,9 @@ app.compose(function () {
return bits; return bits;
} }
function colorizeBits(container) { function colorizeBits(container) {
var list = container.querySelectorAll('.bin'); var list = container.querySelectorAll('.bin');
Array.prototype.forEach.call(list, function(el){ Array.prototype.forEach.call(list, function(el){

View File

@@ -1,6 +1,7 @@
(function(app) { (function(app) {
"use strict"; "use strict";
function BitwiseOperation (expression) { function BitwiseOperation (expression) {
this.expression = expression; this.expression = expression;
this.operand1 = expression.operand1; this.operand1 = expression.operand1;
@@ -20,6 +21,54 @@
}); });
} }
function BitwiseExpression(expression) {
this.items = [];
this.maxNumberOfBits = 0;
var op = expression.expressions[0],
i = 1, l = expression.expressions.length,
ex;
this.addOperand(op);
for (;i<l;i++) {
ex = expression.expressions[i];
this.addExpression(ex);
op = ex.apply(op.value);
this.addResult(op);
}
this.maxNumberOfBits = this.emphasizeBytes(this.maxNumberOfBits);
}
BitwiseExpression.prototype.addOperand = function(operand) {
this.maxNumberOfBits = Math.max(operand.getLengthInBits(), this.maxNumberOfBits);
this.items.push({ label: operand.toString(), bin: operand.bin, other: operand.other, css: ''});
};
BitwiseExpression.prototype.addExpression = function(expression) {
this.maxNumberOfBits = Math.max(expression.operand1.getLengthInBits(), this.maxNumberOfBits);
this.items.push({ label: expression.toString(), bin: expression.operand1.bin, other: expression.operand1.other, css: ''});
};
BitwiseExpression.prototype.addResult = function(operand) {
this.maxNumberOfBits = Math.max(operand.getLengthInBits(), this.maxNumberOfBits);
this.items.push({ label: "=" + operand.toString(), bin: operand.bin, other: operand.other, css: 'expression-result'});
};
BitwiseExpression.prototype.emphasizeBytes = function (bits) {
var cmdConfig = app.get('cmdConfig');
if(cmdConfig.emphasizeBytes && bits % 8 != 0) {
if(bits < 8) {
return 8;
}
var n = bits - (bits % 8);
return n + 8;
}
return bits;
};
function ErrorResult(message) { function ErrorResult(message) {
this.message = message; this.message = message;
} }
@@ -39,5 +88,6 @@
app.models.ErrorResult = ErrorResult; app.models.ErrorResult = ErrorResult;
app.models.ViewResult = ViewResult; app.models.ViewResult = ViewResult;
app.models.DisplayResult = DisplayResult; app.models.DisplayResult = DisplayResult;
app.models.BitwiseExpression = BitwiseExpression;
})(window.app); })(window.app);

View File

@@ -65,6 +65,10 @@ describe("expression parse", function() {
} }
} }
}); });
it ("should parse multiple operands expression", function () {
var actual = expression.parse("1|2&3");
})
}); });
describe('parse operands', function() { describe('parse operands', function() {