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; +}