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', 'id', 'owner', 'pkey', 'bhl_bind' //TODO: //'obscured', //'bitfields', //'diffable', ]; } 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); } )); $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); } )); } 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 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); } function is_nullable_type(\mtgType $type) { return $type instanceof \mtgArrType || ($type instanceof \mtgMetaStruct && !$type->hasToken('POD')); }