First commit

This commit is contained in:
Pavel Shevaev 2022-12-06 19:16:13 +03:00
commit 7e8720b387
4 changed files with 544 additions and 0 deletions

15
composer.json Normal file
View File

@ -0,0 +1,15 @@
{
"name": "bit/metagen_cs",
"version" : "v0.0.1",
"description": "C# codgen support from meta descriptions",
"homepage": "https://git.bit5.ru/bit/metagen_cs",
"require": {
"php": ">=7.4",
"twig/twig" : "v3.4.3"
},
"autoload": {
"files": [
"src/codegen.inc.php"
]
}
}

286
src/codegen.inc.php Normal file
View File

@ -0,0 +1,286 @@
<?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;
}
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);
}
));
}
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;
}
function is_nullable_type(\mtgType $type)
{
return $type instanceof \mtgArrType ||
($type instanceof \mtgMetaStruct && !$type->hasToken('POD'));
}

54
tpl/codegen_bundle.twig Normal file
View File

@ -0,0 +1,54 @@
//THIS FILE IS GENERATED AUTOMATICALLY, DO NOT TOUCH IT!
using System.Collections.Generic;
using metagen;
{%- import "macro.twig" as macro -%}
{% if namespace is defined %}
namespace {{namespace}} {
{% endif %}
{{ macro.decl_units(meta) }}
static 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)
{
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;
}
}
}
}
{% if namespace is defined %}
} //namespace {{namespace}}
{% endif %}

189
tpl/macro.twig Normal file
View File

@ -0,0 +1,189 @@
{% macro decl_units(meta) %}
{%- for u in meta.getunits ~%}
{%- if u.object is instanceof('\\mtgMetaStruct') -%}
{{ _self.decl_struct(u.object) }}
{%- elseif u.object is instanceof('\\mtgMetaEnum') -%}
{{ _self.decl_enum(u.object) }}
{%- endif ~%}
{%- endfor ~%}
{% endmacro %}
{% macro decl_struct(o, extra = '') %}
{%- if o.parent and has_token(o, 'bitfields') -%}
{{Error("@bitfields structs can't have a parent: " ~ o.name)}}
{%- endif -%}
{{_self.attributes(o)}}
public {{_self.struct_type(o)}} {{o.name}} {{_self.base_class(o)}}
{
{{_self.decl_struct_fields(o)}}
public {{o.parent ? 'new'}} const uint STATIC_CLASS_ID = {{o.classid}};
public {{_self.override(o)}} uint CLASS_ID()
{
return {{o.classid}};
}
{{_self.comment_POD_begin(o)}}
public {{o.name}}()
{
reset();
}
{{_self.comment_POD_end(o)}}
public {{_self.override(o)}} void reset()
{
{{_self.struct_fields_reset(o)}}
}
{{_self.comment_non_cloneable_begin(o)}}
public {{_self.virtual_clone(o)}} void copy(IMetaStruct other)
{
copyFrom(({{o.name}})other);
}
public void copyFrom({{o.name}} other)
{
var ctx = Meta.PrepareForClone(ref other);
ctx.factory = AutogenBundle.createById;
ctx.reader.BeginContainer();
reset();
syncFields(ctx);
ctx.reader.EndContainer();
{{_self.copy_fields(o)}}
}
public {{_self.virtual_clone(o)}} IMetaStruct clone()
{
var copy = new {{o.name}}();
copy.copy(this);
return copy;
}
{{_self.comment_non_cloneable_end(o)}}
public {{_self.override(o)}} void syncFields(MetaSyncContext ctx)
{
%bitfields_sync_context%
%sync_buffer%
}
public {{_self.override(o)}} int getFieldsCount()
{
return %fields_count%;
}
public {{_self.override(o)}} int getWritableFieldsCount()
{
return %dirty_fields_count%;
}
{{extra}}
}
{% endmacro %}
{%- macro decl_struct_fields(o) -%}
{%- if has_token(o, 'bitfields') ~%}
public long fields_mask;
{%- endif -%}
{%- for f in o.fields ~%}
{{_self.decl_struct_field(o, f)}}
{%- endfor -%}
{%- endmacro -%}
{%- macro decl_struct_field(o, f) -%}
{{_self.attributes(f)}}
public {{f.type|cs_type}} {{f.name}} {% if not has_token(f, 'POD') -%} {{_self.decl_init_value(f.type)}} {%- endif -%};
{%- endmacro -%}
{% macro decl_init_value(type) %}
{%- if type is instanceof('\\mtgBuiltinType') -%}
{%- if type.isstring -%} = ""{%- endif -%}
{%- else -%}
= new {{type|cs_type}}()
{%- endif -%}
{% endmacro %}
{% macro struct_fields_reset(o) %}
{%- if o.parent ~%}
base.reset();
{%- endif -%}
{%- for f in o.fields ~%}
{{var_reset(f.name, f.type, token_or(f, 'default', null))}}
{%- endfor -%}
{%- if has_token(o, 'bitfields') ~%}
fields_mask = 0L;
{%- endif -%}
{% endmacro %}
{%- macro attributes(o) %}
{%- if has_token(o, 'cs_attributes') -%}
{%- for attr in token(o, 'cs_attributes')|split(',') -%}
[{{attr}}]
{%- endfor -%}
{%- endif -%}
{%- endmacro %}
{%- macro struct_type(o) -%}
{{has_token(o, 'POD') ? 'struct' : 'class'}}
{%- endmacro -%}
{%- macro base_class(o) -%}
{%- if not has_token(o, 'POD') -%}
: {{o.parent ? o.parent.name : 'BaseMetaStruct'}} {{has_token(o, 'cloneable')?',IMetaCloneable'}}
{%- endif -%}
{%- endmacro -%}
{%- macro override(o) -%}
{%- if not has_token(o, 'POD') -%}
override
{%- endif -%}
{%- endmacro -%}
{%- macro comment_POD_begin(o) -%}
{%- if has_token(o, 'POD') -%}
/*
{%- endif -%}
{%- endmacro -%}
{%- macro comment_POD_end(o) -%}
{%- if has_token(o, 'POD') -%}
*/
{%- endif -%}
{%- endmacro -%}
{%- macro comment_non_cloneable_begin(o) -%}
{%- if not has_token(o, 'cloneable') -%}
/*
{%- endif -%}
{%- endmacro -%}
{%- macro comment_non_cloneable_end(o) -%}
{%- if not has_token(o, 'cloneable') -%}
*/
{%- endif -%}
{%- endmacro -%}
{%- macro virtual_clone(o) -%}
{%- if not has_token(o, 'POD') -%}
{%- if o.parent and has_token_in_parent(o.parent, 'cloneable') -%}
override
{%- else -%}
virtual
{%- endif -%}
{%- endif -%}
{%- endmacro -%}
{%- macro copy_fields(o) -%}
{%- if has_token(o, 'POD') ~%}
fields_mask = other.fields_mask;
{%- endif -%}
{%- endmacro -%}
{% macro decl_enum(o) %}
{% endmacro %}