jCalc

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

HTML

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

Разбор строки осуществляется с помощью регулярных выражений без применения дополнительных алгоритмов. Незаконченная функция specFuncsEx является попыткой реализовать изначально задуманный функционал вложенных функций. На данный момент калькулятор понимает только функции первого порядка и арифметические выражения любого уровня вложенности.-->

<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>*, /, - 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,</li><li>(a+b)*(c-d) should work fine.</li></ul></li><li>sin(), cos(), pow (e.g. 2^2) and factorial (e.g. 5!) functions are supported at the moment. They may be NOT 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;
        }
        return expression;
    }
    
    function solve(expression) {
        var lever = false;
        expression = ease(expression,"*"); if (lever) return expression;
        expression = ease(expression,"/"); if (lever) return expression;
        expression = ease(expression,"-"); if (lever) return expression;
        expression = ease(expression,"+"); return expression;
    }
    
    function reduce(expression){
        expression = expression.replace(/\([^\(\)]*\)/g, function(match,offset,string){
                match = match.replace(/(^\()|(\)$)/g,"");
                return (match=solve(match));
        });
        return (expression.match(/\(.*\)/) ? reduce(expression) : solve(expression));
    }
    
    function specFuncs(expression){
        var funcList = /(sin\(\d+\.?\d*\))|(cos\(\d+\.?\d*\))|(\d+\.?\d*\^\d+\.?\d*)|(\d+\.?\d*\!)/gi;
        expression = expression.replace(funcList, function(match,p1,p2,p3,p4,offset,string){
           ...