jCalc

by Андрей Иванов

HTML

<!--Задача:
Реализовать средствами javascript калькулятор, понимающий функции синуса, косинуса, возведения в степень и факториала. Калькулятор должен поддерживать вложенные выражения в скобках любой вложенности и приоритет арифметических операторов (2+2*2 должно выдавать результат 6, а (2+2)*2 - 8).

Разбор строки осуществляется с помощью регулярных выражений без применения дополнительных алгоритмов (по крайней мере - C-style парсинга строки, подсчета скобок и т.д.). Незаконченная функция specFuncsEx (если присутствует) была попыткой реализовать изначально задуманный функционал вложенных функций ( sin(0.5*(0.5+1)) ). На данный момент калькулятор понимает только функции первого порядка и арифметические выражения любого уровня вложенности.-->

<div class="wrapper"><input id="input" type="text" /><br />
    <button id="calculate">Calculate</button><br />
    <input id="output" disabled="true" type="text" />
        <br /><br />
    <div class="syntax">Syntax:<br /><ul><li>* or x, /, - and + operators may be used with or without spaces. If "-" is ised as a negative sign it shouldn't be followed by spaces.</li><li>Parentheses are welcome, but they won't do multiplication when not separated by * sign. In that case they will concatenate their values;<ul><li>(a+b)(c-d) WON'T do multiplication but will concatenate themselves: (2+2)(2-2) => 40,</li><li>(a+b)*(c-d) should work as usual: (2+2)*(2-2) => 0.</li></ul></li><li>sin(), cos(), pow (e.g. 2^2) and factorial (e.g. 5!) functions are supported at the moment. They may NOT be nested and can NOT contain arithmetic expressions (planned but not implemented yet).</li> </ul></div>
</div>

CSS

.wrapper {
    text-align: center;
}

#output {
    text-align: center;
}

#input {
    text-align: right;
}

.syntax {
    font-size: 70%;
    border: 1px solid lightgray;
    text-align: left;
}

ul {
    color: gray;
}

JavaScript

$(function(){
    
    function ease(expression,sign){
        lever = false;
        var regStr = "\\-?\\d+\\.?\\d*\\s*\\"+sign+"\\s*\\-*\\d+\\.?\\d*";
        var regEx = new RegExp(regStr);
        var comparison = new RegExp(".*\\d+\\s*\\"+sign+"\\s*\\-*\\d+.*");
        expression = expression.replace(/(^\s*)|(\s*$)|(^\s*\+)/g,"");
        
        while(expression.match(comparison)){
            expression = expression.replace(/(\-{3,})|(\-\+)/g,"-").replace(/(\++\s*\++)/,"+").replace(/\*{2,}/,"*").replace(/\/{2,}/,"/");
            expression = expression.replace(/\-\s*\-/,"+");
            expression = expression.replace(/\d+\s+\-?\d+/, function(match){
            return (match.replace(/\s+/," + "));
            });
            expression = expression.replace(regEx, function(match,offset,string){
                args = match.split(new RegExp("\\"+sign));
                return (eval(parseFloat(args[0]).toString()+ sign +parseFloat(args[1]).toString()))
            });
        lever = true;
        }
        console.log("Ease => %s\n",expression);
        return expression;
    }
    
    function solve(expression) {
        var lever = false;
        expression = ease(expression,"*"); console.log("Solve => %s\n",expression); if (lever) return expression;
        expression = ease(expression,"/"); console.log("Solve => %s\n",expression); if (lever) return expression;
        expression = ease(expression,"-"); console.log("Solve => %s\n",expression); if (lever) return expression;
        expression = ease(expression,"+"); console.log("Solve => %s\n",expression); return expression;
    }
    
    function reduce(expression){
        expression = expression.replace(/\([^\(\)]*\)/g, function(match,offset,string){
                match = match.replace(/(^\()|(\)$)/g,"");
                return (match=solve(match));
        });
        console.log("Reduce => %s\n",expression);
        return (expression.match(/\(.*\)/) ? reduce(expression) :...