public function getType() { $newtype = new Type(NativeQuackType::T_LIST); $type = (object) ['from' => $this->from->getType(), 'to' => $this->to->getType(), 'by' => null !== $this->by ? $this->by->getType() : null]; $throw_error_on = function ($operand, $got) { throw new ScopeError(['message' => "Expected number on operand `{$operand}' of range expression. Got {$got}"]); }; if (!$type->from->isNumber()) { $throw_error_on('from', $type->from); } if (!$type->to->isNumber()) { $throw_error_on('to', $type->to); } if (null !== $type->by && !$type->by->isNumber()) { $throw_error_on('by', $type->by); } $newtype->subtype = Type::getBaseType(array_filter(array_values((array) $type), function ($t) { return null !== $t; })); return $newtype; }
public function getType() { $conds = 1; $type = null; foreach (array_map(function ($case) { return (object) $case; }, $this->cases) as $case) { // Assert all conditions are booleans if (null !== $case->condition) { $condition_type = $case->condition->getType(); if (NativeQuackType::T_BOOL !== $condition_type->code) { throw new ScopeError(['message' => "Expected condition {$conds} of `when' to be boolean. Got `{$condition_type}'"]); } } $action_type = $case->action->getType(); $type = null === $type ? $action_type : Type::getBaseType([$type, $action_type]); $conds++; } return $type; }
public function getType() { $type = (object) ['left' => $this->left->getType(), 'right' => 'string' === gettype($this->right) ? null : $this->right->getType()]; $op_name = Tag::getOperatorLexeme($this->operator); $member_access = ['.', '?.']; if (in_array($this->operator, $member_access, true)) { // When member access and the property exists on the left type if (is_array($type->left->props) && array_key_exists($this->right, $type->left->props)) { return $type->left->props[$this->right]; } throw new ScopeError(['message' => "Expression of type `{$type->left}' has no property `{$this->right}'"]); } // Type-checking for assignment. Don't worry. Left-hand assignment was handled on // scope injection if (':-' === $this->operator) { // When right side cannot be attributed to left side if (!$type->right->isExactlySameAs($type->left)) { $target = $this->left instanceof NameExpr ? "variable `{$this->left->name}' :: {$type->left}" : (string) $type->right; throw new ScopeError(['message' => "Trying to set value of type `{$type->right}` to {$target}"]); } // We've just removed covariance and contravariance :) return $type->right; } // Type checking for numeric and string concat operations $numeric_op = ['+', '-', '*', '**', '/', '>>', '<<', '^', '&', '|', Tag::T_MOD]; if (in_array($this->operator, $numeric_op, true)) { if ('+' === $this->operator && $type->left->isString() && $type->right->isString()) { return new Type(NativeQuackType::T_STR); } if ($type->left->isNumber() && $type->right->isNumber()) { return Type::getBaseType([$type->left, $type->right]); } throw new ScopeError(['message' => "No type overload found for operator `{$op_name}' at " . "{{$type->left} {$op_name} {$type->right}}"]); } // Type checking for equality operators and coalescence $eq_op = ['=', '<>', '??', '>', '>=', '<', '<=']; if (in_array($this->operator, $eq_op, true)) { if ($type->left->isExactlySameAs($type->right)) { return '??' === $this->operator ? $type->left : new Type(NativeQuackType::T_BOOL); } throw new ScopeError(['message' => "Why in the world are you trying to compare two expressions of different types? at " . "{{$type->left} {$op_name} {$type->right}}"]); } // Type checking for string matched by regex if ('=~' === $this->operator) { if (!$type->left->isString() || !$type->right->isRegex()) { throw new ScopeError(['message' => "No type overload found for operator `=~' at " . "{{$type->left} =~ {$type->right}}"]); } return new Type(NativeQuackType::T_BOOL); } // Boolean algebra $bool_op = [Tag::T_AND, Tag::T_OR, Tag::T_XOR]; if (in_array($this->operator, $bool_op, true)) { if (!$type->left->isBoolean() || !$type->right->isBoolean()) { throw new ScopeError(['message' => "No type overload found for operator `{$op_name}' " . "{{$type->left} {$op_name} {$type->right}}"]); } return new Type(NativeQuackType::T_BOOL); } }