ФорумПрограммированиеPHP для идиотов → Пишем интерпретатор логических выражений (для разгр. прав)

Пишем интерпретатор логических выражений (для разгр. прав)

  • Trej Gun

    Сообщения: 5299 Репутация: N Группа: в ухо

    Spritz 29 сентября 2009 г. 9:42, спустя 1 час 19 минут 42 секунды

    :)
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 29 сентября 2009 г. 11:40, спустя 1 час 58 минут 7 секунд

    Теперь класс синтаксического анализа.
    Он же и формирует строку для вычислителя.
    Здесь используется немного доработанный класс лексического анализатора.
    Переименовали его в lexer и имена лексем вынесли в константы (так их названия подхватывает "завершитель кода"  в редакторе)
    class sintax{
     /**
     * Группы лексем, и соответствующие им коды операций
     * <oper_1>    ::= "+" | "-"
     * <oper_2>    ::= "*" | "/" | "%"      
     *
     * @var array
     */
     private $lexGroups = array(
      'oper_1' => array(
                    lxPlus     => '+',
                    lxMinus    => '-'  ),
      'oper_2' => array(
                    lxAsterisk => '*',
                    lxDiv      => '/',
                    lxMod      => '%')
     );
     
     /**
     * Объект вычислителя
     * @var calc
     */
     private $calc;
     
     /**
     * Конструктор
     */
     public function __construct(){
       $this->calc = new calc();    
     }

     /**
     * <operand>   ::= <number> | "(" <expression_1> ")"
     * // Это и следующее правило интегрировано в lexer
     * <number>    ::= <digit> { <digit> }
     * <digit>     ::= "0" | .. | "9"  
     *
     * @param lex $lexer
     */
     private function bnfOperand($lexer){
       if(lxNumber == $lexer->type()){
         $opn =  'c'.$lexer->value();
       }elseif(lxBracketL == $lexer->type()){
         $lexer->next();
         $opn = $this->bnfExpr_1($lexer);
         // дальше должна быть закрывающая скобка
         if(lxBracketR != $lexer->type()){
           throw new Exception('Ожидается lxBracketR');
         }
       }else{ // Ни числа ни скобки - исключение!
         throw new Exception('Ожидается lxNumber или lxBracketL');
       }
       $lexer->next(); // переступаем через число или скобку … кто там был
       return $opn;
     }
     
     /**
     * <expr_3>    ::= <operand>
     * @param mixed $lexer
     */
     private function bnfExpr_3($lexer){
       return $this->bnfOperand($lexer);
     }
     
     /**
     * <expr_2>    ::= <expr_3> { <oper_2> <expr_3>}
     * @param lexer $lexer
     */
     private function bnfExpr_2($lexer){
       $opn = $this->bnfExpr_3($lexer);
       while(isset($this->lexGroups['oper_2'][$lexer->type()])){
         $oper = 'p'.$this->lexGroups['oper_2'][$lexer->type()];
         $lexer->next();
         $tmp  = $this->bnfExpr_3($lexer);
         $opn .= ' '.$tmp.' '.$oper;
       }    
       return $opn;
     }

     /**
     * <expr_1>    ::= <expr_2> { <oper_1> <expr_2>}
     * @param lexer $lexer
     */
     private function bnfExpr_1($lexer){
       $opn = $this->bnfExpr_2($lexer);
       while(isset($this->lexGroups['oper_1'][$lexer->type()])){
         $oper = 'p'.$this->lexGroups['oper_1'][$lexer->type()];
         $lexer->next();
         $tmp  = $this->bnfExpr_2($lexer);
         $opn .= ' '.$tmp.' '.$oper;
       }
       return $opn;
     }
       
     /**
     * Запуск вычисления выражения
     *
     * @param string $expression
     * @return integer
     */
     public function calculate($expression){
       $lexer  = new lexer($expression);
       $opn    = $this->bnfExpr_1($lexer);
       if(lxEof != $lexer->type()){
         throw new Exception('Выражение разобрано не до конца!');
       }
       // Это для демонстрашки. интересно же :)
       echo "\n ОПН      : \"$opn\"";
       $result = $this->calc->calcString($opn);
       return $result;    
     }  
    }
    Спустя 93 сек.
    Пример запуска:
     $str = '5 / 2 * (3 + 4) * 2';
    $sintax = new sintax();
    echo "<PRE>\n Выражение: $str";
    $res = $sintax->calculate($str);
    echo "\n Результат: $res</PRE>";


    Ииии….. !!!!!

    [tt] Выражение: 5 / 2 * (3 + 4) * 2
    ОПН      : "c5 c2 p/ c3 c4 p+ p* c2 p*"
    Результат: 28
    [/tt]
    Черновой вариант готов. Работает. Теперь надо это всё обдумать …
  • NRG

    Сообщения: 4761 Репутация: N Группа: в ухо

    Spritz 29 сентября 2009 г. 11:42, спустя 1 минуту 55 секунд

    private $calc;

    /**
    * Конструктор
    */
    public function __construct(){
    $this->calc = new calc();
    }

    первое что бросилось в глаза…

    почем у не так ?

    private $_calc = null;

    public function init()
    {
    if (is_null($this->_calc)) {
    $this->_calc = new calc;
    }
    return $this;
    }

    public function __construct(){
    $this->init();
    }
    Спустя 72 сек.
    про именование переменных и методов я опять молчу….
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 29 сентября 2009 г. 11:44, спустя 2 минуты 12 секунд

    calc создается один раз при создании самого sintax и не удаляется. Нет надобности проверять, есть он или нет его. Но это так.
    Код я совсем не вылизывал … пока добивался работоспособности.
  • NRG

    Сообщения: 4761 Репутация: N Группа: в ухо

    Spritz 29 сентября 2009 г. 11:50, спустя 6 минут 26 секунд

    не обижайся, я вечно доебуюсь к хуйне
  • phpdude

    Сообщения: 26624 Репутация: N Группа: в ухо

    Spritz 29 сентября 2009 г. 11:53, спустя 2 минуты 44 секунды

    ухня!
    Сапожник без сапог
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 29 сентября 2009 г. 12:30, спустя 37 минут 18 секунд

    Я в экстазе!
    Добавляем унарные плюс и минус:
    Подправляем БНФ правило
    <operand>   ::= <oper_u> <operand> | <number> | "(" <expression_1> ")"
    <oper_u>    ::= "+" | "-"

    Изменений в коде - фигня. Расширяем массив private $lexGroups - добавляем группу 'oper_u' с парой элементов и подправляем метод bnfOperand


       }elseif(isset($this->lexGroups['oper_u'][$lexer->type()])){
         $oper = $this->lexGroups['oper_u'][$lexer->type()];
         $lexer->next();
         $tmp  = $this->bnfOperand($lexer);
         $opn  = $tmp.' '.'p'.$oper;
       }elseif(lxBracketL == $lexer->type()){


    И всё! Теперь мы и унарные операции знаем:
    [tt] Выражение: 8 % 3 * -(3 + 4) * 2
    ОПН      : "c8 c3 p% c3 c4 p+ pu- p* c2 p*"
    Результат: -28
    [/tt]

  • md5

    Сообщения: 11949 Репутация: N Группа: в ухо

    Spritz 29 сентября 2009 г. 12:34, спустя 3 минуты 19 секунд

    а зачем оно вобще нужно?
    где применятеся на практике?

    p.s. я смотрю, на пыхе все больше и больше миллионеров появляется)
    все умрут, а я изумруд
  • phpdude

    Сообщения: 26624 Репутация: N Группа: в ухо

    Spritz 29 сентября 2009 г. 12:39, спустя 5 минут 23 секунды


    а зачем оно вобще нужно?
    где применятеся на практике?

    p.s. я смотрю, на пыхе все больше и больше миллионеров появляется)
    +1
    Сапожник без сапог
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 29 сентября 2009 г. 12:48, спустя 8 минут 57 секунд

    Будет использоваться в "разграничении прав на уровне записи".
    Выражением буде задано, имеет ли право юзер выполнять над данной записью (из какой-нить одной/нескольких таблиц) указанное действие.
  • md5

    Сообщения: 11949 Репутация: N Группа: в ухо

    Spritz 29 сентября 2009 г. 12:49, спустя 1 минуту 17 секунд

    сложно-то все как, ладно
    а я пойду дальше зарабатывать деньги :D
    все умрут, а я изумруд
  • Trej Gun

    Сообщения: 5299 Репутация: N Группа: в ухо

    Spritz 29 сентября 2009 г. 16:09, спустя 3 часа 19 минут 33 секунды

    миллионеры на пiха.ру
  • AndryG

    Сообщения: 237 Репутация: N Группа: Адекваты

    Spritz 30 сентября 2009 г. 13:16, спустя 21 час 7 минут 11 секунд

    Волна_2 добавим макрооперации для удобной записи проверки бит.

    Очень часто в выражениях нужно устраивать проверки (сразу же привожу классический вариант записи)
    "все биты a,b,c в D подняты"
    [tt] (D & (1<<a | 1<<b | 1<<c)) == (1<<a | 1<<b | 1<<c) [/tt]

    "встречаются установленные биты среди битов a,b,c в D"
    [tt] (D & (1<<a | 1<<b | 1<<c)) > 0[/tt]


    Всё ещё страшнее, если a,b,c - выражения, а не константы.

    Для более наглядной записи [tt](1<<a | 1<<b | 1<<c)[/tt] я придумал такой вариант [tt]{a,b,c..d,e}[/tt]

    Прошу помочь с наглядным вариантом для записи проверки бит. Вместо
    [tt]A & B == B[/tt] … ?
    [tt]A & B != 0[/tt] … ?
    и т.д.
  • Trej Gun

    Сообщения: 5299 Репутация: N Группа: в ухо

    Spritz 30 сентября 2009 г. 13:50, спустя 33 минуты 44 секунды

    AndryG, на самом деле ты крут. эта хуйня никаму нахуй не надо. те кому она могла понадобится уже с ней разобрались и забыли. а ты сидишь и упорно ебешся. с одной стороны я не знаю битовых операций. нет я конечно знаю чо делают операторы над операндами но я не знаю где бы я сумел это применить даже если бы очень захотел. но ты! ты крут! ты расчехлишь битовые операции. обратную польскую нотацию. напишешь свой велосыпед калькулятор. и похвастаешся перед всеми своими знаниями. которые ты нигде не сможешь применить. но ты будешь знать. и будеш знать что применить их нельзя. и жызнь потеряет краски и ты впадешь в депресию и повесишся. и на могиле напишут что ты знал но никто не оценил.

    пойди лучше чтото полезное выучи, регулярки что ли…
  • phpdude

    Сообщения: 26624 Репутация: N Группа: в ухо

    Spritz 30 сентября 2009 г. 13:56, спустя 6 минут 12 секунд

    я сейчас ебусь с побитовыми операциями, уж не думал)))

    но ничо, успешно :)
    Сапожник без сапог

Пожалуйста, авторизуйтесь, чтобы написать комментарий!