Calc

by kadymov

HTML

<input type="text" id="math" value="(2.5 * 2) * (10 / 2) - 5"/><span id="result"> =</span>

JavaScript

document.getElementById('math').addEventListener('change', function() {
    var math = document.getElementById('math').value;
    document.getElementById('result').innerText = ' =' + calculate(math);
});

function calculate(str) {
    var exprArr = parseExp(str),
        startNode = new Node(exprArr);
    createTree(startNode);
    return calcTree(startNode);
}


//------------------------------------------

function Node (nodeValue) {
    nodeValue = nodeValue || '';

    var children = [];

    return {
        value : function (value) {
            if (arguments.length) {
                nodeValue = value;
                return this;
            } else {
                return nodeValue;
            }
        },

        child : function (id) {
            return children[id];
        },

        addChild : function (node) {
            children.push(node);
            return this;
        }
    };
}

//-----------------------------------

function parseExp(exp) {
    return exp.replace(/\s/g, '')
              .replace(/([+-\/*]|^|\()-([\d.]+)/g, '$1(0-$2)') // -1 -> (0-1)
              .match(/[\d.]+|[+-\/*\(\)]/g);
}

function isOperator(char) {
    return  char === '+' ||
            char === '-' ||
            char === '*' ||
            char === '/';
}

function getClosingBracket(expr, pos) {
    var counter = 0;
    for (len = expr.length; pos < len; pos++) {
        var char = expr[pos];

        if (char === '(') {
            counter++;
        } else if (char === ')') {
            if (!--counter) return pos;
        }        
    }

    return 0;
}

function getLowPriorityOp(expArr) {
    var len = expArr.length,
        i, val,
        lowOp = '',
        lowPos = 0;

    for (i = 0; i < len; i++) {
        val = expArr[i];

        if (val === '(') {
            i = getClosingBracket(expArr, i);
            continue;
        }

        if (isOperator(val)) { 
            if (!lowOp || 
                (lowOp === '*' || lowOp === '/' && 
        ...