setFields($fields); $this->parent = $parent; $this->tokens = $tokens; $this->implements = $implements; } function getClassId() : int { if($this->hasToken('class_id')) return $this->getToken('class_id'); if(is_null($this->crc_class_id)) { if($this->scope != null && !($this->scope instanceof mtgMetaParsedModule)) $this->crc_class_id = crc32($this->getFullName()); else //TODO: stop using crc28, use crc32 $this->crc_class_id = crc32($this->name) & 0xFFFFFFF; } return $this->crc_class_id; } function getFullName() : string { $full_name = $this->getName(); $scope = $this->getScope(); while($scope != null && !($scope instanceof mtgMetaParsedModule)) { $full_name = $scope->getName() . '.' . $full_name; $scope = $scope->getScope(); } return $full_name; } function validate(mtgMetaInfo $meta) { $parent = $this->getParent(); while($parent != null) { foreach($this->fields as $name => $_) if($parent->hasField($name)) throw new Exception("{$this->origin} : Parent struct '{$parent->getName()}' already has field '{$name}'"); foreach($this->funcs as $name => $_) if($parent->hasFunc($name)) throw new Exception("{$this->origin} : Parent struct '{$parent->getName()}' already has func '{$name}'"); $parent = $parent->getParent(); } $s = new SplObjectStorage(); foreach($this->getImplements() as $imp) { if(!($imp instanceof mtgMetaInterface)) throw new Exception("{$this->origin} : Not an interface '{$imp}'"); if($s->contains($imp)) throw new Exception("{$this->origin} : Duplicate interface reference '{$imp->getName()}'"); $s->attach($imp); } foreach($this->funcs as $func) { $func->validate($meta); } } function getParent() : ?mtgMetaStruct { if(!$this->parent) return null; $resolved = $this->parent->resolve(); if(!($resolved instanceof mtgMetaStruct)) throw new Exception("Bad parent type"); return $resolved; } function getImplements() : array { if(!$this->implements) return array(); $imps = array(); foreach($this->implements as $imp) $imps[] = $imp->resolve(); return $imps; } function getFields() : array { return $this->fields; } function setFields(array $fields, bool $replace = false) { if($replace) $this->fields = array(); foreach($fields as $field) $this->addField($field); } function addField(mtgMetaField $field) { if($this->hasField($field->getName())) throw new Exception("Struct '{$this->name}' already has field '{$field->getName()}'"); $this->fields[$field->getName()] = $field; } function delField(mtgMetaField $field) { if(isset($this->fields[$field->getName()])) unset($this->fields[$field->getName()]); } function hasField(string $name) : bool { return isset($this->fields[$name]); } function getField(string $name) : mtgMetaField { if(!isset($this->fields[$name])) throw new Exception("No such field '$name'"); return $this->fields[$name]; } function getFuncs() : array { return $this->funcs; } function addFunc(mtgMetaFunc $fn) { if($this->hasFunc($fn->getName())) throw new Exception("Struct '{$this->name}' already has func '{$fn->getName()}'"); $this->funcs[$fn->getName()] = $fn; } function hasFunc(string $name) : bool { return isset($this->funcs[$name]); } function getFunc(string $name) : mtgMetaFunc { if(!isset($this->funcs[$name])) throw new Exception("No such funcs '$name'"); return $this->funcs[$name]; } //TODO: doesn't belong here function hasTokenInParent(string $name) : bool { return mtg_try_get_token_in_hierarchy($this, $name, $value); } } class mtgMetaField { private $name; private $type; private $tokens = array(); function __construct($name, mtgTypeRef $type) { $this->name = $name; $this->type = $type; } function setName($name) { $this->name = $name; } function getName() { return $this->name; } function getType() { return $this->type->resolve(); } function getTokens() { return $this->tokens; } function setTokens(array $tokens) { $this->tokens = $tokens; } function setToken($name, $val) { $this->tokens[$name] = $val; } function hasToken($name) { return array_key_exists($name, $this->tokens); } function getToken($name) { return $this->hasToken($name) ? $this->tokens[$name] : null; } }