first commit
This commit is contained in:
commit
e10147eed3
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,702 @@
|
|||
<?php
|
||||
|
||||
require_once(dirname(__FILE__) . '/../../metagen.inc.php');
|
||||
require_once(dirname(__FILE__) . '/cs_tpl.inc.php');
|
||||
|
||||
class mtgCsCodegen extends mtgCodegen
|
||||
{
|
||||
public $namespace = 'game';
|
||||
|
||||
function genUnit(mtgMetaInfoUnit $unit)
|
||||
{
|
||||
$obj = $unit->object;
|
||||
if($obj instanceof mtgMetaEnum)
|
||||
return $this->genEnum($obj);
|
||||
else if($obj instanceof mtgMetaStruct)
|
||||
return $this->genStruct($obj);
|
||||
else if($obj instanceof mtgMetaRPC)
|
||||
return $this->genRPC($obj);
|
||||
else
|
||||
{
|
||||
echo "WARN: skipping meta unit '{$obj->getId()}'\n";
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function genEnum(mtgMetaEnum $struct)
|
||||
{
|
||||
$templater = new mtg_cs_templater();
|
||||
|
||||
$repl = array();
|
||||
$repl['%namespace%'] = $this->namespace;
|
||||
$repl['%class%'] = $struct->getName();
|
||||
$repl['%class_id%'] = $struct->getClassId();
|
||||
|
||||
$tpl = $templater->tpl_enum();
|
||||
|
||||
$this->fillEnumRepls($struct, $repl);
|
||||
|
||||
return mtg_fill_template($tpl, $repl);
|
||||
}
|
||||
|
||||
function fillEnumRepls(mtgMetaEnum $enum, &$repl)
|
||||
{
|
||||
$repl['%fields%'] = '';
|
||||
$repl['%attributes%'] = '';
|
||||
$fields =& $repl['%fields%'];
|
||||
if($enum->hasToken('cs_attributes'))
|
||||
{
|
||||
$attrs = explode(",", $enum->getToken('cs_attributes'));
|
||||
foreach($attrs as $attr)
|
||||
$repl['%attributes%'] .= "[$attr] ";
|
||||
}
|
||||
foreach($enum->getValues() as $vname => $value)
|
||||
$fields .= sprintf("\n %s = %s,", $vname, $value);
|
||||
}
|
||||
|
||||
function genRPC(mtgMetaRPC $rpc)
|
||||
{
|
||||
$templater = new mtg_cs_templater();
|
||||
|
||||
$repl = array();
|
||||
$repl['%namespace%'] = $this->namespace;
|
||||
$repl['%class%'] = $rpc->getName();
|
||||
$repl['%code%'] = $rpc->getCode();
|
||||
$repl['%req_class%'] = $rpc->getReq()->getName();
|
||||
$repl['%rsp_class%'] = $rpc->getRsp()->getName();
|
||||
|
||||
$tpl = $templater->tpl_rpc();
|
||||
|
||||
return
|
||||
mtg_fill_template($tpl, $repl) .
|
||||
$this->genStruct($rpc->getReq()) .
|
||||
$this->genStruct($rpc->getRsp());
|
||||
}
|
||||
|
||||
function genStruct(mtgMetaStruct $struct)
|
||||
{
|
||||
$templater = new mtg_cs_templater();
|
||||
|
||||
$repl = array();
|
||||
$repl['%namespace%'] = $this->namespace;
|
||||
$repl['%class%'] = $struct->getName();
|
||||
$repl['%class_id%'] = $struct->getClassId();
|
||||
|
||||
$tpl = $templater->tpl_struct();
|
||||
|
||||
$this->fillStructRepls($struct, $repl);
|
||||
|
||||
return mtg_fill_template($tpl, $repl);
|
||||
}
|
||||
|
||||
function preprocessField(mtgMetaStruct $struct, mtgMetaField $field)
|
||||
{}
|
||||
|
||||
function countFields(mtgMetaStruct $struct)
|
||||
{
|
||||
$fields = $struct->getFields();
|
||||
$count = 0;
|
||||
foreach($fields as $field)
|
||||
{
|
||||
if($field->hasToken('i18n'))
|
||||
$count += 2;
|
||||
else
|
||||
$count += 1;
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
function fillStructRepls(mtgMetaStruct $struct, &$repl)
|
||||
{
|
||||
foreach($struct->getFields() as $field)
|
||||
$this->preprocessField($struct, $field);
|
||||
|
||||
$all_fields = mtg_get_all_fields($this->info, $struct);
|
||||
$all_fields_count = $this->countAllFields($struct);
|
||||
|
||||
$repl['%includes%'] = '';
|
||||
$repl['%fields%'] = '';
|
||||
$repl['%fields_reset%'] = '';
|
||||
$repl['%attributes%'] = '';
|
||||
|
||||
$repl['%sync_buffer%'] = '';
|
||||
$repl['%fields_count%'] = $all_fields_count;
|
||||
$repl["%ext_methods%"] = "";
|
||||
|
||||
$repl['%new_method%'] = '';
|
||||
$repl['%virt_method%'] = ' virtual ';
|
||||
$repl['%commented_in_child_begin%'] = '';
|
||||
$repl['%commented_in_child_end%'] = '';
|
||||
$repl['%commented_in_pod_begin%'] = '';
|
||||
$repl['%commented_in_pod_end%'] = '';
|
||||
|
||||
$has_bitfields_token = $struct->hasToken("bitfields");
|
||||
|
||||
$bitctx_name = 'bitctx';
|
||||
|
||||
$repl['%bitfields_sync_context%'] = "";
|
||||
if($has_bitfields_token)
|
||||
{
|
||||
$repl['%bitfields_sync_context%'] .= $this->offset(2)."var $bitctx_name = new Meta.BitfieldsContext(ctx, fields_mask);\n";
|
||||
$repl['%bitfields_sync_context%'] .= $this->offset(2)."$bitctx_name.SyncMaskHeader();\n";
|
||||
}
|
||||
|
||||
if($struct->hasToken('cs_attributes'))
|
||||
{
|
||||
$attrs = explode(",", $struct->getToken('cs_attributes'));
|
||||
foreach($attrs as $attr)
|
||||
$repl['%attributes%'] .= "[$attr] ";
|
||||
}
|
||||
|
||||
$parent = $struct->getParent();
|
||||
$is_pod = $struct->hasToken('POD');
|
||||
|
||||
if($parent && $has_bitfields_token)
|
||||
throw new Exception("@bitfields struct can't have a parent: {$struct->getName()}");
|
||||
|
||||
if($is_pod)
|
||||
{
|
||||
$fields = $all_fields;
|
||||
$repl['%type_name%'] = 'struct';
|
||||
$repl['%parent%'] = " : IMetaStruct";
|
||||
$repl['%virt_method%'] = '';
|
||||
$repl['%commented_in_pod_begin%'] = '/*';
|
||||
$repl['%commented_in_pod_end%'] = '*/';
|
||||
}
|
||||
else
|
||||
{
|
||||
$repl['%type_name%'] = 'class';
|
||||
$repl['%parent%'] = " : " . ($parent ? $parent : " BaseMetaStruct");
|
||||
if($parent)
|
||||
$repl['%sync_buffer%'] .= "\n base.syncFields(ctx);\n";
|
||||
$repl['%commented_in_child_begin%'] = '/*';
|
||||
$repl['%commented_in_child_end%'] = '*/';
|
||||
$repl['%virt_method%'] = " override ";
|
||||
|
||||
if($parent)
|
||||
{
|
||||
$repl['%new_method%'] = " new ";
|
||||
$repl['%fields_reset%'] = "base.reset();\n";
|
||||
}
|
||||
|
||||
$fields = $struct->getFields();
|
||||
}
|
||||
|
||||
$repl['%copy_fields%'] = '';
|
||||
$repl['%virt_clone_method%'] = '';
|
||||
|
||||
if($struct->hasToken("cloneable"))
|
||||
{
|
||||
$repl['%parent%'] .= ",IMetaCloneable";
|
||||
$repl['%commented_in_non_cloneable_begin%'] = '';
|
||||
$repl['%commented_in_non_cloneable_end%'] = '';
|
||||
|
||||
if($is_pod)
|
||||
$repl['%virt_clone_method%'] = '';
|
||||
else if($parent && $parent->hasTokenInParent("cloneable"))
|
||||
$repl['%virt_clone_method%'] = ' override ';
|
||||
else
|
||||
$repl['%virt_clone_method%'] = ' virtual ';
|
||||
}
|
||||
else
|
||||
{
|
||||
$repl['%commented_in_non_cloneable_begin%'] = '/*';
|
||||
$repl['%commented_in_non_cloneable_end%'] = '*/';
|
||||
}
|
||||
|
||||
$field_index = 0;
|
||||
foreach($fields as $field)
|
||||
{
|
||||
$repl['%fields%'] .= $this->offset() . $this->genFieldDecl($struct, $field)."\n";
|
||||
|
||||
$repl['%fields_reset%'] .= $this->offset() . $this->genFieldReset($field)."\n";
|
||||
|
||||
$sync_opts = $this->resolveSyncOpts($struct, $bitctx_name);
|
||||
|
||||
$repl['%sync_buffer%'] .= $this->offset(2) . $this->genBufSyncRegular($field, "ctx", $sync_opts)."\n";
|
||||
|
||||
$field_index++;
|
||||
}
|
||||
|
||||
if($has_bitfields_token)
|
||||
{
|
||||
$repl['%fields%'] .= "\n".$this->offset()."public long fields_mask;\n";
|
||||
|
||||
$repl['%ext_methods%'] .= "\n".$this->genBitmaskHelpers($struct, $fields);
|
||||
|
||||
$repl['%copy_fields%'] .= $this->offset() . "fields_mask = other.fields_mask;\n";
|
||||
$repl['%fields_reset%'] .= $this->offset() . "fields_mask = 0L;\n";
|
||||
|
||||
$repl['%dirty_fields_count%'] = "Meta.GetDirtyFieldsCount(fields_mask, fields_count: $all_fields_count) + Meta.MASK_HEADER_FIELDS_COUNT";
|
||||
}
|
||||
else
|
||||
$repl['%dirty_fields_count%'] = "$all_fields_count /* equal to getFieldsCount */ ";
|
||||
|
||||
$repl['%fields%'] = trim($repl['%fields%'], "\n ");
|
||||
|
||||
$this->undoTemp();
|
||||
}
|
||||
|
||||
function resolveSyncOpts(mtgMetaStruct $struct, string $bitctx_name)
|
||||
{
|
||||
$opts = array();
|
||||
if($struct->hasToken("bitfields"))
|
||||
$opts[] = "$bitctx_name.GetNextOpts()";
|
||||
|
||||
if(count($opts) == 0)
|
||||
return "0";
|
||||
|
||||
return implode(" | ", $opts);
|
||||
}
|
||||
|
||||
function countAllFields(mtgMetaStruct $struct)
|
||||
{
|
||||
$count = count($struct->getFields()); //preprocessed fields list
|
||||
|
||||
//raw fields lists
|
||||
$curr = $struct->getParent();
|
||||
while($curr)
|
||||
{
|
||||
$count += $this->countFields($curr);
|
||||
$curr = $curr->getParent();
|
||||
}
|
||||
|
||||
return $count;
|
||||
}
|
||||
|
||||
function genNativePlainType(mtgType $type)
|
||||
{
|
||||
if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
switch($type->getName())
|
||||
{
|
||||
case "int8":
|
||||
return "sbyte";
|
||||
case "uint8":
|
||||
return "byte";
|
||||
case "int16":
|
||||
return "short";
|
||||
case "uint16":
|
||||
return "ushort";
|
||||
case "int32":
|
||||
case "int":
|
||||
return "int";
|
||||
case "uint32":
|
||||
case "uint":
|
||||
return "uint";
|
||||
case "float":
|
||||
return "float";
|
||||
case "double":
|
||||
return "double";
|
||||
case "uint64":
|
||||
return "ulong";
|
||||
case "int64":
|
||||
return "long";
|
||||
case "string":
|
||||
return "string";
|
||||
case "bool":
|
||||
return "bool";
|
||||
case "blob":
|
||||
return "byte[]";
|
||||
}
|
||||
throw new Exception("Unknown type '{$type}'");
|
||||
}
|
||||
return $type->getName();
|
||||
}
|
||||
|
||||
function genTypePrefix(mtgType $type)
|
||||
{
|
||||
switch($type->getName())
|
||||
{
|
||||
case "float":
|
||||
return "Float";
|
||||
case "double":
|
||||
return "Double";
|
||||
case "uint64":
|
||||
return "U64";
|
||||
case "int64":
|
||||
return "I64";
|
||||
case "uint":
|
||||
case "uint32":
|
||||
return "U32";
|
||||
case "uint16":
|
||||
return "U16";
|
||||
case "uint8":
|
||||
return "U8";
|
||||
case "int":
|
||||
case "int32":
|
||||
return "I32";
|
||||
case "int16":
|
||||
return "I16";
|
||||
case "int8":
|
||||
return "I8";
|
||||
case "string":
|
||||
return "String";
|
||||
case "bool":
|
||||
return "Bool";
|
||||
case "blob":
|
||||
return "Blob";
|
||||
default:
|
||||
throw new Exception("Unknown type '{$type}'");
|
||||
}
|
||||
}
|
||||
|
||||
function genBufSyncRegular(mtgMetaField $field, $buf, $opts = "")
|
||||
{
|
||||
return $this->genBufSync($field->getName(), $field, $buf, $opts);
|
||||
}
|
||||
|
||||
function genBufSync(string $fname, mtgMetaField $field, $buf, $opts)
|
||||
{
|
||||
return $this->genBufSyncEx($fname, $field->getType(), $buf, $field->getTokens(), $opts);
|
||||
}
|
||||
|
||||
function genBufSyncEx($fname, mtgType $type, $buf, array $tokens = array(), $opts = "")
|
||||
{
|
||||
$optional = array_key_exists('optional', $tokens);
|
||||
if($optional)
|
||||
$opts .= " | MetaSyncFieldOpts.SKIP_OPTIONAL";
|
||||
|
||||
$str = '';
|
||||
if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
$str .= "Meta.Sync({$buf}, ref {$fname}, {$opts});\n";
|
||||
}
|
||||
else if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
if(array_key_exists('virtual', $tokens))
|
||||
$str .= "{$fname} = ({$type->getName()})Meta.SyncGeneric({$buf}, {$fname}, {$opts});\n";
|
||||
else
|
||||
$str .= "Meta.Sync({$buf}, ref {$fname}, {$opts});\n";
|
||||
}
|
||||
else if($type instanceof mtgMetaEnum)
|
||||
{
|
||||
$str .= "int __tmp_{$fname} = (int)$fname;\n";
|
||||
$str .= "Meta.Sync({$buf}, ref __tmp_{$fname}, {$opts});\n";
|
||||
$str .= "if($buf.is_read) {$fname} = ({$type->getName()})__tmp_{$fname};\n";
|
||||
}
|
||||
else if($type instanceof mtgArrType)
|
||||
{
|
||||
if(array_key_exists('virtual', $tokens))
|
||||
$str .= "Meta.SyncGeneric({$buf}, {$fname}, {$opts});\n";
|
||||
else
|
||||
{
|
||||
if($type->getValue() instanceof mtgMetaEnum)
|
||||
{
|
||||
$str .= "Meta.tmp_enums_list.Clear(); if(!{$buf}.is_read) { foreach(var _enum_tmp in {$fname}) Meta.tmp_enums_list.Add((int)_enum_tmp); }\n";
|
||||
$str .= "Meta.Sync({$buf}, Meta.tmp_enums_list, {$opts});\n";
|
||||
$str .= "if({$buf}.is_read) foreach(var _int_tmp in Meta.tmp_enums_list) { {$fname}.Add(({$type->getValue()->getName()}) _int_tmp); }\n";
|
||||
}
|
||||
else
|
||||
$str .= "Meta.Sync({$buf}, {$fname}, {$opts});\n";
|
||||
}
|
||||
}
|
||||
else
|
||||
throw new Exception("Unknown type '$type'");
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
function genConstructArgs($type)
|
||||
{
|
||||
if($type == "string")
|
||||
return '""';
|
||||
return "new {$type}()";
|
||||
}
|
||||
|
||||
function isPOD(mtgMetaStruct $struct)
|
||||
{
|
||||
return $struct->hasToken('POD');
|
||||
}
|
||||
|
||||
function genFieldDecl(mtgMetaStruct $struct, mtgMetaField $field)
|
||||
{
|
||||
$str = "";
|
||||
|
||||
if($field->hasToken('cs_attributes'))
|
||||
{
|
||||
$attrs = explode(",", $field->getToken('cs_attributes'));
|
||||
foreach($attrs as $attr)
|
||||
$str .= "[$attr] ";
|
||||
}
|
||||
|
||||
$str .= "public " . $this->genFieldNativeType($field) . " ";
|
||||
|
||||
if(!$this->isPOD($struct))
|
||||
$str .= $this->genFieldDeclInit($field);
|
||||
else
|
||||
$str .= $this->genFieldName($field) . ";";
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
function offset($amount = 1)
|
||||
{
|
||||
return str_repeat(" ", $amount);
|
||||
}
|
||||
|
||||
function genFieldCopy(mtgMetaField $field)
|
||||
{
|
||||
$tokens = $field->getTokens();
|
||||
$name = $this->genFieldName($field);
|
||||
$type = $field->getType();
|
||||
$str = '';
|
||||
|
||||
if($type instanceof mtgArrType)
|
||||
{
|
||||
$str .= "for(int i=0;other.{$name} != null && i<other.{$name}.Count;++i) { \n";
|
||||
|
||||
$vtype = $type->getValue();
|
||||
if($vtype instanceof mtgMetaStruct)
|
||||
{
|
||||
if(array_key_exists('virtual', $tokens))
|
||||
$str .= $this->offset(3)."var tmp = ({$vtype->getName()})other.{$name}[i].clone();\n";
|
||||
else
|
||||
$str .= $this->offset(3)."var tmp = new {$vtype->getName()}(); tmp.copyFrom(other.{$name}[i]);\n";
|
||||
$str .= $this->offset(3)."{$name}.Add(tmp);\n";
|
||||
}
|
||||
else
|
||||
$str .= $this->offset(3)."{$name}.Add(other.{$name}[i]);\n";
|
||||
|
||||
$str .= $this->offset(2)."}\n";
|
||||
}
|
||||
else if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
if(array_key_exists('virtual', $tokens))
|
||||
$str .= $this->offset(2)."{$name} = ({$type->getName()})other.{$name}.clone();\n";
|
||||
else
|
||||
$str .= $this->offset(2)."{$name}.copyFrom(other.{$name});\n";
|
||||
}
|
||||
else
|
||||
$str .= $this->offset()."{$name} = other.{$name};\n";
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
function genFieldDeclInit(mtgMetaField $field)
|
||||
{
|
||||
$tokens = $field->getTokens();
|
||||
|
||||
$str = $this->genFieldName($field);
|
||||
$type = $field->getType();
|
||||
if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
if($type->isString())
|
||||
$str .= ' = ""';
|
||||
}
|
||||
else
|
||||
$str .= " = new ".$this->genFieldNativeType($field)."()";
|
||||
$str .= ";";
|
||||
return $str;
|
||||
}
|
||||
|
||||
function genFieldReset(mtgMetaField $field)
|
||||
{
|
||||
$tokens = $field->getTokens();
|
||||
$default = array_key_exists('default', $tokens) ? $tokens['default'] : null;
|
||||
|
||||
return $this->genFieldResetEx($this->genFieldName($field), $field->getType(), $default);
|
||||
}
|
||||
|
||||
function genFieldResetEx($name, mtgType $type, $default = null)
|
||||
{
|
||||
$str = '';
|
||||
if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
if($type->isNumeric())
|
||||
{
|
||||
$str = $name;
|
||||
//NOTE: numeric check is against str2num
|
||||
if($default && is_numeric($default))
|
||||
{
|
||||
if($type->isFloat())
|
||||
$default .= "f";
|
||||
$str .= " = $default;";
|
||||
}
|
||||
else
|
||||
$str .= " = 0;";
|
||||
}
|
||||
else if($type->isString())
|
||||
{
|
||||
$str = $name;
|
||||
if($default)
|
||||
{
|
||||
$str .= " = \"".trim($default, '"')."\";";
|
||||
}
|
||||
else
|
||||
$str .= ' = "";';
|
||||
}
|
||||
else if($type->isBool())
|
||||
{
|
||||
$str = $name;
|
||||
if($default)
|
||||
{
|
||||
$str .= " = ".trim($default, '"').";";
|
||||
}
|
||||
else
|
||||
$str .= ' = false;';
|
||||
}
|
||||
else if($type->isBlob())
|
||||
{
|
||||
$str = $name;
|
||||
if($default)
|
||||
{
|
||||
$str .= " = ".trim($default, '"').";";
|
||||
}
|
||||
else
|
||||
$str .= ' = null;';
|
||||
}
|
||||
else
|
||||
throw new Exception("Unknown type '$type'");
|
||||
}
|
||||
else if($type instanceof mtgArrType)
|
||||
{
|
||||
$str = "Meta.ClearList(ref $name);";
|
||||
|
||||
if($default)
|
||||
{
|
||||
$default_arr = is_array($default) ? $default : json_decode($default, true);
|
||||
if(!is_array($default_arr))
|
||||
throw new Exception("Bad default value for array: '$default'");
|
||||
|
||||
if($default_arr)
|
||||
{
|
||||
$type_str = $this->genNativeType($type->getValue());
|
||||
$str .= "{ $type_str tmp__";
|
||||
if($this->isNullableType($type->getValue()))
|
||||
$str .= " = null";
|
||||
$str .= ";";
|
||||
foreach($default_arr as $v)
|
||||
{
|
||||
$str .= $this->genFieldResetEx("tmp__", $type->getValue(), $v);
|
||||
$str .= "$name.Add(tmp__);";
|
||||
}
|
||||
$str .= "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
else if($type instanceof mtgMetaEnum)
|
||||
{
|
||||
if($default)
|
||||
$str = "$name = ".$this->genNativeType($type).".".trim($default,'"')."; ";
|
||||
else
|
||||
$str = "$name = new ".$this->genNativeType($type)."(); ";
|
||||
}
|
||||
else if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
$str = "";
|
||||
$is_pod = $type->hasToken('POD');
|
||||
if($is_pod)
|
||||
$str .= "$name.reset(); ";
|
||||
else
|
||||
$str .= "Meta.Reset(ref $name); ";
|
||||
|
||||
if($default)
|
||||
{
|
||||
$default = is_array($default) ? $default : json_decode($default, true);
|
||||
if(!is_array($default))
|
||||
throw new Exception("Bad default value for struct: $default");
|
||||
|
||||
foreach($default as $k => $v)
|
||||
{
|
||||
$kf = $type->getField($k);
|
||||
$str .= $this->genFieldResetEx("$name." . $this->genFieldName($kf), $kf->getType(), $v);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
throw new Exception("Bad type '$type'");
|
||||
return $str;
|
||||
}
|
||||
|
||||
function isNullableType(mtgType $type)
|
||||
{
|
||||
return $type instanceof mtgArrType ||
|
||||
($type instanceof mtgMetaStruct && !$type->hasToken('POD'));
|
||||
}
|
||||
|
||||
function genFieldName(mtgMetaField $field)
|
||||
{
|
||||
return $field->getName();
|
||||
}
|
||||
|
||||
function genFieldNativeType(mtgMetaField $field)
|
||||
{
|
||||
return $this->genNativeType($field->getType(), $field->getTokens());
|
||||
}
|
||||
|
||||
function genNativeType(mtgType $type, array $tokens = array())
|
||||
{
|
||||
if($type instanceof mtgBuiltinType || $type instanceof mtgUserType)
|
||||
return $this->genNativePlainType($type);
|
||||
else if($type instanceof mtgArrType)
|
||||
return "List<" . $this->genNativePlainType($type->getValue()) . ">";
|
||||
else
|
||||
throw new Exception("Unknown type '{$type}'");
|
||||
}
|
||||
|
||||
function genBitmaskHelpers(mtgMetaStruct $struct, array $fields)
|
||||
{
|
||||
//RESET FIELD MASK
|
||||
$str = $this->offset()."public void ResetFieldMask()\n";
|
||||
$str .= $this->offset()."{\n";
|
||||
$str .= $this->offset(2)."fields_mask = 0L;\n";
|
||||
$str .= $this->offset()."}\n\n";
|
||||
|
||||
//SET PRIMARY FIELDS CHANGED
|
||||
$primary_fields = array();
|
||||
if($struct->hasToken("id"))
|
||||
$primary_fields[] = $struct->getToken("id");
|
||||
|
||||
if($struct->hasToken("owner"))
|
||||
$primary_fields[] = $struct->getToken("owner");
|
||||
|
||||
if($struct->hasToken("pkey"))
|
||||
{
|
||||
foreach(explode(",", $struct->getToken("pkey")) as $name)
|
||||
$primary_fields[] = $name;
|
||||
}
|
||||
|
||||
$str .= $this->offset()."public void SetPrimaryFieldsChanged()\n";
|
||||
$str .= $this->offset()."{\n";
|
||||
$field_index = 0;
|
||||
foreach($fields as $field)
|
||||
{
|
||||
if(in_array($field->getName(), $primary_fields))
|
||||
$str .= $this->offset(2)."Meta.SetFieldDirty(ref fields_mask, $field_index);\n";
|
||||
|
||||
$field_index++;
|
||||
}
|
||||
$str .= $this->offset()."}\n\n";
|
||||
|
||||
//DIRTY FIELD MASK
|
||||
$str .= $this->offset()."public void SetDirtyMask()\n";
|
||||
$str .= $this->offset()."{\n";
|
||||
$str .= $this->offset(2)."fields_mask = ~0L;\n";
|
||||
$str .= $this->offset()."}\n\n";
|
||||
|
||||
$str .= $this->offset()."public void SetDirtyMaskDeep()\n";
|
||||
$str .= $this->offset()."{\n";
|
||||
$str .= $this->offset(2)."SetDirtyMask();\n";
|
||||
foreach($fields as $field)
|
||||
{
|
||||
$type = $field->getType();
|
||||
|
||||
if($type instanceof mtgMetaStruct && $type->hasToken("bitfields"))
|
||||
$str .= $this->offset(2)."{$field->getName()}.SetDirtyMaskDeep();\n";
|
||||
else if($type instanceof mtgArrType && $type->getValue() instanceof mtgMetaStruct && $type->getValue()->hasToken("bitfields"))
|
||||
{
|
||||
$str .= $this->offset(2)."for(int i=0;i<{$field->getName()}.Count;++i) {\n";
|
||||
$str .= $this->offset(2)."var __tmp = {$field->getName()}[i];\n";
|
||||
$str .= $this->offset(2)."__tmp.SetDirtyMaskDeep();\n";
|
||||
$str .= $this->offset(2)."{$field->getName()}[i] = __tmp;\n";
|
||||
$str .= $this->offset(2)."}\n";
|
||||
}
|
||||
}
|
||||
$str .= $this->offset()."}\n\n";
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
<?php
|
||||
require_once(dirname(__FILE__) . '/cs.inc.php');
|
||||
|
||||
class mtgCsGenerator extends mtgGenerator
|
||||
{
|
||||
function makeTargets(mtgMetaInfo $meta)
|
||||
{
|
||||
$targets = array();
|
||||
|
||||
mtg_mkdir(mtg_conf('out-dir'));
|
||||
|
||||
$codegen = mtg_conf("codegen", null);
|
||||
if(!$codegen)
|
||||
$codegen = new mtgCsCodegen();
|
||||
$codegen->setMetaInfo($meta);
|
||||
|
||||
$refl = new ReflectionClass($codegen);
|
||||
$SHARED_DEPS = array(
|
||||
dirname(__FILE__) . '/cs.inc.php',
|
||||
dirname(__FILE__) . '/cs_tpl.inc.php',
|
||||
__FILE__,
|
||||
$refl->getFileName()
|
||||
);
|
||||
|
||||
$units = array();
|
||||
$files = array();
|
||||
foreach($meta->getUnits() as $unit)
|
||||
{
|
||||
$units[] = $unit;
|
||||
$files = array_merge($files, mtg_get_file_deps($meta, $unit));
|
||||
}
|
||||
$files = array_unique($files);
|
||||
|
||||
$bundle = mtg_conf("bundle", null);
|
||||
if($bundle && $units)
|
||||
{
|
||||
$targets[] = mtg_new_bundle($bundle,
|
||||
array_merge($SHARED_DEPS, $files),
|
||||
array(array($this, 'genBundle'), $meta, $codegen, $units));
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
|
||||
function genBundle($OUT, array $DEPS, mtgMetaInfo $meta, mtgCsCodegen $codegen, array $units)
|
||||
{
|
||||
$units_src = '';
|
||||
$id2type = '';
|
||||
$create_struct_by_crc28 = '';
|
||||
$create_rpc_by_id = '';
|
||||
|
||||
foreach($units as $unit)
|
||||
{
|
||||
if($unit->object instanceof mtgMetaRPC)
|
||||
{
|
||||
$units_src .= $codegen->genUnit($unit);
|
||||
$rpc = $unit->object;
|
||||
$create_rpc_by_id .= "\n case {$rpc->getCode()}: { return new {$rpc->getName()}(); };";
|
||||
}
|
||||
else if($unit->object instanceof mtgMetaEnum || $unit->object instanceof mtgMetaStruct)
|
||||
{
|
||||
$units_src .= $codegen->genUnit($unit) . "\n";
|
||||
|
||||
if($unit->object->hasToken('POD') || $unit->object instanceof mtgMetaEnum)
|
||||
continue;
|
||||
|
||||
$id2type .= "\n case {$unit->object->getClassId()}: { return typeof({$unit->object->getName()}); };";
|
||||
$create_struct_by_crc28 .= "\n case {$unit->object->getClassId()}: { return new {$unit->object->getName()}(); };";
|
||||
}
|
||||
}
|
||||
|
||||
$templater = new mtg_cs_templater();
|
||||
$tpl = $templater->tpl_bundle();
|
||||
return mtg_fill_template($tpl, array(
|
||||
'%namespace%' => $codegen->namespace,
|
||||
'%units_src%' => $units_src,
|
||||
'%id2type%' => $id2type,
|
||||
'%create_struct_by_crc28%' => $create_struct_by_crc28,
|
||||
'%create_rpc_by_id%' => $create_rpc_by_id,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
function gen_cs_struct($OUT, array $DEPS, mtgCsCodegen $codegen, mtgMetaInfoUnit $unit)
|
||||
{
|
||||
return $codegen->genUnit($unit);
|
||||
}
|
|
@ -0,0 +1,216 @@
|
|||
<?php
|
||||
|
||||
class mtg_cs_templater
|
||||
{
|
||||
function tpl_struct()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
%includes%
|
||||
|
||||
namespace %namespace% {
|
||||
|
||||
%attributes%
|
||||
public %type_name% %class% %parent%
|
||||
{
|
||||
%fields%
|
||||
|
||||
public %new_method% const uint STATIC_CLASS_ID = %class_id%;
|
||||
|
||||
public %virt_method% uint CLASS_ID()
|
||||
{
|
||||
return %class_id%;
|
||||
}
|
||||
|
||||
%commented_in_pod_begin%
|
||||
public %class%()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
%commented_in_pod_end%
|
||||
|
||||
public %virt_method% void reset()
|
||||
{
|
||||
%fields_reset%
|
||||
}
|
||||
|
||||
%commented_in_non_cloneable_begin%
|
||||
public %virt_clone_method% void copy(IMetaStruct other)
|
||||
{
|
||||
copyFrom((%class%)other);
|
||||
}
|
||||
|
||||
public void copyFrom(%class% other)
|
||||
{
|
||||
var ctx = Meta.PrepareForClone(ref other);
|
||||
ctx.factory = AutogenBundle.createById;
|
||||
|
||||
ctx.reader.BeginContainer();
|
||||
reset();
|
||||
syncFields(ctx);
|
||||
ctx.reader.EndContainer();
|
||||
|
||||
%copy_fields%
|
||||
}
|
||||
|
||||
public %virt_clone_method% IMetaStruct clone()
|
||||
{
|
||||
%class% copy = new %class%();
|
||||
copy.copy(this);
|
||||
return copy;
|
||||
}
|
||||
%commented_in_non_cloneable_end%
|
||||
|
||||
public %virt_method% void syncFields(MetaSyncContext ctx)
|
||||
{
|
||||
%bitfields_sync_context%
|
||||
|
||||
%sync_buffer%
|
||||
}
|
||||
|
||||
public %virt_method% int getFieldsCount()
|
||||
{
|
||||
return %fields_count%;
|
||||
}
|
||||
|
||||
public %virt_method% int getWritableFieldsCount()
|
||||
{
|
||||
return %dirty_fields_count%;
|
||||
}
|
||||
|
||||
%ext_methods%
|
||||
}
|
||||
|
||||
} //namespace %namespace%
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
function tpl_enum()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
|
||||
namespace %namespace% {
|
||||
|
||||
%attributes%
|
||||
public enum %class% {
|
||||
%fields%
|
||||
}
|
||||
|
||||
} //namespace %namespace%
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
function tpl_rpc()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
|
||||
namespace %namespace% {
|
||||
|
||||
public class %class% : IRpc
|
||||
{
|
||||
public IRpcError error = null;
|
||||
public %req_class% req = new %req_class%();
|
||||
public %rsp_class% rsp = new %rsp_class%();
|
||||
|
||||
public int getCode()
|
||||
{
|
||||
return %code%;
|
||||
}
|
||||
|
||||
public void setError(IRpcError error)
|
||||
{
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
public IRpcError getError()
|
||||
{
|
||||
return error;
|
||||
}
|
||||
|
||||
public IMetaStruct getRequest()
|
||||
{
|
||||
return req as IMetaStruct;
|
||||
}
|
||||
|
||||
public IMetaStruct getResponse()
|
||||
{
|
||||
return rsp as IMetaStruct;
|
||||
}
|
||||
}
|
||||
|
||||
} //namespace %namespace%
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
function tpl_bundle()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
using System.Collections.Generic;
|
||||
using metagen;
|
||||
|
||||
%units_src%
|
||||
|
||||
namespace %namespace% {
|
||||
|
||||
public class AutogenBundle {
|
||||
|
||||
static public IRpc createRpc(int code)
|
||||
{
|
||||
switch(code)
|
||||
{
|
||||
%create_rpc_by_id%
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public IMetaStruct createById(uint class_id)
|
||||
{
|
||||
//NOTE: reflection based creation, not reliable?
|
||||
//var type = id2type(class_id);
|
||||
//if(type == null)
|
||||
// return null;
|
||||
//return (IMetaStruct)System.Activator.CreateInstance(type);
|
||||
|
||||
//NOTE: faster alternative
|
||||
switch(class_id)
|
||||
{
|
||||
%create_struct_by_crc28%
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public System.Type id2type(uint class_id)
|
||||
{
|
||||
switch(class_id)
|
||||
{
|
||||
%id2type%
|
||||
default:
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace %namespace%
|
||||
|
||||
EOD;
|
||||
|
||||
return $TPL;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,628 @@
|
|||
<?php
|
||||
|
||||
require_once(dirname(__FILE__) . '/../../metagen.inc.php');
|
||||
require_once(dirname(__FILE__) . '/go_tpl.inc.php');
|
||||
|
||||
class mtgGoCodegen extends mtgCodegen
|
||||
{
|
||||
function genUnit(mtgMetaInfoUnit $unit)
|
||||
{
|
||||
$obj = $unit->object;
|
||||
if($obj instanceof mtgMetaRPC)
|
||||
return $this->genRPC($obj);
|
||||
if($obj instanceof mtgMetaEnum)
|
||||
return $this->genEnum($obj);
|
||||
else if($obj instanceof mtgMetaStruct)
|
||||
return $this->genStruct($obj);
|
||||
else
|
||||
{
|
||||
echo "WARN: skipping meta unit '{$obj->getId()}'\n";
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function genEnum(mtgMetaEnum $struct)
|
||||
{
|
||||
$templater = new mtg_go_templater();
|
||||
|
||||
$repl = array();
|
||||
$repl['%class%'] = $struct->getName();
|
||||
$repl['%class_id%'] = $struct->getClassId();
|
||||
|
||||
$tpl = $templater->tpl_enum();
|
||||
|
||||
$this->fillEnumRepls($struct, $repl);
|
||||
|
||||
return mtg_fill_template($tpl, $repl);
|
||||
}
|
||||
|
||||
function fillEnumRepls(mtgMetaEnum $enum, &$repl)
|
||||
{
|
||||
$repl['%values%'] = '';
|
||||
$repl['%vnames_list%'] = '';
|
||||
$repl['%values_list%'] = '';
|
||||
|
||||
$vnames = array();
|
||||
$values = array();
|
||||
$values_map = array();
|
||||
$default_value = null;
|
||||
$consts = array();
|
||||
|
||||
$enum_name = $enum->getName();
|
||||
$map = array();
|
||||
foreach($enum->getValues() as $vname => $value)
|
||||
{
|
||||
$map[$vname] = $value;
|
||||
$vnames[] = "'$vname'";
|
||||
$values[] = $value;
|
||||
if(!$default_value)
|
||||
$default_value = "$value; // $vname";
|
||||
$consts[$value] = "{$enum->getName()}_{$vname} {$enum->getName()} = $value";
|
||||
}
|
||||
|
||||
// must be sorted
|
||||
sort($values, SORT_NUMERIC);
|
||||
|
||||
$repl['%vnames_list%'] = implode(',', $vnames);
|
||||
$repl['%values_list%'] = implode(',', $values);
|
||||
$repl['%values_map%'] = $this->genIntMap($map);
|
||||
$repl['%default_enum_value%'] = $default_value;
|
||||
$repl['%consts%'] = "\n " . implode("\n ", $consts) . "\n";
|
||||
}
|
||||
|
||||
function preprocessField(mtgMetaStruct $struct, mtgMetaField $field) {}
|
||||
|
||||
function genRPC(mtgMetaRPC $rpc)
|
||||
{
|
||||
return $this->genRPCPacket($rpc->getReq()) .
|
||||
$this->genRPCPacket($rpc->getRsp()) .
|
||||
$this->genRPCReqResp($rpc);
|
||||
}
|
||||
|
||||
function genRPCReqResp(mtgMetaRPC $rpc)
|
||||
{
|
||||
$templater = new mtg_go_templater();
|
||||
|
||||
$repl = array();
|
||||
$repl['%code%'] = $rpc->getCode();
|
||||
$repl['%rpc_name%'] = $rpc->getName();
|
||||
$repl['%rpc_req_name%'] = $rpc->getReq()->getName();
|
||||
$repl['%rpc_rsp_name%'] = $rpc->getRsp()->getName();
|
||||
|
||||
$tpl = $templater->tpl_rpc();
|
||||
return mtg_fill_template($tpl, $repl);
|
||||
}
|
||||
|
||||
function genRPCPacket(mtgMetaPacket $packet)
|
||||
{
|
||||
$templater = new mtg_go_templater();
|
||||
|
||||
$repl = array();
|
||||
$repl['%type%'] = $packet->getName();
|
||||
$repl['%code%'] = $packet->getNumericCode();
|
||||
$repl['%class%'] = $packet->getName();
|
||||
$repl['%class_id%'] = $packet->getClassId();
|
||||
$repl['%parent_class%'] = '';
|
||||
|
||||
if(strpos($packet->getName(), '_RSP_') !== false)
|
||||
$repl['%class_invert%'] = str_replace('_RSP_', '_REQ_', $packet->getName());
|
||||
else
|
||||
$repl['%class_invert%'] = str_replace('_REQ_', '_RSP_', $packet->getName());
|
||||
|
||||
$fields = $packet->getFields();
|
||||
|
||||
$tpl = $templater->tpl_packet();
|
||||
|
||||
$this->fillStructRepls($packet, $repl);
|
||||
|
||||
return mtg_fill_template($tpl, $repl);
|
||||
}
|
||||
|
||||
function genStruct(mtgMetaStruct $struct)
|
||||
{
|
||||
$templater = new mtg_go_templater();
|
||||
|
||||
$repl = array();
|
||||
$repl['%class%'] = $struct->getName();
|
||||
$repl['%class_id%'] = $struct->getClassId();
|
||||
$repl['%parent_class%'] = $struct->getParent() ? ''.$struct->getParent() : "";
|
||||
|
||||
$tpl = $templater->tpl_struct();
|
||||
|
||||
$this->fillStructRepls($struct, $repl);
|
||||
|
||||
$result = mtg_fill_template($tpl, $repl);
|
||||
|
||||
if($struct->hasToken('POD') && $struct->hasToken("id") && $struct->hasToken("table") && $struct->hasToken("owner"))
|
||||
$result .= "\n" . str_replace(array_keys($repl), array_values($repl), $templater->tpl_collection_item());
|
||||
return $result;
|
||||
}
|
||||
|
||||
function fillStructRepls(mtgMetaStruct $struct, &$repl)
|
||||
{
|
||||
foreach($struct->getFields() as $field)
|
||||
$this->preprocessField($struct, $field);
|
||||
|
||||
$repl['%includes%'] = '';
|
||||
$repl['%fields%'] = '';
|
||||
$repl['%fields_names%'] = '[]string{';
|
||||
$repl['%fields_props%'] = 'map[string]map[string]string{';
|
||||
$repl['%fields_types%'] = 'map[string]string{';
|
||||
$repl['%fields_reset%'] = '';
|
||||
$repl['%read_buffer%'] = '';
|
||||
$repl['%write_buffer%'] = '';
|
||||
$repl['%total_top_fields%'] = sizeof($struct->getFields());
|
||||
$repl['%ext_methods%'] = "";
|
||||
$repl['%analytics_methods%'] = "";
|
||||
|
||||
$repl['%class_props%'] = 'map[string]string{' . $this->genStrMap($struct->getTokens()) . '}';
|
||||
|
||||
if($struct->hasToken("bitfields"))
|
||||
{
|
||||
$repl['%read_buffer%'] .= "\n use_mask, mask, err := reader.TryReadMask()\n";
|
||||
$repl['%read_buffer%'] .= "\n if err != nil {\n";
|
||||
$repl['%read_buffer%'] .= "\n return err\n";
|
||||
$repl['%read_buffer%'] .= "\n }\n";
|
||||
$repl['%read_buffer%'] .= "\n self.fieldsMask = mask\n";
|
||||
}
|
||||
|
||||
$parent = $struct->getParent();
|
||||
$all_fields = mtg_get_all_fields($this->info, $struct);
|
||||
|
||||
foreach($all_fields as $field)
|
||||
{
|
||||
$name = $field->getName();
|
||||
$repl['%fields_names%'] .= '"' . $name . '",';
|
||||
$repl['%fields_props%'] .= '"' . $this->genFieldName($field) . '" : map[string]string{' . $this->genStrMap($field->getTokens()) . "},";
|
||||
$field_type = $field->getType();
|
||||
$repl['%fields_types%'] .= '"' . $name . '" : "' . $field_type . '",';
|
||||
}
|
||||
if($struct->hasToken('statist'))
|
||||
{
|
||||
$table_name = $struct->getToken('statist');
|
||||
|
||||
$repl['%analytics_methods%'] .= "func (self *".$struct->getName().") Table() string {\n";
|
||||
$repl['%analytics_methods%'] .= "\t return \"$table_name\"\n";
|
||||
$repl['%analytics_methods%'] .= "}\n";
|
||||
|
||||
$repl['%analytics_methods%'] .= "func (self *".$struct->getName().") Columns() []string {\n";
|
||||
$repl['%analytics_methods%'] .= "\treturn []string{\n";
|
||||
foreach($all_fields as $field)
|
||||
{
|
||||
if ($field->hasToken('statist_skip'))
|
||||
continue;
|
||||
|
||||
$column_name = $field->hasToken('statist_alias') ? $field->getToken('statist_alias') : $field->getName();
|
||||
|
||||
$repl['%analytics_methods%'] .= "\t\t\"$column_name\",\n";
|
||||
}
|
||||
$repl['%analytics_methods%'] .= "\t}\n";
|
||||
$repl['%analytics_methods%'] .= "}\n";
|
||||
|
||||
$repl['%analytics_methods%'] .= "func (self *".$struct->getName().") Values() []interface{} {\n";
|
||||
$repl['%analytics_methods%'] .= "\treturn []interface{}{\n";
|
||||
foreach($all_fields as $field)
|
||||
{
|
||||
if ($field->hasToken('statist_skip'))
|
||||
continue;
|
||||
|
||||
$repl['%analytics_methods%'] .= "\t\tself." . ucfirst($field->getName()) . ",\n";
|
||||
}
|
||||
$repl['%analytics_methods%'] .= "\t}\n";
|
||||
$repl['%analytics_methods%'] .= "}\n";
|
||||
}
|
||||
|
||||
$repl['%read_buffer%'] .= "\n _cont_size, err := reader.GetContainerSize()";
|
||||
$repl['%read_buffer%'] .= "\n if err != nil {";
|
||||
$repl['%read_buffer%'] .= "\n return err";
|
||||
$repl['%read_buffer%'] .= "\n }";
|
||||
|
||||
$optional = 0;
|
||||
foreach($struct->getFields() as $field)
|
||||
{
|
||||
if($field->hasToken('optional'))
|
||||
$optional++;
|
||||
}
|
||||
$initial_fields_amount = count($struct->getFields()) - $optional;
|
||||
$repl['%read_buffer%'] .= "\n if _cont_size < $initial_fields_amount {";
|
||||
$repl['%read_buffer%'] .= "\n _cont_size = $initial_fields_amount";
|
||||
$repl['%read_buffer%'] .= "\n }";
|
||||
|
||||
if($struct->hasToken('POD'))
|
||||
{
|
||||
$fields = $all_fields;
|
||||
}
|
||||
else
|
||||
{
|
||||
if($parent = $struct->getParent())
|
||||
{
|
||||
$repl['%read_buffer%'] .= "\n if err := self." . $parent . ".ReadFields(reader); err != nil { return err }";
|
||||
$repl['%write_buffer%'] .= "\n if err := self." . $parent . ".WriteFields(writer); err != nil { return err }";
|
||||
}
|
||||
$fields = $struct->getFields();
|
||||
}
|
||||
|
||||
$field_index = -1;
|
||||
foreach($fields as $field)
|
||||
{
|
||||
++$field_index;
|
||||
$options = $this->readFieldOptions($field);
|
||||
|
||||
$repl['%fields%'] .= "\n " . $this->genFieldDecl($field);
|
||||
$repl['%fields_reset%'] .= "\n " . $this->genFieldReset($field);
|
||||
$repl['%read_buffer%'] .= "\n if _cont_size <= 0 {";
|
||||
$repl['%read_buffer%'] .= "\n return nil";
|
||||
$repl['%read_buffer%'] .= "\n }";
|
||||
if($struct->hasToken("bitfields"))
|
||||
$repl['%read_buffer%'] .= "\n if !use_mask {";
|
||||
$repl['%read_buffer%'] .= "\n _cont_size--";
|
||||
if($struct->hasToken("bitfields"))
|
||||
$repl['%read_buffer%'] .= "\n }";
|
||||
if($struct->hasToken("bitfields"))
|
||||
$repl['%read_buffer%'] .= "\n if !use_mask || (use_mask && self.HasValue($field_index)) {";
|
||||
$repl['%read_buffer%'] .= "\n " . $this->genBufRead($field->getName(), $options['fname'], $field->getType(), $options['buf'], $field->getTokens());
|
||||
if($struct->hasToken("bitfields"))
|
||||
$repl['%read_buffer%'] .= "\n }";
|
||||
$repl['%write_buffer%'] .= "\n " . $this->genBufWrite($field, "writer", $field->hasToken("virtual"));
|
||||
}
|
||||
|
||||
$repl['%fields%'] = trim($repl['%fields%'], "\n ");
|
||||
|
||||
$repl['%fields_names%'] .= '}';
|
||||
$repl['%fields_props%'] .= '}';
|
||||
$repl['%fields_types%'] .= '}';
|
||||
|
||||
$repl['%import_from_mysql%'] = '';
|
||||
$repl["%export_to_arr%"] = "";
|
||||
if($struct->hasToken('POD') && $struct->hasToken("table"))
|
||||
{
|
||||
$ind = 0;
|
||||
|
||||
$repl['%import_from_mysql%'] .= "\n row := data.(".$struct->getName().")";
|
||||
foreach($all_fields as $field)
|
||||
{
|
||||
$name = $this->genFieldName($field);
|
||||
$repl['%import_from_mysql%'] .= sprintf("\n self.%s = row.%s", $name, $name);
|
||||
$repl['%export_to_arr%'] .= sprintf("\n data[%d] = self.%s", $ind, $name);
|
||||
$ind++;
|
||||
}
|
||||
}
|
||||
|
||||
$repl["%table_name%"] = $struct->getToken("table");
|
||||
$repl["%owner%"] = $struct->getToken("owner");
|
||||
$repl["%id_field_name%"] = $struct->getToken("id");
|
||||
$repl["%id_field%"] = ucfirst($struct->getToken("id"));
|
||||
|
||||
if($struct->hasToken("bitfields"))
|
||||
{
|
||||
$repl['%fields%'] .= "\nfieldsMask uint64\n";
|
||||
|
||||
$repl['%fields_reset%'] .= "\nself.fieldsMask = 0\n";
|
||||
|
||||
$repl['%ext_methods%'] .= "func(self *".$struct->getName().") HasValue(index uint64) bool {\n";
|
||||
$repl['%ext_methods%'] .= " value := uint64(1 << index)\n";
|
||||
$repl['%ext_methods%'] .= " return (self.fieldsMask & value) > 0\n";
|
||||
$repl['%ext_methods%'] .= "}\n";
|
||||
|
||||
$repl['%ext_methods%'] .= "func(self *".$struct->getName().") SetFieldChanged(index uint64) {\n";
|
||||
$repl['%ext_methods%'] .= " value := uint64(1 << index)\n";
|
||||
$repl['%ext_methods%'] .= " self.fieldsMask |= value\n";
|
||||
$repl['%ext_methods%'] .= "}\n";
|
||||
|
||||
$repl['%ext_methods%'] .= "func(self *".$struct->getName().") IsMaskFilled() bool {\n";
|
||||
$repl['%ext_methods%'] .= " return self.fieldsMask > 0\n";
|
||||
$repl['%ext_methods%'] .= "}\n";
|
||||
|
||||
$repl['%ext_methods%'] .= "func(self *".$struct->getName().") GetMask() uint64 {\n";
|
||||
$repl['%ext_methods%'] .= " return self.fieldsMask\n";
|
||||
$repl['%ext_methods%'] .= "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
function readFieldOptions(mtgMetaField $field)
|
||||
{
|
||||
return array(
|
||||
'fname' => 'self.'.ucfirst($field->getName()),
|
||||
'buf' => "reader",
|
||||
);
|
||||
}
|
||||
|
||||
function genReadBuiltinField($name, $fname, mtgType $type, $buf, array $tokens)
|
||||
{
|
||||
return $this->genRead($tokens, $buf.'.Read'.$this->genBuiltinTypePrefix($type).'(&'.$fname.', "'.$name.'")');
|
||||
}
|
||||
|
||||
function genBufRead($name, $fname, mtgType $type, $buf, array $tokens = array(), $is_ptr = false)
|
||||
{
|
||||
$str = '';
|
||||
|
||||
if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
$str .= $this->genReadBuiltinField($name, $fname, $type, $buf, $tokens);
|
||||
}
|
||||
else if($type instanceof mtgMetaEnum)
|
||||
{
|
||||
$str .= $this->genRead($tokens, "{$buf}.ReadI32((*int32)(&{$fname}), \"$name\")");
|
||||
$str .= "\n if !{$fname}.IsValid() { return errors.Errorf(\"Bad enum value %d for $name\", $fname) }";
|
||||
}
|
||||
else if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
if(array_key_exists('virtual', $tokens))
|
||||
$str .= "if v, err := meta.ReadStructGeneric($buf, CreateById, \"{$name}\"); err != nil { return err } else { {$fname} = v.(I{$type}) }";
|
||||
else
|
||||
$str .= $this->genRead($tokens, "meta.ReadStruct($buf, ".($is_ptr?"":"&")."$fname, \"$name\")");
|
||||
}
|
||||
else if($type instanceof mtgArrType)
|
||||
{
|
||||
$is_virtual = array_key_exists("virtual", $tokens);
|
||||
$native_type = $this->genNativeType($type->getValue(), $tokens);
|
||||
$offset = "\n ";
|
||||
$str .= "/*[]{$name}*/";
|
||||
$str .= $offset . $this->genRead($tokens, "{$buf}.BeginContainer(\"$name\")");
|
||||
//we don't want to propagate 'optionalness' below
|
||||
unset($tokens['optional']);
|
||||
$str .= $offset . "_{$name}_size, err := {$buf}.GetContainerSize()";
|
||||
$str .= $offset . "if err != nil { return err }";
|
||||
|
||||
$need_new = !$is_virtual && $type->getValue() instanceof mtgMetaStruct;
|
||||
|
||||
$str .= $offset . "for ; _{$name}_size > 0; _{$name}_size-- {";
|
||||
$offset = "\n ";
|
||||
$str .= $offset . "var tmp_{$name} {$native_type}";
|
||||
|
||||
$str .= $offset . $this->genBufRead("", "tmp_{$name}", $type->getValue(), $buf, $tokens, $is_virtual);
|
||||
$str .= $offset . "{$fname} = append({$fname}, ".($need_new?"&":"")."tmp_{$name})";
|
||||
|
||||
$offset = "\n ";
|
||||
$str .= $offset . "}";
|
||||
$str .= $offset . $this->genRead($tokens, "{$buf}.EndContainer()");
|
||||
$str .= "\n";
|
||||
}
|
||||
else
|
||||
throw new Exception("Unknown type '{$type}'");
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
function genRead(array $tokens, $op)
|
||||
{
|
||||
return "if err := $op; err != nil { return " . (array_key_exists("optional", $tokens) ? "/*optional*/nil" : "err"). " }";
|
||||
}
|
||||
|
||||
function genWrite($op)
|
||||
{
|
||||
return "if err := $op; err != nil { return err }";
|
||||
}
|
||||
|
||||
function genBufWrite(mtgMetaField $field, $buf, $is_ptr = false)
|
||||
{
|
||||
return $this->genBufWriteEx($field->getName(), "self.".ucfirst($field->getName()), $field->getType(), $buf, $field->getTokens(), $is_ptr);
|
||||
}
|
||||
|
||||
function genBufWriteEx($name, $fname, mtgType $type, $buf, array $tokens = array(), $is_ptr = false)
|
||||
{
|
||||
$str = '';
|
||||
|
||||
if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
$str .= $this->genWrite("{$buf}.Write".$this->genBuiltinTypePrefix($type)."($fname, \"$name\")")."\n";
|
||||
}
|
||||
else if($type instanceof mtgMetaEnum)
|
||||
{
|
||||
$str .= $this->genWrite("{$buf}.WriteI32(int32($fname), \"$name\")");
|
||||
}
|
||||
else if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
if(array_key_exists('virtual', $tokens))
|
||||
$str .= $this->genWrite("meta.WriteStructGeneric($buf, ".($is_ptr?"":"&")."$fname, \"$name\")");
|
||||
else
|
||||
$str .= $this->genWrite("meta.WriteStruct($buf, ".($is_ptr?"":"&")."$fname, \"$name\")");
|
||||
}
|
||||
else if($type instanceof mtgArrType)
|
||||
{
|
||||
$is_virtual = array_key_exists("virtual", $tokens);
|
||||
|
||||
$str .= "{$buf}.BeginContainer(\"{$name}\")\n";
|
||||
$str .= " for _, v := range({$fname}) {\n";
|
||||
$str .= " ".$this->genBufWriteEx("", "v", $type->getValue(), $buf, $tokens, true)."\n";
|
||||
$str .= " }\n";
|
||||
$str .= " ".$this->genWrite("{$buf}.EndContainer()")."\n";
|
||||
}
|
||||
else
|
||||
throw new Exception("Unknown type '$type'");
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
function genFieldDecl(mtgMetaField $field)
|
||||
{
|
||||
return $this->genFieldName($field) . " " .
|
||||
$this->genFieldNativeType($field) .
|
||||
($field->getName()[0] != '_' ? " `json:\"{$field->getName()}\"`" : "")
|
||||
;
|
||||
}
|
||||
|
||||
function genFieldName(mtgMetaField $field)
|
||||
{
|
||||
return ucfirst($field->getName());
|
||||
}
|
||||
|
||||
function genFieldReset(mtgMetaField $field)
|
||||
{
|
||||
$tokens = $field->getTokens();
|
||||
|
||||
$str = '';
|
||||
$name = $this->genFieldName($field);
|
||||
|
||||
$type = $field->getType();
|
||||
|
||||
if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
if($type->isNumeric())
|
||||
{
|
||||
//NOTE: numeric check is against str2num
|
||||
if(array_key_exists('default', $tokens) && is_numeric($tokens['default']))
|
||||
$str .= "self.$name = ".$tokens['default'];
|
||||
else
|
||||
$str .= " self.$name = 0";
|
||||
}
|
||||
else if($type->isString())
|
||||
{
|
||||
if(array_key_exists('default', $tokens))
|
||||
$str .= "self.$name = ".$tokens['default'];
|
||||
else
|
||||
$str .= "self.$name = \"\"";
|
||||
}
|
||||
else if($type->isBool())
|
||||
{
|
||||
if(array_key_exists('default', $tokens))
|
||||
$str .= "self.$name = ".$tokens['default'];
|
||||
else
|
||||
$str .= "self.$name = false";
|
||||
}
|
||||
else if($type->isBlob())
|
||||
{
|
||||
if(array_key_exists('default', $tokens))
|
||||
$str .= "self.$name = ".$tokens['default'];
|
||||
else
|
||||
$str .= "self.$name = nil";
|
||||
}
|
||||
else
|
||||
throw new Exception("Unknown type '$type'");
|
||||
}
|
||||
else if($type instanceof mtgArrType)
|
||||
{
|
||||
$str = "if self.$name == nil { \nself.$name = make(".$this->genFieldNativeType($field).",0) \n}\n ";
|
||||
$str .= "self.$name = self.{$name}[0:0]";
|
||||
}
|
||||
else if($type instanceof mtgMetaEnum)
|
||||
{
|
||||
if(array_key_exists('default', $tokens))
|
||||
$str = "self.$name = ".$this->genFieldNativeType($field)."_".trim($tokens['default'], '"');
|
||||
else
|
||||
$str = "self.$name = 0";
|
||||
}
|
||||
else if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
$is_virtual = array_key_exists("virtual", $tokens);
|
||||
if($is_virtual)
|
||||
$str .= "self.$name = New".ltrim($this->genFieldNativeType($field),'I')."() ";
|
||||
else
|
||||
$str .= "self.$name.Reset()";
|
||||
}
|
||||
else
|
||||
throw new Exception("Unknown type '$type'");
|
||||
return $str;
|
||||
}
|
||||
|
||||
function genFieldNativeType(mtgMetaField $field)
|
||||
{
|
||||
return $this->genNativeType($field->getType(), $field->getTokens());
|
||||
}
|
||||
|
||||
function genNativeType(mtgType $type, array $tokens = array())
|
||||
{
|
||||
if($type instanceof mtgArrType)
|
||||
{
|
||||
$vtype = $type->getValue();
|
||||
|
||||
$native = $this->genNativePlainType($vtype);
|
||||
|
||||
$str = "[]";
|
||||
if(array_key_exists("virtual", $tokens))
|
||||
$str .= "I";
|
||||
else
|
||||
$str .= $vtype instanceof mtgMetaStruct ? "*" : "";
|
||||
|
||||
$str .= $native;
|
||||
|
||||
return $str;
|
||||
}
|
||||
else
|
||||
return $this->genNativePlainType($type, $tokens);
|
||||
}
|
||||
|
||||
function genBuiltinTypePrefix(mtgBuiltinType $type)
|
||||
{
|
||||
switch($type->getName())
|
||||
{
|
||||
case "string":
|
||||
return "String";
|
||||
case "bool":
|
||||
return "Bool";
|
||||
case "blob":
|
||||
return "Blob";
|
||||
case "float":
|
||||
return "Float";
|
||||
case "double":
|
||||
return "Double";
|
||||
case "uint64":
|
||||
return "U64";
|
||||
case "int64":
|
||||
return "I64";
|
||||
case "uint":
|
||||
case "uint32":
|
||||
return "U32";
|
||||
case "uint16":
|
||||
return "U16";
|
||||
case "uint8":
|
||||
return "U8";
|
||||
case "int":
|
||||
case "int32":
|
||||
return "I32";
|
||||
case "int16":
|
||||
return "I16";
|
||||
case "int8":
|
||||
return "I8";
|
||||
default:
|
||||
throw new Exception("Unknown type '{$type}'");
|
||||
}
|
||||
}
|
||||
|
||||
function genNativePlainType(mtgType $type, array $tokens = array())
|
||||
{
|
||||
if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
if($type->isFloat())
|
||||
return "float32";
|
||||
else if($type->isDouble())
|
||||
return "float64";
|
||||
else if($type->isBlob())
|
||||
return "[]byte";
|
||||
else
|
||||
return $type->getName();
|
||||
}
|
||||
else if($type instanceof mtgMetaEnum)
|
||||
return $type->getName();
|
||||
else if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
if(array_key_exists("virtual", $tokens))
|
||||
return "I{$type->getName()}";
|
||||
else
|
||||
return $type->getName();
|
||||
}
|
||||
else
|
||||
throw new Exception("Unknown type '$type'");
|
||||
}
|
||||
|
||||
function genStrMap(array $source)
|
||||
{
|
||||
$str = '';
|
||||
foreach($source as $k => $v)
|
||||
$str .= '"' . $k . '": "' . ($v ? str_replace('"', '\\"', $v) : "") . '",';
|
||||
return $str;
|
||||
}
|
||||
|
||||
function genIntMap(array $source)
|
||||
{
|
||||
$str = '';
|
||||
foreach($source as $k => $v)
|
||||
$str .= "\"$k\": {$v},";
|
||||
return $str;
|
||||
}
|
||||
|
||||
function offset($num = 1)
|
||||
{
|
||||
return str_repeat(" ", $num);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
<?php
|
||||
require_once(dirname(__FILE__) . '/go.inc.php');
|
||||
|
||||
class mtgGoGenerator extends mtgGenerator
|
||||
{
|
||||
function makeTargets(mtgMetaInfo $meta)
|
||||
{
|
||||
$targets = array();
|
||||
|
||||
$codegen = mtg_conf("codegen", null);
|
||||
if(!$codegen)
|
||||
$codegen = new mtgGoCodegen();
|
||||
$codegen->setMetaInfo($meta);
|
||||
|
||||
$refl = new ReflectionClass($codegen);
|
||||
$SHARED_DEPS = array(
|
||||
dirname(__FILE__) . '/go.inc.php',
|
||||
dirname(__FILE__) . '/go_tpl.inc.php',
|
||||
__FILE__,
|
||||
$refl->getFileName()
|
||||
);
|
||||
|
||||
mtg_mkdir(mtg_conf('out-dir'));
|
||||
|
||||
$units = $meta->getUnits();
|
||||
$files = array();
|
||||
foreach($units as $unit)
|
||||
$files = array_merge($files, mtg_get_file_deps($meta, $unit));
|
||||
$files = array_unique($files);
|
||||
|
||||
$bundle = mtg_conf("bundle", null);
|
||||
if($bundle && $units)
|
||||
{
|
||||
$targets[] = mtg_new_bundle($bundle,
|
||||
array_merge($SHARED_DEPS, $files),
|
||||
array('gen_go_bundle', $meta, $codegen, $units));
|
||||
}
|
||||
|
||||
return $targets;
|
||||
}
|
||||
}
|
||||
|
||||
function gen_go_struct($OUT, array $DEPS, mtgCodegen $codegen, mtgMetaInfoUnit $unit)
|
||||
{
|
||||
return $codegen->genUnit($unit);
|
||||
}
|
||||
|
||||
function gen_go_bundle($OUT, array $DEPS, mtgMetaInfo $meta, mtgGoCodegen $codegen, array $units)
|
||||
{
|
||||
$bundle = '';
|
||||
$units_src = '';
|
||||
$create_struct_by_crc28 = '';
|
||||
$create_struct_by_name = '';
|
||||
$create_rpc_by_code = '';
|
||||
|
||||
foreach($units as $unit)
|
||||
{
|
||||
if($unit->object instanceof mtgMetaEnum ||
|
||||
$unit->object instanceof mtgMetaStruct ||
|
||||
$unit->object instanceof mtgMetaRPC
|
||||
)
|
||||
$units_src .= $codegen->genUnit($unit);
|
||||
}
|
||||
|
||||
foreach($units as $unit)
|
||||
{
|
||||
if($unit->object instanceof mtgMetaRPC)
|
||||
{
|
||||
$rpc = $unit->object;
|
||||
$create_rpc_by_code .= "\n case {$rpc->getCode()}: { return New{$rpc->getName()}(), nil; }";
|
||||
continue;
|
||||
}
|
||||
|
||||
if($unit->object->hasToken('POD') ||
|
||||
$unit->object instanceof mtgMetaFunc ||
|
||||
$unit->object instanceof mtgMetaEnum
|
||||
)
|
||||
continue;
|
||||
|
||||
$create_struct_by_crc28 .= "\n case {$unit->object->getClassId()}: { return New{$unit->object->getName()}(), nil }";
|
||||
$create_struct_by_name .= "\n case \"{$unit->object->getName()}\": { return New{$unit->object->getName()}(), nil }";
|
||||
}
|
||||
|
||||
$templater = new mtg_go_templater();
|
||||
$tpl = $templater->tpl_bundle();
|
||||
return mtg_fill_template($tpl,
|
||||
array('%bundle%' => $bundle,
|
||||
'%units_src%' => $units_src,
|
||||
'%create_struct_by_name%' => $create_struct_by_name,
|
||||
'%create_struct_by_crc28%' => $create_struct_by_crc28,
|
||||
'%create_rpc_by_code%' => $create_rpc_by_code,
|
||||
));
|
||||
}
|
|
@ -0,0 +1,387 @@
|
|||
<?php
|
||||
|
||||
class mtg_go_templater
|
||||
{
|
||||
function tpl_struct()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
//package autogen
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
|
||||
%includes%
|
||||
|
||||
type %class% struct {
|
||||
%parent_class%
|
||||
%fields%
|
||||
}
|
||||
|
||||
var _%class%_class_props map[string]string = %class_props%
|
||||
var _%class%_class_fields []string = %fields_names%
|
||||
var _%class%_fields_props meta.ClassFieldsProps = %fields_props%
|
||||
|
||||
func %class%_CLASS_ID() uint32 {
|
||||
return %class_id%
|
||||
}
|
||||
|
||||
type I%class% interface {
|
||||
meta.IMetaStruct
|
||||
Ptr%class%() *%class%
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_ID() uint32 {
|
||||
return %class_id%
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_NAME() string {
|
||||
return "%class%"
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_PROPS() *map[string]string {
|
||||
return &_%class%_class_props
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_FIELDS() []string {
|
||||
return _%class%_class_fields
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_FIELDS_PROPS() *meta.ClassFieldsProps {
|
||||
return &_%class%_fields_props
|
||||
}
|
||||
|
||||
//convenience getter
|
||||
func Ptr%class%(m meta.IMetaStruct) *%class% {
|
||||
p, ok := m.(I%class%)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
return p.Ptr%class%()
|
||||
}
|
||||
|
||||
func (self *%class%) Ptr%class%() *%class% {
|
||||
return self
|
||||
}
|
||||
|
||||
func New%class%() *%class% {
|
||||
item := new (%class%)
|
||||
item.Reset()
|
||||
return item
|
||||
}
|
||||
|
||||
func (self *%class%) Reset() {
|
||||
%fields_reset%
|
||||
}
|
||||
|
||||
func (self *%class%) Read(reader meta.Reader) error {
|
||||
return meta.ReadStruct(reader, self, "")
|
||||
}
|
||||
|
||||
func (self *%class%) ReadFields(reader meta.Reader) error {
|
||||
self.Reset()
|
||||
%read_buffer%
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *%class%) Write(writer meta.Writer) error {
|
||||
return meta.WriteStruct(writer, self, "")
|
||||
}
|
||||
|
||||
func (self *%class%) WriteFields(writer meta.Writer) error {
|
||||
%write_buffer%
|
||||
return nil
|
||||
}
|
||||
|
||||
%ext_methods%
|
||||
|
||||
%analytics_methods%
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
function tpl_collection_item()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
|
||||
func (self *%class%) NewInstance() meta.IMetaDataItem {
|
||||
return New%class%()
|
||||
}
|
||||
|
||||
func (self *%class%) GetDbTableName() string {
|
||||
return "%table_name%"
|
||||
}
|
||||
|
||||
func (self *%class%) GetDbFields() []string {
|
||||
return self.CLASS_FIELDS()
|
||||
}
|
||||
|
||||
func (self *%class%) GetOwnerFieldName() string {
|
||||
return "%owner%"
|
||||
}
|
||||
|
||||
func (self *%class%) GetIdFieldName() string {
|
||||
return "%id_field_name%"
|
||||
}
|
||||
|
||||
func (self *%class%) GetIdValue() uint64 {
|
||||
return uint64(self.%id_field%)
|
||||
}
|
||||
|
||||
func (self *%class%) Import(data interface{}) {
|
||||
switch data.(type) {
|
||||
case %class%:
|
||||
{
|
||||
%import_from_mysql%
|
||||
break
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func (self *%class%) Export(data []interface{}) {
|
||||
%export_to_arr%
|
||||
}
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
function tpl_enum()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
//package autogen
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
|
||||
const (%consts%)
|
||||
|
||||
type %class% int32
|
||||
|
||||
var _%class%_values []int = []int{%values_list%}
|
||||
var _%class%_map map[string]%class% = map[string]%class%{%values_map%}
|
||||
|
||||
func (*%class%) CLASS_ID() uint32 {
|
||||
return %class_id%
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_NAME() string {
|
||||
return "%class%"
|
||||
}
|
||||
|
||||
func (*%class%) DEFAULT_VALUE() int32 {
|
||||
return %default_enum_value%
|
||||
}
|
||||
|
||||
func (self *%class%) IsValid() bool {
|
||||
return sort.SearchInts(_%class%_values, int(*self)) != -1
|
||||
}
|
||||
|
||||
func %class%_GetNameByValue(value int) string {
|
||||
for name, num := range _%class%_map {
|
||||
if value == int(num) {
|
||||
return name
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func New%class%ByName(name string) (%class%, error) {
|
||||
if v, ok := _%class%_map[name]; ok == true {
|
||||
return v, nil
|
||||
}
|
||||
return 0, errors.Errorf("Wrong name of %class%: '%s'", name)
|
||||
}
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
function tpl_packet()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
//package autogen
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
|
||||
%includes%
|
||||
|
||||
type %class% struct {
|
||||
%parent_class%
|
||||
%fields%
|
||||
}
|
||||
|
||||
var _%class%_class_props map[string]string = %class_props%
|
||||
var _%class%_class_fields []string = %fields_names%
|
||||
var %class%_ func(interface{}, *%class%, *%class_invert%) error
|
||||
var _%class%_fields_props meta.ClassFieldsProps = nil
|
||||
|
||||
type I%class% interface {
|
||||
meta.IMetaStruct
|
||||
I%class%() *%class%
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_ID() uint32 {
|
||||
return %class_id%
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_NAME() string {
|
||||
return "%class%"
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_FIELDS_PROPS() *meta.ClassFieldsProps {
|
||||
return &_%class%_fields_props
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_PROPS() *map[string]string {
|
||||
return &_%class%_class_props
|
||||
}
|
||||
|
||||
func (*%class%) CLASS_FIELDS() []string {
|
||||
return _%class%_class_fields
|
||||
}
|
||||
|
||||
func (self *%class%) I%class%() *%class% {
|
||||
return self
|
||||
}
|
||||
|
||||
func (*%class%) GetCode() int32 {
|
||||
return %code%
|
||||
}
|
||||
|
||||
func New%class%() *%class% {
|
||||
item := new (%class%)
|
||||
item.Reset()
|
||||
return item
|
||||
}
|
||||
|
||||
func (self *%class%) Reset() {
|
||||
%fields_reset%
|
||||
}
|
||||
|
||||
func (self *%class%) Read(reader meta.Reader) error {
|
||||
return meta.ReadStruct(reader, self, "")
|
||||
}
|
||||
|
||||
func (self *%class%) ReadFields(reader meta.Reader) error {
|
||||
self.Reset()
|
||||
%read_buffer%
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *%class%) Write(writer meta.Writer) error {
|
||||
return meta.WriteStruct(writer, self, "")
|
||||
}
|
||||
|
||||
func (self *%class%) WriteFields(writer meta.Writer) error {
|
||||
%write_buffer%
|
||||
return nil
|
||||
}
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
function tpl_bundle()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
package autogen
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
%bundle%
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"github.com/bit.games/meta"
|
||||
"github.com/bit.games/errors"
|
||||
)
|
||||
|
||||
//supress *imported but not used* warnings
|
||||
var _ = fmt.Printf
|
||||
var _ = sort.SearchInts
|
||||
|
||||
// Common interfaces and types
|
||||
|
||||
%units_src%
|
||||
|
||||
func CreateById(classId uint32) (meta.IMetaStruct, error) {
|
||||
switch classId {
|
||||
%create_struct_by_crc28%
|
||||
default : return nil, errors.Errorf("Can't find struct for class id %d", classId)
|
||||
}
|
||||
}
|
||||
|
||||
func CreateByName(name string) (meta.IMetaStruct, error) {
|
||||
switch name {
|
||||
%create_struct_by_name%
|
||||
default : return nil, errors.Errorf("Can't find struct for name %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
func CreateRPC(code uint32)(meta.IRPC, error) {
|
||||
switch code {
|
||||
%create_rpc_by_code%
|
||||
default: return nil, errors.Errorf("Can't find rpc for code %d", code)
|
||||
}
|
||||
}
|
||||
|
||||
EOD;
|
||||
|
||||
return $TPL;
|
||||
}
|
||||
function tpl_rpc()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
func New%rpc_name%() *%rpc_name% {
|
||||
rpc := %rpc_name%{}
|
||||
rpc.Req = New%rpc_req_name%()
|
||||
rpc.Rsp = New%rpc_rsp_name%()
|
||||
return &rpc
|
||||
}
|
||||
|
||||
type %rpc_name% struct {
|
||||
ErrCode int32
|
||||
ErrMsg string
|
||||
Req *%rpc_req_name%
|
||||
Rsp *%rpc_rsp_name%
|
||||
}
|
||||
|
||||
func (rpc *%rpc_name%) SetError(code int32, msg string) {
|
||||
rpc.ErrCode = code
|
||||
rpc.ErrMsg = msg
|
||||
}
|
||||
|
||||
func (rpc *%rpc_name%) GetError() (int32, string) {
|
||||
return rpc.ErrCode, rpc.ErrMsg
|
||||
}
|
||||
|
||||
func (rpc *%rpc_name%) GetRequest() meta.IMetaStruct {
|
||||
return rpc.Req
|
||||
}
|
||||
|
||||
func (rpc *%rpc_name%) GetResponse() meta.IMetaStruct {
|
||||
return rpc.Rsp
|
||||
}
|
||||
|
||||
func (rpc *%rpc_name%) GetName() string {
|
||||
return "%rpc_name%"
|
||||
}
|
||||
|
||||
func (rpc *%rpc_name%) GetCode() int32 {
|
||||
return %code%
|
||||
}
|
||||
|
||||
type IRPCHandler_%rpc_name% interface {
|
||||
%rpc_name%(*%rpc_name%) error
|
||||
}
|
||||
|
||||
func (rpc *%rpc_name%) Execute(h interface{}) error{
|
||||
mh, ok := h.(IRPCHandler_%rpc_name%)
|
||||
if !ok {
|
||||
return errors.New("RPC '%rpc_name%(*%rpc_name%) error' is not implemented")
|
||||
}
|
||||
return mh.%rpc_name%(rpc)
|
||||
}
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,620 @@
|
|||
<?php
|
||||
|
||||
require_once(dirname(__FILE__) . '/../../metagen.inc.php');
|
||||
require_once(dirname(__FILE__) . '/php_tpl.inc.php');
|
||||
|
||||
class mtgPHPCodegen extends mtgCodegen
|
||||
{
|
||||
protected $filter_aliases = array();
|
||||
|
||||
function __construct(array $filter_aliases = array())
|
||||
{
|
||||
$this->filter_aliases = $filter_aliases;
|
||||
}
|
||||
|
||||
function genUnit(mtgMetaInfoUnit $unit)
|
||||
{
|
||||
$obj = $unit->object;
|
||||
if($obj instanceof mtgMetaRPC)
|
||||
return $this->genRPC($obj);
|
||||
if($obj instanceof mtgMetaEnum)
|
||||
return $this->genEnum($obj);
|
||||
else if($obj instanceof mtgMetaStruct)
|
||||
return $this->genStruct($obj);
|
||||
else
|
||||
{
|
||||
echo "WARN: skipping meta unit '{$obj->getId()}'\n";
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
function genEnum(mtgMetaEnum $struct)
|
||||
{
|
||||
$templater = new mtg_php_templater();
|
||||
|
||||
$repl = array();
|
||||
$repl['%class%'] = $struct->getName();
|
||||
$repl['%class_id%'] = $struct->getClassId();
|
||||
|
||||
$tpl = $templater->tpl_enum();
|
||||
|
||||
$this->fillEnumRepls($struct, $repl);
|
||||
|
||||
return mtg_fill_template($tpl, $repl);
|
||||
}
|
||||
|
||||
function fillEnumRepls(mtgMetaEnum $enum, &$repl)
|
||||
{
|
||||
$repl['%values%'] = '';
|
||||
$repl['%vnames_list%'] = '';
|
||||
$repl['%values_list%'] = '';
|
||||
|
||||
$vnames = array();
|
||||
$values = array();
|
||||
$txt_values = array();
|
||||
$values_map = array();
|
||||
$names_map = array();
|
||||
$default_value = null;
|
||||
|
||||
$enum_name = $enum->getName();
|
||||
foreach($enum->getValues() as $vname => $value)
|
||||
{
|
||||
$txt_values[] = "const $vname = $value;";
|
||||
$vnames[] = "'$vname'";
|
||||
$values[] = $value;
|
||||
$values_map[] = "'$vname' => $value";
|
||||
$names_map[] = "$value => '$vname'";
|
||||
if(!$default_value)
|
||||
$default_value = "$value; // $vname";
|
||||
}
|
||||
|
||||
$repl['%values%'] = implode("\n ", $txt_values);
|
||||
$repl['%vnames_list%'] = implode(',', $vnames);
|
||||
$repl['%values_list%'] = implode(',', $values);
|
||||
$repl['%values_map%'] = implode(',', $values_map);
|
||||
$repl['%names_map%'] = implode(',', $names_map);
|
||||
$repl['%default_enum_value%'] = $default_value;
|
||||
}
|
||||
|
||||
function genRPC(mtgMetaRpc $rpc)
|
||||
{
|
||||
$req = $this->genRPCPacket($rpc->getReq());
|
||||
$rsp = $this->genRPCPacket($rpc->getRsp());
|
||||
return $req . str_replace('<?php', '', $rsp);
|
||||
}
|
||||
|
||||
function genRPCPacket(mtgMetaPacket $packet)
|
||||
{
|
||||
$templater = new mtg_php_templater();
|
||||
|
||||
$repl = array();
|
||||
$repl['%type%'] = $packet->getName();
|
||||
$repl['%code%'] = $packet->getNumericCode();
|
||||
$repl['%class%'] = $packet->getName();
|
||||
$repl['%class_id%'] = $packet->getClassId();
|
||||
$repl['%parent_class%'] = 'extends gmeStrictPropsObject';
|
||||
|
||||
$tpl = $templater->tpl_packet();
|
||||
|
||||
$this->fillStructRepls($packet, $repl);
|
||||
|
||||
return mtg_fill_template($tpl, $repl);
|
||||
}
|
||||
|
||||
function genStruct(mtgMetaStruct $struct)
|
||||
{
|
||||
$templater = new mtg_php_templater();
|
||||
|
||||
$repl = array();
|
||||
$repl['%class%'] = $struct->getName();
|
||||
$repl['%class_id%'] = $struct->getClassId();
|
||||
$repl['%parent_class%'] = $struct->getParent() ? "extends " . $struct->getParent() : "extends gmeStrictPropsObject";
|
||||
|
||||
$tpl = $templater->tpl_struct();
|
||||
|
||||
$this->fillStructRepls($struct, $repl);
|
||||
|
||||
return mtg_fill_template($tpl, $repl);
|
||||
}
|
||||
|
||||
function preprocessField(mtgMetaStruct $struct, mtgMetaField $field)
|
||||
{}
|
||||
|
||||
function fillStructRepls(mtgMetaStruct $struct, &$repl)
|
||||
{
|
||||
foreach($struct->getFields() as $field)
|
||||
$this->preprocessField($struct, $field);
|
||||
|
||||
$parent = $struct->getParent();
|
||||
|
||||
$repl['%includes%'] = '';
|
||||
$repl['%fields%'] = '';
|
||||
$repl['%fields_names%'] = ($parent ? 'array_merge(parent::CLASS_FIELDS(), ' : '') . 'array(';
|
||||
$repl['%fields_props%'] = ($parent ? 'array_merge(parent::CLASS_FIELDS_PROPS(), ' : '') . 'array(';
|
||||
$repl['%fields_types%'] = ($parent ? 'array_merge(parent::CLASS_FIELDS_TYPES(), ' : '') . 'array(';
|
||||
$repl['%fields_init%'] = '';
|
||||
$repl['%fill_fields%'] = '';
|
||||
$repl['%fill_buffer%'] = '';
|
||||
$repl['%total_top_fields%'] = sizeof($struct->getFields());
|
||||
$repl["%ext_methods%"] = "";
|
||||
|
||||
$repl['%class_props%'] = str_replace("\n", "", var_export($struct->getTokens(), true));
|
||||
|
||||
$deps = array();
|
||||
if($parent)
|
||||
$deps[] = $parent.'';
|
||||
foreach($struct->getFields() as $field)
|
||||
{
|
||||
$type = $field->getType();
|
||||
if($type instanceof mtgArrType)
|
||||
$type = $type->getValue();
|
||||
|
||||
if($type instanceof mtgUserType)
|
||||
$deps[] = $type->getName();
|
||||
}
|
||||
$deps = array_unique($deps);
|
||||
|
||||
foreach($deps as $dep)
|
||||
$repl['%includes%'] .= "require_once(dirname(__FILE__) . '/$dep.class.php');\n";
|
||||
|
||||
if($parent)
|
||||
{
|
||||
$repl['%fields_init%'] .= "parent::__construct();\n";
|
||||
$repl['%fill_fields%'] .= "\$IDX = parent::import(\$message, \$assoc, false);\n";
|
||||
$repl['%fill_buffer%'] .= "\$__last_var = 'parent';\$__last_val='<<skipped>>';parent::fill(\$message, \$assoc, false);\n";
|
||||
}
|
||||
|
||||
$indent = " ";
|
||||
$local_indent = " ";
|
||||
$fill_func_indent = " ";
|
||||
|
||||
foreach($struct->getFields() as $field)
|
||||
{
|
||||
$repl['%fields_names%'] .= "'" . $field->getName() . "',";
|
||||
$repl['%fields_props%'] .= "'" . $field->getName() . "' => " . str_replace("\n", "", var_export($field->getTokens(), true)) . ",";
|
||||
$field_type = $field->getType();
|
||||
$repl['%fields_types%'] .= "'" . $field->getName() . "' => '" . $field_type . "',";
|
||||
|
||||
$repl['%fields_init%'] .= $this->genFieldInit($field, ' $this->') . "\n";
|
||||
$repl['%fill_fields%'] .= $local_indent.$this->genFieldFill($field, '$message', '$this->', $indent) . $indent."++\$IDX;\n";
|
||||
$repl['%fill_buffer%'] .= $this->genBufFill($field, '$message', '$this->', $fill_func_indent) . "\n";
|
||||
$repl['%fields%'] .= $this->genFieldDecl($field) . "\n";
|
||||
}
|
||||
|
||||
$repl['%fields_names%'] .= ')' . ($parent ? ')' : '');
|
||||
$repl['%fields_props%'] .= ')' . ($parent ? ')' : '');
|
||||
$repl['%fields_types%'] .= ')' . ($parent ? ')' : '');
|
||||
|
||||
$this->undoTemp();
|
||||
}
|
||||
|
||||
function genFieldInitValue(mtgMetaField $field)
|
||||
{
|
||||
$tokens = $field->getTokens();
|
||||
$name = $field->getName();
|
||||
$type = $field->getType();
|
||||
|
||||
if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
return "new {$type}()";
|
||||
}
|
||||
else if($type instanceof mtgMetaEnum)
|
||||
{
|
||||
if(array_key_exists('default', $tokens))
|
||||
$default = $this->genDefault($tokens['default']);
|
||||
else
|
||||
$default = 'DEFAULT_VALUE';
|
||||
|
||||
$default = str_replace('"', '', $default);
|
||||
return "{$type}::$default";
|
||||
}
|
||||
else if($type instanceof mtgArrType)
|
||||
{
|
||||
return "array()";
|
||||
}
|
||||
else if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
if(array_key_exists('default', $tokens))
|
||||
{
|
||||
$raw_default_value = $this->genDefault($tokens['default']);
|
||||
$default_value = $this->genApplyFieldFilters($name, $tokens, $raw_default_value, false);
|
||||
return "mtg_php_val_{$type}($default_value)";
|
||||
}
|
||||
if ($type->isString())
|
||||
return "''";
|
||||
return '0';
|
||||
}
|
||||
else
|
||||
throw new Exception("Unable generate field initial value for unknown type '{$type}'");
|
||||
}
|
||||
|
||||
function genFieldInit(mtgMetaField $field, $prefix = "")
|
||||
{
|
||||
$value = $this->genFieldInitValue($field);
|
||||
|
||||
return $prefix . $field->getName() . " = $value;";
|
||||
}
|
||||
|
||||
function genFieldFill(mtgMetaField $field, $buf, $prefix, $indent = "")
|
||||
{
|
||||
return $this->genFieldFillEx($field->getName(), $field->getType(), $buf, $prefix, $field->getTokens(), false, "", $indent);
|
||||
}
|
||||
|
||||
function genFieldFillEx($name, mtgType $type, $buf, $prefix = '', $tokens = array(), $as_is = false, $postfix = '', $indent = "")
|
||||
{
|
||||
$str = '';
|
||||
$tmp_val = '$tmp_val__';
|
||||
$pname = $prefix.$name;
|
||||
|
||||
$cond_indent = $indent." ";
|
||||
|
||||
$default_value_arg = array_key_exists('default', $tokens) ? $this->genDefault($tokens['default']) : 'null';
|
||||
if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
$default = array_key_exists('default', $tokens) ? $tokens['default'] : null;
|
||||
if($default)
|
||||
{
|
||||
$default = json_decode($default, true);
|
||||
if(!is_array($default))
|
||||
throw new Exception("Bad default struct: " . $tokens['default']);
|
||||
$default = str_replace("\n", "", var_export($default, true));
|
||||
$default_value_arg = "\$assoc ? $default : array_values($default)";
|
||||
}
|
||||
else
|
||||
$default_value_arg = "null";
|
||||
}
|
||||
|
||||
$str .= "\n";
|
||||
|
||||
if($as_is)
|
||||
$tmp_val = $buf;
|
||||
else
|
||||
$str .= $indent."{$tmp_val} = mtg_php_array_extract_val({$buf}, \$assoc, '{$name}', {$default_value_arg});\n";
|
||||
|
||||
if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
$str .= $cond_indent."{$tmp_val} = " . $this->genApplyFieldFilters($name, $tokens, "{$tmp_val}"). ";\n";
|
||||
$str .= $cond_indent."{$pname} = mtg_php_val_{$type}({$tmp_val});\n";
|
||||
}
|
||||
else if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
if(array_key_exists('virtual', $tokens))
|
||||
{
|
||||
$str .= $cond_indent."{$tmp_val} = " . $this->genApplyFieldFilters($name, $tokens, "{$tmp_val}"). ";\n";
|
||||
$str .= $cond_indent."\$tmp_sub_arr__ = mtg_php_val_arr({$tmp_val});\n";
|
||||
$str .= $cond_indent."\$vclass__ = AutogenBundle::getClassName(mtg_php_val_uint32(mtg_php_array_extract_val(\$tmp_sub_arr__, \$assoc, 'vclass__')));\n";
|
||||
$str .= $cond_indent."{$pname} = new \$vclass__(\$tmp_sub_arr__, \$assoc);\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
$str .= $cond_indent."{$tmp_val} = " . $this->genApplyFieldFilters($name, $tokens, "{$tmp_val}"). ";\n";
|
||||
$str .= $cond_indent."\$tmp_sub_arr__ = mtg_php_val_arr({$tmp_val});\n";
|
||||
$str .= $cond_indent."{$pname} = new {$type}(\$tmp_sub_arr__, \$assoc);\n";
|
||||
}
|
||||
}
|
||||
else if($type instanceof mtgArrType)
|
||||
{
|
||||
//TODO: maybe filters should be applied to the whole array as well?
|
||||
$str .= $cond_indent."\$tmp_arr__ = mtg_php_val_arr({$tmp_val});\n";
|
||||
$str .= $cond_indent."foreach(\$tmp_arr__ as \$tmp_arr_item__)\n";
|
||||
$str .= $cond_indent."{\n";
|
||||
//NOTE: удаляем дефолтное значение уровня поля, а не его значений
|
||||
unset($tokens['default']);
|
||||
$str .= $this->genFieldFillEx('$tmp__', $type->getValue(), "\$tmp_arr_item__", '', $tokens, true, "{$pname}[] = \$tmp__;", $cond_indent." ") . "\n";
|
||||
$str .= $cond_indent."}\n";
|
||||
}
|
||||
else if($type instanceof mtgMetaEnum)
|
||||
{
|
||||
$str .= $cond_indent."{$tmp_val} = " . $this->genApplyFieldFilters($name, $tokens, "{$tmp_val}"). ";\n";
|
||||
$check_enum_validity = array_key_exists('is_enum_mask', $tokens) ? 'false' : 'true';
|
||||
$str .= $cond_indent."{$pname} = mtg_php_val_enum('$type', {$tmp_val}, {$check_enum_validity});\n";
|
||||
}
|
||||
else
|
||||
throw new Exception("Unknown type '{$type}'");
|
||||
|
||||
if($postfix)
|
||||
$str .= $cond_indent.$postfix."\n";
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
function genApplyFieldFilters($field_name, array $tokens, $val, $add_assoc_check = true)
|
||||
{
|
||||
$str = $val;
|
||||
foreach($tokens as $token => $args_json)
|
||||
{
|
||||
if(isset($this->filter_aliases[$token]))
|
||||
$token = $this->filter_aliases[$token];
|
||||
|
||||
if(strpos($token, 'flt_') === false)
|
||||
continue;
|
||||
|
||||
$filter_func = 'mtg_' . $token;
|
||||
|
||||
if(function_exists($filter_func))
|
||||
{
|
||||
$args = array();
|
||||
if($args_json)
|
||||
{
|
||||
$args = json_decode($args_json, true);
|
||||
if(!is_array($args))
|
||||
throw new Exception("Bad filter args: $args_json");
|
||||
}
|
||||
if ($add_assoc_check)
|
||||
return "\$assoc ? $filter_func($val, '$field_name', \$this, " . str_replace("\n", "", var_export($args, true)) . ") : $val";
|
||||
else
|
||||
return "$filter_func($val, '$field_name', \$this, " . str_replace("\n", "", var_export($args, true)) . ")";
|
||||
}
|
||||
else
|
||||
throw new Exception("No such filter '$filter_func'");
|
||||
}
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
function genBufFill(mtgMetaField $field, $buf, $prefix, $indent = "")
|
||||
{
|
||||
return $this->genBufFillEx($field->getName(), $field->getType(), $buf, $prefix, $field->getTokens(), $indent);
|
||||
}
|
||||
|
||||
function genBufFillEx($name, mtgType $type, $buf, $prefix = '', $tokens = array(), $indent = "")
|
||||
{
|
||||
$pname = $prefix.$name;
|
||||
|
||||
$str = '';
|
||||
$str .= "\$__last_var = '$name';";
|
||||
$str .= "\$__last_val = $pname;";
|
||||
|
||||
if($type instanceof mtgBuiltinType)
|
||||
{
|
||||
if($type->isNumeric())
|
||||
$str .= $indent."mtg_php_array_set_value({$buf}, \$assoc, '$name', 1*{$pname});";
|
||||
else if($type->isString())
|
||||
$str .= $indent."mtg_php_array_set_value({$buf}, \$assoc, '$name', ''.{$pname});";
|
||||
else if($type->isBool())
|
||||
$str .= $indent."mtg_php_array_set_value({$buf}, \$assoc, '$name', (bool){$pname});";
|
||||
else if($type->isBlob())
|
||||
$str .= $indent."mtg_php_array_set_value({$buf}, \$assoc, '$name', {$pname});";
|
||||
else
|
||||
throw new Exception("Unknown type '$type'");
|
||||
}
|
||||
else if($type instanceof mtgMetaStruct)
|
||||
{
|
||||
if(array_key_exists('virtual', $tokens))
|
||||
$str .= $indent."mtg_php_array_set_value({$buf}, \$assoc, '$name', is_array({$pname}) ? {$pname} : {$pname}->export(\$assoc, true/*virtual*/));";
|
||||
else
|
||||
$str .= $indent."mtg_php_array_set_value({$buf}, \$assoc, '$name', is_array({$pname}) ? {$pname} : {$pname}->export(\$assoc));";
|
||||
}
|
||||
else if($type instanceof mtgMetaEnum)
|
||||
{
|
||||
$str .= $indent."mtg_php_array_set_value({$buf}, \$assoc, '$name', 1*{$pname});";
|
||||
}
|
||||
else if($type instanceof mtgArrType)
|
||||
{
|
||||
$str .= $indent."\$arr_tmp__ = array();\n";
|
||||
//NOTE: adding optimization, checking if the first item is array
|
||||
$str .= $indent."if(!\$assoc && {$pname} && is_array(current({$pname})))\n";
|
||||
$str .= $indent."{\n";
|
||||
//$str .= " mtg_php_debug('BULK:' . get_class(\$this));\n";
|
||||
$str .= $indent." \$arr_tmp__ = {$pname};\n";
|
||||
$str .= $indent."}\n";
|
||||
$str .= $indent."else\n";
|
||||
$str .= $indent." foreach({$pname} as \$idx__ => \$arr_tmp_item__)\n";
|
||||
$str .= $indent." {\n";
|
||||
$str .= $this->genBufFillEx('$arr_tmp_item__', $type->getValue(), "\$arr_tmp__", "", $tokens, $indent." ") . "\n";
|
||||
$str .= $indent." if(\$assoc)\n";
|
||||
$str .= $indent." {\n";
|
||||
$str .= $indent." \$arr_tmp__[] = \$arr_tmp__['\$arr_tmp_item__'];\n";
|
||||
$str .= $indent." unset(\$arr_tmp__['\$arr_tmp_item__']);\n";
|
||||
$str .= $indent." }\n";
|
||||
$str .= $indent." }\n";
|
||||
$str .= $indent."mtg_php_array_set_value({$buf}, \$assoc, '$name', \$arr_tmp__);\n";
|
||||
}
|
||||
else
|
||||
throw new Exception("Unknown type '{$type}'");
|
||||
|
||||
return $str;
|
||||
}
|
||||
|
||||
function genDefault($default)
|
||||
{
|
||||
if($default == "[]")
|
||||
return 'array()';
|
||||
if(strlen($default) > 2 && strpos($default, '[') === 0 && strpos($default, ']') === strlen($default)-1)
|
||||
{
|
||||
$str = trim($default, '][');
|
||||
if(strpos($str, "{") !== false )
|
||||
$str = var_export(json_decode(trim($default, ']['), true), true);
|
||||
return 'array(' . $str . ')';
|
||||
}
|
||||
return str_replace('$', '\$', $default);
|
||||
}
|
||||
|
||||
function genFieldDecl(mtgMetaField $field)
|
||||
{
|
||||
$type = $field->getType();
|
||||
|
||||
$type_comment = "/** @var " . $type ." */\n";
|
||||
if($type instanceof mtgBuiltinType && $type->isString())
|
||||
return $type_comment."public \$" . $field->getName() . " = '';";
|
||||
else if($type instanceof mtgMetaStruct)
|
||||
return $type_comment."public \$" . $field->getName() . " = null;";
|
||||
else if($type instanceof mtgArrType)
|
||||
return $type_comment."public \$" . $field->getName() . " = array();";
|
||||
else
|
||||
return $type_comment."public \$" . $field->getName() . ";";
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
function mtg_php_array_extract_val(&$arr, $assoc, $name, $default = null)
|
||||
{
|
||||
if(!is_array($arr))
|
||||
throw new Exception("$name: Not an array");
|
||||
|
||||
if(!$assoc)
|
||||
{
|
||||
if(sizeof($arr) == 0)
|
||||
{
|
||||
if($default !== null)
|
||||
return $default;
|
||||
|
||||
throw new Exception("$name: No next array item");
|
||||
}
|
||||
return array_shift($arr);
|
||||
}
|
||||
|
||||
if(!isset($arr[$name]))
|
||||
{
|
||||
if($default !== null)
|
||||
return $default;
|
||||
|
||||
throw new Exception("$name: No array item");
|
||||
}
|
||||
|
||||
$val = $arr[$name];
|
||||
unset($arr[$name]);
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_array_set_value(&$arr, $assoc, $name, $value)
|
||||
{
|
||||
if($assoc)
|
||||
$arr[$name] = $value;
|
||||
else
|
||||
$arr[] = $value;
|
||||
}
|
||||
|
||||
function mtg_php_val_string($val)
|
||||
{
|
||||
//special case for empty strings
|
||||
if(is_bool($val) && $val === false)
|
||||
return '';
|
||||
if(!is_string($val))
|
||||
throw new Exception("Bad item, not a string(" . serialize($val) . ")");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_bool($val)
|
||||
{
|
||||
if(!is_bool($val))
|
||||
throw new Exception("Bad item, not a bool(" . serialize($val) . ")");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_float($val)
|
||||
{
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
|
||||
$val = 1*$val;
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_double($val)
|
||||
{
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
|
||||
return 1*$val;
|
||||
}
|
||||
|
||||
function mtg_php_val_uint64($val)
|
||||
{
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
|
||||
$val = 1*$val;
|
||||
if(is_float($val))
|
||||
throw new Exception("Value not in range: $val");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_int64($val)
|
||||
{
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
|
||||
$val = 1*$val;
|
||||
if(is_float($val))
|
||||
throw new Exception("Value not in range: $val");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_uint32($val)
|
||||
{
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
|
||||
$val = 1*$val;
|
||||
if(($val < 0 && $val < -2147483648) || ($val > 0 && $val > 0xFFFFFFFF) || is_float($val))
|
||||
throw new Exception("Value not in range: $val");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_int32($val)
|
||||
{
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
|
||||
$val = 1*$val;
|
||||
if($val > 2147483647 || $val < -2147483648 || is_float($val))
|
||||
throw new Exception("Value not in range: $val");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_uint16($val)
|
||||
{
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
|
||||
$val = 1*$val;
|
||||
if($val > 0xFFFF || $val < 0 || is_float($val))
|
||||
throw new Exception("Value not in range: $val");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_int16($val)
|
||||
{
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
|
||||
$val = 1*$val;
|
||||
if($val > 32767 || $val < -32768 || is_float($val))
|
||||
throw new Exception("Value not in range: $val");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_uint8($val)
|
||||
{
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
|
||||
$val = 1*$val;
|
||||
if($val > 0xFF || $val < 0 || is_float($val))
|
||||
throw new Exception("Value not in range: $val");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_int8($val)
|
||||
{
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad item, not a number(" . serialize($val) . ")");
|
||||
$val = 1*$val;
|
||||
if($val > 127 || $val < -128 || is_float($val))
|
||||
throw new Exception("Value not in range: $val");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_arr($val)
|
||||
{
|
||||
if(!is_array($val))
|
||||
throw new Exception("Bad item, not an array(" . serialize($val) . ")");
|
||||
return $val;
|
||||
}
|
||||
|
||||
function mtg_php_val_enum($enum, $val, $check_enum_validity = true)
|
||||
{
|
||||
if(is_string($val))
|
||||
return call_user_func_array(array($enum, "getValueByName"), array($val));
|
||||
|
||||
if(!is_numeric($val))
|
||||
throw new Exception("Bad enum value, not a numeric or string(" . serialize($val) . ")");
|
||||
|
||||
if($check_enum_validity)
|
||||
call_user_func_array(array($enum, "checkValidity"), array($val));
|
||||
return $val;
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
require_once(dirname(__FILE__) . '/php.inc.php');
|
||||
|
||||
class mtgPHPGenerator extends mtgGenerator
|
||||
{
|
||||
function makeTargets(mtgMetaInfo $meta)
|
||||
{
|
||||
$targets = array();
|
||||
|
||||
$codegen = mtg_conf("codegen", null);
|
||||
if(!$codegen)
|
||||
$codegen = new mtgPHPCodegen();
|
||||
$codegen->setMetaInfo($meta);
|
||||
|
||||
$refl = new ReflectionClass($codegen);
|
||||
$SHARED_DEPS = array(
|
||||
dirname(__FILE__) . '/php.inc.php',
|
||||
dirname(__FILE__) . '/php_tpl.inc.php',
|
||||
__FILE__,
|
||||
$refl->getFileName()
|
||||
);
|
||||
|
||||
mtg_mkdir(mtg_conf('out-dir'));
|
||||
|
||||
$units = array();
|
||||
$rpcs = array();
|
||||
$files = array();
|
||||
foreach($meta->getUnits() as $unit)
|
||||
{
|
||||
if($unit->object instanceof mtgMetaRPC)
|
||||
{
|
||||
$rpcs[] = $unit->object;
|
||||
$files = array_merge($files, mtg_get_file_deps($meta, $unit));
|
||||
}
|
||||
else if($unit->object instanceof mtgMetaEnum || $unit->object instanceof mtgMetaStruct)
|
||||
{
|
||||
$files = array_merge($files, mtg_get_file_deps($meta, $unit));
|
||||
$units[] = $unit;
|
||||
}
|
||||
}
|
||||
$files = array_unique($files);
|
||||
|
||||
$bundle = mtg_conf("bundle", null);
|
||||
if($bundle && ($units || $rpcs))
|
||||
$targets[] = mtg_new_bundle($bundle, array_merge($SHARED_DEPS, $files), array('gen_php_bundle', $codegen, $units, $rpcs, mtg_conf("inc-dir")));
|
||||
|
||||
return $targets;
|
||||
}
|
||||
}
|
||||
|
||||
function gen_php_struct($OUT, array $DEPS, mtgCodegen $codegen, mtgMetaInfoUnit $unit)
|
||||
{
|
||||
return $codegen->genUnit($unit);
|
||||
}
|
||||
|
||||
function gen_php_bundle($OUT, array $DEPS, mtgPHPCodegen $codegen, array $units, array $rpcs, $inc_dir)
|
||||
{
|
||||
$bundle = '';
|
||||
$class_map = '';
|
||||
$packet_map = '';
|
||||
$units_src = '';
|
||||
|
||||
$map = array();
|
||||
foreach($units as $unit)
|
||||
{
|
||||
$class_id = $unit->object->getClassId();
|
||||
$class_name = $unit->object->getName();
|
||||
if(isset($map[$class_id]))
|
||||
throw new Exception("Duplicating class id '$class_id'($class_name vs {$map[$class_id]})");
|
||||
$map[$class_id] = $class_name;
|
||||
|
||||
$class_map .= "case " . 1*$class_id . ": return \"$class_name\";\n";
|
||||
|
||||
if($unit->object instanceof mtgMetaEnum || $unit->object instanceof mtgMetaStruct)
|
||||
$units_src .= $codegen->genUnit($unit);
|
||||
}
|
||||
|
||||
foreach($rpcs as $rpc)
|
||||
{
|
||||
$code = $rpc->getCode();
|
||||
$name = $rpc->getReq()->getName();
|
||||
|
||||
$packet_map .= "case " . 1*$code . ": return new $name(\$data);\n";
|
||||
|
||||
$units_src .= $codegen->genRPC($rpc);
|
||||
}
|
||||
|
||||
$templater = new mtg_php_templater();
|
||||
$tpl = $templater->tpl_packet_bundle();
|
||||
return mtg_fill_template($tpl, array('%bundle%' => $bundle,
|
||||
'%units_src%' => $units_src,
|
||||
'%class_map%' => $class_map,
|
||||
'%packet_map%' => $packet_map));
|
||||
}
|
||||
|
|
@ -0,0 +1,336 @@
|
|||
<?php
|
||||
|
||||
class mtg_php_templater
|
||||
{
|
||||
function tpl_struct()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
|
||||
class %class% %parent_class%
|
||||
{
|
||||
const CLASS_ID = %class_id%;
|
||||
|
||||
%fields%
|
||||
|
||||
static function CLASS_PROPS()
|
||||
{
|
||||
static \$props = %class_props%;
|
||||
return \$props;
|
||||
}
|
||||
|
||||
static function CLASS_FIELDS()
|
||||
{
|
||||
static \$flds = null;
|
||||
if(\$flds === null)
|
||||
\$flds = %fields_names%;
|
||||
return \$flds;
|
||||
}
|
||||
|
||||
static function CLASS_FIELDS_TYPES()
|
||||
{
|
||||
static \$flds = null;
|
||||
if(\$flds === null)
|
||||
\$flds = %fields_types%;
|
||||
return \$flds;
|
||||
}
|
||||
|
||||
function CLASS_FIELDS_PROPS()
|
||||
{
|
||||
static \$flds = null;
|
||||
if(\$flds === null)
|
||||
\$flds = %fields_props%;
|
||||
return \$flds;
|
||||
}
|
||||
|
||||
function __construct(&\$message = null, \$assoc = false)
|
||||
{
|
||||
%fields_init%
|
||||
|
||||
if(!is_null(\$message))
|
||||
\$this->import(\$message, \$assoc);
|
||||
}
|
||||
|
||||
function getClassId()
|
||||
{
|
||||
return self::CLASS_ID;
|
||||
}
|
||||
|
||||
%ext_methods%
|
||||
|
||||
function import(&\$message, \$assoc = false, \$root = true)
|
||||
{
|
||||
\$IDX = 0;
|
||||
try
|
||||
{
|
||||
if(!is_array(\$message))
|
||||
throw new Exception("Bad message: \$message");
|
||||
|
||||
try
|
||||
{
|
||||
%fill_fields%
|
||||
}
|
||||
catch(Exception \$e)
|
||||
{
|
||||
\$FIELDS = self::CLASS_FIELDS();
|
||||
throw new Exception("Error while filling field '{\$FIELDS[\$IDX]}': " . \$e->getMessage());
|
||||
}
|
||||
|
||||
if(\$root && \$assoc && sizeof(\$message) > 0)
|
||||
throw new Exception("Junk fields: " . implode(',', array_keys(\$message)));
|
||||
}
|
||||
catch(Exception \$e)
|
||||
{
|
||||
throw new Exception("Error while filling fields of '%class%':" . \$e->getMessage());
|
||||
}
|
||||
return \$IDX;
|
||||
}
|
||||
|
||||
function export(\$assoc = false, \$virtual = false)
|
||||
{
|
||||
\$message = array();
|
||||
\$this->fill(\$message, \$assoc, \$virtual);
|
||||
return \$message;
|
||||
}
|
||||
|
||||
function fill(&\$message, \$assoc = false, \$virtual = false)
|
||||
{
|
||||
if(\$virtual)
|
||||
mtg_php_array_set_value(\$message, \$assoc, 'vclass__', \$this->getClassId());
|
||||
|
||||
try
|
||||
{
|
||||
\$__last_var = null;
|
||||
\$__last_val = null;
|
||||
%fill_buffer%
|
||||
}
|
||||
catch(Exception \$e)
|
||||
{
|
||||
throw new Exception("Error while dumping fields of '%class%'->\$__last_var: ". PHP_EOL . serialize(\$__last_val) . PHP_EOL."\t" . \$e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
function tpl_enum()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
|
||||
class %class%
|
||||
{
|
||||
const CLASS_ID = %class_id%;
|
||||
|
||||
%values%
|
||||
|
||||
const DEFAULT_VALUE = %default_enum_value%;
|
||||
|
||||
function getClassId()
|
||||
{
|
||||
return self::CLASS_ID;
|
||||
}
|
||||
|
||||
static function isValueValid(\$value)
|
||||
{
|
||||
\$values_list = self::getValuesList();
|
||||
return in_array(\$value, self::\$values_list_);
|
||||
}
|
||||
|
||||
static private \$values_map_;
|
||||
static private \$names_map_;
|
||||
|
||||
static function getValueByName(\$name)
|
||||
{
|
||||
if(!self::\$values_map_)
|
||||
{
|
||||
self::\$values_map_ = array(
|
||||
%values_map%
|
||||
);
|
||||
}
|
||||
if(!isset(self::\$values_map_[\$name]))
|
||||
throw new Exception("Value with name \$name isn't defined in enum %class%. Accepted: " . implode(',', self::getNamesList()));
|
||||
return self::\$values_map_[\$name];
|
||||
}
|
||||
|
||||
static function getNameByValue(\$value)
|
||||
{
|
||||
if(!self::\$names_map_)
|
||||
{
|
||||
self::\$names_map_ = array(
|
||||
%names_map%
|
||||
);
|
||||
}
|
||||
if(!isset(self::\$names_map_[\$value]))
|
||||
throw new Exception("Value \$value isn't defined in enum %class%. Accepted: " . implode(',', self::getValuesList()));
|
||||
return self::\$names_map_[\$value];
|
||||
}
|
||||
|
||||
static function checkValidity(\$value)
|
||||
{// throws exception if \$value is not valid numeric enum value
|
||||
if(!is_numeric(\$value))
|
||||
throw new Exception("Numeric expected but got \$value");
|
||||
if(!self::isValueValid(\$value))
|
||||
throw new Exception("Numeric value \$value isn't value from enum %class%. Accepted numerics are " . implode(',', self::getValuesList()) . " but better to use one of names instead: " . implode(',', self::getNamesList()));
|
||||
}
|
||||
|
||||
static private \$values_list_;
|
||||
static function getValuesList()
|
||||
{
|
||||
if(!self::\$values_list_)
|
||||
{
|
||||
self::\$values_list_ = array(
|
||||
%values_list%
|
||||
);
|
||||
}
|
||||
return self::\$values_list_;
|
||||
}
|
||||
|
||||
static private \$names_list_;
|
||||
static function getNamesList()
|
||||
{
|
||||
if(!self::\$names_list_)
|
||||
{
|
||||
self::\$names_list_ = array(
|
||||
%vnames_list%
|
||||
);
|
||||
}
|
||||
return self::\$names_list_;
|
||||
}
|
||||
}
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
function tpl_packet()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
|
||||
#PACKET %type% %code%
|
||||
|
||||
class %class% %parent_class%
|
||||
{
|
||||
const CLASS_ID = %class_id%;
|
||||
|
||||
#public \$__stamp = 0;
|
||||
public \$__seq_id = 0;
|
||||
|
||||
%fields%
|
||||
|
||||
function CLASS_FIELDS()
|
||||
{
|
||||
static \$flds = %fields_names%;
|
||||
return \$flds;
|
||||
}
|
||||
|
||||
function getClassId()
|
||||
{
|
||||
return self::CLASS_ID;
|
||||
}
|
||||
|
||||
function __construct(&\$message = null, \$assoc = false)
|
||||
{
|
||||
%fields_init%
|
||||
|
||||
if(!is_null(\$message))
|
||||
\$this->import(\$message, \$assoc);
|
||||
}
|
||||
|
||||
function getCode()
|
||||
{
|
||||
return %code%;
|
||||
}
|
||||
|
||||
function getType()
|
||||
{
|
||||
return "%type%";
|
||||
}
|
||||
|
||||
function import(&\$message, \$assoc = false, \$root = true)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(!\$message || !is_array(\$message))
|
||||
throw new Exception("Bad message");
|
||||
|
||||
\$IDX = 0;
|
||||
try
|
||||
{
|
||||
%fill_fields%
|
||||
}
|
||||
catch(Exception \$e)
|
||||
{
|
||||
\$FIELDS = self::CLASS_FIELDS();
|
||||
throw new Exception("Error while filling field '{\$FIELDS[\$IDX]}': " . \$e->getMessage());
|
||||
}
|
||||
|
||||
if(\$root && \$assoc && sizeof(\$message) > 0)
|
||||
throw new Exception("Junk fields: " . implode(',', array_keys(\$message)));
|
||||
}
|
||||
catch(Exception \$e)
|
||||
{
|
||||
throw new Exception("Error while filling fields of '%class%':" . \$e->getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
function export(\$assoc = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
\$message = array();
|
||||
|
||||
%fill_buffer%
|
||||
|
||||
return \$message;
|
||||
}
|
||||
catch(Exception \$e)
|
||||
{
|
||||
throw new Exception("Error while dumping fields of '%class%':" . \$e->getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EOD;
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
function tpl_packet_bundle()
|
||||
{
|
||||
$TPL = <<<EOD
|
||||
<?php
|
||||
//THIS FILE IS GENERATED AUTOMATICALLY, DON'T TOUCH IT!
|
||||
%bundle%
|
||||
|
||||
%units_src%
|
||||
|
||||
class AutogenBundle
|
||||
{
|
||||
static function getClassName(\$id)
|
||||
{
|
||||
switch(\$id)
|
||||
{
|
||||
%class_map%
|
||||
default : throw new Exception("Can't find class for id: \$id");
|
||||
}
|
||||
}
|
||||
|
||||
static function createPacket(\$code, \$data)
|
||||
{
|
||||
switch(\$code)
|
||||
{
|
||||
%packet_map%
|
||||
default : throw new Exception("Can't find packet for code: \$code");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
EOD;
|
||||
|
||||
return $TPL;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,192 @@
|
|||
// +build ignore
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
. "./autogen"
|
||||
"bit.games/data"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func checkErr(err error) {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func checkEqual(v, n interface{}) {
|
||||
if !reflect.DeepEqual(v, n) || v != n {
|
||||
panic(errors.New(fmt.Sprintf("%v!=%v", v, n)))
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadPlayer(t *testing.T) {
|
||||
player0 := new(DataPlayer)
|
||||
player0.Id = 10
|
||||
player0.Version = "v1"
|
||||
player0.Gold = 255
|
||||
|
||||
writer := data.NewJsonWriter()
|
||||
err := player0.Write(writer, "", true /* assoc */)
|
||||
bytes, err := writer.GetData()
|
||||
checkErr(err)
|
||||
|
||||
fmt.Printf("%s\n", bytes)
|
||||
|
||||
reader, err := data.NewJsonReader(bytes)
|
||||
checkErr(err)
|
||||
|
||||
player := new(DataPlayer)
|
||||
err = player.Read(reader, "")
|
||||
checkErr(err)
|
||||
|
||||
checkEqual(player.Id, uint32(10))
|
||||
checkEqual(player.Version, "v1")
|
||||
checkEqual(player.Gold, player0.Gold)
|
||||
}
|
||||
|
||||
func TestReadExtends(t *testing.T) {
|
||||
const json_str = `{"id":11,"title":"Chemist's","floors":1000}`
|
||||
reader, err := data.NewJsonReader(json.RawMessage(json_str))
|
||||
checkErr(err)
|
||||
|
||||
building := new(ProtoBuilding)
|
||||
err = building.Read(reader, "")
|
||||
checkErr(err)
|
||||
|
||||
checkEqual(building.Id, uint32(11))
|
||||
checkEqual(building.Title, "Chemist's")
|
||||
checkEqual(building.Floors, int32(1000))
|
||||
}
|
||||
|
||||
func TestReadArray(t *testing.T) {
|
||||
player0 := new(DataPlayer)
|
||||
player0.Id = 1
|
||||
player0.Version = "Mike"
|
||||
player0.Gold = 5
|
||||
writer := data.NewJsonWriter()
|
||||
err := player0.Write(writer, "", false /* assoc */)
|
||||
bytes, err := writer.GetData()
|
||||
checkErr(err)
|
||||
|
||||
if string(bytes) != "[1,\"Mike\",5]" {
|
||||
panic(fmt.Errorf("Wrong data %s", bytes))
|
||||
}
|
||||
|
||||
reader, err := data.NewJsonReader(bytes)
|
||||
checkErr(err)
|
||||
|
||||
player := new(DataPlayer)
|
||||
err = player.Read(reader, "")
|
||||
checkErr(err)
|
||||
|
||||
checkEqual(player.Id, uint32(1))
|
||||
checkEqual(player.Version, "Mike")
|
||||
checkEqual(player.Gold, uint8(5))
|
||||
}
|
||||
|
||||
func TestReadSubObject(t *testing.T) {
|
||||
man0 := new(ProtoMan)
|
||||
man0.Left_hand.Fingers = 4
|
||||
man0.Right_hand.Fingers = 5
|
||||
|
||||
writer := data.NewJsonWriter()
|
||||
err := man0.Write(writer, "", false /* assoc */)
|
||||
bytes, err := writer.GetData()
|
||||
checkErr(err)
|
||||
|
||||
reader, err := data.NewJsonReader(json.RawMessage(bytes))
|
||||
checkErr(err)
|
||||
|
||||
man := new(ProtoMan)
|
||||
err = man.Read(reader, "")
|
||||
checkErr(err)
|
||||
|
||||
checkEqual(man.Left_hand.Fingers, uint32(4))
|
||||
checkEqual(man.Right_hand.Fingers, uint32(5))
|
||||
}
|
||||
|
||||
func TestReadFieldArray(t *testing.T) {
|
||||
tags0 := new(ProtoTags)
|
||||
tags0.Tags = make([]string, 2)
|
||||
tags0.Tags[0] = "tag1"
|
||||
tags0.Tags[1] = "tag2"
|
||||
|
||||
tags0.Children = make([]ProtoBase, 2)
|
||||
tags0.Children[0].Id = 1
|
||||
tags0.Children[1].Title = "Child #2"
|
||||
|
||||
writer := data.NewJsonWriter()
|
||||
err := tags0.Write(writer, "", false /* assoc */)
|
||||
bytes, err := writer.GetData()
|
||||
checkErr(err)
|
||||
|
||||
reader, err := data.NewJsonReader(json.RawMessage(bytes))
|
||||
checkErr(err)
|
||||
|
||||
tags := new(ProtoTags)
|
||||
err = tags.Read(reader, "")
|
||||
checkErr(err)
|
||||
|
||||
checkEqual(len(tags.Tags), 2)
|
||||
checkEqual(tags.Tags[0], "tag1")
|
||||
checkEqual(tags.Tags[1], "tag2")
|
||||
|
||||
checkEqual(len(tags.Children), 2)
|
||||
checkEqual(tags.Children[0].Id, uint32(1))
|
||||
checkEqual(tags.Children[1].Title, "Child #2")
|
||||
}
|
||||
|
||||
func TestReadVirtual(t *testing.T) {
|
||||
rsp0 := new(RPC_RSP_GET_ALL_PROTO)
|
||||
rsp0.List = make([]IProtoBase, 2)
|
||||
rsp0.List[0] = new(ProtoBase)
|
||||
rsp0.List[1] = new(ProtoBuilding)
|
||||
rsp0.List[1].(*ProtoBuilding).Floors = 5
|
||||
|
||||
writer := data.NewJsonWriter()
|
||||
err := rsp0.Write(writer, "", false /* assoc */)
|
||||
bytes, err := writer.GetData()
|
||||
checkErr(err)
|
||||
|
||||
fmt.Printf("%s\n", bytes)
|
||||
|
||||
reader, err := data.NewJsonReader(json.RawMessage(bytes))
|
||||
checkErr(err)
|
||||
|
||||
rsp := new(RPC_RSP_GET_ALL_PROTO)
|
||||
err = rsp.Read(reader, "")
|
||||
checkErr(err)
|
||||
|
||||
checkEqual(rsp.List[0].CLASS_ID(), ProtoBase_CLASS_ID())
|
||||
checkEqual(rsp.List[1].CLASS_ID(), ProtoBuilding_CLASS_ID())
|
||||
building := rsp.List[1].(*ProtoBuilding)
|
||||
checkEqual(building.Floors, int32(5))
|
||||
}
|
||||
|
||||
func TestReadEnum(t *testing.T) {
|
||||
stock0 := new(ConfStock)
|
||||
stock0.Id = EnumStock_XP
|
||||
|
||||
writer := data.NewJsonWriter()
|
||||
err := stock0.Write(writer, "", false /* assoc */)
|
||||
bytes, err := writer.GetData()
|
||||
checkErr(err)
|
||||
|
||||
reader, err := data.NewJsonReader(json.RawMessage(bytes))
|
||||
checkErr(err)
|
||||
|
||||
stock := new(ConfStock)
|
||||
err = stock.Read(reader, "")
|
||||
checkErr(err)
|
||||
|
||||
checkEqual(stock.Id, EnumStock_XP)
|
||||
|
||||
stock_id, err := NewEnumStockByName("GOLD")
|
||||
checkErr(err)
|
||||
checkEqual(stock_id, EnumStock_GOLD)
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
func GetPlayer(
|
||||
id : uint32
|
||||
) : DataPlayer
|
||||
|
||||
func Apply(
|
||||
fn : func(float, DataPlayer) : uint32
|
||||
)
|
||||
|
||||
struct DataPlayer
|
||||
@POD @table:player @pkey:id
|
||||
id : uint32
|
||||
version : string @strmax:16
|
||||
gold : uint8 @default:0 #bonus
|
||||
registered : bool @default:true
|
||||
|
||||
func Equals(
|
||||
@bhl_ret_type:bool,bool
|
||||
o : DataPlayer
|
||||
v : uint32
|
||||
) : bool,bool
|
||||
end
|
||||
|
||||
struct ProtoTags
|
||||
children : ProtoBase[]
|
||||
tags : string[]
|
||||
end
|
||||
|
||||
struct ProtoBase
|
||||
id : uint32
|
||||
title : string @strmax:16 @default:"`'Hello"
|
||||
end
|
||||
|
||||
struct ProtoBuilding extends ProtoBase
|
||||
floors : int32
|
||||
arr : uint32[] @default:[1,2,3]
|
||||
end
|
||||
|
||||
struct ProtoMan
|
||||
left_hand : ConfHand
|
||||
right_hands : ConfHand[] @default:[{"fingers": 1, "skill": 100}]
|
||||
end
|
||||
|
||||
struct ConfHand
|
||||
fingers : uint32
|
||||
fingers2 : uint32 @default:77
|
||||
skill : uint32
|
||||
end
|
||||
|
||||
RPC 10 GET_ALL_PROTO(
|
||||
ticket : string @strmax:128
|
||||
)
|
||||
list : ProtoBase[] @virtual
|
||||
end
|
||||
|
||||
enum EnumStock
|
||||
GOLD = 108683766 # @stock/gold
|
||||
XP = 90110385 # @stock/xp
|
||||
end
|
||||
|
||||
struct ConfStock
|
||||
id : EnumStock
|
||||
end
|
|
@ -0,0 +1,116 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
require_once(__DIR__ . '/../metagen.inc.php');
|
||||
require_once(__DIR__ . '/../targets/cs/cs_generator.inc.php');
|
||||
require_once(__DIR__ . '/../targets/go/go_generator.inc.php');
|
||||
require_once(__DIR__ . '/../targets/php/php_generator.inc.php');
|
||||
|
||||
assert_options(ASSERT_ACTIVE, true);
|
||||
assert_options(ASSERT_BAIL, true);
|
||||
assert_options(ASSERT_WARNING, true);
|
||||
|
||||
touch(__DIR__ . '/meta/DataAll.meta');
|
||||
$meta = mtg_parse_meta(array(__DIR__ . '/meta/'));
|
||||
|
||||
$u = $meta->findUnit("DataPlayer");
|
||||
assert($u->object instanceof mtgMetaStruct);
|
||||
assert($u->object->getName() == "DataPlayer");
|
||||
assert($u->object->getTokens() == array('POD' => null, 'table' => 'player', 'pkey' => 'id'));
|
||||
assert(sizeof($u->object->getFields()) == 4);
|
||||
assert($u->object->getFields()["id"]->getType() == "uint32");
|
||||
assert($u->object->getFields()["version"]->getType() == "string");
|
||||
assert($u->object->getFields()["version"]->getTokens() == array('strmax' => 16));
|
||||
assert($u->object->getFields()["gold"]->getType() == "uint8");
|
||||
assert($u->object->getFields()["gold"]->getTokens() == array('default' => 0));
|
||||
assert($u->object->getFields()["registered"]->getType() == "bool");
|
||||
assert($u->object->getFields()["registered"]->getTokens() == array('default' => 'true'));
|
||||
assert(sizeof($u->object->getFuncs()) == 1);
|
||||
assert($u->object->getFuncs()["Equals"]->getTokens() == array('bhl_ret_type' => "bool,bool"));
|
||||
assert(sizeof($u->object->getFuncs()["Equals"]->getArgs()) == 2);
|
||||
assert($u->object->getFuncs()["Equals"]->getArgs()["o"]->getType() == "DataPlayer");
|
||||
assert($u->object->getFuncs()["Equals"]->getArgs()["v"]->getType() == "uint32");
|
||||
assert($u->object->getFuncs()["Equals"]->getReturnType() instanceof mtgMultiType);
|
||||
assert($u->object->getFuncs()["Equals"]->getReturnType()->getValues()[0] == "bool");
|
||||
assert($u->object->getFuncs()["Equals"]->getReturnType()->getValues()[1] == "bool");
|
||||
|
||||
$u = $meta->findUnit("GetPlayer");
|
||||
assert($u->object instanceof mtgMetaFunc);
|
||||
assert($u->object->getName() == "GetPlayer");
|
||||
assert($u->object->getReturnType() == "DataPlayer");
|
||||
assert(sizeof($u->object->getArgs()) == 1);
|
||||
assert($u->object->getArgs()["id"]->getName() == "id");
|
||||
assert($u->object->getArgs()["id"]->getType() == "uint32");
|
||||
|
||||
$u = $meta->findUnit("Apply");
|
||||
assert($u->object instanceof mtgMetaFunc);
|
||||
assert($u->object->getName() == "Apply");
|
||||
assert($u->object->getReturnType() === null);
|
||||
assert($u->object->getArgs()["fn"]->getName() == "fn");
|
||||
assert($u->object->getArgs()["fn"]->getType() == "func (float,DataPlayer):uint32");
|
||||
|
||||
$u = $meta->findUnit("ProtoBase");
|
||||
assert($u->object instanceof mtgMetaStruct);
|
||||
assert(sizeof($u->object->getFields()) == 2);
|
||||
assert($u->object->getFields()["id"]->getType() == "uint32");
|
||||
assert($u->object->getFields()["title"]->getType() == "string");
|
||||
assert($u->object->getFields()["title"]->getTokens() == array('strmax' => 16, 'default' => "\"`'Hello\""));
|
||||
|
||||
$u = $meta->findUnit("ProtoBuilding");
|
||||
assert($u->object instanceof mtgMetaStruct);
|
||||
assert($u->object->getParent() == "ProtoBase");
|
||||
|
||||
$u = $meta->findUnit("ProtoTags");
|
||||
assert($u->object instanceof mtgMetaStruct);
|
||||
assert(sizeof($u->object->getFields()) == 2);
|
||||
assert($u->object->getFields()["children"]->getType() instanceof mtgArrType);
|
||||
assert($u->object->getFields()["children"]->getType() == "ProtoBase[]");
|
||||
assert($u->object->getFields()["tags"]->getType() == "string[]");
|
||||
|
||||
//NOTE: RPCs are identified by ids
|
||||
$u = $meta->findUnit("10");
|
||||
assert($u->object instanceof mtgMetaRPC);
|
||||
assert($u->object->getName() == "RPC_GET_ALL_PROTO");
|
||||
assert($u->object->getNumericCode() == 10);
|
||||
assert(sizeof($u->object->getReq()->getFields()) == 1);
|
||||
assert($u->object->getReq()->getFields()["ticket"]->getType() == "string");
|
||||
assert(sizeof($u->object->getRsp()->getFields()) == 1);
|
||||
assert($u->object->getRsp()->getFields()["list"]->getType() == "ProtoBase[]");
|
||||
|
||||
$u = $meta->findUnit("EnumStock");
|
||||
assert($u->object instanceof mtgMetaEnum);
|
||||
assert($u->object->getValues() == array('GOLD' => 108683766, 'XP' => 90110385));
|
||||
|
||||
$u = $meta->findUnit("ConfStock");
|
||||
assert($u->object instanceof mtgMetaStruct);
|
||||
assert(sizeof($u->object->getFields()) == 1);
|
||||
assert($u->object->getFields()["id"]->getType() == "EnumStock");
|
||||
|
||||
mtg_run(new mtgCsGenerator(), array(
|
||||
"meta" => $meta,
|
||||
"out-dir" => __DIR__ . "/autogen",
|
||||
"bundle" => __DIR__ . "/autogen/bundle.cs"
|
||||
));
|
||||
|
||||
mtg_run(new mtgGoGenerator(), array(
|
||||
"meta" => $meta,
|
||||
"out-dir" => __DIR__ . "/autogen",
|
||||
"bundle" => __DIR__ . "/autogen/bundle.go"
|
||||
));
|
||||
|
||||
mtg_run(new mtgPHPGenerator(), array(
|
||||
"meta" => $meta,
|
||||
"out-dir" => __DIR__ . "/autogen",
|
||||
"inc-dir" => __DIR__ . "/autogen",
|
||||
"bundle" => __DIR__ . "/autogen/bundle.inc.php"
|
||||
));
|
||||
|
||||
//TODO: было бы хорошо реализовать
|
||||
//require_once(__DIR__ . '/../../gme/src/utils.inc.php');
|
||||
//require_once(__DIR__ . '/autogen/bundle.inc.php');
|
||||
//
|
||||
//$man = new ProtoMan();
|
||||
//assert(count($man->right_hands) == 1);
|
||||
//assert($man->right_hands[0]->skill == 100);
|
||||
//assert($man->right_hands[0]->fingers == 1);
|
||||
//assert($man->right_hands[0]->fingers2 == 77);
|
Loading…
Reference in New Issue