first commit

This commit is contained in:
Pavel Shevaev 2022-05-16 14:20:20 +03:00
commit e10147eed3
15 changed files with 7144 additions and 0 deletions

0
README.md Normal file
View File

2002
metagen.inc.php Normal file

File diff suppressed because it is too large Load Diff

702
targets/cs/cs.inc.php Normal file
View File

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

View File

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

216
targets/cs/cs_tpl.inc.php Normal file
View File

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

1608
targets/cs/metagen.cs Normal file

File diff suppressed because it is too large Load Diff

628
targets/go/go.inc.php Normal file
View File

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

View File

@ -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,
));
}

387
targets/go/go_tpl.inc.php Normal file
View File

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

620
targets/php/php.inc.php Normal file
View File

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

View File

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

336
targets/php/php_tpl.inc.php Normal file
View File

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

192
tests/autogen_test.go Normal file
View File

@ -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)
}

62
tests/meta/DataAll.meta Normal file
View File

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

116
tests/run.php Executable file
View File

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