Example #1
0
 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;
 }
Example #2
0
 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;
 }
Example #3
0
 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);
     }
 }