First commit
This commit is contained in:
commit
7e8720b387
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -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'));
|
||||
}
|
||||
|
|
@ -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 %}
|
|
@ -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 %}
|
||||
|
Loading…
Reference in New Issue