Created
August 10, 2013 18:31
-
-
Save datayja/6201585 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
namespace Coral\Compiler\Engine\AST; | |
use Coral\Compiler\Tokenizer\Constants\Operator; | |
use Coral\Compiler\Tokenizer\Tokens\OperatorToken; | |
class OperatorNode extends Node | |
{ | |
const UNARY = 'unary'; | |
const BINARY = 'binary'; | |
private $operator; | |
private $mode; | |
/** | |
* @var Node | |
*/ | |
private $left; | |
/** | |
* @var Node | |
*/ | |
private $right; | |
public function __construct (OperatorToken $operator) | |
{ | |
parent::__construct(); | |
$this->operator = $operator->getOperator(); | |
$this->initialize_mode($operator); | |
} | |
public static function process (\SplStack $stack, OperatorToken $token, LocalVariablesPool $local_variables) | |
{ | |
switch ($token->getOperator()) | |
{ | |
case Operator::kComma: | |
$right_operand = $stack->pop(); | |
$left_operand = $stack->pop(); | |
if ($left_operand instanceof ExpressionListNode) | |
{ | |
$last_list_node = $left_operand->last_node(); | |
if (isset($last_list_node) | |
&& $last_list_node instanceof MethodCallWithArgumentsNode) | |
{ | |
$arguments = $last_list_node->arguments(); | |
if ($arguments instanceof ExpressionListNode) | |
{ | |
$last_argument = $arguments->last_node(); | |
if (isset($last_argument) | |
&& $last_argument === $arguments->first_node() | |
&& $last_argument instanceof ExpressionNode) | |
{ | |
$left_operand->add($right_operand); | |
} | |
else | |
{ | |
self::attach_argument($last_list_node, $right_operand); | |
} | |
} | |
elseif ($arguments instanceof ExpressionNode) | |
{ | |
$left_operand->add($right_operand); | |
} | |
else | |
{ | |
self::attach_argument($last_list_node, $right_operand); | |
} | |
} | |
else | |
{ | |
$left_operand->add($right_operand); | |
} | |
$stack->push($left_operand); | |
} | |
elseif ($left_operand instanceof MethodCallWithArgumentsNode) | |
{ | |
self::attach_argument($left_operand, $right_operand); | |
$stack->push($left_operand); | |
} | |
else | |
{ | |
$list = new ExpressionListNode; | |
$list->add($left_operand); | |
$list->add($right_operand); | |
$stack->push($list); | |
} | |
break; | |
case Operator::kAssign: | |
$right_operand = $stack->pop(); | |
$left_operand = $stack->pop(); | |
self::detect_local_variables_in_operands($left_operand, $right_operand, $local_variables); | |
$operator = new OperatorNode($token); | |
$operator->set_left_operand($left_operand); | |
$operator->set_right_operand($right_operand); | |
$stack->push($operator); | |
break; | |
default: | |
$operator = new OperatorNode($token); | |
if ($operator->is_binary()) | |
{ | |
$right_operand = $stack->pop(); | |
$left_operand = $stack->pop(); | |
self::detect_local_variables_in_operand($left_operand, false, $local_variables); | |
if ($operator->operator() !== Operator::kMessage) | |
{ | |
self::detect_local_variables_in_operand($right_operand, false, $local_variables); | |
} | |
elseif ($operator->operator() === Operator::kMessage | |
&& $right_operand instanceof IdentifierNode) | |
{ | |
// message operator implies method name, | |
// no matter what local variables are present | |
$right_operand->set_role(IdentifierNode::MethodNameRole); | |
} | |
$operator->set_left_operand($left_operand); | |
$operator->set_right_operand($right_operand); | |
} | |
elseif ($operator->is_unary()) | |
{ | |
$right_operand = $stack->pop(); | |
self::detect_local_variables_in_operand($right_operand, false, $local_variables); | |
$operator->set_right_operand($right_operand); | |
} | |
$stack->push($operator); | |
} | |
} | |
private static function attach_argument (MethodCallWithArgumentsNode $left_operand, Node $right_operand) | |
{ | |
$arguments = $left_operand->arguments(); | |
if ($arguments instanceof ExpressionListNode) | |
{ | |
$arguments->add($right_operand); | |
} | |
else | |
{ | |
$list = new ExpressionListNode; | |
$list->add($arguments); | |
$list->add($right_operand); | |
$left_operand->set_arguments($list); | |
} | |
} | |
public function operator () | |
{ | |
return $this->operator; | |
} | |
public function is_binary () | |
{ | |
return ($this->mode === self::BINARY); | |
} | |
public function is_unary () | |
{ | |
return ($this->mode === self::UNARY); | |
} | |
public function set_left_operand (Node $operand) | |
{ | |
$this->left = $operand; | |
$operand->set_parent($this); | |
} | |
public function set_right_operand (Node $operand) | |
{ | |
$this->right = $operand; | |
$operand->set_parent($this); | |
} | |
public function left_operand () | |
{ | |
return $this->left; | |
} | |
public function right_operand () | |
{ | |
return $this->right; | |
} | |
private function initialize_mode (OperatorToken $operator) | |
{ | |
switch ($operator->getOperator()) | |
{ | |
# unary operators | |
case Operator::kNot: | |
case Operator::kNotWord: | |
$this->mode = self::UNARY; | |
break; | |
# binary operators with secondary (unary) meanings | |
case Operator::kMinus: | |
case Operator::kPlus: | |
case Operator::kToBlock: | |
case Operator::kArrayExpand: | |
case Operator::kHashToNamedArguments: | |
if ($operator->is_secondary()) | |
{ | |
$this->mode = self::UNARY; | |
} | |
else | |
{ | |
$this->mode = self::BINARY; | |
} | |
break; | |
# binary operators without secondary meanings | |
default: | |
$this->mode = self::BINARY; | |
} | |
} | |
private static function detect_local_variables_in_operands (Node $left_operand, Node $right_operand, LocalVariablesPool $local_variables) | |
{ | |
self::detect_local_variables_in_operand($left_operand, true, $local_variables); | |
self::detect_local_variables_in_operand($right_operand, false, $local_variables); | |
} | |
private static function detect_local_variables_in_operand (Node $operand, $allow_new, LocalVariablesPool $local_variables) | |
{ | |
if ($operand instanceof IdentifierNode | |
&& ($allow_new || $local_variables->contains_local_variable_name($operand->identifier()))) | |
{ | |
$operand->set_role(IdentifierNode::LocalVariableRole); | |
$local_variables->add_local_variable_name($operand->identifier()); | |
} | |
elseif ($operand instanceof ExpressionListNode) | |
{ | |
foreach ($operand as $expression_node) | |
{ | |
if ($expression_node instanceof IdentifierNode | |
&& ($allow_new || $local_variables->contains_local_variable_name($expression_node->identifier()))) | |
{ | |
$expression_node->set_role(IdentifierNode::LocalVariableRole); | |
$local_variables->add_local_variable_name($expression_node->identifier()); | |
} | |
} | |
} | |
} | |
public function debug_dump_nodes ($level = 0) | |
{ | |
if ($this->is_binary()) | |
{ | |
$output = $this->debug_dump_prefix($level + 1) . ":left_operand => \n"; | |
$output .= $this->left_operand()->debug_dump($level + 1) . "\n"; | |
$output .= $this->debug_dump_prefix($level + 1) . ":operator => "; | |
$output .= ":'{$this->operator()}'\n"; | |
$output .= $this->debug_dump_prefix($level + 1) . ":right_operand => \n"; | |
$output .= $this->right_operand()->debug_dump($level + 1); | |
} | |
else | |
{ | |
$output = $this->debug_dump_prefix($level + 1) . ":operand => \n"; | |
$output .= $this->left_operand()->debug_dump($level + 1) . "\n"; | |
$output .= $this->debug_dump_prefix($level + 1) . ":operator => "; | |
$output .= $this->operator(); | |
} | |
return $output; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment