<?php
/**
 * IBSE
 *
 * Information Bot Service Engine
 *
 * @package        IBSE
 * @author        HSDN Team
 * @copyright    Copyright (c) 2006-2010, Information Networks Ltd.
 * @link        http://www.hsdn.org
 * @since        Version 4.0
 */

/**
 * IBAPI
 *
 * Information Bot Application Program Interface
 *
 * @package        IBSE
 * @author        HSDN Team
 * @copyright    Copyright (c) 2006-2010, Information Networks Ltd.
 * @since        Version 4.0
 */

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

/**
 * Класс Expr
 *
 * @author        HSDN Team
 *
 * @original    instead-urq
 * @link        http://code.google.com/p/instead-games/
 */
class Expr extends Urq
{
    const 
LEFT 0;
    const 
RIGHT 1;
    const 
FUNC = -2;
    const 
PAR = -1;

    private 
$strings;
    private 
$operators;


    public function 
__construct($strings = array())
    {
        
$this->strings $strings;
    }

    public function 
call($s)
    {
        
$this->operators = array
        (
            
"||" => $this->op(1022'op_or'),
            
"&&" => $this->op(2022'op_and'),
            
"<>" => $this->op(3022'op_ne'"<[ \t]*>"),
            
"!=" => $this->op(3022'op_ne'"![ \t]*="),
            
"~=" => $this->op(3022'op_ne'"~[ \t]*="),
            
"=" => $this->op(4022'op_eq'"=[ \t]*"),
            
"==" => $this->op(4022'op_seq'),
            
">=" => $this->op(5022'op_ge'">[ \t]*="),
            
">" => $this->op(5022'op_gt'">"),
            
"<=" => $this->op(5022'op_le'"<[ \t]*="),
            
"<" => $this->op(5022'op_lt'"<"),
            
"-" => $this->op(6021'op_minus'),
            
"+" => $this->op(6021'op_plus'),
            
"/" => $this->op(7022'op_div'),
            
"*" => $this->op(7022'op_mul'),
            
"^" => $this->op(8022'op_pow'),
            
"!" => $this->op(8011'op_not'),
            
"(" => $this->op(self::PAR0),
            
")" => $this->op(self::PAR0),
        );

        
$t $this->expr2rpn($s);

        
$r $this->rpnexpr($t);
        
$t $this->get_string($r);

        if (
$t !== NULL)
        {
            
$r = (string) $t;
        }
        else
        {
            
$r = (float) $r;
        }

        return 
$r;
    }

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

    
public function get_string($a)
    {
        if (
$this->is_string_pattern($a))
        {
            if (
$a == '')
            {
                return 
'';
            }

            
$a sub($a15);

            return isset(
$this->strings[$a]) ? $this->strings[$a] : '';
        }
        if (!
is_numeric($a))
        {
            return (string) 
$a;
        }

        return 
NULL;
    }

    function 
from_strings($a$b$nt FALSE)
    {
        
$aa $this->get_string($a);
        
$bb $this->get_string($b);

        if (
$aa !== NULL and $nt === FALSE)
        {
            
$a strlen($aa);
        }

        if (
$bb !== NULL and $nt === FALSE)
        {
            
$b strlen($bb);
        }

        if (
$a === NULL OR $b === NULL)
        {
            die(
"Error in line: ".$this->urq['ip']);
        }

        return array((float) 
$a, (float) $b);
    }

    private function 
op($pri 0$dir 0$na 0$mna 0$f ''$regex '')
    {
        return array
        (
            
'pri' => $pri
            
'dir' => $dir
            
'nr' => $na
            
'mnr' => $mna
            
'f' => $f
            
'reg' => $regex
        
);
    }

    private function 
op_or($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        if (
$a != FALSE OR $b != FALSE)
        {
            return 
TRUE;
        }

        return 
FALSE;
    }

    private function 
op_and($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        if (
$a != FALSE AND $b != FALSE)
        {
            return 
TRUE;
        }

        return 
FALSE;
    }

    private function 
op_div($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        if (
$b == 0)
        {
            return 
0;
        }

        return (float) (
$a $b);
    }

    private function 
op_mul($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        return (float) (
$a $b);
    }

