Информация об изменениях

Сообщение Re: вычислить выражение записанное в виде строки символов от 01.02.2026 7:49

Изменено 01.02.2026 8:04 kov_serg

Re: вычислить выражение записанное в виде строки символов
Здравствуйте, system.console, Вы писали:

SC>hi all,

SC>подскажите, как в PHP сделать сабж.
SC>Функции передается строка, содержащая арифметическое выражение.
SC>Функция должна вернуть результат.
SC>Выражение несложное — без всяких там функций и переменных, только +,-,*,/ и скобки.

SC>вот типа такого, только без eval()


SC>
SC>function string_calculator($str){
SC>    eval("\$str = $str;");
SC>    return $str;
SC>}

SC>echo string_calculator("(10+60/2)*2");
SC>


Например так:
  function expr
<?php

class Expr {
    var $s, $p, $n;
    function calc($text) {
        $this->s=$text; $this->p=0; $this->n=strlen($text);
        $r=$this->L0();
        $this->skipspace(); if (!$this->eos()) $this->wtf();
        return $r;
    }
    function L0() {
        $r=$this->L1();
        for(;;) {
            if ($this->chk('+')) $r=$r+$this->L1();
            elseif ($this->chk('-')) $r=$r-$this->L1();
            else break;
        }
        return $r;
    }
    function L1() {
        $r=$this->L2();
        for(;;) {
            if ($this->chk('*')) $r=$r*$this->L2();
            elseif ($this->chk('/')) $r=$r/$this->L2();
            else break;
        }
        return $r;
    }
    function L2() {
        if ($this->chk('(')) {
            $r=$this->L0();
            if (!$this->chk(')')) $this->error("no closing braket");
            return $r;
        }
        return $this->L3();
    }
    function L3() {
        if ($this->chk('-')) return -$this->L2();
        if ($this->chk('+')) return $this->L2();
        return $this->L4();
    }
    function L4() {
        $this->skipspace(); $c=$this->peek();
        if (self::is_digit($c) || $c=='-') return $this->number();
        elseif (self::is_letter($c)) return $this->name();
        else $this->wtf();
    }
    function number() {
        $r=0; $sign=$this->chk('-');
        $c=$this->peek(); if (!self::is_digit($c)) $this->error("invalid number");
        while(self::is_digit($c)) {
            $r=$r*10 + ($c-'0');
            $this->p++; $c=$this->peek();
        }
        if ($this->chk('.')) {
            $np=1.0; $c=$this->peek();
            while(self::is_digit($c)) {
                $np/=10;
                $r+=($c-'0')*$np;
                $this->p++; $c=$this->peek();
            }
        }
        if ($this->chk('e') || $this->chk('E')) {
            $ne=0; $ns=false;
            $c=$this->peek(); 
            if ($c=='-') { $ns=true; $this->p++; $c=$this->peek(); }
            elseif($c=='+') { $this->p++; $c=$this->peek(); }
            if (!self::is_digit($c)) $this->error("invalid number");
            while(self::is_digit($c)) {
                $ne=$ne*10 + ($c-'0');
                $this->p++; $c=$this->peek();
            }
            if ($ns) $ne=-$ne;
            $r*=pow(10.0,$ne);
        }
        return $sign ? -$r : $r;
    }
    var $names=[
        'pi'=>3.1415926535897932,
        'e' =>2.7182818284590452,
    ];
    function init() {
        $this->names['sin']=function($x) { return sin($x); };
        $this->names['cos']=function($x) { return cos($x); };
        $this->names['exp']=function($x) { return exp($x); };
        $this->names['ln']=function($x) { return log($x); };
        $this->names['pow']=function($x,$n) { return pow($x,$n); };
    }
    function name() {
        if (preg_match('/\b([A-Za-z_]+)/',$this->s,$m,0,$this->p)) {
            $name=$m[1]; $len=strlen($name);
            if (!isset($this->names[$name])) $this->error("undefined name $name",$len);
            $this->p+=$len;
            if ($this->chk('(')) return $this->func($name);
            return $this->names[$name];
        }
        $this->error("name expected");
    }
    function func($name) {
        $args=[];
        if (!$this->chk(')')) for(;;) {
            if ($this->eos()) $this->error("unexpected end");
            array_push($args,$this->L0());
            if ($this->chk(')')) break;
            if (!$this->chk(',')) $this->wtf();
        }
        $fn=$this->names[$name];
        $r=call_user_func_array($fn,$args);
        return $r;
    }
    static function is_space($c) { return ord($c)>0 && $c<=' '; }
    static function is_digit($c) { return $c>='0' && $c<='9'; }
    static function is_letter($c) { return ($c>='a' && $c<='z') || ($c>='A' && $c<='Z') || ($c=='_'); }
    function eos() { return $this->p>=$this->n; }
    function peek() { return $this->p<$this->n ? $this->s[$this->p] : '\0'; }
    function get() { return $this->p<$this->n ? $this->s[$this->p++] : '\0'; }
    function skipspace() { while(self::is_space($this->peek())) $this->p++; }
    function chk($c) {
        $this->skipspace();
        if ($this->peek()!=$c) return false;
        $this->p++; return true;
    }
    function wtf() { $this->error("unexpected symbol ".$this->peek()); }
    function error($msg,$len=1) {
        $p=$this->p; $n=$this->n; $w=20;
        $q=[ $p-$w, $p, $p+$len, $p+$len+$w ]; $qn=count($q);
        foreach($q as &$x) if ($x<0) $x=0; elseif ($x>$n) $x=$n;
        $ss=$q[0]==0 ? "" : "...";
        for($i=1;$i<$qn;$i++) {
            if ($i==2) $ss.='[??';
            $ss.=substr($this->s,$q[$i-1],$q[$i]-$q[$i-1]);
            if ($i==2) $ss.='??]';
        }
        if ($q[$qn-1]!=$n) $ss.="...";
        throw new Exception($msg.": $ss\n"); 
    }
}

