metagen/metagen.inc.php

763 lines
13 KiB
PHP

<?php
interface mtgType
{}
class mtgTypeRef implements mtgType
{
private static $all = array();
private $name;
private $file;
private $line;
private $meta;
private $resolved;
function __construct($name_or_resolved, mtgMetaInfo $meta = null, $file = '', $line= 0)
{
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;
$this->meta = $meta;
$this->file = $file;
$this->line = $line;
self::$all[] = $this;
}
static function checkAllResolved()
{
foreach(self::$all as $ref)
$ref->resolve();
}
function resolve()
{
if($this->resolved)
return $this->resolved;
$u = $this->meta->findUnit($this->name, false/*non strict*/);
if($u)
{
$this->resolved = $u->object;
return $this->resolved;
}
else
{
throw new Exception("{$this->file}@{$this->line} : Symbol '{$this->name}' not found");
}
}
}
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, $strict = true)
{
if(isset($this->units[$id]))
return $this->units[$id];
else if($strict)
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()
{
return $this->parent ? $this->parent->resolve() : null;
}
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 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;
}