support of hexadecimal operations

This commit is contained in:
Borys Levytskyi
2015-04-11 22:06:23 +03:00
parent 087d96ea12
commit 0346408254
9 changed files with 208 additions and 144 deletions

View File

@@ -16,6 +16,7 @@ code { font-size: 1.2em; font-weight: bold; }
.expression .result td { border-top: dotted 1px gray; }
.expression { font-size: 1.5em; font-family: monospace }
.expression .prefix { font-weight: normal; display: none; font-size: 0.9em }
.expression .other { font-size: 0.9em}
.hex .prefix { display: inline; }
@@ -36,6 +37,7 @@ code { font-size: 1.2em; font-weight: bold; }
.light .indicator { color: #ddd; }
.light .on { color: #121212; }
.light .prefix { color: #888}
.light .other { font-size: 0.9em}
/* Dark */
.dark { background: #121212; color: white;}
@@ -45,5 +47,6 @@ code { font-size: 1.2em; font-weight: bold; }
.dark .on { color: white; }
.dark .zero { color: #999;}
.dark .prefix { color: #999}
.dark .other { color: #444;}

View File

@@ -115,40 +115,40 @@
</div>
</script>
<script data-template="shiftExpressionView" type="text/template">
<script data-template="shiftExpressionView" data-compiled="" type="text/template">
<table class="expression">
<tr>
<td class="label {mode}"><span class="prefix">0x</span>{operand1Str}</td>
<td class="bin">{operand1Binary}</td>
<td class="other {otherMode}"><span class="prefix">0x</span>{operand1Other}</td>
<td class="label">{m.operand1.input}</td>
<td class="bin">{m.operand1Binary}</td>
<td class="other">{m.operand1.other}</td>
</tr>
<tr class="result">
<td class="label {mode}"><span class="prefix">0x</span>{operand1Str}{sign}<span class="prefix">0x</span>{operand2Str}=<span class="prefix">0x</span>{resultStr}</td>
<td class="bin">{resultBinary}</td>
<td class="other {otherMode}">{resultOther}</td>
<td class="label">{m.string.replace(/\s/g,'')}={m.result.input}</td>
<td class="bin">{m.resultBinary}</td>
<td class="other">{m.result.other}</td>
</tr>
</table>
</script>
<script data-template="binaryExpressionView" type="text/template">
<script data-template="binaryExpressionView" data-compiled="" type="text/template">
<table class="expression">
<tr>
<td></td>
<td class="label {mode}"><span class="prefix">0x</span>{operand1Str}</td>
<td class="bin">{operand1Binary}</td>
<td class="other {otherMode}"><span class="prefix">0x</span>{operand1Other}</td>
<td class="label">{m.operand1.input}</td>
<td class="bin">{m.operand1Binary}</td>
<td class="other">{m.operand1.other}</td>
</tr>
<tr>
<td>{sign}</td>
<td class="label {mode}"><span class="prefix">0x</span>{operand2Str}</td>
<td class="bin">{operand2Binary}</td>
<td class="other {otherMode}"><span class="prefix">0x</span>{operand2Other}</td>
<td>{m.sign}</td>
<td class="label">{m.operand2.input}</td>
<td class="bin">{m.operand2Binary}</td>
<td class="other">{m.operand2.other}</td>
</tr>
<tr class="result">
<td>=</td>
<td class="label {mode}"><span class="prefix">0x</span>{resultStr}</td>
<td class="bin">{resultBinary}</td>
<td class="other {otherMode}"><span class="prefix">0x</span>{resultOther}</td>
<td class="label">{m.result.input}</td>
<td class="bin">{m.resultBinary}</td>
<td class="other">{m.result.other}</td>
</tr>
</table>
</script>
@@ -170,6 +170,7 @@
else {
cmd.execute('1|2');
cmd.execute('1<<0x2a');
cmd.execute('2 4 8 16 32');
}

View File

@@ -1,51 +1,48 @@
app.set('expression', function() {
"use strict";
var decNumber = "\d+";
var hexNumber = "(?:\d|a|b|c|d|e|f)";
var modes = {
'dec': {
expr: /^(\d+)\s*(<<|>>|\||\&|\^)\s*(\d+)$/,
list: /^((\d*)+\s?)+$/
},
'hex': {
expr: /^([\d,a-f]+)\s*(<<|>>|\||\&|\^)\s*([\d,a-f]+)$/,
list: /^(([\d,a-f]*)+\s?)+$/
}
};
var exprRegex = /^(\d+|0x[\d,a-f]+)\s*(<<|>>|\||\&|\^)\s*(\d+|0x[\d,a-f]+)$/;
var listRegex = /^((\d+|0x[\d,a-f]+)\s?)+$/
return {
canParse: function(string, mode) {
var regex = modes[mode || 'dec'];
return regex.expr.test(string) || regex.list.test(string);
return exprRegex.test(string) || listRegex.test(string);
},
parse: function(string, mode) {
mode = (mode || 'dec');
var trimmed = string.replace(/^\s+|\s+$/, '');
var regex = modes[mode];
var base = getBase(mode);
var matches = regex.expr.exec(trimmed);
var matches = exprRegex.exec(trimmed);
if(matches != null) {
return createCalculableExpression(matches, base);
}
matches = regex.list.exec(string);
matches = listRegex.exec(string);
if(matches != null) {
return createListOfNumbersExpression(string, base)
}
},
parseOperand: function(input) {
return new Operand(input);
},
createOperand: function(number, kind) {
var str = number.toString(getBase(kind));
if(kind == 'hex') {
str = "0x" + str;
}
return new Operand(str);
}
};
function createCalculableExpression(matches, base) {
var o1 = parseInt(matches[1], base);
var o2 = parseInt(matches[3], base);
var m = new app.models.BitwiseOperation();
m.operand1 = new Operand(o1);
m.operand2 = new Operand(o2);
m.operand1 = new Operand(matches[1]);
m.operand2 = new Operand(matches[3]);
m.sign = matches[2];
m.string = matches.input;
//m.result = eval(matches.input);
@@ -54,15 +51,15 @@ app.set('expression', function() {
}
function createListOfNumbersExpression(input, base) {
var numbers = [];
var operands = [];
input.split(' ').forEach(function(n){
if(n.trim().length > 0) {
numbers.push(parseInt(n, base));
operands.push(new Operand(n.trim()));
}
});
return new app.models.BitwiseNumbers(numbers);
return new app.models.BitwiseNumbers(operands);
}
function getBase(mode) {
@@ -73,11 +70,16 @@ app.set('expression', function() {
}
}
function Operand(n) {
this.value = n;
this.hex = n.toString(16);
this.dec = n.toString(10);
this.bin = n.toString(2);
function Operand(input) {
// console.log('input: ' + input);
this.input = input;
this.value = parseInt(input);
// console.log('value: ' + this.value);
this.hex = '0x' + this.value.toString(16);
this.dec = this.value.toString(10);
this.bin = this.value.toString(2);
this.kind = this.input.indexOf('0x') == 0 ? 'hex' : 'dec';
this.other = this.kind == 'dec' ? this.hex : this.dec;
}
Operand.prototype.valueOf = function () {

View File

@@ -6,32 +6,18 @@ app.compose(function () {
var calc = app.get('calc');
var html = app.get('html');
var cmdConfig = app.get('cmdConfig');
var expression = app.get('expression');
app.modelView(app.models.BitwiseOperation, {
renderView: function(expr) {
var result = calc.calcExpression(expr);
var maxLen = getBinaryLength([expr.operand1.value, expr.operand2.value, result]);
var result = expression.createOperand(calc.calcExpression(expr), getResultMode([expr.operand1, expr.operand2]));
var maxLen = getBinaryLength([expr.operand1.value, expr.operand2.value, result.value]);
var model = Object.create(expr);
var otherMode = cmdConfig.mode == 'dec' ? 'hex' : 'dec';
model.mode = cmdConfig.mode;
model.otherMode = otherMode;
model.operand1Str = expr.operand1[cmdConfig.mode];
model.result = result;
model.operand1Binary = formatter.padLeft(expr.operand1.bin, maxLen);
model.operand1Other = formatter.padLeft(expr.operand1[otherMode]);
model.operand2Str = expr.operand2[cmdConfig.mode];
model.operand2Binary = formatter.padLeft(expr.operand2.bin, maxLen);
model.operand2Other = expr.operand2[otherMode];
model.resultStr = formatter.formatString(result, cmdConfig.mode);
model.resultBinary = formatter.padLeft(formatter.formatString(result, cmdConfig.mode), maxLen);
model.resultOther = formatter.formatString(result, otherMode);
console.log(model);
model.resultBinary = formatter.padLeft(model.result.bin, maxLen);
var templateId = /<<|>>/.test(model.sign) ? 'shiftExpressionView' : 'binaryExpressionView';
var template = app.template(templateId);
@@ -48,7 +34,7 @@ app.compose(function () {
var table = html.element('<table class="expression {mode}"></table>');
var otherMode = cmdConfig.mode == 'dec' ? 'hex' : 'dec';
model.numbers.forEach(function(n){
model.operands.forEach(function(n){
var row = table.insertRow();
var decCell = row.insertCell();
@@ -59,12 +45,12 @@ app.compose(function () {
var binCell = row.insertCell();
binCell.className = 'bin';
decCell.innerHTML = html.template('<span class="prefix">0x</span>{n}', { n: formatter.formatString(n, cmdConfig.mode) });
binCell.textContent = formatter.padLeft(formatter.formatString(n), maxLen);
decCell.textContent = n.input;
binCell.textContent = formatter.padLeft(n.bin, maxLen);
var otherCell = row.insertCell();
otherCell.className = 'other ' + otherMode;
otherCell.innerHTML = html.template('<span class="prefix">0x</span>{n}', { n: formatter.formatString(n, otherMode) });
otherCell.className = 'other';
otherCell.textContent = n.other;
});
colorizeBits(table);
@@ -118,5 +104,15 @@ app.compose(function () {
.replace(/1/g, '<span class="one">1</span>');
});
}
function getResultMode(operands) {
for(var i=0; i<operands.length; i++) {
if(operands[i].kind == 'hex') {
return 'hex';
}
}
return 'dec';
}
});

View File

@@ -8,8 +8,13 @@
return eval(this.string);
};
function BitwiseNumbers(numbers) {
this.numbers = numbers;
function BitwiseNumbers(operands) {
this.operands = operands;
var numbers = this.numbers = [];
operands.forEach(function (o) {
numbers.push(o.value);
});
}
function ErrorResult(message) {

View File

@@ -6,40 +6,4 @@
app.set('should', core.should);
app.set('bindr', core.bindr);
/*
var template = {
compile: function (template) {
var regex = /(?:{([^}]+)})/g;
var sb = [];
sb.push('(function() {')
sb.push('return function (model) { ')
sb.push('\tvar html = [];')
sb.push('\twith (model) { ')
var m, index = 0;
while ((m = regex.exec(template)) !== null) {
if(m.index > index) {
sb.push("\t\thtml.push('" + normalize(template.substr(index, m.index - index)) + "');");
}
sb.push('\t\thtml.push(' + m[1] + ');');
index = m.index + m[0].length;
}
if(index < template.length - 1) {
sb.push("\t\thtml.push('" + normalize(template.substr(index, template.length - index)) + "');");
}
sb.push('\t}');
sb.push("\treturn html.join('');");
sb.push('}');
sb.push('})()')
console.log(sb.join('\r\n'));
return eval(sb.join('\r\n'));
}
};
function normalize(str) {
return str.replace(/(\r|\n)+/g, '').replace("'", "\\\'");
}
*/
})(window.app, window.core);

View File

@@ -1,11 +1,16 @@
(function(app) {
"use strict";
function Template(html) {
function Template(html, isCompiled) {
this.html = html;
this.isCompiled = isCompiled === true;
}
Template.prototype.render = function (model) {
if(this.isCompiled) {
return app.get('html').element(this.process(model));
}
return app.get('html').element(this.html, model);
};
@@ -16,11 +21,11 @@
throw new Error(key + ' template is not found');
}
return tpl;
}
};
app.run(function() {
readTemplates(app.get('rootView'));
})
});
function readTemplates(containerEl) {
var els = containerEl.querySelectorAll('[data-template]');
@@ -34,9 +39,49 @@
return;
}
store[key] = new Template(element.innerHTML);
var template = new Template(element.innerHTML);
store[key] = template;
if(element.hasAttribute('data-compiled')) {
template.process = compile(template.html);
template.isCompiled = true;
}
});
}
function compile (template) {
var regex = /(?:{([^}]+)})/g;
var sb = [];
sb.push('(function() {')
sb.push('return function (m) { ')
sb.push('\tvar html = [];')
sb.push('console.log(m)');
var m, index = 0;
while ((m = regex.exec(template)) !== null) {
if(m.index > index) {
sb.push("\t\thtml.push('" + normalize(template.substr(index, m.index - index)) + "');");
}
sb.push('\t\thtml.push(' + m[1] + ');');
index = m.index + m[0].length;
}
if(index < template.length - 1) {
sb.push("\t\thtml.push('" + normalize(template.substr(index, template.length - index)) + "');");
}
sb.push("\treturn html.join('');");
sb.push('}');
sb.push('})()');
console.log(sb.join('\r\n'));
return eval(sb.join('\r\n'));
}
};
function normalize(str) {
return str.replace(/(\r|\n)+/g, '').replace("'", "\\\'");
}
})(window.app);

View File

@@ -1,38 +1,86 @@
var app = window.app;
var expression = app.get('expression');
describe("expression parse", function() {
var app = window.app;
var expression = app.get('expression');
var decCases = {
"1 2 3": { numbers: [1,2,3] },
"1": { numbers: [1] },
"2>>1": { operand1: 2, operand2:1, "sign":">>", string:"2>>1" },
var shouldParse = ['0x2>>1', '1 2 3', '0x1 1 2 3 5', '0x1>>0x2', '1|2'];
it("should be able to parse", function() {
shouldParse.forEach(function(expr) {
console.log(expr);
expect(expression.canParse(expr)).toBe(true);
})
});
var expressionCases = {
"0x2>>1": { operand1: 2, operand2:1, "sign":">>", string:"0x2>>1" },
"123|111": { operand1: 123, operand2:111, "sign":"|", string: "123|111" },
"23^1": { operand1: 23, operand2:1, "sign":"^", string: "23^1" }
"23^0x1": { operand1: 23, operand2:1, "sign":"^", string: "23^0x1" },
"0xf>>0xa": { operand1: 15, operand2:10, "sign":">>", string:"0xf>>0xa" },
"0x10&0x11": { operand1: 0x10, operand2:0x11, "sign":"&", string:"0x10&0x11" },
"0x1a^11": { operand1: 0x1a, operand2:11, "sign":"^", string:"0x1a^11" }
};
var hexCases = {
"1 10 f" : { numbers: [1, 16, 15] },
"f>>a": { operand1: 15, operand2:10, "sign":">>", string:"f>>a" },
"10&11": { operand1: 16, operand2:17, "sign":"&", string:"10&11" },
"10^11": { operand1: 16, operand2:17, "sign":"^", string:"10^11" }
};
it("should parse decimal expressions", function() {
it("should parse expressions", function() {
var input, expr;
for(input in decCases) {
expect(expression.canParse(input)).toBe(true);
expr = expression.parse(input);
expect(JSON.stringify(expr)).toEqual(JSON.stringify(decCases[input]));
for(input in expressionCases) {
var actual = expression.parse(input);
var expected = expressionCases[input];
expect(actual).toBeDefined();
expect(actual).not.toBe(null);
expect(actual.sign).toBe(expected.sign);
expect(actual.operand1.value).toBe(expected.operand1);
expect(actual.operand2.value).toBe(expected.operand2);
expect(actual.string).toBe(expected.string);
}
});
var listCases = {
'1 2 3': [1, 2, 3],
'0x1 2 0xa6b': [0x1, 2, 0xa6b],
'0x11a': [0x11a]
};
it("should parse hexadecimal expressions", function() {
var input, expr;
for(input in hexCases) {
console.log('input: ' + input)
expect(expression.canParse(input, 'hex')).toBe(true);
expr = expression.parse(input, 'hex');
expect(JSON.stringify(expr)).toEqual(JSON.stringify(hexCases[input]));
var input, expr, i;
for(input in listCases) {
var actual = expression.parse(input);
var expected = listCases[input];
for(i =0; i<expected.length;i++) {
expect(actual.operands[i].value).toBe(expected[i]);
}
}
});
});
describe('operands', function() {
var hexOperand = expression.parseOperand('0x10');
var decOperand = expression.parseOperand('10');
it('should remember input form', function() {
expect(hexOperand.input).toBe('0x10');
expect(decOperand.input).toBe('10');
});
it('should return integer value', function () {
expect(hexOperand.value).toBe(0x10);
expect(decOperand.value).toBe(10);
});
it('should have all kinds', function () {
expect(hexOperand.kind).toBe('hex');
expect(hexOperand.dec).toBe('16');
expect(hexOperand.bin).toBe('10000');
expect(hexOperand.hex).toBe('10');
expect(decOperand.kind).toBe('dec');
expect(decOperand.dec).toBe('10');
expect(decOperand.bin).toBe('1010');
expect(decOperand.hex).toBe('a');
});
});

View File

@@ -2,19 +2,19 @@ describe('expression formatter', function () {
var di = app.di.clone();
var formatter = di.resolve('formatter');
it('should format number to binary by default', function() {
xit('should format number to binary by default', function() {
expect(formatter.formatString(10)).toBe("1010");
});
it('should format number hexadecimal', function() {
xit('should format number hexadecimal', function() {
expect(formatter.formatString(15, 'hex')).toBe("f");
});
it('should format number decimal', function() {
xit('should format number decimal', function() {
expect(formatter.formatString(16, 'dec')).toBe('16');
});
it('should respect padding', function() {
xit('should respect padding', function() {
expect(formatter.padLeft("a", 6)).toBe("00000a");
});
});