function expr($s) {
    $e=new Expr(); $e->init();
    return $e->calc($s);
}
$s="2+2";
$r=expr($s);
print("$s=$r\n");
2+2=4
Re: вычислить выражение записанное в виде строки символов
Здравствуйте, system.console, Вы писали:

SC>hi all,

SC>подскажите, как в PHP сделать сабж.
SC>Функции передается строка, содержащая арифметическое выражение.
SC>Функция должна вернуть результат.
SC>Выражение несложное — без всяких там функций и переменных, только +,-,*,/ и скобки.

SC>вот типа такого, только без eval()


SC>
SC>function string_calculator($str){
SC>    eval("\$str = $str;");
SC>    return $str;
SC>}

SC>echo string_calculator("(10+60/2)*2");
SC>


Например так:
  function expr
<?php

class Expr {
    var $s, $p, $n;
    function calc($text) {
        $this->s=$text; $this->p=0; $this->n=strlen($text);
        $r=$this->L0();
        $this->skipspace(); if (!$this->eos()) $this->wtf();
        return $r;
    }
    function L0() {
        $r=$this->L1();
        for(;;) {
            if ($this->chk('+')) $r=$r+$this->L1();
            elseif ($this->chk('-')) $r=$r-$this->L1();
            else break;
        }
        return $r;
    }
    function L1() {
        $r=$this->L2();
        for(;;) {
            if ($this->chk('*')) $r=$r*$this->L2();
            elseif ($this->chk('/')) $r=$r/$this->L2();
            else break;
        }
        return $r;
    }
    function L2() {
        if ($this->chk('(')) {
            $r=$this->L0();
            if (!$this->chk(')')) $this->error("no closing braket");
            return $r;
        }
        return $this->L3();
    }
    function L3() {
        if ($this->chk('-')) return -$this->L2();
        if ($this->chk('+')) return $this->L2();
        return $this->L4();
    }
    function L4() {
        $this->skipspace(); $c=$this->peek();
        if (self::is_digit($c) || $c=='-') return $this->number();
        elseif (self::is_letter($c)) return $this->name();
        else $this->wtf();
    }
    function number() {
        $r=0; $sign=$this->chk('-');
        $c=$this->peek(); if (!self::is_digit($c)) $this->error("invalid number");
        while(self::is_digit($c)) {
            $r=$r*10 + ($c-'0');
            $this->p++; $c=$this->peek();
        }
        if ($this->chk('.')) {
            $np=1.0; $c=$this->peek();
            while(self::is_digit($c)) {
                $np/=10;
                $r+=($c-'0')*$np;
                $this->p++; $c=$this->peek();
            }
        }
        if ($this->chk('e') || $this->chk('E')) {
            $ne=0; $ns=false;
            $c=$this->peek(); 
            if ($c=='-') { $ns=true; $this->p++; $c=$this->peek(); }
            elseif($c=='+') { $this->p++; $c=$this->peek(); }
            if (!self::is_digit($c)) $this->error("invalid number");
            while(self::is_digit($c)) {
                $ne=$ne*10 + ($c-'0');
                $this->p++; $c=$this->peek();
            }
            if ($ns) $ne=-$ne;
            $r*=pow(10.0,$ne);
        }
        return $sign ? -$r : $r;
    }
    var $names=[
        'pi'=>3.1415926535897932,
        'e' =>2.7182818284590452,
    ];
    function init() {
        $this->names['sin']=function($x) { return sin($x); };
        $this->names['cos']=function($x) { return cos($x); };
        $this->names['exp']=function($x) { return exp($x); };
        $this->names['ln']=function($x) { return log($x); };
        $this->names['pow']=function($x,$n) { return pow($x,$n); };
    }
    function name() {
        if (preg_match('/\b([A-Za-z_][A-Za-z0-9_]*)/',$this->s,$m,0,$this->p)) {
            $name=$m[1]; $len=strlen($name);
            if (!isset($this->names[$name])) $this->error("undefined name $name",$len);
            $this->p+=$len;
            if ($this->chk('(')) return $this->func($name);
            return $this->names[$name];
        }
        $this->error("name expected");
    }
    function func($name) {
        $args=[];
        if (!$this->chk(')')) for(;;) {
            if ($this->eos()) $this->error("unexpected end");
            array_push($args,$this->L0());
            if ($this->chk(')')) break;
            if (!$this->chk(',')) $this->wtf();
        }
        $fn=$this->names[$name];
        $r=call_user_func_array($fn,$args);
        return $r;
    }
    static function is_space($c) { return ord($c)>0 && $c<=' '; }
    static function is_digit($c) { return $c>='0' && $c<='9'; }
    static function is_letter($c) { return ($c>='a' && $c<='z') || ($c>='A' && $c<='Z') || ($c=='_'); }
    function eos() { return $this->p>=$this->n; }
    function peek() { return $this->p<$this->n ? $this->s[$this->p] : '\0'; }
    function get() { return $this->p<$this->n ? $this->s[$this->p++] : '\0'; }
    function skipspace() { while(self::is_space($this->peek())) $this->p++; }
    function chk($c) {
        $this->skipspace();
        if ($this->peek()!=$c) return false;
        $this->p++; return true;
    }
    function wtf() { $this->error("unexpected symbol ".$this->peek()); }
    function error($msg,$len=1) {
        $p=$this->p; $n=$this->n; $w=20;
        $q=[ $p-$w, $p, $p+$len, $p+$len+$w ]; $qn=count($q);
        foreach($q as &$x) if ($x<0) $x=0; elseif ($x>$n) $x=$n;
        $ss=$q[0]==0 ? "" : "...";
        for($i=1;$i<$qn;$i++) {
            if ($i==2) $ss.='[??';
            $ss.=substr($this->s,$q[$i-1],$q[$i]-$q[$i-1]);
            if ($i==2) $ss.='??]';
        }
        if ($q[$qn-1]!=$n) $ss.="...";
        throw new Exception($msg.": $ss\n"); 
    }
}

function expr($s) {
    $e=new Expr(); $e->init();
    return $e->calc($s);
}
$s="2+2";
$r=expr($s);
print("$s=$r\n");
2+2=4