'===', '!=' => '!==', 'of' => 'in' ); static $INVERSIONS = array( '!==' => '===', '===' => '!==' ); public $children = array('first', 'second'); public $operator = NULL; public $invert = TRUE; function constructor($op, $first, $second = NULL, $flip = NULL) { if ($op === 'in') { return yy('In', $first, $second); } if ($op === 'do') { return $this->generate_do($first); } if ($op === 'new') { if ($first instanceof yy_Call && ! (isset($first->do) && $first->do) && ! (isset($first->is_new) && $first->is_new)) { return $first->new_instance(); } if ($first instanceof yy_Code && $first->bound || (isset($first->do) && $first->do)) { $first = yy('Parens', $first); } } $this->operator = isset(self::$CONVERSIONS[$op]) ? self::$CONVERSIONS[$op] : $op; $this->first = $first; $this->second = $second; $this->flip = !! $flip; return $this; } function compile_chain($options) { $tmp = $this->first->second->cache($options); $this->first->second = $tmp[0]; $shared = $tmp[1]; $fst = $this->first->compile($options, LEVEL_OP); $code = "{$fst} ".($this->invert ? '&&' : '||').' '.$shared->compile($options).' ' .$this->operator.' '.$this->second->compile($options, LEVEL_OP); return "({$code})"; } function compile_existence($options) { if ($this->first->is_complex() && $options['level'] > LEVEL_TOP) { $ref = yy('Literal', $options['scope']->free_variable('ref')); $fst = yy('Parens', yy('Assign', $ref, $this->first)); } else { $fst = $this->first; $ref = $fst; } $tmp = yy('If', yy('Existence', $fst), $ref, array('type' => 'if')); $tmp->add_else($this->second); return $tmp->compile($options); } function compile_node($options, $level = NULL) { $is_chain = $this->is_chainable() && $this->first->is_chainable(); if ( ! $is_chain) { $this->first->front = $this->front; } $tmp = $this->first->unwrap_all(); $tmp = isset($tmp->value) ? $tmp->value : NULL; if ($this->operator === 'delete' && $options['scope']->check($tmp)) { throw new SyntaxError('delete operand may not be argument or var'); } if (in_array($this->operator, array('--', '++')) && in_array($tmp, Lexer::$STRICT_PROSCRIBED)) { throw new SyntaxError('prefix increment/decrement may not have eval or arguments operand'); } if ($this->is_unary()) { return $this->compile_unary($options); } if ($is_chain) { return $this->compile_chain($options); } if ($this->operator === '?') { return $this->compile_existence($options); } $this->first->front = $this->front; $code = $this->first->compile($options, LEVEL_OP).' '.$this->operator.' ' .$this->second->compile($options, LEVEL_OP); return $options['level'] <= LEVEL_OP ? $code : "({$code})"; } function compile_unary($options) { if ($options['level'] >= LEVEL_ACCESS) { return yy('Parens', $this)->compile($options); } $parts = array($op = $this->operator); $plus_minus = in_array($op, array('+', '-'), TRUE); if (in_array($op, array('new', 'typeof', 'delete'), TRUE) || $plus_minus && $this->first instanceof yy_Op && $this->first->operator === $op) { $parts[] = ' '; } if (($plus_minus && $this->first instanceof yy_Op) || ($op === 'new' && $this->first->is_statement($options))) { $this->first = yy('Parens', $this->first); } $parts[] = $this->first->compile($options, LEVEL_OP); if ($this->flip) { $parts = array_reverse($parts); } return implode('', $parts); } function is_chainable() { return in_array($this->operator, array('<', '>', '>=', '<=', '===', '!=='), TRUE); } function is_complex() { return ! ($this->is_unary() && in_array($this->operator, array('+', '-'))) || $this->first->is_complex(); } function invert() { if ($this->is_chainable() && $this->first->is_chainable()) { $all_invertable = TRUE; $curr = $this; while ($curr && (isset($curr->operator) && $curr->operator)) { if ($all_invertable) { $all_invertable = isset(self::$INVERSIONS[$curr->operator]); } $curr = $curr->first; } if ( ! $all_invertable) { return yy('Parens', $this)->invert(); } $curr = $this; while ($curr && (isset($curr->operator) && $curr->operator)) { $curr->invert = ! $curr->invert; $curr->operator = self::$INVERSIONS[$curr->operator]; $curr = $curr->first; } return $this; } else if (isset(self::$INVERSIONS[$this->operator]) && ($op = self::$INVERSIONS[$this->operator])) { $this->operator = $op; if ($this->first->unwrap() instanceof yy_Op) { $this->first->invert(); } return $this; } else if ($this->second) { return yy('Parens', $this)->invert(); } else if ($this->operator === '!' && (($fst = $this->first->unwrap()) instanceof yy_Op) && in_array($fst->operator, array('!', 'in', 'instanceof'), TRUE)) { return $fst; } else { return yy('Op', '!', $this); } } function generate_do($exp) { $passed_params = array(); $func = $exp; if ($exp instanceof yy_Assign && ($ref = $exp->value->unwrap()) instanceof yy_Code) { $func = $ref; } foreach ((isset($func->params) && $func->params ? $func->params : array()) as $param) { if (isset($param->value) && $param->value) { $passed_params[] = $param->value; unset($param->value); } else { $passed_params[] = $param; } } $call = yy('Call', $exp, $passed_params); $call->do = TRUE; return $call; } function is_simple_number() { return FALSE; } function is_unary() { return ! (isset($this->second) && $this->second); } function unfold_soak($options = NULL) { if (in_array($this->operator, array('++', '--', 'delete'), TRUE)) { return unfold_soak($options, $this, 'first'); } return NULL; } function to_string($idt = '', $name = __CLASS__) { return parent::to_string($idt, $name.' '.$this->operator); } } ?>