resolved = $name_or_resolved; } else { $this->name = $name_or_resolved; if(!$module) throw new Exception("Module is not set"); self::$unresolved[] = $this; } $this->module = $module; $this->file = $file; $this->line = $line; } static function checkAllResolved() { while(sizeof(self::$unresolved) > 0) { $ref = array_shift(self::$unresolved); $ref->resolve(); } } function resolve() { if($this->resolved) return $this->resolved; $u = $this->module->findUnit($this->name); if($u) { $this->resolved = $u->object; return $this->resolved; } else throw new Exception("{$this->file}@{$this->line} : Symbol '{$this->name}' not found"); } function __toString() { if($this->resolved) return ''.$this->resolved; else return $this->name; } } class mtgMetaInfoUnit { public $file; public $object; function __construct($f, mtgMetaUnit $o) { $this->file = $f; $this->object = $o; } } 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->getId()])) throw new Exception("Meta info unit '{$unit->object->getId()}' already defined in file '{$this->units[$unit->object->getId()]->file}'"); $this->units[$unit->object->getId()] = $unit; } function getUnits() { return $this->units; } function findUnit($id) { if(isset($this->units[$id])) return $this->units[$id]; return null; } function getUnit($id) { if(isset($this->units[$id])) return $this->units[$id]; throw new Exception("Unit '$id' not found"); } } abstract class mtgMetaUnit { abstract function getId(); protected $tokens = array(); function getTokens() { return $this->tokens; } function setTokens(array $tokens) { $this->tokens = $tokens; } function setToken($name, $val) { $this->tokens[$name] = $val; } function getToken($name) { return $this->hasToken($name) ? $this->tokens[$name] : null; } function hasToken($name) { return array_key_exists($name, $this->tokens); } } class mtgUserType extends mtgMetaUnit implements mtgType { protected $name; function __construct($name) { $this->name = $name; } function getName() { return $this->name; } function setName($name) { $this->name = $name; } function getId() { return $this->name; } function getClassId() { //TODO: use more flexible schema, maybe get rid of this method //NOTE: using crc28 actually, leaving some extra reserved space 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 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 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 __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 getId() { 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 getId() { 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 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 __toString() { $str = ''; foreach($this->getValues() as $val) $str .= $val . ';'; return $str; } } class mtgArrType implements mtgType { private $value; function __construct(mtgTypeRef $value) { $this->value = $value; } function __toString() { return $this->getValue() . '[]'; } 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 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 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; }