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', 'table', 'id', 'owner', 'pkey', 'statist', 'statist_skip', 'statist_alias', ]; } 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->addFunction(new \Twig\TwigFunction('get_all_fields', function($meta, $o) { return \mtg_get_all_fields($o); } )); $twig->addFunction(new \Twig\TwigFunction('count_optional', function($units) { $opts = 0; foreach($units as $u) if($u->hasToken('optional')) ++$opts; return $opts; } )); $twig->addFunction(new \Twig\TwigFunction('field_reset', function($name, $type, $tokens) { return field_reset($name, $type, $tokens); } )); $twig->addFunction(new \Twig\TwigFunction('buf2var', function($name, $fname, $type, $buf, $tokens, $is_ptr) { return buf2var($name, $fname, $type, $buf, $tokens, $is_ptr); } )); $twig->addFunction(new \Twig\TwigFunction('var2buf', function($name, $fname, $type, $buf, $tokens, $is_ptr) { return var2buf($name, $fname, $type, $buf, $tokens, $is_ptr); } )); $twig->addFilter(new \Twig\TwigFilter('rpc_invert', function($name) { return rpc_invert($name); } )); $twig->addFilter(new \Twig\TwigFilter('go_type', function($type, $tokens) { return go_type($type, $tokens); } )); $twig->addFilter(new \Twig\TwigFilter('ucfirst', function($str) { return ucfirst($str); } )); } function go_type(\mtgType $type, array $tokens = array()) { if($type instanceof \mtgArrType) { $vtype = $type->getValue(); $native = go_type($vtype); $str = "[]"; if(array_key_exists("virtual", $tokens)) $str .= "I"; else $str .= $vtype instanceof \mtgMetaStruct ? "*" : ""; $str .= $native; return $str; } else if($type instanceof \mtgBuiltinType) { if($type->isFloat()) return "float32"; else if($type->isDouble()) return "float64"; else if($type->isBlob()) return "[]byte"; else return $type->getName(); } else if($type instanceof \mtgMetaEnum) return $type->getName(); else if($type instanceof \mtgMetaStruct) { if(array_key_exists("virtual", $tokens)) return "I{$type->getName()}"; else return $type->getName(); } else throw new Exception("Unknown type '$type'"); } function builtin_type_prefix(\mtgBuiltinType $type) { switch($type->getName()) { case "string": return "String"; case "bool": return "Bool"; case "blob": return "Blob"; 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"; default: throw new Exception("Unknown type '{$type}'"); } } function field_reset($name, \mtgType $type, array $tokens) { $str = ''; if($type instanceof \mtgBuiltinType) { if($type->isNumeric()) { if(array_key_exists('default', $tokens) && is_numeric($tokens['default'])) $str .= "self.$name = ".$tokens['default']; else $str .= " self.$name = 0"; } else if($type->isString()) { if(array_key_exists('default', $tokens)) $str .= "self.$name = ".$tokens['default']; else $str .= "self.$name = \"\""; } else if($type->isBool()) { if(array_key_exists('default', $tokens)) $str .= "self.$name = ".$tokens['default']; else $str .= "self.$name = false"; } else if($type->isBlob()) { if(array_key_exists('default', $tokens)) $str .= "self.$name = ".$tokens['default']; else $str .= "self.$name = nil"; } else throw new Exception("Unknown type '$type'"); } else if($type instanceof \mtgArrType) { $str = "if self.$name == nil { \nself.$name = make(".go_type($field->getType(), $field->getTokens()).",0) \n}\n "; $str .= "self.$name = self.{$name}[0:0]"; } else if($type instanceof \mtgMetaEnum) { if(array_key_exists('default', $tokens)) $str = "self.$name = ".go_type($field->getType(), $field->getTokens())."_".trim($tokens['default'], '"'); else $str = "self.$name = 0"; } else if($type instanceof \mtgMetaStruct) { $is_virtual = array_key_exists("virtual", $tokens); if($is_virtual) $str .= "self.$name = New".ltrim(go_type($field->getType(), $field->getTokens()),'I')."() "; else $str .= "self.$name.Reset()"; } else throw new Exception("Unknown type '$type'"); return $str; } function buf2var($name, $fname, \mtgType $type, $buf, array $tokens = array(), $is_ptr = false) { $str = ''; if($type instanceof \mtgBuiltinType) { $str .= _read_op($tokens, $buf.'.Read'.builtin_type_prefix($type).'(&'.$fname.', "'.$name.'")'); } else if($type instanceof \mtgMetaEnum) { $str .= _read_op($tokens, "{$buf}.ReadI32((*int32)(&{$fname}), \"$name\")"); $str .= "\n if !{$fname}.IsValid() { return errors.Errorf(\"Bad enum value %d for $name\", $fname) }"; } else if($type instanceof \mtgMetaStruct) { if(array_key_exists('virtual', $tokens)) $str .= "if v, err := meta.ReadStructGeneric($buf, CreateById, \"{$name}\"); err != nil { return err } else { {$fname} = v.(I{$type}) }"; else $str .= _read_op($tokens, "meta.ReadStruct($buf, ".($is_ptr?"":"&")."$fname, \"$name\")"); } else if($type instanceof \mtgArrType) { $is_virtual = array_key_exists("virtual", $tokens); $native_type = go_type($type->getValue(), $tokens); $offset = "\n "; $str .= "/*[]{$name}*/"; $str .= $offset . _read_op($tokens, "{$buf}.BeginContainer(\"$name\")"); //we don't want to propagate 'optionalness' below unset($tokens['optional']); $str .= $offset . "_{$name}_size, err := {$buf}.GetContainerSize()"; $str .= $offset . "if err != nil { return err }"; $need_new = !$is_virtual && $type->getValue() instanceof \mtgMetaStruct; $str .= $offset . "for ; _{$name}_size > 0; _{$name}_size-- {"; $offset = "\n "; $str .= $offset . "var tmp_{$name} {$native_type}"; $str .= $offset . buf2var("", "tmp_{$name}", $type->getValue(), $buf, $tokens, $is_virtual); $str .= $offset . "{$fname} = append({$fname}, ".($need_new?"&":"")."tmp_{$name})"; $offset = "\n "; $str .= $offset . "}"; $str .= $offset . _read_op($tokens, "{$buf}.EndContainer()"); $str .= "\n"; } else throw new Exception("Unknown type '{$type}'"); return $str; } function var2buf($name, $fname, \mtgType $type, $buf, array $tokens = array(), $is_ptr = false) { $str = ''; if($type instanceof \mtgBuiltinType) { $str .= _write_op("{$buf}.Write".builtin_type_prefix($type)."($fname, \"$name\")")."\n"; } else if($type instanceof \mtgMetaEnum) { $str .= _write_op("{$buf}.WriteI32(int32($fname), \"$name\")"); } else if($type instanceof \mtgMetaStruct) { if(array_key_exists('virtual', $tokens)) $str .= _write_op("meta.WriteStructGeneric($buf, ".($is_ptr?"":"&")."$fname, \"$name\")"); else $str .= _write_op("meta.WriteStruct($buf, ".($is_ptr?"":"&")."$fname, \"$name\")"); } else if($type instanceof \mtgArrType) { $str .= "{$buf}.BeginContainer(\"{$name}\")\n"; $str .= " for _, v := range({$fname}) {\n"; $str .= " ".var2buf("", "v", $type->getValue(), $buf, $tokens, true)."\n"; $str .= " }\n"; $str .= " "._write_op("{$buf}.EndContainer()")."\n"; } else throw new Exception("Unknown type '$type'"); return $str; } function _write_op($op) { return "if err := $op; err != nil { return err }"; } function _read_op(array $tokens, $op) { return "if err := $op; err != nil { return " . (array_key_exists("optional", $tokens) ? "/*optional*/nil" : "err"). " }"; } function rpc_invert($name) { if(strpos($name, '_RSP_') !== false) return str_replace('_RSP_', '_REQ_', $name); else return str_replace('_REQ_', '_RSP_', $name); }