metagen_cs/src/codegen.inc.php

458 lines
10 KiB
PHP
Raw Normal View History

2022-12-06 19:16:13 +03:00
<?php
namespace metagen_cs;
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;
}
2022-12-07 13:40:33 +03:00
function supported_tokens()
{
return [
'POD',
'default',
'optional',
2022-12-07 13:42:25 +03:00
'bitfields',
2022-12-07 13:40:33 +03:00
'cloneable',
'virtual',
'id',
'owner',
'pkey',
'bhl_bind'
2022-12-07 13:40:33 +03:00
//TODO:
//'obscured',
//'bitfields',
//'diffable',
];
}
2022-12-06 19:16:13 +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;
}
));
$twig->addFilter(new \Twig\TwigFilter('cs_type',
function($type)
{
return cs_type($type);
}
));
$twig->addFilter(new \Twig\TwigFilter('cs_type_prefix',
function($type)
{
return cs_type_prefix($type);
}
));
$twig->addFunction(new \Twig\TwigFunction('var_reset',
function($name, $type, $default)
{
return var_reset($name, $type, $default);
}
));
2022-12-07 11:49:34 +03:00
$twig->addFunction(new \Twig\TwigFunction('var_sync',
function($name, $type, $buf, $tokens, $opts)
{
return var_sync($name, $type, $buf, $tokens, $opts);
}
));
$twig->addFunction(new \Twig\TwigFunction('get_sync_opts',
function($o, $name)
{
return get_sync_opts($o, $name);
}
));
$twig->addFunction(new \Twig\TwigFunction('fields_count',
function($o)
{
return fields_count($o);
}
));
$twig->addFunction(new \Twig\TwigFunction('is_primary_field',
function($o, $f)
{
return is_primary_field($o, $f);
}
));
$twig->addFunction(new \Twig\TwigFunction('get_diff_related_units',
function($o)
{
return get_diff_related_units($o);
}
));
2022-12-06 19:16:13 +03:00
}
function cs_type(\mtgType $type)
{
if($type instanceof \mtgBuiltinType || $type instanceof \mtgUserType)
return cs_simple_type($type);
else if($type instanceof \mtgArrType)
return "List<" . cs_simple_type($type->getValue()) . ">";
else
throw new Exception("Unknown type '{$type}'");
}
function cs_simple_type(\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 cs_type_prefix(\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 var_reset($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 = cs_type($type->getValue());
$str .= "{ $type_str tmp__";
if(is_nullable_type($type->getValue()))
$str .= " = null";
$str .= ";";
foreach($default_arr as $v)
{
$str .= var_reset("tmp__", $type->getValue(), $v);
$str .= "$name.Add(tmp__);";
}
$str .= "}";
}
}
}
else if($type instanceof \mtgMetaEnum)
{
if($default)
$str = "$name = ".cs_type($type).".".trim($default,'"')."; ";
else
$str = "$name = new ".cs_type($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 .= var_reset("$name." . $kf->name, $kf->getType(), $v);
}
}
}
else
throw new Exception("Bad type '$type'");
return $str;
}
2022-12-07 11:49:34 +03:00
function var_sync($fname, \mtgType $type, $buf, array $tokens, $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 get_sync_opts(\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 fields_count(\mtgMetaStruct $struct)
{
//NOTE: why not using fields_count(..) here as well?
// (becase they include i18n fields already?)
$count = count($struct->getFields());
$curr = $struct->getParent();
while($curr)
{
$count += fields_count_self($curr);
$curr = $curr->getParent();
}
return $count;
}
function fields_count_self(\mtgMetaStruct $struct)
{
$fields = $struct->getFields();
$count = 0;
foreach($fields as $field)
{
//TODO: do we still need this
if($field->hasToken('i18n'))
$count += 2;
else
$count += 1;
}
return $count;
}
function is_primary_field(\mtgMetaStruct $struct, $fld)
{
$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;
}
return in_array($fld->getName(), $primary_fields);
}
2022-12-06 19:16:13 +03:00
function is_nullable_type(\mtgType $type)
{
return $type instanceof \mtgArrType ||
($type instanceof \mtgMetaStruct && !$type->hasToken('POD'));
}
function get_diff_units(\mtgMetaStruct $struct)
{
$result = array();
foreach($struct->getFields() as $field)
{
if($field->hasToken('nodiff'))
continue;
if($field->getType() instanceof mtgMetaStruct)
$result[] = $field->getType();
else if($field->getType() instanceof mtgArrType)
$result[] = $field->getType()->getValue();
}
return $result;
}