From a77527d7c2f61e448e1daf6d9afa4d2604d0c0c7 Mon Sep 17 00:00:00 2001 From: Fedor Vikhnin Date: Tue, 10 Dec 2024 23:31:27 +0300 Subject: [PATCH] add non function struct body --- internal/tlcodegen/tlgen.go | 9 ++ internal/tlcodegen/tlgen_php.go | 34 ++++- internal/tlcodegen/type_rw.go | 53 ++++++- internal/tlcodegen/type_rw_bool.go | 9 ++ internal/tlcodegen/type_rw_maybe.go | 9 ++ internal/tlcodegen/type_rw_primitive.go | 18 +++ internal/tlcodegen/type_rw_struct.go | 190 ++++++++++++++++++++++++ internal/tlcodegen/type_rw_tuple.go | 8 + internal/tlcodegen/type_rw_union.go | 8 + 9 files changed, 327 insertions(+), 11 deletions(-) diff --git a/internal/tlcodegen/tlgen.go b/internal/tlcodegen/tlgen.go index 635400a..f697be7 100644 --- a/internal/tlcodegen/tlgen.go +++ b/internal/tlcodegen/tlgen.go @@ -1217,6 +1217,15 @@ func GenerateCode(tl tlast.TL, options Gen2Options) (*Gen2, error) { return nil, err } case "php": + if gen.copyrightText == "" { + gen.copyrightText = ` +/** + * AUTOGENERATED, DO NOT EDIT! If you want to modify it, check tl schema. + * + * This autogenerated code represents tl class for typed RPC API. + */ +` + } if err := gen.generateCodePHP(generateByteVersions); err != nil { return nil, err } diff --git a/internal/tlcodegen/tlgen_php.go b/internal/tlcodegen/tlgen_php.go index 23c56e5..c6c660d 100644 --- a/internal/tlcodegen/tlgen_php.go +++ b/internal/tlcodegen/tlgen_php.go @@ -2,6 +2,7 @@ package tlcodegen import ( "fmt" + "path/filepath" "reflect" "strings" ) @@ -26,14 +27,29 @@ func (gen *Gen2) generateCodePHP(generateByteVersions []string) error { // select files where to write code gen.PhpChoosePaths() + createdTypes := make(map[string]bool) + for _, wrapper := range gen.generatedTypesList { + if wrapper.PHPTypePath() == "" { + continue + } + if wrapper.PHPIsPrimitiveType() { + continue + } + if createdTypes[wrapper.trw.PhpClassName(true)] { + continue + } var code strings.Builder - // add + // add start symbol code.WriteString(PHPFileStart) + code.WriteString("\n") // add copyright text code.WriteString(gen.copyrightText) + code.WriteString(fmt.Sprintf("namespace VK\\%s;\n", strings.Join(wrapper.PHPTypePathElements(), "\\"))) - wrapper.PHPGenerateCode(&code, true) + if err := wrapper.PHPGenerateCode(&code, true); err != nil { + return err + } fmt.Printf("TL[%[1]s] = Go {%[2]s, %[4]s} -> PHP {%[3]s, %[5]s}\n", wrapper.tlName.String(), @@ -43,10 +59,16 @@ func (gen *Gen2) generateCodePHP(generateByteVersions []string) error { wrapper.trw.PhpTypeName(true), ) - //filepathName := wrapper.phpInfo.FileName - //if err := gen.addCodeFile(filepathName, code.String()); err != nil { - // return err - //} + fmt.Printf("Core[%s] = %s, %s\n", wrapper.goGlobalName, wrapper.PHPGenCoreType().goGlobalName, reflect.TypeOf(wrapper.PHPGenCoreType().trw)) + + filepathParts := []string{"VK"} + filepathParts = append(filepathParts, wrapper.PHPTypePathElements()...) + filepathParts = append(filepathParts, fmt.Sprintf("%s.php", wrapper.trw.PhpClassName(false))) + filepathName := filepath.Join(filepathParts...) + if err := gen.addCodeFile(filepathName, code.String()); err != nil { + return err + } + createdTypes[wrapper.trw.PhpClassName(true)] = true } return nil } diff --git a/internal/tlcodegen/type_rw.go b/internal/tlcodegen/type_rw.go index 5fa21c8..067a80e 100644 --- a/internal/tlcodegen/type_rw.go +++ b/internal/tlcodegen/type_rw.go @@ -490,15 +490,15 @@ func (w *TypeRWWrapper) IsTrueType() bool { return len(structElement.Fields) == 0 } -func (w *TypeRWWrapper) PHPGenerateCode(code *strings.Builder, bytes bool) { - +func (w *TypeRWWrapper) PHPGenerateCode(code *strings.Builder, bytes bool) error { + return w.trw.PhpGenerateCode(code, bytes) } -func (w *TypeRWWrapper) PHPTypePath() string { +func (w *TypeRWWrapper) PHPTypePathElements() []string { _, isStruct := w.trw.(*TypeRWStruct) _, isUnion := w.trw.(*TypeRWUnion) if !(isStruct || isUnion) { - return "" + return nil } category := "Types" @@ -509,7 +509,48 @@ func (w *TypeRWWrapper) PHPTypePath() string { if w.tlName.Namespace != "" { group = w.tlName.Namespace } - return fmt.Sprintf("TL\\%[1]s\\%[2]s\\", group, category) + return []string{"TL", group, category} +} + +func (w *TypeRWWrapper) PHPDefaultValue() string { + core := w.PHPGenCoreType() + return core.trw.PhpDefaultValue() +} + +func (w *TypeRWWrapper) PHPTypePath() string { + path := w.PHPTypePathElements() + if path == nil { + return "" + } else { + return strings.Join(path, "\\") + "\\" + } +} + +func (w *TypeRWWrapper) PHPGenCoreType() *TypeRWWrapper { + if w.unionParent == nil { + struct_, isStruct := w.trw.(*TypeRWStruct) + if isStruct && len(struct_.Fields) == 1 && struct_.ResultType == nil { + return struct_.Fields[0].t.PHPGenCoreType() + } + } + return w +} + +func (w *TypeRWWrapper) PHPIsPrimitiveType() bool { + core := w.PHPGenCoreType() + if _, isPrimitive := core.trw.(*TypeRWPrimitive); isPrimitive { + return true + } + if struct_, isStruct := core.trw.(*TypeRWStruct); isStruct { + isDict, _, _, valueType := isDictionaryElement(struct_.wr) + if isDict { + return valueType.t.PHPIsPrimitiveType() + } + } + if _, isBrackets := core.trw.(*TypeRWBrackets); isBrackets { + return true + } + return false } func (w *TypeRWWrapper) CPPFillRecursiveChildren(visitedNodes map[*TypeRWWrapper]bool) { @@ -751,6 +792,8 @@ outer: type TypeRWPHPData interface { PhpClassName(withPath bool) string PhpTypeName(withPath bool) string + PhpGenerateCode(code *strings.Builder, bytes bool) error + PhpDefaultValue() string } // TODO remove skipAlias after we start generating go code like we do for C++ diff --git a/internal/tlcodegen/type_rw_bool.go b/internal/tlcodegen/type_rw_bool.go index ca4aca3..9e5a476 100644 --- a/internal/tlcodegen/type_rw_bool.go +++ b/internal/tlcodegen/type_rw_bool.go @@ -8,6 +8,7 @@ package tlcodegen import ( "fmt" + "strings" ) type TypeRWBool struct { @@ -117,3 +118,11 @@ func (trw *TypeRWBool) PhpClassName(withPath bool) string { func (trw *TypeRWBool) PhpTypeName(withPath bool) string { return trw.PhpClassName(withPath) } + +func (trw *TypeRWBool) PhpGenerateCode(code *strings.Builder, bytes bool) error { + return fmt.Errorf("boolean doesn't have php code") +} + +func (trw *TypeRWBool) PhpDefaultValue() string { + return "false" +} diff --git a/internal/tlcodegen/type_rw_maybe.go b/internal/tlcodegen/type_rw_maybe.go index 009cb27..aa740e5 100644 --- a/internal/tlcodegen/type_rw_maybe.go +++ b/internal/tlcodegen/type_rw_maybe.go @@ -8,6 +8,7 @@ package tlcodegen import ( "fmt" + "strings" ) type TypeRWMaybe struct { @@ -136,3 +137,11 @@ func (trw *TypeRWMaybe) getInnerTarget() *TypeRWWrapper { return trw.element.t } } + +func (trw *TypeRWMaybe) PhpGenerateCode(code *strings.Builder, bytes bool) error { + return fmt.Errorf("maybe doesn't have php code") +} + +func (trw *TypeRWMaybe) PhpDefaultValue() string { + return "null" +} diff --git a/internal/tlcodegen/type_rw_primitive.go b/internal/tlcodegen/type_rw_primitive.go index 571afaf..3cd1969 100644 --- a/internal/tlcodegen/type_rw_primitive.go +++ b/internal/tlcodegen/type_rw_primitive.go @@ -9,6 +9,7 @@ package tlcodegen import ( "fmt" "log" + "strings" ) type TypeRWPrimitive struct { @@ -187,3 +188,20 @@ func (trw *TypeRWPrimitive) PhpClassName(withPath bool) string { func (trw *TypeRWPrimitive) PhpTypeName(withPath bool) string { return trw.PhpClassName(withPath) } + +func (trw *TypeRWPrimitive) PhpGenerateCode(code *strings.Builder, bytes bool) error { + return fmt.Errorf("primitives don't have php code") +} + +func (trw *TypeRWPrimitive) PhpDefaultValue() string { + switch trw.goType { + case "int32", "int64", "uint32": + return "0" + case "string": + return "\"\"" + case "float32", "float64": + return "0" + default: + return fmt.Sprintf("", trw.tlType) + } +} diff --git a/internal/tlcodegen/type_rw_struct.go b/internal/tlcodegen/type_rw_struct.go index febe441..f9c7083 100644 --- a/internal/tlcodegen/type_rw_struct.go +++ b/internal/tlcodegen/type_rw_struct.go @@ -601,3 +601,193 @@ func (trw *TypeRWStruct) PhpTypeName(withPath bool) string { } return trw.PhpClassName(withPath) } + +func (trw *TypeRWStruct) PhpGenerateCode(code *strings.Builder, bytes bool) error { + code.WriteString(`use VK\TL; + +/** + * @kphp-tl-class + */ +`) + code.WriteString(fmt.Sprintf("class %s ", trw.PhpClassName(false))) + if trw.wr.unionParent != nil { + code.WriteString(fmt.Sprintf("implements %s ", trw.wr.unionParent.PhpClassName(true))) + } + code.WriteString("{\n") + // print fieldmasks + for _, f := range trw.Fields { + if f.fieldMask == nil { + continue + } + code.WriteString( + fmt.Sprintf( + ` + /** Field mask for %[1]s field */ + const BIT_%[2]s_%[3]d = (1 << %[3]d); +`, + f.originalName, + strings.ToUpper(f.originalName), + f.BitNumber, + ), + ) + } + // print fields declarations + for _, f := range trw.Fields { + fieldType, defaultValue := fieldTypeAndDefaultValue(f) + code.WriteString( + fmt.Sprintf( + ` + /** @var %[1]s */ + public $%[2]s = %[3]s; +`, + fieldType, + f.originalName, + defaultValue, + ), + ) + } + // print constructor + necessaryFieldsInConstructor := make([]Field, 0) + usedFieldMasksIndecies := make([]int, 0) + usedFieldMasks := make(map[int][]Field) + for _, f := range trw.Fields { + if f.fieldMask == nil { + necessaryFieldsInConstructor = append(necessaryFieldsInConstructor, f) + } else { + index := f.fieldMask.FieldIndex + if !f.fieldMask.isField { + for i, argument := range trw.wr.NatParams { + if argument == f.fieldMask.name { + index = -(i + 1) + break + } + } + } + if usedFieldMasks[index] == nil { + usedFieldMasksIndecies = append(usedFieldMasksIndecies, index) + } + usedFieldMasks[index] = append(usedFieldMasks[index], f) + } + } + + code.WriteString(` + /** +`) + for _, f := range necessaryFieldsInConstructor { + fieldType, _ := fieldTypeAndDefaultValue(f) + code.WriteString(fmt.Sprintf(" * @param %[1]s $%[2]s\n", fieldType, f.originalName)) + } + + code.WriteString(` */ +`) + code.WriteString(" public function __construct(") + + for i, f := range necessaryFieldsInConstructor { + _, defaultValue := fieldTypeAndDefaultValue(f) + if i != 0 { + code.WriteString(", ") + } + code.WriteString(fmt.Sprintf("$%[1]s = %[2]s", f.originalName, defaultValue)) + } + + code.WriteString(") {\n") + for _, f := range necessaryFieldsInConstructor { + code.WriteString(fmt.Sprintf(" $this->$%[1]s = $%[1]s;\n", f.originalName)) + } + code.WriteString(" }\n") + + sort.Ints(usedFieldMasksIndecies) + for _, natIndex := range usedFieldMasksIndecies { + natName := "" + if natIndex < 0 { + natName = trw.wr.NatParams[-(natIndex + 1)] + } else { + natName = trw.Fields[natIndex].originalName + } + code.WriteString(` + /**`) + additionalArgs := make([]string, 0) + // arguments with ambiguous existence + for _, dependentField := range usedFieldMasks[natIndex] { + if _, isMaybe := dependentField.t.PHPGenCoreType().trw.(*TypeRWMaybe); isMaybe { + additionalArgs = append(additionalArgs, fmt.Sprintf("$has_%s", dependentField.originalName)) + code.WriteString(fmt.Sprintf("\n * @param bool $has_%s", dependentField.originalName)) + } + } + code.WriteString(` + * @return int + */ +`, + ) + code.WriteString( + fmt.Sprintf( + " public function calculate%[1]s(%[2]s) {\n $mask = 0;\n", + ToUpperFirst(natName), + strings.Join(additionalArgs, ", "), + ), + ) + + for _, dependentField := range usedFieldMasks[natIndex] { + condition := "" + if dependentField.t.IsTrueType() { + condition = fmt.Sprintf( + "$this->%[1]s", + dependentField.originalName, + ) + } else if _, isMaybe := dependentField.t.PHPGenCoreType().trw.(*TypeRWMaybe); isMaybe { + condition = fmt.Sprintf("$has_%s", dependentField.originalName) + } else { + condition = fmt.Sprintf( + "$this->%[1]s !== null", + dependentField.originalName, + ) + } + code.WriteString( + fmt.Sprintf( + ` + if (%[1]s) { + $mask |= BIT_%[2]s_%[3]d; + } +`, + condition, + strings.ToUpper(dependentField.originalName), + dependentField.BitNumber, + ), + ) + } + + code.WriteString(" return $mask;\n") + code.WriteString(" }\n") + } + + code.WriteString("\n}") + return nil +} + +func fieldTypeAndDefaultValue(f Field) (string, string) { + fieldType := f.t.trw.PhpTypeName(true) + defaultValue := f.t.trw.PhpDefaultValue() + if f.t.IsTrueType() { + fieldType = "boolean" + defaultValue = "true" + if f.fieldMask != nil { + defaultValue = "false" + } + } else { + if f.fieldMask != nil { + defaultValue = "null" + if _, isMaybe := f.t.PHPGenCoreType().trw.(*TypeRWMaybe); !isMaybe { + fieldType = fieldType + "|null" + } + } + } + return fieldType, defaultValue +} + +func (trw *TypeRWStruct) PhpDefaultValue() string { + core := trw.wr.PHPGenCoreType() + if core != trw.wr { + return core.PHPDefaultValue() + } + return "null" +} diff --git a/internal/tlcodegen/type_rw_tuple.go b/internal/tlcodegen/type_rw_tuple.go index cd4f8da..8a6bdab 100644 --- a/internal/tlcodegen/type_rw_tuple.go +++ b/internal/tlcodegen/type_rw_tuple.go @@ -230,3 +230,11 @@ func (trw *TypeRWBrackets) PhpTypeName(withPath bool) string { } return fmt.Sprintf("", trw.wr.goGlobalName) } + +func (trw *TypeRWBrackets) PhpGenerateCode(code *strings.Builder, bytes bool) error { + return fmt.Errorf("tuples don't have php code") +} + +func (trw *TypeRWBrackets) PhpDefaultValue() string { + return "[]" +} diff --git a/internal/tlcodegen/type_rw_union.go b/internal/tlcodegen/type_rw_union.go index e1552be..9903a10 100644 --- a/internal/tlcodegen/type_rw_union.go +++ b/internal/tlcodegen/type_rw_union.go @@ -213,3 +213,11 @@ func (trw *TypeRWUnion) PhpClassName(withPath bool) string { func (trw *TypeRWUnion) PhpTypeName(withPath bool) string { return trw.PhpClassName(withPath) } + +func (trw *TypeRWUnion) PhpGenerateCode(code *strings.Builder, bytes bool) error { + return nil +} + +func (trw *TypeRWUnion) PhpDefaultValue() string { + return "null" +}