metagen/struct.inc.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;
}
}