Skip to content

Instantly share code, notes, and snippets.

@datayja
Created August 10, 2013 18:31
Show Gist options
  • Save datayja/6201585 to your computer and use it in GitHub Desktop.
Save datayja/6201585 to your computer and use it in GitHub Desktop.
<?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