    private function 
op_plus($a NULL$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        
// For FireURQ
        /*if (isset($this->urq['extension_furq']))
        {
            if ($a != NULL AND $b != NULL)
            {
                array_push($this->urq['strings'], $a.$b);

                return "__urq_string__".sizeof($this->urq['strings']);
            }
        }*/

        
if ($b === NULL)
        {
            return (float) 
$a;
        }

        return (float) (
$a $b);
    }

    private function 
op_minus($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        if (
$b === NULL)
        {
            return (float) -
$a;
        }

        return (float) (
$a $b);
    }

    private function 
op_pow($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        return (float) 
pow($a$b);
    }

    private function 
op_seq($a NULL$b NULL)
    {
        
// For FireURQ
        
if (isset($this->urq['extension_strings']))
        {
            
$aa $this->get_string($a);
            
$bb $this->get_string($b);

            if (
$aa != NULL AND $bb != NULL)
            {
                
$aa str_replace("[?]"".?"str_replace("\*"".*"strtolower($aa)));
                
$bb str_replace("[?]"".?"str_replace("\*"".*"strtolower($bb)));

                if (
preg_match("^".$bb."$"$aa) OR preg_match("^".$aa."$"$bb))
                {
                    return 
TRUE;
                }
            }
        }

        return 
$this->op_eq($a$b); // ok, is it hack?
    
}

    private function 
op_eq($a NULL$b NULL)
    {
        
// For FireURQ
        
if (isset($this->urq['extension_strings']))
        {
            
$aa $this->get_string($a);
            
$bb $this->get_string($b);

            if (
$aa != NULL AND $bb != NULL)
            {
                if (
strtolower($aa) == strtolower($bb))
                {
                    return 
TRUE;
                }

                return 
FALSE;
            }
        }

        list(
$a$b) = $this->from_strings($a$b, !$this->get_string($b));

        if (
$a == $b)
        {
            return 
TRUE;
        }

        return 
FALSE;
    }


    function 
op_lt($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        if (
$a $b)
        {
            return 
TRUE;
        }

        return 
FALSE;
    }

    private function 
op_le($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        if (
$a <= $b)
        {
            return 
TRUE;
        }

        return 
FALSE;
    }

    private function 
op_gt($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        if (
$a $b)
        {
            return 
TRUE;
        }

        return 
FALSE;
    }

    private function 
op_ge($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        if (
$a >= $b)
        {
            return 
TRUE;
        }

        return 
FALSE;
    }

    private function 
op_ne($a NULL$b NULL)
    {
        if (isset(
$this->urq['extension_strings']))
        {
            
$aa $this->get_string($a);
            
$bb $this->get_string($b);

            if (
$aa !== NULL AND $bb !== NULL)
            {
                if (
strtolower($aa) != strtolower($bb))
                {
                    return 
TRUE;
                }

                return 
FALSE;
            }
        }

        list(
$a$b) = $this->from_strings($a$b NULL);

        if (
$a != $b)
        {
            return 
TRUE;
        }

        return 
FALSE;
    }

    private function 
op_not($a$b NULL)
    {
        list(
$a$b) = $this->from_strings($a$b);

        if (
$a != $b)
        {
            return 
FALSE;
        }

        return 
TRUE;
    }

    private function 
is_op($s)
    {
        return (isset(
$this->operators[$s]) AND $this->operators[$s]['pri'] > 0);
    }

    private function 
is_token($s)
    {
        return isset(
$this->operators[$s]);
    }

    private function 
find_op($s)
    {
        
$oper NULL;
        
$resi NULL;
        
$resk NULL;

        foreach (
$this->operators as $k => $o)
        {
            if (
$o['reg'] != '')
            {
                list(
$i$n) = find($s$o['reg']);
            }
            else
            {
                list(
$i$n) = find($s$k1TRUE);
            }
            
            if (
$resi === NULL)
            {
                
$resi $i
                
$resk $n
                
$oper $k;
            }

            if (
$i AND (($i $resi) OR ($i == $resi AND strlen($k) > strlen($oper))))
            {
                
$resi $i;
                
$resk $n;
                
$oper $k;
            }
        }
            
        return array(
$resi$resk$oper);
    }

    function 
get_token($s)
    {
        if (
$s === '')
        {
            return;
        }

        
$s preg_replace("#[ \t]+#"""$s);

        if (
preg_match("#^[ \t]*$#"$s))
        {
            return array(
NULLNULL);
        }

        
$f $this->find_op($s);

        if (
$f === FALSE)
        {
            return array(
$sNULL);
        }

        list(
$i$k$oper) = $f;

        if (
$i === NULL)
        {
            return array(
preg_replace("#[ \t]+$#"""$s), NULL);
        }

        if (
$i === 1)
        {
            return array(
$opersub($s$k 1)); // unar
        
}

        return array(
sub(preg_replace("#[ \t]+$#"""$s), 1$i 1), sub($s$i));

    }

