Adding initial support for service type
Publish PHP Package / docker (push) Successful in 6s Details

This commit is contained in:
Pavel Shevaev 2024-04-04 19:20:38 +03:00
parent ada7729320
commit 5dcffe2c80
2 changed files with 209 additions and 26 deletions

View File

@ -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;

View File

@ -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<int,string>*/
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] = '<FloatConstant>';
$this->T2descr[self::T_Enum] = '<enum>';
$this->T2descr[self::T_RPC] = '<RPC>';
$this->T2descr[self::T_Service] = '<Service>';
$this->T2descr[self::T_End] = '<end>';
$this->T2descr[self::T_UserSymbol] = '<Identifier>';
$this->T2descr[self::T_Struct] = '<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]))