2022-12-08 11:40:55 +03:00
|
|
|
<?php
|
|
|
|
namespace metagen_go;
|
|
|
|
use Exception;
|
|
|
|
|
|
|
|
function get_twig(array $inc_path = [])
|
|
|
|
{
|
|
|
|
array_unshift($inc_path, __DIR__ . "/../tpl/");
|
|
|
|
$loader = new \Twig\Loader\FilesystemLoader($inc_path);
|
|
|
|
|
|
|
|
$twig = new \Twig\Environment($loader, [
|
|
|
|
'debug' => true,
|
|
|
|
'autoescape' => false,
|
|
|
|
'strict_variables' => true]
|
|
|
|
);
|
|
|
|
$twig->addExtension(new \Twig\Extension\DebugExtension());
|
|
|
|
|
|
|
|
_add_twig_support($twig);
|
|
|
|
|
|
|
|
return $twig;
|
|
|
|
}
|
|
|
|
|
|
|
|
function supported_tokens()
|
|
|
|
{
|
|
|
|
return [
|
|
|
|
'POD',
|
|
|
|
'default',
|
|
|
|
'optional',
|
|
|
|
'bitfields',
|
|
|
|
'cloneable',
|
|
|
|
'virtual',
|
|
|
|
|
|
|
|
'table',
|
|
|
|
'id',
|
|
|
|
'owner',
|
|
|
|
'pkey',
|
|
|
|
|
|
|
|
'statist',
|
|
|
|
'statist_skip',
|
2022-12-08 16:51:08 +03:00
|
|
|
'statist_alias',
|
2022-12-08 11:40:55 +03:00
|
|
|
];
|
|
|
|
}
|
|
|
|
|
|
|
|
function _add_twig_support(\Twig\Environment $twig)
|
|
|
|
{
|
|
|
|
$twig->addTest(new \Twig\TwigTest('instanceof',
|
|
|
|
function($obj, $class)
|
|
|
|
{
|
|
|
|
return (new \ReflectionClass($class))->isInstance($obj);
|
|
|
|
}
|
|
|
|
));
|
|
|
|
$twig->addFunction(new \Twig\TwigFunction('Error',
|
|
|
|
function($e)
|
|
|
|
{
|
|
|
|
throw new Exception($e);
|
|
|
|
}
|
|
|
|
));
|
|
|
|
$twig->addFunction(new \Twig\TwigFunction('has_token',
|
|
|
|
function($o, $token)
|
|
|
|
{
|
|
|
|
return $o->hasToken($token);
|
|
|
|
}
|
|
|
|
));
|
|
|
|
$twig->addFunction(new \Twig\TwigFunction('has_token_in_parent',
|
|
|
|
function($o, $token)
|
|
|
|
{
|
|
|
|
return $o->hasTokenInParent($token);
|
|
|
|
}
|
|
|
|
));
|
|
|
|
$twig->addFunction(new \Twig\TwigFunction('token',
|
|
|
|
function($o, $name)
|
|
|
|
{
|
|
|
|
return $o->getToken($name);
|
|
|
|
}
|
|
|
|
));
|
|
|
|
$twig->addFunction(new \Twig\TwigFunction('token_or',
|
|
|
|
function($o, $name, $v)
|
|
|
|
{
|
|
|
|
if($o->hasToken($name))
|
|
|
|
return $o->getToken($name);
|
|
|
|
else
|
|
|
|
return $v;
|
|
|
|
}
|
|
|
|
));
|
2022-12-08 14:23:55 +03:00
|
|
|
$twig->addFunction(new \Twig\TwigFunction('get_all_fields',
|
2022-12-08 19:30:54 +03:00
|
|
|
function($o)
|
2022-12-08 14:23:55 +03:00
|
|
|
{
|
2022-12-08 19:21:43 +03:00
|
|
|
return \mtg_get_all_fields($o);
|
2022-12-08 14:23:55 +03:00
|
|
|
}
|
|
|
|
));
|
2022-12-08 14:50:32 +03:00
|
|
|
$twig->addFunction(new \Twig\TwigFunction('count_optional',
|
|
|
|
function($units)
|
|
|
|
{
|
|
|
|
$opts = 0;
|
|
|
|
foreach($units as $u)
|
|
|
|
if($u->hasToken('optional'))
|
|
|
|
++$opts;
|
|
|
|
return $opts;
|
|
|
|
}
|
|
|
|
));
|
2022-12-08 16:21:04 +03:00
|
|
|
$twig->addFunction(new \Twig\TwigFunction('field_reset',
|
|
|
|
function($name, $type, $tokens)
|
|
|
|
{
|
|
|
|
return field_reset($name, $type, $tokens);
|
|
|
|
}
|
|
|
|
));
|
2022-12-08 15:21:54 +03:00
|
|
|
$twig->addFunction(new \Twig\TwigFunction('buf2var',
|
|
|
|
function($name, $fname, $type, $buf, $tokens, $is_ptr)
|
|
|
|
{
|
|
|
|
return buf2var($name, $fname, $type, $buf, $tokens, $is_ptr);
|
|
|
|
}
|
|
|
|
));
|
2022-12-08 16:05:26 +03:00
|
|
|
$twig->addFunction(new \Twig\TwigFunction('var2buf',
|
|
|
|
function($name, $fname, $type, $buf, $tokens, $is_ptr)
|
|
|
|
{
|
|
|
|
return var2buf($name, $fname, $type, $buf, $tokens, $is_ptr);
|
|
|
|
}
|
|
|
|
));
|
2022-12-08 17:45:28 +03:00
|
|
|
$twig->addFilter(new \Twig\TwigFilter('rpc_invert',
|
|
|
|
function($name)
|
|
|
|
{
|
|
|
|
return rpc_invert($name);
|
|
|
|
}
|
|
|
|
));
|
2022-12-08 13:29:16 +03:00
|
|
|
$twig->addFilter(new \Twig\TwigFilter('go_type',
|
|
|
|
function($type, $tokens)
|
|
|
|
{
|
|
|
|
return go_type($type, $tokens);
|
|
|
|
}
|
|
|
|
));
|
|
|
|
$twig->addFilter(new \Twig\TwigFilter('ucfirst',
|
|
|
|
function($str)
|
|
|
|
{
|
|
|
|
return ucfirst($str);
|
|
|
|
}
|
|
|
|
));
|
|
|
|
}
|
|
|
|
|
|
|
|
function go_type(\mtgType $type, array $tokens = array())
|
|
|
|
{
|
|
|
|
if($type instanceof \mtgArrType)
|
|
|
|
{
|
|
|
|
$vtype = $type->getValue();
|
|
|
|
|
|
|
|
$native = go_type($vtype);
|
|
|
|
|
|
|
|
$str = "[]";
|
|
|
|
if(array_key_exists("virtual", $tokens))
|
|
|
|
$str .= "I";
|
|
|
|
else
|
2022-12-08 15:21:54 +03:00
|
|
|
$str .= $vtype instanceof \mtgMetaStruct ? "*" : "";
|
2022-12-08 13:29:16 +03:00
|
|
|
|
|
|
|
$str .= $native;
|
|
|
|
|
|
|
|
return $str;
|
|
|
|
}
|
|
|
|
else 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'");
|
2022-12-08 11:40:55 +03:00
|
|
|
}
|
2022-12-08 13:29:16 +03:00
|
|
|
|
2022-12-08 15:21:54 +03:00
|
|
|
function builtin_type_prefix(\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}'");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-08 16:21:04 +03:00
|
|
|
function field_reset($name, \mtgType $type, array $tokens)
|
|
|
|
{
|
|
|
|
$str = '';
|
|
|
|
|
|
|
|
if($type instanceof \mtgBuiltinType)
|
|
|
|
{
|
|
|
|
if($type->isNumeric())
|
|
|
|
{
|
|
|
|
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)
|
|
|
|
{
|
2022-12-08 19:30:54 +03:00
|
|
|
$str = "if self.$name == nil { \nself.$name = make(".go_type($type, $tokens).",0) \n}\n ";
|
2022-12-08 16:21:04 +03:00
|
|
|
$str .= "self.$name = self.{$name}[0:0]";
|
|
|
|
}
|
|
|
|
else if($type instanceof \mtgMetaEnum)
|
|
|
|
{
|
|
|
|
if(array_key_exists('default', $tokens))
|
2022-12-08 19:30:54 +03:00
|
|
|
$str = "self.$name = ".go_type($type, $tokens)."_".trim($tokens['default'], '"');
|
2022-12-08 16:21:04 +03:00
|
|
|
else
|
|
|
|
$str = "self.$name = 0";
|
|
|
|
}
|
|
|
|
else if($type instanceof \mtgMetaStruct)
|
|
|
|
{
|
|
|
|
$is_virtual = array_key_exists("virtual", $tokens);
|
|
|
|
if($is_virtual)
|
2022-12-08 19:30:54 +03:00
|
|
|
$str .= "self.$name = New".ltrim(go_type($type, $tokens),'I')."() ";
|
2022-12-08 16:21:04 +03:00
|
|
|
else
|
|
|
|
$str .= "self.$name.Reset()";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw new Exception("Unknown type '$type'");
|
|
|
|
return $str;
|
|
|
|
}
|
|
|
|
|
2022-12-08 15:21:54 +03:00
|
|
|
function buf2var($name, $fname, \mtgType $type, $buf, array $tokens = array(), $is_ptr = false)
|
|
|
|
{
|
|
|
|
$str = '';
|
|
|
|
|
|
|
|
if($type instanceof \mtgBuiltinType)
|
|
|
|
{
|
|
|
|
$str .= _read_op($tokens, $buf.'.Read'.builtin_type_prefix($type).'(&'.$fname.', "'.$name.'")');
|
|
|
|
}
|
|
|
|
else if($type instanceof \mtgMetaEnum)
|
|
|
|
{
|
|
|
|
$str .= _read_op($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 .= _read_op($tokens, "meta.ReadStruct($buf, ".($is_ptr?"":"&")."$fname, \"$name\")");
|
|
|
|
}
|
|
|
|
else if($type instanceof \mtgArrType)
|
|
|
|
{
|
|
|
|
$is_virtual = array_key_exists("virtual", $tokens);
|
|
|
|
$native_type = go_type($type->getValue(), $tokens);
|
|
|
|
$offset = "\n ";
|
|
|
|
$str .= "/*[]{$name}*/";
|
|
|
|
$str .= $offset . _read_op($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 . buf2var("", "tmp_{$name}", $type->getValue(), $buf, $tokens, $is_virtual);
|
|
|
|
$str .= $offset . "{$fname} = append({$fname}, ".($need_new?"&":"")."tmp_{$name})";
|
|
|
|
|
|
|
|
$offset = "\n ";
|
|
|
|
$str .= $offset . "}";
|
|
|
|
$str .= $offset . _read_op($tokens, "{$buf}.EndContainer()");
|
|
|
|
$str .= "\n";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw new Exception("Unknown type '{$type}'");
|
|
|
|
|
|
|
|
return $str;
|
|
|
|
}
|
|
|
|
|
2022-12-08 16:05:26 +03:00
|
|
|
function var2buf($name, $fname, \mtgType $type, $buf, array $tokens = array(), $is_ptr = false)
|
|
|
|
{
|
|
|
|
$str = '';
|
|
|
|
|
|
|
|
if($type instanceof \mtgBuiltinType)
|
|
|
|
{
|
|
|
|
$str .= _write_op("{$buf}.Write".builtin_type_prefix($type)."($fname, \"$name\")")."\n";
|
|
|
|
}
|
|
|
|
else if($type instanceof \mtgMetaEnum)
|
|
|
|
{
|
|
|
|
$str .= _write_op("{$buf}.WriteI32(int32($fname), \"$name\")");
|
|
|
|
}
|
|
|
|
else if($type instanceof \mtgMetaStruct)
|
|
|
|
{
|
|
|
|
if(array_key_exists('virtual', $tokens))
|
|
|
|
$str .= _write_op("meta.WriteStructGeneric($buf, ".($is_ptr?"":"&")."$fname, \"$name\")");
|
|
|
|
else
|
|
|
|
$str .= _write_op("meta.WriteStruct($buf, ".($is_ptr?"":"&")."$fname, \"$name\")");
|
|
|
|
}
|
|
|
|
else if($type instanceof \mtgArrType)
|
|
|
|
{
|
|
|
|
$str .= "{$buf}.BeginContainer(\"{$name}\")\n";
|
|
|
|
$str .= " for _, v := range({$fname}) {\n";
|
|
|
|
$str .= " ".var2buf("", "v", $type->getValue(), $buf, $tokens, true)."\n";
|
|
|
|
$str .= " }\n";
|
|
|
|
$str .= " "._write_op("{$buf}.EndContainer()")."\n";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
throw new Exception("Unknown type '$type'");
|
|
|
|
|
|
|
|
return $str;
|
|
|
|
}
|
|
|
|
|
|
|
|
function _write_op($op)
|
|
|
|
{
|
|
|
|
return "if err := $op; err != nil { return err }";
|
|
|
|
}
|
|
|
|
|
2022-12-08 15:21:54 +03:00
|
|
|
function _read_op(array $tokens, $op)
|
|
|
|
{
|
|
|
|
return "if err := $op; err != nil { return " . (array_key_exists("optional", $tokens) ? "/*optional*/nil" : "err"). " }";
|
|
|
|
}
|
2022-12-08 17:45:28 +03:00
|
|
|
|
|
|
|
function rpc_invert($name)
|
|
|
|
{
|
|
|
|
if(strpos($name, '_RSP_') !== false)
|
|
|
|
return str_replace('_RSP_', '_REQ_', $name);
|
|
|
|
else
|
|
|
|
return str_replace('_REQ_', '_RSP_', $name);
|
|
|
|
}
|
|
|
|
|