    function 
expr2rpn($s)
    {
        
$lastt NULL;

        
$opstack = new Stack;
        
$output = new Stack;

        while (
TRUE)
        {
            
$sign 1;

            list(
$t$s) = $this->get_token($s);

            while (
$t == '-' and ($this->is_op($lastt) OR $lastt == NULL)) // hack for unar -
            
{
                
$sign = -$sign;

                list(
$t$s) = $this->get_token($s);

                if (
is_numeric($t)) 
                {
                    
$t $sign $t

                    break;
                }
            }

            if (
$t != '(' AND $t != ')')
            {
                
$lastt $t;
            }

            if (
$t == NULL// no token
            
{
                while (
$opstack->top())
                {
                    
$t $opstack->pop();

                    if (
$t == "(" or $t == ")")
                    {
                        return 
NULL;
                    }

                    
$output->push($t);
                }

                return 
$output->v;
            }

            if (
$t == '(')
            {
                
$opstack->push($t);
            }
            else if (
$t == ')')
            {
                while ((
$top $opstack->top()) AND $top != '(')
                {
                    
$output->push($opstack->pop());

                    if ((
$top $opstack->top()) AND $this->operators[$top]['pri'] == self::FUNC)
                    {
                        
$output->push($opstack->pop());
                    }
                }

                if (
$opstack->top() != '(')
                {
                    return 
NULL;
                }

                
$opstack->pop();
            }
            else if (!isset(
$this->operators[$t]) OR $this->operators[$t] == NULL)
            {
                
$output->push($t);
            }
            else if (isset(
$this->operators[$t]['pri']) AND $this->operators[$t]['pri'] == self::FUNC)
            {
                
$opstack->push($t);
            }
            else if (isset(
$this->operators[$t]))
            {
                
$o1 $this->operators[$t];
                
                while (
$this->is_op($opstack->top()))
                {
                    
$o2 $this->operators[$opstack->top()];

                    if ((
$o1['dir'] == self::LEFT AND $o1['pri'] <= $o2['pri']) 
                        OR 
                        (
$o1['dir'] == self::RIGHT AND $o1['pri'] < $o2['pri']))
                    {
                        
$output->push($opstack->pop());
                    }
                    else
                    {
                        break;
                    }
                }

                
$opstack->push($t);
            }
        }
    }

    private function 
rpnexpr($t)
    {
        
$t = new Table($t1);

        
$top 1;

        while (
$t->AND $top <= $t->maxn())
        {
            if (
$this->is_op($t->t[$top]))
            {
                
$o $t->t[$top];
                
$t->remove($top);

                
$oper $this->operators[$o];

                
$n $oper['nr'];
                
$f $oper['f'];
                
$top--;

                if (
$top $n)
                {
                    if (isset(
$oper['mnr']) AND $top >= $oper['mnr'])
                    {
                        
$n $top;
                    }
                    else
                    {
                        return 
NULL// no enouth args
                    
}
                }

                
$a = array();
                
$i $top $n 1;

                while (
$n != 0)
                {
                    if (!isset(
$t->t[$i]))
                    {
                        die(
"Error in expression in line: ".$i);
                    }

                    if (!
is_bool($t->t[$i]) AND !is_numeric($t->t[$i]))
                    {
                        
$t->t[$i] = (float) $t->t[$i];
                    }

                    
array_push($a$t->t[$i]);

                    
$t->remove($i);
                    
$n--;
                }

                
//print("\n".$a[0].",".$a[1]." --> ".$f."\n");

                
$r call_user_func_array(array($this$f), $a);

                if (
$r === NULL)
                {
                    return 
NULL;
                }

                
$t->insert($r$i);

                
$top $i 1;
            }
            else
            {
                
$top++;
            }
        }

        if (
$t->== NULL OR sizeof($t->t) < 1)
        {
            return 
NULL// no result
        
}

        if (
sizeof($t->t) > 1)
        {
            return 
NULL// multi?
        
}

        return 
$t->t[1];
    }
}

/* EOF Expr.class.php */