From 3718958480c266e9ec1a7c3cf2a2db123a97bf6f Mon Sep 17 00:00:00 2001 From: Pavel Shevaev Date: Mon, 19 May 2025 19:23:11 +0300 Subject: [PATCH] Starting major cleanup and preparation for namespaces --- enum.inc.php | 55 +++ func.inc.php | 77 ++++ interface.inc.php | 54 +++ metagen.inc.php | 1095 +++++---------------------------------------- parser.inc.php | 12 +- parser1.inc.php | 25 +- parser2.inc.php | 31 +- scope.inc.php | 28 ++ service.inc.php | 217 +++++++++ struct.inc.php | 232 ++++++++++ type.inc.php | 272 +++++++++++ util.inc.php | 40 ++ 12 files changed, 1123 insertions(+), 1015 deletions(-) create mode 100644 enum.inc.php create mode 100644 func.inc.php create mode 100644 interface.inc.php create mode 100644 scope.inc.php create mode 100644 service.inc.php create mode 100644 struct.inc.php create mode 100644 type.inc.php create mode 100644 util.inc.php diff --git a/enum.inc.php b/enum.inc.php new file mode 100644 index 0000000..d52c29e --- /dev/null +++ b/enum.inc.php @@ -0,0 +1,55 @@ +values[$value_name])) + throw new Exception("Enum '{$this->name}' already has value with name '{$value_name}'"); + + if(in_array($value, $this->values)) + throw new Exception("Enum '{$this->name}' already has value '{$value}'"); + + $this->values[$value_name] = $value; + } + + function calcOrValue($or_keys) + { + $res = 0; + foreach($or_keys as $value_name) + { + if(!isset($this->values[$value_name])) + throw new Exception("Enum '{$this->name}' has no value '{$value_name}'"); + $res |= $this->values[$value_name]; + } + + return $res; + } + + function addOrValues($values) + { + foreach($values as $value_name => $or_keys) + $this->addValue($value_name, $this->calcOrValue($or_keys)); + } + + function getValues() + { + return $this->values; + } + + function override(mtgMetaEnum $other) + { + $this->tokens = array_merge($this->tokens, $other->getTokens()); + $this->values = array_merge($this->values, $other->getValues()); + } + + function replace(mtgMetaEnum $other) + { + $this->tokens = $other->getTokens(); + $this->values = $other->getValues(); + } +} diff --git a/func.inc.php b/func.inc.php new file mode 100644 index 0000000..5028e10 --- /dev/null +++ b/func.inc.php @@ -0,0 +1,77 @@ +name = $name; + } + + function validate(mtgMetaInfo $meta) + {} + + function __toString() + { + $str = "func "; + + $str .= $this->name.'('; + foreach($this->getArgs() as $arg) + $str .= $arg->getType() . ','; + $str = rtrim($str, ','); + $str .= ')'; + if($ret_type = $this->getReturnType()) + $str .= ':'.$ret_type; + return $str; + } + + function setReturnType(mtgTypeRef $type) + { + $this->ret_type = $type; + } + + function getReturnType() : ?mtgType + { + return $this->ret_type ? $this->ret_type->resolve() : null; + } + + function getName() : string + { + return $this->name; + } + + function getArgs() + { + return $this->args; + } + + function setArgs(array $args) + { + foreach($args as $arg) + $this->addArg($arg); + } + + function addArg(mtgMetaField $arg) + { + if($this->hasArg($arg->getName())) + throw new Exception("Func '{$this->name}' already has arg '{$arg->getName()}'"); + + $this->args[$arg->getName()] = $arg; + } + + function hasArg($name) + { + return isset($this->args[$name]); + } + + function getArg($name) + { + if(!isset($this->args[$name])) + throw new Exception("No such arg '$name'"); + return $this->args[$name]; + } +} + diff --git a/interface.inc.php b/interface.inc.php new file mode 100644 index 0000000..c774959 --- /dev/null +++ b/interface.inc.php @@ -0,0 +1,54 @@ +implements = $implements; + $this->tokens = $tokens; + } + + function validate(mtgMetaInfo $meta) {} + + function getImplements() + { + if(!$this->implements) + return array(); + + $imps = array(); + foreach($this->implements as $imp) + $imps[] = $imp->resolve(); + return $imps; + } + + function getFuncs() + { + return $this->funcs; + } + + function addFunc(mtgMetaFunc $fn) + { + if($this->hasFunc($fn->getName())) + throw new Exception("Interface '{$this->name}' already has func '{$fn->getName()}'"); + + $this->funcs[$fn->getName()] = $fn; + } + + function hasFunc($name) + { + return isset($this->funcs[$name]); + } + + function getFunc($name) + { + if(!isset($this->funcs[$name])) + throw new Exception("No such funcs '$name'"); + return $this->funcs[$name]; + } +} + diff --git a/metagen.inc.php b/metagen.inc.php index 5a2a708..9fc0293 100644 --- a/metagen.inc.php +++ b/metagen.inc.php @@ -1,104 +1,97 @@ scopes[] = $scope; + if(isset($this->units[$unit->object->getName()])) + throw new Exception("Meta info unit '{$unit->object->getName()}' already defined in file '{$this->units[$unit->object->getName()]->file}'"); + + $this->units[$unit->object->getName()] = $unit; + + if($unit->object instanceof mtgMetaStruct) + $this->_registerFactoryStruct($unit->object); + else if($unit->object instanceof mtgMetaService) + { + foreach($unit->object->getUserTypes() as $user_type) + if($user_type instanceof mtgMetaStruct) + $this->_registerFactoryStruct($user_type); + } } - function findSymbol(string $name) : ?mtgMetaUnit + function delUnit(mtgMetaInfoUnit $unit) { - foreach($this->scopes as $scope) + unset($this->units[$unit->object->getName()]); + + if($unit->object instanceof mtgMetaStruct) + $this->_unregisterFactoryStruct($unit->object); + else if($unit->object instanceof mtgMetaService) { - $unit = $scope->findSymbol($name); - if($unit) - return $unit; + foreach($unit->object->getUserTypes() as $user_type) + if($user_type instanceof mtgMetaStruct) + $this->_unregisterFactoryStruct($user_type); } + } + + function getUnits() : array + { + return $this->units; + } + + function findUnit($id) : ?mtgMetaInfoUnit + { + if(isset($this->units[$id])) + return $this->units[$id]; return null; } -} -class mtgTypeRef implements mtgType -{ - private static $unresolved = array(); - - private $name; - private $scope; - private $resolved; - private $origin; - - function __construct($name_or_resolved, mtgScope $scope = null, mtgOrigin $origin = null) + function getUnit($id) : mtgMetaInfoUnit { - if(is_object($name_or_resolved)) - { - if(!($name_or_resolved instanceof mtgType)) - throw new Exception("Bad type"); - $this->resolved = $name_or_resolved; - } - else - { - $this->name = $name_or_resolved; - if(!$scope) - throw new Exception("Scope is not set"); - - self::$unresolved[] = $this; - } - - $this->scope = $scope; - $this->origin = $origin ?? new mtgOrigin(); - + $unit = $this->findUnit($id); + if(!$unit) + throw new Exception("Unit '$id' not found"); + return $unit; } - function getName() + private function _registerFactoryStruct(mtgMetaStruct $struct) { - if($this->resolved) - return ''.$this->resolved; - else - return $this->name; + if(isset($this->factory_types[$struct->getClassId()])) + throw new Exception("Factory user type with id '{$struct->getClassId()}' already defined for type '{$this->factory_types[$struct->getClassId()]->getName()}'"); + + $this->factory_types[$struct->getClassId()] = $struct; } - static function checkAllResolved() + private function _unregisterFactoryStruct(mtgMetaStruct $struct) { - while(sizeof(self::$unresolved) > 0) - { - $ref = array_shift(self::$unresolved); - $resolved = $ref->resolve(); - if($resolved instanceof mtgMetaFunc && $resolved->getName()) - throw new Exception("Invalid resolving of named function as type: " . $resolved->getName() . " at " . $ref->origin); - } + unset($this->factory_types[$struct->getClassId()]); } - function resolve() + function getFactoryTypes() : array { - if($this->resolved) - return $this->resolved; + return $this->factory_types; + } - $symb = $this->scope->findSymbol($this->name); - if($symb) - { - $this->resolved = $symb; - return $this->resolved; - } - else - throw new Exception("{$this->origin} : Symbol '{$this->name}' not found"); - } - - function __toString() + function validate() { - return $this->getName(); + foreach($this->units as $u) + $u->object->validate($this); } } @@ -125,90 +118,25 @@ class mtgMetaInfoUnit } } -class mtgMetaInfo -{ - public static $BUILTIN_TYPES = array( - 'int8', 'int16', 'int32', 'uint8', 'uint16', 'uint32', - 'float', 'double', 'uint64', 'int64', 'bool', 'string', 'blob' - ); - - private $units = array(); - - function addUnit(mtgMetaInfoUnit $unit) - { - if(isset($this->units[$unit->object->getMetaId()])) - throw new Exception("Meta info unit '{$unit->object->getMetaId()}' already defined in file '{$this->units[$unit->object->getMetaId()]->file}'"); - - $this->units[$unit->object->getMetaId()] = $unit; - } - - function delUnit(mtgMetaInfoUnit $unit) - { - unset($this->units[$unit->object->getMetaId()]); - } - - function getUnits() : array - { - return $this->units; - } - - function findUnit($id) : ?mtgMetaInfoUnit - { - if(isset($this->units[$id])) - return $this->units[$id]; - return null; - } - - function getUnit($id) : mtgMetaInfoUnit - { - if(isset($this->units[$id])) - return $this->units[$id]; - throw new Exception("Unit '$id' not found"); - } - - function validate() - { - foreach($this->units as $u) - $u->object->validate($this); - } -} - +//NOTE: represents a meta symbol abstract class mtgMetaUnit { - abstract function getMetaId(); - - protected $tokens = array(); + protected ?mtgScope $scope = null; protected ?mtgOrigin $origin; - function getTokens() + protected $tokens = array(); + + abstract function getName() : string; + + function setScope(mtgScope $scope) { - return $this->tokens; + $this->scope = $scope; } - function setTokens(array $tokens) + function getScope() : ?mtgScope { - $this->tokens = $tokens; - } - - function setToken(string $name, $val) - { - $this->tokens[$name] = $val; - } - - function delToken(string $name) - { - unset($this->tokens[$name]); - } - - function getToken(string $name) - { - return $this->hasToken($name) ? $this->tokens[$name] : null; - } - - function hasToken($name) - { - return array_key_exists($name, $this->tokens); + return $this->scope; } function setOrigin(mtgOrigin $origin) @@ -221,6 +149,36 @@ abstract class mtgMetaUnit return $this->origin; } + function getTokens() : array + { + return $this->tokens; + } + + function setTokens(array $tokens) + { + $this->tokens = $tokens; + } + + function setToken(string $name, mixed $val) + { + $this->tokens[$name] = $val; + } + + function delToken(string $name) + { + unset($this->tokens[$name]); + } + + function getToken(string $name) : mixed + { + return $this->hasToken($name) ? $this->tokens[$name] : null; + } + + function hasToken(string $name) : bool + { + return array_key_exists($name, $this->tokens); + } + abstract function validate(mtgMetaInfo $meta); } @@ -241,842 +199,3 @@ class mtgOrigin } } -abstract class mtgUserType extends mtgMetaUnit implements mtgType -{ - protected $name; - - function __construct($name) - { - $this->name = $name; - } - - function getMetaId() - { - return $this->name; - } - - function getName() - { - return $this->name; - } - - function setName($name) - { - $this->name = $name; - } - - function getClassId() - { - if($this->hasToken('class_id')) - return $this->getToken('class_id'); - - //TODO: migrate to crc32 from crc28 - return crc32($this->name) & 0xFFFFFFF; - } - - function __toString() - { - return $this->name; - } -} - -class mtgMetaStruct extends mtgUserType -{ - protected $fields = array(); - protected $funcs = array(); - protected $parent = null; - protected $implements = array(); - - function __construct($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 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() - { - return $this->parent ? $this->parent->resolve() : null; - } - - function getImplements() - { - if(!$this->implements) - return array(); - - $imps = array(); - foreach($this->implements as $imp) - $imps[] = $imp->resolve(); - return $imps; - } - - function getFields() - { - return $this->fields; - } - - function setFields(array $fields, $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($name) - { - return isset($this->fields[$name]); - } - - function getField($name) - { - if(!isset($this->fields[$name])) - throw new Exception("No such field '$name'"); - return $this->fields[$name]; - } - - function getFuncs() - { - 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($name) - { - return isset($this->funcs[$name]); - } - - function getFunc($name) - { - if(!isset($this->funcs[$name])) - throw new Exception("No such funcs '$name'"); - return $this->funcs[$name]; - } - - //TODO: doesn't belong here - function hasTokenInParent($name) - { - return mtg_try_get_token_in_hierarchy($this, $name, $value); - } -} - -class mtgMetaInterface extends mtgUserType -{ - protected $funcs = array(); - protected $implements = null; - - function __construct($name, array $implements = array(), $tokens = array()) - { - parent::__construct($name); - - $this->implements = $implements; - $this->tokens = $tokens; - } - - function validate(mtgMetaInfo $meta) {} - - function getImplements() - { - if(!$this->implements) - return array(); - - $imps = array(); - foreach($this->implements as $imp) - $imps[] = $imp->resolve(); - return $imps; - } - - function getFuncs() - { - return $this->funcs; - } - - function addFunc(mtgMetaFunc $fn) - { - if($this->hasFunc($fn->getName())) - throw new Exception("Interface '{$this->name}' already has func '{$fn->getName()}'"); - - $this->funcs[$fn->getName()] = $fn; - } - - function hasFunc($name) - { - return isset($this->funcs[$name]); - } - - function getFunc($name) - { - if(!isset($this->funcs[$name])) - throw new Exception("No such funcs '$name'"); - return $this->funcs[$name]; - } -} - -class mtgMetaFunc extends mtgMetaUnit implements mtgType -{ - private $name; - private $args = array(); - private $ret_type; - - function __construct($name) - { - $this->name = $name; - } - - function validate(mtgMetaInfo $meta) - {} - - function getMetaId() - { - return $this->name; - } - - function __toString() - { - $str = "func "; - - $str .= $this->name.'('; - foreach($this->getArgs() as $arg) - $str .= $arg->getType() . ','; - $str = rtrim($str, ','); - $str .= ')'; - if($ret_type = $this->getReturnType()) - $str .= ':'.$ret_type; - return $str; - } - - function setReturnType(mtgTypeRef $type) - { - $this->ret_type = $type; - } - - function getReturnType() - { - return $this->ret_type ? $this->ret_type->resolve() : null; - } - - function getName() - { - return $this->name; - } - - function getArgs() - { - return $this->args; - } - - function setArgs(array $args) - { - foreach($args as $arg) - $this->addArg($arg); - } - - function addArg(mtgMetaField $arg) - { - if($this->hasArg($arg->getName())) - throw new Exception("Func '{$this->name}' already has arg '{$arg->getName()}'"); - - $this->args[$arg->getName()] = $arg; - } - - function hasArg($name) - { - return isset($this->args[$name]); - } - - function getArg($name) - { - if(!isset($this->args[$name])) - throw new Exception("No such arg '$name'"); - return $this->args[$name]; - } -} - -class mtgMetaRPC extends mtgMetaUnit -{ - private $name; - private $code; - private $in; - private $out; - - function __construct($name, $code, mtgMetaPacket $in, mtgMetaPacket $out, array $tokens = array()) - { - $this->name = $name; - $this->code = $code; - $this->in = $in; - $this->out = $out; - $this->tokens = $tokens; - } - - function validate(mtgMetaInfo $meta) {} - - function getMetaId() - { - return $this->code; - } - - function getName() - { - return $this->name; - } - - function getCode() - { - return $this->code; - } - - function getNumericCode() - { - return (int)$this->code; - } - - function getReq() - { - return $this->in; - } - - function getRsp() - { - return $this->out; - } -} - -class mtgMetaService extends mtgMetaUnit implements mtgScope -{ - private string $name; - //for partial services - private mtgMultiScope $parent_scope; - private $rpcs = array(); - private $events = array(); - private $user_types = array(); - private $partials = array(); - - function __construct(string $name, mtgScope $parent_scope, array $tokens = array()) - { - $this->name = $name; - $this->parent_scope = new mtgMultiScope(); - $this->parent_scope->addScope($parent_scope); - $this->tokens = $tokens; - } - - function getParentScope() : mtgScope - { - return $this->parent_scope; - } - - function validate(mtgMetaInfo $meta) {} - - function addPartial(mtgMetaParsedModule $module, mtgMetaService $service) - { - $this->setTokens(array_merge($this->getTokens(), $service->getTokens())); - $this->parent_scope->addScope($service->getParentScope()); - $this->partials[$module->file] = $service; - } - - function getPartials() : array - { - return $this->partials; - } - - function getName() : string - { - return $this->name; - } - - function getMetaId() - { - return $this->name; - } - - function findSymbol(string $name) : ?mtgMetaUnit - { - if(isset($this->user_types[$name])) - return $this->user_types[$name]; - - return $this->parent_scope->findSymbol($name); - } - - function getRPCs() : array - { - return $this->rpcs; - } - - function addRPC(mtgMetaRPC $rpc) - { - foreach($this->rpcs as $name => $_rpc) - { - if($name == $rpc->getName()) - throw new Exception("Service '{$this->name}' already has RPC '{$rpc->getName()}'"); - - if($rpc->getNumericCode() == $_rpc->getNumericCode()) - throw new Exception("Service '{$this->name}' already has RPC '{$_rpc->getName()}' with code '{$rpc->getNumericCode()}'"); - } - - $this->rpcs[$rpc->getName()] = $rpc; - } - - function hasRPC(string $name) : bool - { - return isset($this->rpcs[$name]); - } - - function getRPC(string $name) : mtgMetaRPC - { - if(!isset($this->rpcs[$name])) - throw new Exception("No such RPC '$name'"); - return $this->rpcs[$name]; - } - - function getUserTypes() : array - { - return $this->user_types; - } - - function addUserType(mtgUserType $utype) - { - if($this->hasRPC($utype->getName()) || $this->hasUserType($utype->getName())) - throw new Exception("Service '{$this->name}' already has type '{$utype->getName()}'"); - - $this->user_types[$utype->getName()] = $utype; - } - - function hasUserType(string $name) : bool - { - return isset($this->user_types[$name]); - } - - function getUserType(bool $name) : mtgUserType - { - if(!isset($this->user_types[$name])) - throw new Exception("No such user type '$name'"); - return $this->user_types[$name]; - } - - function addEvent(mtgMetaStruct $evt) - { - $this->events[] = $evt; - $this->addUserType($evt); - } - - function getEvents() : array - { - return $this->events; - } - - function getClassId() : int - { - if($this->hasToken('class_id')) - return $this->getToken('class_id'); - - return crc32($this->name); - } - - function __toString() - { - return $this->name; - } -} - -class mtgBuiltinType implements mtgType -{ - private $name; - - function __construct($name) - { - if(!in_array($name, mtgMetaInfo::$BUILTIN_TYPES)) - throw new Exception("Not a built-in type '$name'"); - - $this->name = $name; - } - - function getName() - { - return $this->name; - } - - function __toString() - { - return $this->name; - } - - function isNumeric() - { - return !$this->isString() && !$this->isBool() && !$this->isBlob(); - } - - function isString() - { - return $this->name === 'string'; - } - - function isInt() - { - return strpos($this->name, 'int') === 0; - } - - function isUint() - { - return strpos($this->name, 'uint') === 0; - } - - function isUint8() - { - return $this->name === 'uint8'; - } - - function isUint16() - { - return $this->name === 'uint16'; - } - - function isUint32() - { - return $this->name === 'uint32'; - } - - function isUint64() - { - return $this->name === 'uint64'; - } - - function isInt64() - { - return $this->name === 'int64'; - } - - function isInt8() - { - return $this->name === 'int8'; - } - - function isInt16() - { - return $this->name === 'int16'; - } - - function isInt32() - { - return $this->name === 'int32'; - } - - function isFloat() - { - return $this->name === 'float'; - } - - function isDouble() - { - return $this->name === 'double'; - } - - function isBool() - { - return $this->name === 'bool'; - } - - function isBlob() - { - return $this->name === 'blob'; - } -} - -class mtgMultiType implements mtgType -{ - private $values = array(); - - function __construct(array $values = array()) - { - foreach($values as $v) - $this->addValue($v); - } - - function addValue(mtgTypeRef $val) - { - $this->values[] = $val; - } - - function getValues() - { - $vals = array(); - foreach($this->values as $val) - $vals[] = $val->resolve(); - - return $vals; - } - - function getName() - { - $str = ''; - foreach($this->getValues() as $val) - $str .= $val . ';'; - return $str; - } - - function __toString() - { - return $this->getName(); - } -} - -class mtgArrType implements mtgType -{ - private $value; - - function __construct(mtgTypeRef $value) - { - $this->value = $value; - } - - function getName() - { - return $this->getValue() . '[]'; - } - - function __toString() - { - return $this->getName(); - } - - function getValue() - { - return $this->value->resolve(); - } -} - -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; - } - -} - -class mtgMetaPacket extends mtgMetaStruct -{ - private $code; - - function __construct($code, $name, array $tokens = array()) - { - $this->code = $code; - - parent::__construct($name, array(), null, $tokens); - } - - function getPrefix() - { - list($prefix,) = explode('_', $this->getName()); - return $prefix; - } - - function getFullName() - { - return $this->code . "_" . $this->getName(); - } - - function getCode() - { - return $this->code; - } - - function getNumericCode() - { - return (int)$this->code; - } -} - -class mtgMetaEnum extends mtgUserType -{ - private $values = array(); - - function validate(mtgMetaInfo $meta) {} - - function addValue($value_name, $value) - { - if(isset($this->values[$value_name])) - throw new Exception("Enum '{$this->name}' already has value with name '{$value_name}'"); - - if(in_array($value, $this->values)) - throw new Exception("Enum '{$this->name}' already has value '{$value}'"); - - $this->values[$value_name] = $value; - } - - function calcOrValue($or_keys) - { - $res = 0; - foreach($or_keys as $value_name) - { - if(!isset($this->values[$value_name])) - throw new Exception("Enum '{$this->name}' has no value '{$value_name}'"); - $res |= $this->values[$value_name]; - } - - return $res; - } - - function addOrValues($values) - { - foreach($values as $value_name => $or_keys) - $this->addValue($value_name, $this->calcOrValue($or_keys)); - } - - function getValues() - { - return $this->values; - } - - function override(mtgMetaEnum $other) - { - $this->tokens = array_merge($this->tokens, $other->getTokens()); - $this->values = array_merge($this->values, $other->getValues()); - } - - function replace(mtgMetaEnum $other) - { - $this->tokens = $other->getTokens(); - $this->values = $other->getValues(); - } -} - -function mtg_get_all_fields(mtgMetaStruct $struct) -{ - $fields = $struct->getFields(); - $parent = $struct->getParent(); - if($parent) - //NOTE: order is important, parent fields must come first - $fields = array_merge(mtg_get_all_fields($parent), $fields); - return $fields; -} - -function mtg_find_field_owner(mtgMetaStruct $struct, $name) -{ - $tmp = $struct; - while($tmp) - { - $fields = $tmp->getFields(); - if(isset($fields[$name])) - return $tmp; - $tmp = $tmp->getParent(); - } - return null; -} - -function mtg_try_get_token_in_hierarchy(mtgMetaStruct $struct, $name, &$value) -{ - $tmp = $struct; - while($tmp) - { - if($tmp->hasToken($name)) - { - $value = $tmp->getToken($name); - return true; - } - $tmp = $tmp->getParent(); - } - - return false; -} diff --git a/parser.inc.php b/parser.inc.php index 14d5dcf..578147b 100644 --- a/parser.inc.php +++ b/parser.inc.php @@ -23,7 +23,7 @@ class mtgMetaParsedModule implements mtgScope function addUnit(mtgMetaInfoUnit $unit) { - $this->units[$unit->object->getMetaId()] = $unit; + $this->units[$unit->object->getName()] = $unit; } function findSymbol(string $name) : ?mtgMetaUnit @@ -54,7 +54,13 @@ class mtgMetaParsedModule implements mtgScope } } -function mtg_parse_meta(array $meta_srcs, $valid_tokens = null, $inc_path = null, $version = null, string $extension = '.meta') +function mtg_parse_meta( + array $meta_srcs, + $valid_tokens = null, + $inc_path = null, + $version = null, + string $extension = '.meta' +) : mtgMetaInfo { if($inc_path === null) { @@ -122,7 +128,7 @@ function mtg_resolve_files(string $dir_or_file, string $extension = '.meta') : a return $files; } -function mtg_find_meta_files($dir, $extension = '.meta') +function mtg_find_meta_files($dir, $extension = '.meta') : array { $items = scandir($dir); if($items === false) diff --git a/parser1.inc.php b/parser1.inc.php index e363e3e..084c6b0 100644 --- a/parser1.inc.php +++ b/parser1.inc.php @@ -359,7 +359,7 @@ class mtgMetaInfoParser implements mtgIMetaInfoParser return $values; } - private function _parseEnum($is_global = true) : mtgMetaEnum + private function _parseEnum(bool $is_global = true) : mtgMetaEnum { $this->_nextT(); $name = $this->_parseDotName(); @@ -398,7 +398,7 @@ class mtgMetaInfoParser implements mtgIMetaInfoParser if(!$is_global) $this->_error("Override supported for global enums only"); - $existing = $this->current_meta->findUnit($enum->getMetaId()); + $existing = $this->current_meta->findUnit($enum->getName()); if(!$existing) $this->_error("Not found '{$name}' enum to override values"); if(!($existing->object instanceof mtgMetaEnum)) @@ -413,7 +413,7 @@ class mtgMetaInfoParser implements mtgIMetaInfoParser if(!$is_global) $this->_error("Replace supported for global enums only"); - $existing = $this->current_meta->findUnit($enum->getMetaId()); + $existing = $this->current_meta->findUnit($enum->getName()); if(!$existing) $this->_error("Not found '{$name}' enum to replace values"); if(!($existing->object instanceof mtgMetaEnum)) @@ -564,6 +564,7 @@ class mtgMetaInfoParser implements mtgIMetaInfoParser private function _addUnit(mtgMetaInfoUnit $unit) { + $unit->object->setScope($this->_scope()); $this->current_meta->addUnit($unit); $this->module->addUnit($unit); } @@ -576,7 +577,7 @@ class mtgMetaInfoParser implements mtgIMetaInfoParser $this->_addUnit(new mtgMetaInfoUnit($this->module, $fn)); } - private function _parseStruct($is_global = true) : mtgMetaStruct + private function _parseStruct(bool $is_global = true) : mtgMetaStruct { $this->_nextT(); $struct_origin = new mtgOrigin($this->file, $this->line); @@ -664,7 +665,7 @@ class mtgMetaInfoParser implements mtgIMetaInfoParser $this->_nextT(); } - private function _parseRPC($is_global = true) : mtgMetaRPC + private function _parseRPC(bool $is_global = true) : mtgMetaRPC { $this->_nextT(); $code = $this->T_value; @@ -683,12 +684,12 @@ class mtgMetaInfoParser implements mtgIMetaInfoParser { return $this->_nextIf(self::T_End); } ); - $req = new mtgMetaPacket($code, $is_global ? "RPC_REQ_$name" : "Request"); + $req = new mtgMetaPacket(intval($code), $is_global ? "RPC_REQ_$name" : "Request"); $req->setFields($req_fields); - $rsp = new mtgMetaPacket($code, $is_global ? "RPC_RSP_$name" : "Response"); + $rsp = new mtgMetaPacket(intval($code), $is_global ? "RPC_RSP_$name" : "Response"); $rsp->setFields($rsp_fields); - $rpc = new mtgMetaRPC($is_global ? "RPC_$name" : $name, $code, $req, $rsp, $tokens); + $rpc = new mtgMetaRPC($is_global ? "RPC_$name" : $name, intval($code), $req, $rsp, $tokens); if($is_global) $this->_addUnit(new mtgMetaInfoUnit($this->module, $rpc)); @@ -712,7 +713,7 @@ class mtgMetaInfoParser implements mtgIMetaInfoParser $existing = null; if($service->hasToken('service_partial')) { - $existing = $this->current_meta->findUnit($service->getMetaId()); + $existing = $this->current_meta->findUnit($service->getName()); if($existing) { if(!($existing->object instanceof mtgMetaService)) @@ -734,11 +735,11 @@ class mtgMetaInfoParser implements mtgIMetaInfoParser break; $key = $this->T_value; if($this->T == self::T_RPC) - $service->addRPC($this->_parseRPC(false)); + $service->addRPC($this->_parseRPC(is_global: false)); else if($this->T == self::T_Enum) - $service->addUserType($this->_parseEnum(false)); + $service->addUserType($this->_parseEnum(is_global: false)); else if($this->T == self::T_Struct) - $service->addUserType($this->_parseStruct(false)); + $service->addUserType($this->_parseStruct(is_global: false)); else $this->_error("Unsupported type"); } diff --git a/parser2.inc.php b/parser2.inc.php index 5ccf2a4..8200f8a 100644 --- a/parser2.inc.php +++ b/parser2.inc.php @@ -359,7 +359,7 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser return $values; } - private function _parseEnum($is_global = true) : mtgMetaEnum + private function _parseEnum(bool $is_global = true) : mtgMetaEnum { $this->_nextT(); $name = $this->_parseDotName(); @@ -400,7 +400,7 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser if(!$is_global) $this->_error("Override supported for global enums only"); - $existing = $this->current_meta->findUnit($enum->getMetaId()); + $existing = $this->current_meta->findUnit($enum->getName()); if(!$existing) $this->_error("Not found '{$name}' enum to override values"); if(!($existing->object instanceof mtgMetaEnum)) @@ -415,7 +415,7 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser if(!$is_global) $this->_error("Replace supported for global enums only"); - $existing = $this->current_meta->findUnit($enum->getMetaId()); + $existing = $this->current_meta->findUnit($enum->getName()); if(!$existing) $this->_error("Not found '{$name}' enum to replace values"); if(!($existing->object instanceof mtgMetaEnum)) @@ -566,6 +566,7 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser private function _addUnit(mtgMetaInfoUnit $unit) { + $unit->object->setScope($this->_scope()); $this->current_meta->addUnit($unit); $this->module->addUnit($unit); } @@ -609,7 +610,7 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser return $s; } - private function _parseStruct() + private function _parseStruct(bool $is_global = true) : mtgMetaStruct { $this->_nextT(); $struct_origin = new mtgOrigin($this->file, $this->line); @@ -640,7 +641,9 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser $s = new mtgMetaStruct($name, array(), $parent, array(), $implements); $s->setOrigin($struct_origin); - $this->_addUnit(new mtgMetaInfoUnit($this->module, $s)); + + if($is_global) + $this->_addUnit(new mtgMetaInfoUnit($this->module, $s)); $tokens = $this->shared_tokens; if($this->T == self::T_Prop) @@ -669,6 +672,8 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser foreach($funcs as $fn) $s->addFunc($fn); } + + return $s; } private function _parseInterface() @@ -695,7 +700,7 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser $this->_nextT(); } - private function _parseRPC($is_global = true) : mtgMetaRPC + private function _parseRPC(bool $is_global = true) : mtgMetaRPC { $this->_nextT(); $code = $this->T_value; @@ -717,12 +722,12 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser { return $this->_nextIf(ord('}')); } ); - $req = new mtgMetaPacket($code, $is_global ? "RPC_REQ_$name" : "Request"); + $req = new mtgMetaPacket(intval($code), $is_global ? "RPC_REQ_$name" : "Request"); $req->setFields($req_fields); - $rsp = new mtgMetaPacket($code, $is_global ? "RPC_RSP_$name" : "Response"); + $rsp = new mtgMetaPacket(intval($code), $is_global ? "RPC_RSP_$name" : "Response"); $rsp->setFields($rsp_fields); - $rpc = new mtgMetaRPC($is_global ? "RPC_$name" : $name, $code, $req, $rsp, $tokens); + $rpc = new mtgMetaRPC($is_global ? "RPC_$name" : $name, intval($code), $req, $rsp, $tokens); if($is_global) $this->_addUnit(new mtgMetaInfoUnit($this->module, $rpc)); @@ -748,7 +753,7 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser $existing = null; if($service->hasToken('service_partial')) { - $existing = $this->current_meta->findUnit($service->getMetaId()); + $existing = $this->current_meta->findUnit($service->getName()); if($existing) { if(!($existing->object instanceof mtgMetaService)) @@ -770,9 +775,11 @@ class mtgMetaInfoParser2 implements mtgIMetaInfoParser break; $key = $this->T_value; if($this->T == self::T_RPC) - $service->addRPC($this->_parseRPC(false)); + $service->addRPC($this->_parseRPC(is_global: false)); else if($this->T == self::T_Enum) - $service->addUserType($this->_parseEnum(false)); + $service->addUserType($this->_parseEnum(is_global: false)); + else if($this->T == self::T_Struct) + $service->addUserType($this->_parseStruct(is_global: false)); else if($this->T == self::T_Event) $service->addEvent($this->_parseEvent()); else diff --git a/scope.inc.php b/scope.inc.php new file mode 100644 index 0000000..f25afea --- /dev/null +++ b/scope.inc.php @@ -0,0 +1,28 @@ +scopes[] = $scope; + } + + function findSymbol(string $name) : ?mtgMetaUnit + { + foreach($this->scopes as $scope) + { + $unit = $scope->findSymbol($name); + if($unit) + return $unit; + } + return null; + } +} + diff --git a/service.inc.php b/service.inc.php new file mode 100644 index 0000000..1802c79 --- /dev/null +++ b/service.inc.php @@ -0,0 +1,217 @@ +name = $name; + $this->merged_scope = new mtgMultiScope(); + $this->merged_scope->addScope($scope); + $this->scope = $scope; + $this->tokens = $tokens; + } + + function validate(mtgMetaInfo $meta) {} + + function addPartial(mtgMetaParsedModule $module, mtgMetaService $service) + { + $this->setTokens(array_merge($this->getTokens(), $service->getTokens())); + $this->merged_scope->addScope($service->getScope()); + $this->partials[$module->file] = $service; + } + + function getPartials() : array + { + return $this->partials; + } + + function getName() : string + { + return $this->name; + } + + function findSymbol(string $name) : ?mtgMetaUnit + { + if(isset($this->user_types[$name])) + return $this->user_types[$name]; + + return $this->merged_scope->findSymbol($name); + } + + function getRPCs() : array + { + return $this->rpcs; + } + + function addRPC(mtgMetaRPC $rpc) + { + $rpc->setScope($this); + + foreach($this->rpcs as $name => $_rpc) + { + if($name == $rpc->getName()) + throw new Exception("Service '{$this->name}' already has RPC '{$rpc->getName()}'"); + + if($rpc->getNumericCode() == $_rpc->getNumericCode()) + throw new Exception("Service '{$this->name}' already has RPC '{$_rpc->getName()}' with code '{$rpc->getNumericCode()}'"); + } + + $this->rpcs[$rpc->getName()] = $rpc; + } + + function hasRPC(string $name) : bool + { + return isset($this->rpcs[$name]); + } + + function getRPC(string $name) : mtgMetaRPC + { + if(!isset($this->rpcs[$name])) + throw new Exception("No such RPC '$name'"); + return $this->rpcs[$name]; + } + + function getUserTypes() : array + { + return $this->user_types; + } + + function addUserType(mtgUserType $utype) + { + $utype->setScope($this); + + if($this->hasRPC($utype->getName()) || $this->hasUserType($utype->getName())) + throw new Exception("Service '{$this->name}' already has user type with name '{$utype->getName()}'"); + + $this->user_types[$utype->getName()] = $utype; + } + + function hasUserType(string $name) : bool + { + return isset($this->user_types[$name]); + } + + function getUserType(bool $name) : mtgUserType + { + if(!isset($this->user_types[$name])) + throw new Exception("No such user type '$name'"); + return $this->user_types[$name]; + } + + function addEvent(mtgMetaStruct $evt) + { + $evt->setScope($this); + + $this->events[] = $evt; + $this->addUserType($evt); + } + + function getEvents() : array + { + return $this->events; + } + + function getClassId() : int + { + if($this->hasToken('class_id')) + return $this->getToken('class_id'); + + return crc32($this->name); + } + + function __toString() + { + return $this->name; + } +} + +class mtgMetaRPC extends mtgMetaUnit +{ + private string $name; + private int $code; + private mtgMetaPacket $in; + private mtgMetaPacket $out; + + function __construct(string $name, int $code, mtgMetaPacket $in, mtgMetaPacket $out, array $tokens = array()) + { + $this->name = $name; + $this->code = $code; + $this->in = $in; + $this->out = $out; + $this->tokens = $tokens; + } + + function validate(mtgMetaInfo $meta) {} + + function getName() : string + { + return $this->name; + } + + function getCode() : int + { + return $this->code; + } + + // deprecated? + function getNumericCode() : int + { + return (int)$this->code; + } + + function getReq() : mtgMetaPacket + { + return $this->in; + } + + function getRsp() : mtgMetaPacket + { + return $this->out; + } +} + + +class mtgMetaPacket extends mtgMetaStruct +{ + private int $code; + + function __construct(int $code, string $name, array $tokens = array()) + { + $this->code = $code; + + parent::__construct($name, array(), null, $tokens); + } + + // deprecated + function getPrefix() : string + { + list($prefix,) = explode('_', $this->getName()); + return $prefix; + } + + // deprecated + function getFullName() : string + { + return $this->code . "_" . $this->getName(); + } + + function getCode() : int + { + return $this->code; + } + + // deprecated + function getNumericCode() : int + { + return (int)$this->code; + } +} + diff --git a/struct.inc.php b/struct.inc.php new file mode 100644 index 0000000..0ec2b0d --- /dev/null +++ b/struct.inc.php @@ -0,0 +1,232 @@ +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->crc_class_id = crc32($this->getFullName()); + else + //TODO: migrate to crc32 from crc28 + $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; + } +} + + diff --git a/type.inc.php b/type.inc.php new file mode 100644 index 0000000..2972248 --- /dev/null +++ b/type.inc.php @@ -0,0 +1,272 @@ +resolved = $name_or_resolved; + } + else + { + $this->name = $name_or_resolved; + if(!$scope) + throw new Exception("Scope is not set"); + + self::$unresolved[] = $this; + } + + $this->scope = $scope; + $this->origin = $origin ?? new mtgOrigin(); + } + + function getName() : string + { + if($this->resolved) + return ''.$this->resolved; + else + return $this->name; + } + + static function checkAllResolved() + { + while(sizeof(self::$unresolved) > 0) + { + $ref = array_shift(self::$unresolved); + $resolved = $ref->resolve(); + if($resolved instanceof mtgMetaFunc && $resolved->getName()) + throw new Exception("Invalid resolving of named function as type: " . $resolved->getName() . " at " . $ref->origin); + } + } + + function resolve() : mtgType + { + if($this->resolved) + return $this->resolved; + + $symb = $this->scope->findSymbol($this->name); + if($symb) + { + if(!($symb instanceof mtgType)) + throw new Exception("{$this->origin} : Symbol '{$this->name}' is not type"); + $this->resolved = $symb; + return $this->resolved; + } + else + throw new Exception("{$this->origin} : Symbol '{$this->name}' not found"); + } + + function __toString() + { + return $this->getName(); + } +} + +abstract class mtgUserType extends mtgMetaUnit implements mtgType +{ + protected string $name; + + function __construct(string $name) + { + $this->name = $name; + } + + function getName() : string + { + return $this->name; + } + + function setName(string $name) + { + $this->name = $name; + } + + function __toString() : string + { + return $this->name; + } +} + +class mtgBuiltinType implements mtgType +{ + private string $name; + + function __construct(string $name) + { + if(!in_array($name, mtgMetaInfo::$BUILTIN_TYPES)) + throw new Exception("Not a built-in type '$name'"); + + $this->name = $name; + } + + function getName() : string + { + return $this->name; + } + + function __toString() : string + { + return $this->name; + } + + function isNumeric() : bool + { + return !$this->isString() && !$this->isBool() && !$this->isBlob(); + } + + function isString() : bool + { + return $this->name === 'string'; + } + + function isInt() : bool + { + return strpos($this->name, 'int') === 0; + } + + function isUint() : bool + { + return strpos($this->name, 'uint') === 0; + } + + function isUint8() : bool + { + return $this->name === 'uint8'; + } + + function isUint16() : bool + { + return $this->name === 'uint16'; + } + + function isUint32() : bool + { + return $this->name === 'uint32'; + } + + function isUint64() : bool + { + return $this->name === 'uint64'; + } + + function isInt64() : bool + { + return $this->name === 'int64'; + } + + function isInt8() : bool + { + return $this->name === 'int8'; + } + + function isInt16() : bool + { + return $this->name === 'int16'; + } + + function isInt32() : bool + { + return $this->name === 'int32'; + } + + function isFloat() : bool + { + return $this->name === 'float'; + } + + function isDouble() : bool + { + return $this->name === 'double'; + } + + function isBool() : bool + { + return $this->name === 'bool'; + } + + function isBlob() : bool + { + return $this->name === 'blob'; + } +} + +class mtgMultiType implements mtgType +{ + private $values = array(); + + function __construct(array $values = array()) + { + foreach($values as $v) + $this->addValue($v); + } + + function addValue(mtgTypeRef $val) + { + $this->values[] = $val; + } + + function getValues() + { + $vals = array(); + foreach($this->values as $val) + $vals[] = $val->resolve(); + + return $vals; + } + + function getName() : string + { + $str = ''; + foreach($this->getValues() as $val) + $str .= $val . ';'; + return $str; + } + + function __toString() : string + { + return $this->getName(); + } +} + +class mtgArrType implements mtgType +{ + private mtgTypeRef $value; + + function __construct(mtgTypeRef $value) + { + $this->value = $value; + } + + function getName() : string + { + return $this->getValue() . '[]'; + } + + function __toString() : string + { + return $this->getName(); + } + + function getValue() : mixed + { + return $this->value->resolve(); + } +} + diff --git a/util.inc.php b/util.inc.php new file mode 100644 index 0000000..3750018 --- /dev/null +++ b/util.inc.php @@ -0,0 +1,40 @@ +getFields(); + $parent = $struct->getParent(); + if($parent) + //NOTE: order is important, parent fields must come first + $fields = array_merge(mtg_get_all_fields($parent), $fields); + return $fields; +} + +function mtg_find_field_owner(mtgMetaStruct $struct, $name) +{ + $tmp = $struct; + while($tmp) + { + $fields = $tmp->getFields(); + if(isset($fields[$name])) + return $tmp; + $tmp = $tmp->getParent(); + } + return null; +} + +function mtg_try_get_token_in_hierarchy(mtgMetaStruct $struct, $name, &$value) +{ + $tmp = $struct; + while($tmp) + { + if($tmp->hasToken($name)) + { + $value = $tmp->getToken($name); + return true; + } + $tmp = $tmp->getParent(); + } + + return false; +}