233 lines
5.0 KiB
PHP
233 lines
5.0 KiB
PHP
<?php
|
|
|
|
class mtgMetaStruct extends mtgUserType
|
|
{
|
|
protected array $fields = array();
|
|
protected array $funcs = array();
|
|
protected ?mtgTypeRef $parent = null;
|
|
protected array $implements = array();
|
|
|
|
private ?int $crc_class_id = null;
|
|
|
|
function __construct(string $name, array $fields = array(),
|
|
mtgTypeRef $parent = null, array $tokens = array(),
|
|
array $implements = array())
|
|
{
|
|
parent::__construct($name);
|
|
|
|
$this->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;
|
|
}
|
|
}
|
|
|
|
|