From 7e8720b387119f881f24a73b3fcad63bc95bc903 Mon Sep 17 00:00:00 2001 From: Pavel Shevaev Date: Tue, 6 Dec 2022 19:16:13 +0300 Subject: [PATCH] First commit --- composer.json | 15 +++ src/codegen.inc.php | 286 ++++++++++++++++++++++++++++++++++++++++ tpl/codegen_bundle.twig | 54 ++++++++ tpl/macro.twig | 189 ++++++++++++++++++++++++++ 4 files changed, 544 insertions(+) create mode 100644 composer.json create mode 100644 src/codegen.inc.php create mode 100644 tpl/codegen_bundle.twig create mode 100644 tpl/macro.twig diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..a92b93f --- /dev/null +++ b/composer.json @@ -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" + ] + } +} diff --git a/src/codegen.inc.php b/src/codegen.inc.php new file mode 100644 index 0000000..c700130 --- /dev/null +++ b/src/codegen.inc.php @@ -0,0 +1,286 @@ + 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')); +} + diff --git a/tpl/codegen_bundle.twig b/tpl/codegen_bundle.twig new file mode 100644 index 0000000..55ef4b2 --- /dev/null +++ b/tpl/codegen_bundle.twig @@ -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 %} diff --git a/tpl/macro.twig b/tpl/macro.twig new file mode 100644 index 0000000..b9026a0 --- /dev/null +++ b/tpl/macro.twig @@ -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 %} +