diff --git a/metagen.inc.php b/metagen.inc.php index 0ab5f21..7e1a48e 100644 --- a/metagen.inc.php +++ b/metagen.inc.php @@ -5,16 +5,21 @@ interface mtgType function getName(); } +interface mtgScope +{ + function findSymbol($name); +} + class mtgTypeRef implements mtgType { private static $unresolved = array(); private $name; - private $module; + private $scope; private $resolved; private $origin; - function __construct($name_or_resolved, mtgMetaParsedModule $module = null, mtgOrigin $origin = null) + function __construct($name_or_resolved, mtgScope $scope = null, mtgOrigin $origin = null) { if(is_object($name_or_resolved)) { @@ -25,13 +30,13 @@ class mtgTypeRef implements mtgType else { $this->name = $name_or_resolved; - if(!$module) - throw new Exception("Module is not set"); + if(!$scope) + throw new Exception("Scope is not set"); self::$unresolved[] = $this; } - $this->module = $module; + $this->scope = $scope; $this->origin = $origin ?? new mtgOrigin(); } @@ -58,10 +63,10 @@ class mtgTypeRef implements mtgType if($this->resolved) return $this->resolved; - $u = $this->module->findUnit($this->name); - if($u) + $symb = $this->scope->findSymbol($this->name); + if($symb) { - $this->resolved = $u->object; + $this->resolved = $symb; return $this->resolved; } else @@ -245,8 +250,7 @@ abstract class mtgUserType extends mtgMetaUnit implements mtgType if($this->hasToken('class_id')) return $this->getToken('class_id'); - //TODO: use more flexible schema, maybe get rid of this method - //NOTE: using crc28 actually, leaving some extra reserved space + //TODO: migrate to crc32 from crc28 return crc32($this->name) & 0xFFFFFFF; } @@ -569,6 +573,110 @@ class mtgMetaRPC extends mtgMetaUnit } } +class mtgMetaService extends mtgMetaUnit implements mtgScope +{ + private $name; + private $parent_scope; + private $rpcs = array(); + private $user_types = array(); + + function __construct($name, mtgScope $parent_scope, array $tokens = array()) + { + $this->name = $name; + $this->parent_scope = $parent_scope; + $this->tokens = $tokens; + } + + function validate(mtgMetaInfo $meta) {} + + function getName() + { + return $this->name; + } + + function getMetaId() + { + return $this->name; + } + + function findSymbol($name) + { + if(isset($this->user_types[$name])) + return $this->user_types[$name]; + + return $this->parent_scope->findSymbol($name); + } + + function getRPCs() + { + 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($name) + { + return isset($this->rpcs[$name]); + } + + function getRPC($name) + { + if(!isset($this->rpcs[$name])) + throw new Exception("No such RPC '$name'"); + return $this->rpcs[$name]; + } + + function getUserTypes() + { + return $this->user_types; + } + + function addUserType(mtgUserType $utype) + { + if($this->hasRPC($utype->getName())) + throw new Exception("Service '{$this->name}' already has type '{$utype->getName()}'"); + + $this->user_types[$utype->getName()] = $utype; + } + + function hasUserType($name) + { + return isset($this->user_types[$name]); + } + + function getUserType($name) + { + if(!isset($this->user_types[$name])) + throw new Exception("No such user type '$name'"); + return $this->user_types[$name]; + } + + function getClassId() + { + 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; diff --git a/parser.inc.php b/parser.inc.php index 9c48129..83b8e79 100644 --- a/parser.inc.php +++ b/parser.inc.php @@ -31,7 +31,8 @@ class mtgMetaInfoParser const T_int64 = 1029; const T_bool = 1030; const T_blob = 1031; - const T_MaxType = 1032; //built-in types end mark + const T_Service = 1032; + const T_MaxType = 1033; //built-in types end mark private array $config = array(); private mtgMetaInfo $current_meta; @@ -54,6 +55,7 @@ class mtgMetaInfoParser /** @var array*/ private $T2descr = array(); private array $shared_tokens = array(); + private array $scopes = array(); function __construct($config = array()) { @@ -90,6 +92,7 @@ class mtgMetaInfoParser "interface" => self::T_Interface, "enum" => self::T_Enum, "RPC" => self::T_RPC, + "service" => self::T_Service, "end" => self::T_End, "extends" => self::T_Extends, "implements" => self::T_Implements, @@ -104,6 +107,7 @@ class mtgMetaInfoParser $this->T2descr[self::T_FloatConstant] = ''; $this->T2descr[self::T_Enum] = ''; $this->T2descr[self::T_RPC] = ''; + $this->T2descr[self::T_Service] = ''; $this->T2descr[self::T_End] = ''; $this->T2descr[self::T_UserSymbol] = ''; $this->T2descr[self::T_Struct] = ''; @@ -184,6 +188,8 @@ class mtgMetaInfoParser $this->_parseFreeFunc(); else if($this->T == self::T_RPC) $this->_parseRPC(); + else if($this->T == self::T_Service) + $this->_parseService(); else $this->_error("Unexpected symbol ('" . $this->_toStr($this->T) . "' " . $this->T_value . ")"); } @@ -223,19 +229,19 @@ class mtgMetaInfoParser { $origin = new mtgOrigin($this->file, $this->line); $func_type = $this->_parseFuncType(); - $type = new mtgTypeRef($func_type, $this->module, $origin); + $type = new mtgTypeRef($func_type, $this->_scope(), $origin); } else if($this->T == self::T_UserSymbol) { $origin = new mtgOrigin($this->file, $this->line); $type_name = $this->_parseDotName(); - $type = new mtgTypeRef($type_name, $this->module, $origin); + $type = new mtgTypeRef($type_name, $this->_scope(), $origin); } else { $origin = new mtgOrigin($this->file, $this->line); $type_name = $this->T_value; - $type = new mtgTypeRef(new mtgBuiltinType($type_name), $this->module, $origin); + $type = new mtgTypeRef(new mtgBuiltinType($type_name), $this->_scope(), $origin); $this->_nextT(); } @@ -244,7 +250,7 @@ class mtgMetaInfoParser $origin = new mtgOrigin($this->file, $this->line); $this->_nextT(); $this->_checkThenNext(ord(']')); - $type = new mtgTypeRef(new mtgArrType($type), $this->module, $origin); + $type = new mtgTypeRef(new mtgArrType($type), $this->_scope(), $origin); } $types[] = $type; @@ -257,7 +263,7 @@ class mtgMetaInfoParser } if(sizeof($types) > 1) - return new mtgTypeRef(new mtgMultiType($types), $this->module, new mtgOrigin($this->file, $this->line)); + return new mtgTypeRef(new mtgMultiType($types), $this->_scope(), new mtgOrigin($this->file, $this->line)); else return $types[0]; } @@ -352,7 +358,7 @@ class mtgMetaInfoParser return $values; } - private function _parseEnum() + private function _parseEnum($is_global = true) { $this->_nextT(); $name = $this->_parseDotName(); @@ -388,6 +394,9 @@ class mtgMetaInfoParser // with additional values if($enum->hasToken('enum_override')) { + if(!$is_global) + $this->_error("Override supported for global enums only"); + $existing = $this->current_meta->findUnit($enum->getMetaId()); if(!$existing) throw new Exception("Not found '{$name}' enum to override values"); @@ -400,6 +409,9 @@ class mtgMetaInfoParser // with additional values else if($enum->hasToken('enum_replace')) { + if(!$is_global) + $this->_error("Replace supported for global enums only"); + $existing = $this->current_meta->findUnit($enum->getMetaId()); if(!$existing) throw new Exception("Not found '{$name}' enum to replace values"); @@ -408,8 +420,10 @@ class mtgMetaInfoParser $existing->object->replace($enum); } - else + else if($is_global) $this->_addUnit(new mtgMetaInfoUnit($this->module, $enum)); + + return $enum; } static private function _isBuiltinType(int $t) : bool @@ -417,6 +431,24 @@ class mtgMetaInfoParser return $t > self::T_MinType && $t < self::T_MaxType; } + private function _pushScope(mtgScope $scope) + { + $this->scopes[] = $scope; + } + + private function _popScope() + { + array_shift($this->scopes); + } + + private function _scope() : mtgScope + { + if(!$this->scopes) + return $this->module; + + return $this->scopes[count($this->scopes)-1]; + } + private function _parseFields(callable $next_doer) { $flds = array(); @@ -555,7 +587,7 @@ class mtgMetaInfoParser $this->_nextT(); $origin = new mtgOrigin($this->file, $this->line); $parent_name = $this->_parseDotName(); - $parent = new mtgTypeRef($parent_name, $this->module, $origin); + $parent = new mtgTypeRef($parent_name, $this->_scope(), $origin); } $implements = array(); @@ -566,7 +598,7 @@ class mtgMetaInfoParser $this->_nextT(); $origin = new mtgOrigin($this->file, $this->line); $if_name = $this->_parseDotName(); - $implements[] = new mtgTypeRef($if_name, $this->module, $origin); + $implements[] = new mtgTypeRef($if_name, $this->_scope(), $origin); } while($this->T == ord(',')); } @@ -627,7 +659,7 @@ class mtgMetaInfoParser $this->_nextT(); } - private function _parseRPC() + private function _parseRPC($is_global = true) { $this->_nextT(); $code = $this->T_value; @@ -646,13 +678,48 @@ class mtgMetaInfoParser { return $this->_nextIf(self::T_End); } ); - $req = new mtgMetaPacket($code, "RPC_REQ_$name"); + $req = new mtgMetaPacket($code, $is_global ? "RPC_REQ_$name" : $name); $req->setFields($req_fields); - $rsp = new mtgMetaPacket($code, "RPC_RSP_$name"); + $rsp = new mtgMetaPacket($code, $is_global ? "RPC_RSP_$name" : $name); $rsp->setFields($rsp_fields); - $rpc = new mtgMetaRPC("RPC_$name", $code, $req, $rsp, $tokens); - $this->_addUnit(new mtgMetaInfoUnit($this->module, $rpc)); + $rpc = new mtgMetaRPC($is_global ? "RPC_$name" : $name, $code, $req, $rsp, $tokens); + + if($is_global) + $this->_addUnit(new mtgMetaInfoUnit($this->module, $rpc)); + + return $rpc; + } + + private function _parseService() + { + $this->_nextT(); + $name = $this->_parseDotName(); + + $service = new mtgMetaService($name, $this->_scope()); + $this->_pushScope($service); + + $tokens = $this->shared_tokens; + if($this->T == self::T_Prop) + $tokens = array_merge($tokens, $this->_parsePropTokens()); + $service->setTokens($tokens); + + while(true) + { + if($this->_nextIf(self::T_End)) + break; + $key = $this->T_value; + if($this->T == self::T_RPC) + $service->addRPC($this->_parseRPC(false)); + else if($this->T == self::T_Enum) + $service->addUserType($this->_parseEnum(false)); + else + $this->_error("Unsupported type"); + } + + $this->_popScope(); + + $this->_addUnit(new mtgMetaInfoUnit($this->module, $service)); } private function _parsePropTokens() : array @@ -904,7 +971,7 @@ class mtgMetaInfoParser } } -class mtgMetaParsedModule +class mtgMetaParsedModule implements mtgScope { public string $file; public $units = array(); @@ -920,6 +987,14 @@ class mtgMetaParsedModule $this->units[$unit->object->getMetaId()] = $unit; } + function findSymbol($name) + { + $unit = $this->findUnit($name); + if(!$unit) + return null; + return $unit->object; + } + function findUnit($id) { if(isset($this->units[$id]))