object; if($obj instanceof mtgMetaRPC) return $this->genRPC($obj); if($obj instanceof mtgMetaEnum) return $this->genEnum($obj); else if($obj instanceof mtgMetaStruct) return $this->genStruct($obj); else { echo "WARN: skipping meta unit '{$obj->getId()}'\n"; return ''; } } function genEnum(mtgMetaEnum $struct) { $templater = new mtg_go_templater(); $repl = array(); $repl['%class%'] = $struct->getName(); $repl['%class_id%'] = $struct->getClassId(); $tpl = $templater->tpl_enum(); $this->fillEnumRepls($struct, $repl); return mtg_fill_template($tpl, $repl); } function fillEnumRepls(mtgMetaEnum $enum, &$repl) { $repl['%values%'] = ''; $repl['%vnames_list%'] = ''; $repl['%values_list%'] = ''; $vnames = array(); $values = array(); $values_map = array(); $default_value = null; $consts = array(); $enum_name = $enum->getName(); $map = array(); foreach($enum->getValues() as $vname => $value) { $map[$vname] = $value; $vnames[] = "'$vname'"; $values[] = $value; if(!$default_value) $default_value = "$value; // $vname"; $consts[$value] = "{$enum->getName()}_{$vname} {$enum->getName()} = $value"; } // must be sorted sort($values, SORT_NUMERIC); $repl['%vnames_list%'] = implode(',', $vnames); $repl['%values_list%'] = implode(',', $values); $repl['%values_map%'] = $this->genIntMap($map); $repl['%default_enum_value%'] = $default_value; $repl['%consts%'] = "\n " . implode("\n ", $consts) . "\n"; } function preprocessField(mtgMetaStruct $struct, mtgMetaField $field) {} function genRPC(mtgMetaRPC $rpc) { return $this->genRPCPacket($rpc->getReq()) . $this->genRPCPacket($rpc->getRsp()) . $this->genRPCReqResp($rpc); } function genRPCReqResp(mtgMetaRPC $rpc) { $templater = new mtg_go_templater(); $repl = array(); $repl['%code%'] = $rpc->getCode(); $repl['%rpc_name%'] = $rpc->getName(); $repl['%rpc_req_name%'] = $rpc->getReq()->getName(); $repl['%rpc_rsp_name%'] = $rpc->getRsp()->getName(); $tpl = $templater->tpl_rpc(); return mtg_fill_template($tpl, $repl); } function genRPCPacket(mtgMetaPacket $packet) { $templater = new mtg_go_templater(); $repl = array(); $repl['%type%'] = $packet->getName(); $repl['%code%'] = $packet->getNumericCode(); $repl['%class%'] = $packet->getName(); $repl['%class_id%'] = $packet->getClassId(); $repl['%parent_class%'] = ''; if(strpos($packet->getName(), '_RSP_') !== false) $repl['%class_invert%'] = str_replace('_RSP_', '_REQ_', $packet->getName()); else $repl['%class_invert%'] = str_replace('_REQ_', '_RSP_', $packet->getName()); $fields = $packet->getFields(); $tpl = $templater->tpl_packet(); $this->fillStructRepls($packet, $repl); return mtg_fill_template($tpl, $repl); } function genStruct(mtgMetaStruct $struct) { $templater = new mtg_go_templater(); $repl = array(); $repl['%class%'] = $struct->getName(); $repl['%class_id%'] = $struct->getClassId(); $repl['%parent_class%'] = $struct->getParent() ? ''.$struct->getParent() : ""; $tpl = $templater->tpl_struct(); $this->fillStructRepls($struct, $repl); $result = mtg_fill_template($tpl, $repl); if($struct->hasToken('POD') && $struct->hasToken("id") && $struct->hasToken("table") && $struct->hasToken("owner")) $result .= "\n" . str_replace(array_keys($repl), array_values($repl), $templater->tpl_collection_item()); return $result; } function fillStructRepls(mtgMetaStruct $struct, &$repl) { foreach($struct->getFields() as $field) $this->preprocessField($struct, $field); $repl['%includes%'] = ''; $repl['%fields%'] = ''; $repl['%fields_names%'] = '[]string{'; $repl['%fields_props%'] = 'map[string]map[string]string{'; $repl['%fields_types%'] = 'map[string]string{'; $repl['%fields_reset%'] = ''; $repl['%read_buffer%'] = ''; $repl['%write_buffer%'] = ''; $repl['%total_top_fields%'] = sizeof($struct->getFields()); $repl['%ext_methods%'] = ""; $repl['%analytics_methods%'] = ""; $repl['%class_props%'] = 'map[string]string{' . $this->genStrMap($struct->getTokens()) . '}'; if($struct->hasToken("bitfields")) { $repl['%read_buffer%'] .= "\n use_mask, mask, err := reader.TryReadMask()\n"; $repl['%read_buffer%'] .= "\n if err != nil {\n"; $repl['%read_buffer%'] .= "\n return err\n"; $repl['%read_buffer%'] .= "\n }\n"; $repl['%read_buffer%'] .= "\n self.fieldsMask = mask\n"; } $parent = $struct->getParent(); $all_fields = mtg_get_all_fields($this->info, $struct); foreach($all_fields as $field) { $name = $field->getName(); $repl['%fields_names%'] .= '"' . $name . '",'; $repl['%fields_props%'] .= '"' . $this->genFieldName($field) . '" : map[string]string{' . $this->genStrMap($field->getTokens()) . "},"; $field_type = $field->getType(); $repl['%fields_types%'] .= '"' . $name . '" : "' . $field_type . '",'; } if($struct->hasToken('statist')) { $table_name = $struct->getToken('statist'); $repl['%analytics_methods%'] .= "func (self *".$struct->getName().") Table() string {\n"; $repl['%analytics_methods%'] .= "\t return \"$table_name\"\n"; $repl['%analytics_methods%'] .= "}\n"; $repl['%analytics_methods%'] .= "func (self *".$struct->getName().") Columns() []string {\n"; $repl['%analytics_methods%'] .= "\treturn []string{\n"; foreach($all_fields as $field) { if ($field->hasToken('statist_skip')) continue; $column_name = $field->hasToken('statist_alias') ? $field->getToken('statist_alias') : $field->getName(); $repl['%analytics_methods%'] .= "\t\t\"$column_name\",\n"; } $repl['%analytics_methods%'] .= "\t}\n"; $repl['%analytics_methods%'] .= "}\n"; $repl['%analytics_methods%'] .= "func (self *".$struct->getName().") Values() []interface{} {\n"; $repl['%analytics_methods%'] .= "\treturn []interface{}{\n"; foreach($all_fields as $field) { if ($field->hasToken('statist_skip')) continue; $repl['%analytics_methods%'] .= "\t\tself." . ucfirst($field->getName()) . ",\n"; } $repl['%analytics_methods%'] .= "\t}\n"; $repl['%analytics_methods%'] .= "}\n"; } $repl['%read_buffer%'] .= "\n _cont_size, err := reader.GetContainerSize()"; $repl['%read_buffer%'] .= "\n if err != nil {"; $repl['%read_buffer%'] .= "\n return err"; $repl['%read_buffer%'] .= "\n }"; $optional = 0; foreach($struct->getFields() as $field) { if($field->hasToken('optional')) $optional++; } $initial_fields_amount = count($struct->getFields()) - $optional; $repl['%read_buffer%'] .= "\n if _cont_size < $initial_fields_amount {"; $repl['%read_buffer%'] .= "\n _cont_size = $initial_fields_amount"; $repl['%read_buffer%'] .= "\n }"; if($struct->hasToken('POD')) { $fields = $all_fields; } else { if($parent = $struct->getParent()) { $repl['%read_buffer%'] .= "\n if err := self." . $parent . ".ReadFields(reader); err != nil { return err }"; $repl['%write_buffer%'] .= "\n if err := self." . $parent . ".WriteFields(writer); err != nil { return err }"; } $fields = $struct->getFields(); } $field_index = -1; foreach($fields as $field) { ++$field_index; $options = $this->readFieldOptions($field); $repl['%fields%'] .= "\n " . $this->genFieldDecl($field); $repl['%fields_reset%'] .= "\n " . $this->genFieldReset($field); $repl['%read_buffer%'] .= "\n if _cont_size <= 0 {"; $repl['%read_buffer%'] .= "\n return nil"; $repl['%read_buffer%'] .= "\n }"; if($struct->hasToken("bitfields")) $repl['%read_buffer%'] .= "\n if !use_mask {"; $repl['%read_buffer%'] .= "\n _cont_size--"; if($struct->hasToken("bitfields")) $repl['%read_buffer%'] .= "\n }"; if($struct->hasToken("bitfields")) $repl['%read_buffer%'] .= "\n if !use_mask || (use_mask && self.HasValue($field_index)) {"; $repl['%read_buffer%'] .= "\n " . $this->genBufRead($field->getName(), $options['fname'], $field->getType(), $options['buf'], $field->getTokens()); if($struct->hasToken("bitfields")) $repl['%read_buffer%'] .= "\n }"; $repl['%write_buffer%'] .= "\n " . $this->genBufWrite($field, "writer", $field->hasToken("virtual")); } $repl['%fields%'] = trim($repl['%fields%'], "\n "); $repl['%fields_names%'] .= '}'; $repl['%fields_props%'] .= '}'; $repl['%fields_types%'] .= '}'; $repl['%import_from_mysql%'] = ''; $repl["%export_to_arr%"] = ""; if($struct->hasToken('POD') && $struct->hasToken("table")) { $ind = 0; $repl['%import_from_mysql%'] .= "\n row := data.(".$struct->getName().")"; foreach($all_fields as $field) { $name = $this->genFieldName($field); $repl['%import_from_mysql%'] .= sprintf("\n self.%s = row.%s", $name, $name); $repl['%export_to_arr%'] .= sprintf("\n data[%d] = self.%s", $ind, $name); $ind++; } } $repl["%table_name%"] = $struct->getToken("table"); $repl["%owner%"] = $struct->getToken("owner"); $repl["%id_field_name%"] = $struct->getToken("id"); $repl["%id_field%"] = ucfirst($struct->getToken("id")); if($struct->hasToken("bitfields")) { $repl['%fields%'] .= "\nfieldsMask uint64\n"; $repl['%fields_reset%'] .= "\nself.fieldsMask = 0\n"; $repl['%ext_methods%'] .= "func(self *".$struct->getName().") HasValue(index uint64) bool {\n"; $repl['%ext_methods%'] .= " value := uint64(1 << index)\n"; $repl['%ext_methods%'] .= " return (self.fieldsMask & value) > 0\n"; $repl['%ext_methods%'] .= "}\n"; $repl['%ext_methods%'] .= "func(self *".$struct->getName().") SetFieldChanged(index uint64) {\n"; $repl['%ext_methods%'] .= " value := uint64(1 << index)\n"; $repl['%ext_methods%'] .= " self.fieldsMask |= value\n"; $repl['%ext_methods%'] .= "}\n"; $repl['%ext_methods%'] .= "func(self *".$struct->getName().") IsMaskFilled() bool {\n"; $repl['%ext_methods%'] .= " return self.fieldsMask > 0\n"; $repl['%ext_methods%'] .= "}\n"; $repl['%ext_methods%'] .= "func(self *".$struct->getName().") GetMask() uint64 {\n"; $repl['%ext_methods%'] .= " return self.fieldsMask\n"; $repl['%ext_methods%'] .= "}\n"; } } function readFieldOptions(mtgMetaField $field) { return array( 'fname' => 'self.'.ucfirst($field->getName()), 'buf' => "reader", ); } function genReadBuiltinField($name, $fname, mtgType $type, $buf, array $tokens) { return $this->genRead($tokens, $buf.'.Read'.$this->genBuiltinTypePrefix($type).'(&'.$fname.', "'.$name.'")'); } function genBufRead($name, $fname, mtgType $type, $buf, array $tokens = array(), $is_ptr = false) { $str = ''; if($type instanceof mtgBuiltinType) { $str .= $this->genReadBuiltinField($name, $fname, $type, $buf, $tokens); } else if($type instanceof mtgMetaEnum) { $str .= $this->genRead($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 .= $this->genRead($tokens, "meta.ReadStruct($buf, ".($is_ptr?"":"&")."$fname, \"$name\")"); } else if($type instanceof mtgArrType) { $is_virtual = array_key_exists("virtual", $tokens); $native_type = $this->genNativeType($type->getValue(), $tokens); $offset = "\n "; $str .= "/*[]{$name}*/"; $str .= $offset . $this->genRead($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 . $this->genBufRead("", "tmp_{$name}", $type->getValue(), $buf, $tokens, $is_virtual); $str .= $offset . "{$fname} = append({$fname}, ".($need_new?"&":"")."tmp_{$name})"; $offset = "\n "; $str .= $offset . "}"; $str .= $offset . $this->genRead($tokens, "{$buf}.EndContainer()"); $str .= "\n"; } else throw new Exception("Unknown type '{$type}'"); return $str; } function genRead(array $tokens, $op) { return "if err := $op; err != nil { return " . (array_key_exists("optional", $tokens) ? "/*optional*/nil" : "err"). " }"; } function genWrite($op) { return "if err := $op; err != nil { return err }"; } function genBufWrite(mtgMetaField $field, $buf, $is_ptr = false) { return $this->genBufWriteEx($field->getName(), "self.".ucfirst($field->getName()), $field->getType(), $buf, $field->getTokens(), $is_ptr); } function genBufWriteEx($name, $fname, mtgType $type, $buf, array $tokens = array(), $is_ptr = false) { $str = ''; if($type instanceof mtgBuiltinType) { $str .= $this->genWrite("{$buf}.Write".$this->genBuiltinTypePrefix($type)."($fname, \"$name\")")."\n"; } else if($type instanceof mtgMetaEnum) { $str .= $this->genWrite("{$buf}.WriteI32(int32($fname), \"$name\")"); } else if($type instanceof mtgMetaStruct) { if(array_key_exists('virtual', $tokens)) $str .= $this->genWrite("meta.WriteStructGeneric($buf, ".($is_ptr?"":"&")."$fname, \"$name\")"); else $str .= $this->genWrite("meta.WriteStruct($buf, ".($is_ptr?"":"&")."$fname, \"$name\")"); } else if($type instanceof mtgArrType) { $is_virtual = array_key_exists("virtual", $tokens); $str .= "{$buf}.BeginContainer(\"{$name}\")\n"; $str .= " for _, v := range({$fname}) {\n"; $str .= " ".$this->genBufWriteEx("", "v", $type->getValue(), $buf, $tokens, true)."\n"; $str .= " }\n"; $str .= " ".$this->genWrite("{$buf}.EndContainer()")."\n"; } else throw new Exception("Unknown type '$type'"); return $str; } function genFieldDecl(mtgMetaField $field) { return $this->genFieldName($field) . " " . $this->genFieldNativeType($field) . ($field->getName()[0] != '_' ? " `json:\"{$field->getName()}\"`" : "") ; } function genFieldName(mtgMetaField $field) { return ucfirst($field->getName()); } function genFieldReset(mtgMetaField $field) { $tokens = $field->getTokens(); $str = ''; $name = $this->genFieldName($field); $type = $field->getType(); if($type instanceof mtgBuiltinType) { if($type->isNumeric()) { //NOTE: numeric check is against str2num 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(".$this->genFieldNativeType($field).",0) \n}\n "; $str .= "self.$name = self.{$name}[0:0]"; } else if($type instanceof mtgMetaEnum) { if(array_key_exists('default', $tokens)) $str = "self.$name = ".$this->genFieldNativeType($field)."_".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($this->genFieldNativeType($field),'I')."() "; else $str .= "self.$name.Reset()"; } else throw new Exception("Unknown type '$type'"); return $str; } function genFieldNativeType(mtgMetaField $field) { return $this->genNativeType($field->getType(), $field->getTokens()); } function genNativeType(mtgType $type, array $tokens = array()) { if($type instanceof mtgArrType) { $vtype = $type->getValue(); $native = $this->genNativePlainType($vtype); $str = "[]"; if(array_key_exists("virtual", $tokens)) $str .= "I"; else $str .= $vtype instanceof mtgMetaStruct ? "*" : ""; $str .= $native; return $str; } else return $this->genNativePlainType($type, $tokens); } function genBuiltinTypePrefix(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 genNativePlainType(mtgType $type, array $tokens = array()) { 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 genStrMap(array $source) { $str = ''; foreach($source as $k => $v) $str .= '"' . $k . '": "' . ($v ? str_replace('"', '\\"', $v) : "") . '",'; return $str; } function genIntMap(array $source) { $str = ''; foreach($source as $k => $v) $str .= "\"$k\": {$v},"; return $str; } function offset($num = 1) { return str_repeat(" ", $num); } }