├── .gitignore ├── LICENSE ├── Make.sh ├── README.md ├── README_v2.md ├── bin └── .gitkeep ├── build └── build.go ├── doc ├── Manual_V2.md ├── error_v2.md ├── log_v2.png ├── logo.png ├── table_v2.png └── vertical_v2.png ├── entry_v2.go ├── entry_v2tov3.go ├── entry_v3.go ├── flag.go ├── go.mod ├── go.sum ├── main.go ├── util ├── cache.go ├── changeext.go ├── conv.go ├── rc2a.go ├── rc2a_test.go └── strwrapper.go ├── v2 ├── cpp │ ├── DataReader.h │ └── Logger.h ├── csharp │ ├── DataReader.cs │ └── Logger.cs ├── dataheaderelem.go ├── datamerge.go ├── dataproc.go ├── example │ ├── CompileExport.bat │ ├── CompileExport.sh │ ├── Export.bat │ ├── Export.sh │ ├── Globals.xlsx │ ├── Sample.xlsx │ ├── combine │ │ ├── CombineConfig.json │ │ ├── Export.bat │ │ ├── Item.xlsx │ │ ├── Item_Equip.xlsx │ │ └── Item_Pet.xlsx │ ├── cpp │ │ ├── cpp.sln │ │ └── cpp │ │ │ ├── Config.h │ │ │ ├── ReadMe.txt │ │ │ ├── cpp.cpp │ │ │ ├── cpp.vcxproj │ │ │ ├── cpp.vcxproj.filters │ │ │ ├── stdafx.cpp │ │ │ ├── stdafx.h │ │ │ └── targetver.h │ ├── csharp │ │ ├── Example │ │ │ ├── App.config │ │ │ ├── Config.bin │ │ │ ├── Config.cs │ │ │ ├── Example.csproj │ │ │ ├── Program.cs │ │ │ └── Properties │ │ │ │ └── AssemblyInfo.cs │ │ └── csharp.sln │ ├── golang │ │ ├── Config.json │ │ ├── Run.bat │ │ ├── main.go │ │ └── table │ │ │ └── table_gen.go │ ├── helloworld │ │ ├── Config.json │ │ ├── Empty.xlsx │ │ └── Export.bat │ ├── lua │ │ ├── Config.lua │ │ ├── Run.bat │ │ └── read.lua │ ├── pb │ │ ├── data.pbt │ │ └── proto.proto │ └── verticalconfig │ │ ├── Export.bat │ │ ├── Vertical.xlsx │ │ └── VerticalConfig.json ├── exportor.go ├── exprvm │ ├── cmd.go │ ├── compiler.go │ ├── compiler_test.go │ ├── error.go │ ├── opcode.go │ ├── stack.go │ └── vm.go ├── file.go ├── filecache.go ├── filter │ ├── log.go │ ├── sflist.go │ ├── structparser.go │ └── valueconv.go ├── i18n │ ├── en_us.go │ ├── i18n.go │ └── zh_cn.go ├── log.go ├── model │ ├── BuiltinTypes.xlsx │ ├── Export.bat │ ├── datamodel.go │ ├── descriptor.go │ ├── fielddesc.go │ ├── filedesc.go │ ├── log.go │ ├── metainfo.go │ ├── node.go │ ├── table.go │ └── types_gen.go ├── printer │ ├── binary.go │ ├── cpp.go │ ├── csharp.go │ ├── globals.go │ ├── golang.go │ ├── json.go │ ├── log.go │ ├── lua.go │ ├── modfile.go │ ├── pbt.go │ ├── printer.go │ ├── proto.go │ ├── type.go │ └── util.go ├── sheet.go ├── sheet_data.go ├── sheet_dataheader.go ├── sheet_datav.go ├── sheet_type.go ├── test │ ├── Config.json │ └── UnitTest.xlsx └── typemodel.go ├── v2tov3 ├── dataheader.go ├── example │ ├── Make.bat │ ├── Make.sh │ ├── golang_gen.go │ └── json_gen.json ├── log.go ├── model │ ├── dataheader.go │ ├── globals.go │ ├── log.go │ └── types.go ├── tab_data.go ├── tab_index.go ├── tab_type.go └── upgrader.go └── v3 ├── api ├── csharp │ └── TableReader.cs └── golang │ ├── tabhelper.go │ └── tabreader.go ├── checker ├── TODO.md ├── checker_data.go ├── checker_enumvalue.go ├── checker_repeat.go ├── checker_type.go └── entry.go ├── compiler ├── flow.go ├── header.go ├── merge.go ├── resolverow.go ├── strtovalue.go ├── tab_data.go ├── tab_index.go ├── tab_kv.go ├── tab_type.go └── variant.go ├── example ├── csharp │ ├── TabtoyExample │ │ ├── Program.cs │ │ ├── TabtoyExample.csproj │ │ ├── app.config │ │ └── table_gen.cs │ └── csharp.sln ├── csv │ ├── ConvertToCSV.xlsm │ ├── Data.csv │ ├── Data2.csv │ ├── Extend.csv │ ├── Index.csv │ ├── KV.csv │ ├── KV2.csv │ ├── Make.sh │ └── Type.csv ├── golang │ ├── Make.sh │ ├── main.go │ └── table_gen.go ├── java │ ├── build.gradle │ ├── cfg │ │ └── table_gen.json │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ ├── main │ │ └── java │ │ │ └── main │ │ │ └── Table.java │ │ └── test │ │ └── java │ │ └── Main.java ├── json │ └── table_gen.json ├── jsondir │ ├── ExampleData.json │ ├── ExampleKV.json │ └── ExtendData.json ├── jsontype │ └── type_gen.json ├── lua │ ├── main.lua │ └── table_gen.lua ├── luadir │ ├── ExampleData.lua │ ├── ExampleKV.lua │ ├── ExtendData.lua │ └── _TableType.lua ├── protobuf │ ├── golang │ │ ├── Make.sh │ │ ├── main.go │ │ └── table.pb.go │ └── table.proto ├── tutorial │ ├── Index.xlsx │ ├── Make.sh │ ├── MyData.xlsx │ └── Type.xlsx └── xlsx │ ├── Data.xlsx │ ├── Data2.xlsx │ ├── Extend.xlsx │ ├── Index.xlsx │ ├── KV.xlsx │ ├── KV2.xlsx │ ├── Make.sh │ └── Type.xlsx ├── gen ├── bindata │ ├── gen.go │ ├── struct.go │ ├── tag.go │ ├── value.go │ └── writer.go ├── cssrc │ ├── func.go │ ├── gen.go │ └── text.go ├── genfunc.go ├── gosrc │ ├── func.go │ ├── gen.go │ └── text.go ├── javasrc │ ├── func.go │ ├── gen.go │ └── text.go ├── jsondata │ ├── gen.go │ └── wrap.go ├── jsontype │ ├── gen.go │ └── model.go ├── luasrc │ ├── func.go │ ├── gen.go │ └── text.go ├── pbdata │ ├── dynamictype.go │ ├── func.go │ └── gen.go ├── pbsrc │ ├── func.go │ ├── gen.go │ └── text.go └── sharefunc.go ├── helper ├── fileloader.go ├── filewriter.go ├── memfile.go ├── sheethelper.go ├── tab_csv.go ├── tab_xlsx.go └── tabfile.go ├── model ├── cell.go ├── datarow.go ├── datatab.go ├── datatablist.go ├── fieldtype.go ├── globals.go ├── header.go ├── index.go ├── tagaction.go ├── type.go └── typetab.go ├── report ├── errid.go ├── error.go └── log.go └── tests ├── Make.sh ├── data_test.go ├── emulator.go ├── launcher.go └── type_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.suo 3 | *.user 4 | *~ 5 | v2/example/lua/*.dll 6 | v2/example/cpp/.vs 7 | v3/example/lua/*.dll 8 | v2tov3/example/*.xlsx 9 | v2/example/csharp/.vs 10 | v2/example/cpp/cpp/Debug 11 | v2/example/csharp/Example/obj 12 | v2/example/csharp/Example/bin 13 | v2/example/cpp/Debug 14 | v3/example/csharp/TabtoyExample/obj 15 | v3/example/csharp/.vs 16 | v3/example/csharp/TabtoyExample/bin 17 | v3/example/java/.gradle 18 | v3/example/java/.idea 19 | v3/example/java/build 20 | v3/example/java/.project 21 | v3/example/java/.settings/org.eclipse.buildship.core.prefs 22 | v3/example/binary/*.bin 23 | *.zip 24 | *.gz 25 | tabtoy 26 | *.pbb 27 | *. 28 | *.DS_Store 29 | protoc 30 | ~*.xlsx 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (C) 2018 davyxu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Make.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | Version=3.1.4 3 | 4 | BuildSourcePackage="github.com/davyxu/tabtoy/build" 5 | BinaryPackage="github.com/davyxu/tabtoy" 6 | BinaryName="tabtoy" 7 | 8 | BuildBinary() 9 | { 10 | set -e 11 | TargetDir=bin/"${1}" 12 | mkdir -p "${TargetDir}" 13 | export GOOS=${1} 14 | BuildTime=$(date -R) 15 | GitCommit=$(git rev-parse HEAD) 16 | VersionString="-X \"${BuildSourcePackage}.BuildTime=${BuildTime}\" -X \"${BuildSourcePackage}.Version=${Version}\" -X \"${BuildSourcePackage}.GitCommit=${GitCommit}\"" 17 | 18 | go build -v -p 4 -o "${TargetDir}"/${BinaryName} -ldflags "${VersionString}" ${BinaryPackage} 19 | PackageDir=$(pwd) 20 | cd "${TargetDir}" 21 | tar zcvf "${PackageDir}"/${BinaryName}-${Version}-"${1}"-x86_64.tar.gz ${BinaryName} 22 | cd "${PackageDir}" 23 | } 24 | 25 | 26 | if [[ ${1} == "" ]]; then 27 | BuildBinary windows 28 | BuildBinary linux 29 | BuildBinary darwin 30 | else 31 | BuildBinary "${1}" 32 | fi 33 | 34 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/bin/.gitkeep -------------------------------------------------------------------------------- /build/build.go: -------------------------------------------------------------------------------- 1 | package build 2 | 3 | import "fmt" 4 | 5 | var ( 6 | Version string 7 | GitCommit string 8 | BuildTime string 9 | ) 10 | 11 | func Print() { 12 | fmt.Println("Version: ", Version) 13 | fmt.Println("GitCommit: ", GitCommit) 14 | fmt.Println("BuildTime: ", BuildTime) 15 | } 16 | -------------------------------------------------------------------------------- /doc/log_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/doc/log_v2.png -------------------------------------------------------------------------------- /doc/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/doc/logo.png -------------------------------------------------------------------------------- /doc/table_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/doc/table_v2.png -------------------------------------------------------------------------------- /doc/vertical_v2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/doc/vertical_v2.png -------------------------------------------------------------------------------- /entry_v2.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/davyxu/tabtoy/build" 6 | "github.com/davyxu/tabtoy/v2" 7 | "github.com/davyxu/tabtoy/v2/i18n" 8 | "github.com/davyxu/tabtoy/v2/printer" 9 | "os" 10 | ) 11 | 12 | // v2特有 13 | var ( 14 | paramProtoVersion = flag.Int("protover", 3, "output .proto file version, 2 or 3") 15 | 16 | paramLuaEnumIntValue = flag.Bool("luaenumintvalue", false, "use int type in lua enum value") 17 | paramLuaTabHeader = flag.String("luatabheader", "", "output string to lua tab header") 18 | 19 | paramGenCSharpBinarySerializeCode = flag.Bool("cs_gensercode", true, "generate c# binary serialize code, default is true") 20 | ) 21 | 22 | func V2Entry() { 23 | g := printer.NewGlobals() 24 | 25 | if !i18n.SetLanguage(*paramLanguage) { 26 | log.Infof("language not support: %s", *paramLanguage) 27 | os.Exit(1) 28 | } 29 | 30 | g.Version = build.Version 31 | 32 | for _, v := range flag.Args() { 33 | g.InputFileList = append(g.InputFileList, v) 34 | } 35 | 36 | g.ParaMode = *paramPara 37 | g.CacheDir = *paramCacheDir 38 | g.UseCache = *paramUseCache 39 | g.CombineStructName = *paramCombineStructName 40 | g.ProtoVersion = *paramProtoVersion 41 | g.LuaEnumIntValue = *paramLuaEnumIntValue 42 | g.LuaTabHeader = *paramLuaTabHeader 43 | g.GenCSSerailizeCode = *paramGenCSharpBinarySerializeCode 44 | g.PackageName = *paramPackageName 45 | 46 | if *paramProtoOut != "" { 47 | g.AddOutputType("proto", *paramProtoOut) 48 | } 49 | 50 | if *paramPbtOut != "" { 51 | g.AddOutputType("pbt", *paramPbtOut) 52 | } 53 | 54 | if *paramJsonOut != "" { 55 | g.AddOutputType("json", *paramJsonOut) 56 | } 57 | 58 | if *paramLuaOut != "" { 59 | g.AddOutputType("lua", *paramLuaOut) 60 | } 61 | 62 | if *paramCSharpOut != "" { 63 | g.AddOutputType("cs", *paramCSharpOut) 64 | } 65 | 66 | if *paramGoOut != "" { 67 | g.AddOutputType("go", *paramGoOut) 68 | } 69 | 70 | if *paramCppOut != "" { 71 | g.AddOutputType("cpp", *paramCppOut) 72 | } 73 | 74 | if *paramBinaryOut != "" { 75 | g.AddOutputType("bin", *paramBinaryOut) 76 | } 77 | 78 | if *paramTypeOut != "" { 79 | g.AddOutputType("type", *paramTypeOut) 80 | } 81 | 82 | if *paramModifyList != "" { 83 | g.AddOutputType("modlist", *paramModifyList) 84 | } 85 | 86 | if !v2.Run(g) { 87 | os.Exit(1) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /entry_v2tov3.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "github.com/davyxu/tabtoy/v2tov3" 6 | "github.com/davyxu/tabtoy/v2tov3/model" 7 | "github.com/davyxu/tabtoy/v3/helper" 8 | "os" 9 | ) 10 | 11 | var ( 12 | paramUpgradeOut = flag.String("up_out", "", "upgrade v2 table to v3 format output dir") 13 | ) 14 | 15 | func V2ToV3Entry() { 16 | 17 | globals := model.NewGlobals() 18 | 19 | globals.TableGetter = helper.NewFileLoader(true, "") 20 | 21 | globals.SourceFileList = flag.Args() 22 | globals.OutputDir = *paramUpgradeOut 23 | 24 | if err := v2tov3.Upgrade(globals); err != nil { 25 | log.Errorln(err) 26 | os.Exit(1) 27 | return 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /flag.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "flag" 4 | 5 | // 标准参数 6 | var ( 7 | // 显示版本号 8 | paramVersion = flag.Bool("version", false, "Show version") 9 | 10 | // 工作模式 11 | paramMode = flag.String("mode", "", "v2") 12 | 13 | // 并发导出,提高导出速度, 输出日志会混乱 14 | paramPara = flag.Bool("para", false, "parallel export by your cpu count") 15 | 16 | // 并发导出,提高导出速度, 输出日志会混乱 17 | paramCacheDir = flag.String("cachedir", "./.tabtoycache", "cache file output dir") 18 | paramUseCache = flag.Bool("usecache", false, "use cache file enhanced exporting speed") 19 | 20 | // 源文件变化列表, 未使用cache的文件 21 | paramModifyList = flag.String("modlistfile", "", "output list to file, include not using cache input file list, means file has been modified") 22 | 23 | // 输出日志语言 24 | paramLanguage = flag.String("lan", "en_us", "set output language") 25 | ) 26 | 27 | var ( 28 | // 导出代码中包/命名空间名称 29 | paramPackageName = flag.String("package", "", "override the package name in table @Types") 30 | 31 | // 导出代码中类型名 32 | paramCombineStructName = flag.String("combinename", "Table", "combine struct name, code struct flagstr") 33 | 34 | // 单文件导出 35 | paramProtoOut = flag.String("proto_out", "", "output protobuf define (*.proto)") 36 | paramPbBinaryOut = flag.String("pbbin_out", "", "output protobuf binary (*.pbb)") 37 | paramPbtOut = flag.String("pbt_out", "", "output proto text format (*.pbt)") 38 | paramLuaOut = flag.String("lua_out", "", "output lua code (*.lua)") 39 | paramJsonOut = flag.String("json_out", "", "output json format (*.json)") 40 | paramJsonTypeOut = flag.String("jsontype_out", "", "output json type (*.json)") 41 | paramCSharpOut = flag.String("csharp_out", "", "output c# class and deserialize code (*.cs)") 42 | paramGoOut = flag.String("go_out", "", "output golang code (*.go)") 43 | paramBinaryOut = flag.String("binary_out", "", "output binary format(*.bin)") 44 | paramTypeOut = flag.String("type_out", "", "output table types(*.json)") 45 | paramCppOut = flag.String("cpp_out", "", "output c++ format (*.cpp)") 46 | paramJavaOut = flag.String("java_out", "", "output java code (*.java)") 47 | 48 | // 按表多文件导出 49 | paramJsonDir = flag.String("json_dir", "", "output json format (*.json) to dir") 50 | paramLuaDir = flag.String("lua_dir", "", "output lua format (*.lua) to dir") 51 | paramBinaryDir = flag.String("binary_dir", "", "output binary format (*.bin) to dir") 52 | paramPbBinaryDir = flag.String("pbbin_dir", "", "output binary format (*.pbb) to dir") 53 | ) 54 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/davyxu/tabtoy 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/ahmetb/go-linq v3.0.0+incompatible 7 | github.com/davyxu/golexer v0.1.0 8 | github.com/davyxu/golog v0.1.0 9 | github.com/davyxu/protoplus v0.1.0 10 | github.com/golang/protobuf v1.4.0 11 | github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect 12 | github.com/pkg/errors v0.8.1 13 | github.com/pkg/profile v1.4.0 14 | github.com/tealeg/xlsx v1.0.5 15 | golang.org/x/net v0.17.0 16 | golang.org/x/text v0.13.0 17 | google.golang.org/protobuf v1.23.0 18 | gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "fmt" 6 | "github.com/davyxu/golog" 7 | "github.com/davyxu/tabtoy/build" 8 | "github.com/pkg/profile" 9 | "os" 10 | ) 11 | 12 | var log = golog.New("main") 13 | var enableProfile = false 14 | 15 | func main() { 16 | 17 | flag.Parse() 18 | 19 | // 版本 20 | if *paramVersion { 21 | build.Print() 22 | return 23 | } 24 | 25 | switch *paramMode { 26 | case "v3": 27 | 28 | type stopper interface { 29 | Stop() 30 | } 31 | 32 | var s stopper 33 | 34 | if enableProfile { 35 | s = profile.Start(profile.CPUProfile, profile.ProfilePath(".")) 36 | } 37 | 38 | V3Entry() 39 | 40 | if s != nil { 41 | s.Stop() 42 | } 43 | case "exportorv2", "v2": 44 | V2Entry() 45 | case "v2tov3": 46 | V2ToV3Entry() 47 | default: 48 | fmt.Println("--mode not specify") 49 | os.Exit(1) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /util/changeext.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "path" 5 | "path/filepath" 6 | "strings" 7 | ) 8 | 9 | func ChangeExtension(filename, newExt string) string { 10 | 11 | file := filepath.Base(filename) 12 | 13 | return strings.TrimSuffix(file, path.Ext(file)) + newExt 14 | } 15 | -------------------------------------------------------------------------------- /util/conv.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import "strconv" 4 | 5 | func StringToPrimitive(str string, value interface{}) (error, bool) { 6 | switch raw := value.(type) { 7 | case *int32: 8 | v, err := strconv.ParseInt(str, 10, 32) 9 | if err != nil { 10 | return err, false 11 | } 12 | 13 | *raw = int32(v) 14 | case *int64: 15 | v, err := strconv.ParseInt(str, 10, 64) 16 | if err != nil { 17 | return err, false 18 | } 19 | 20 | *raw = v 21 | case *uint32: 22 | v, err := strconv.ParseUint(str, 10, 32) 23 | if err != nil { 24 | return err, false 25 | } 26 | 27 | *raw = uint32(v) 28 | case *uint64: 29 | v, err := strconv.ParseUint(str, 10, 64) 30 | if err != nil { 31 | return err, false 32 | } 33 | 34 | *raw = v 35 | case *string: 36 | *raw = str 37 | case *bool: 38 | 39 | var v bool 40 | var err error 41 | 42 | switch str { 43 | case "是": 44 | v = true 45 | case "否", "": 46 | v = false 47 | default: 48 | v, err = strconv.ParseBool(str) 49 | if err != nil { 50 | return err, false 51 | } 52 | } 53 | 54 | *raw = v 55 | case *float32: 56 | v, err := strconv.ParseFloat(str, 32) 57 | if err != nil { 58 | return err, false 59 | } 60 | 61 | *raw = float32(v) 62 | case *float64: 63 | v, err := strconv.ParseFloat(str, 64) 64 | if err != nil { 65 | return err, false 66 | } 67 | 68 | *raw = float64(v) 69 | 70 | default: 71 | return nil, false 72 | } 73 | 74 | return nil, true 75 | } 76 | -------------------------------------------------------------------------------- /util/rc2a.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "math" 7 | ) 8 | 9 | func mod(a, b int) int { 10 | return int(math.Mod(float64(a), float64(b))) 11 | } 12 | 13 | func str2int(s string) int { 14 | return int([]byte(s)[0]) 15 | } 16 | 17 | var asciiA = str2int("A") 18 | 19 | const unit = 26 20 | 21 | // 按excel格式 R1C1格式转A1 22 | func index2Alphabet(number int) string { 23 | 24 | if number < 1 { 25 | return "" 26 | } 27 | 28 | n := number 29 | 30 | nl := make([]int, 0) 31 | 32 | for { 33 | 34 | quo := n / unit 35 | 36 | var reminder int 37 | x := mod(n, unit) 38 | 39 | // 余数为0时, 要跳过这个0, 重新计算除数(影响进位) 40 | if x == 0 { 41 | reminder = unit 42 | n-- 43 | quo = n / unit 44 | } else { 45 | reminder = x 46 | } 47 | 48 | nl = append(nl, reminder) 49 | 50 | if quo == 0 { 51 | break 52 | } 53 | 54 | n = quo 55 | } 56 | 57 | var out bytes.Buffer 58 | 59 | for i := len(nl) - 1; i >= 0; i-- { 60 | 61 | v := nl[i] 62 | 63 | out.WriteString(string(v + asciiA - 1)) 64 | } 65 | 66 | return out.String() 67 | } 68 | 69 | // r,c都是base1 70 | func R1C1ToA1(r, c int) string { 71 | return fmt.Sprintf("%s%d", index2Alphabet(c), r) 72 | } 73 | -------------------------------------------------------------------------------- /util/rc2a_test.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestA1toR1C1(t *testing.T) { 8 | 9 | if index2Alphabet(1) != "A" { 10 | t.Fatal("A") 11 | } 12 | 13 | if index2Alphabet(25) != "Y" { 14 | t.Fatal("Y") 15 | } 16 | 17 | if index2Alphabet(26) != "Z" { 18 | t.Fatal("Z") 19 | } 20 | 21 | if index2Alphabet(135) != "EE" { 22 | t.Fatal("EE") 23 | } 24 | 25 | if index2Alphabet(675) != "YY" { 26 | t.Fatal("YY") 27 | } 28 | 29 | if index2Alphabet(676) != "YZ" { 30 | t.Fatal("YZ") 31 | } 32 | 33 | if index2Alphabet(677) != "ZA" { 34 | t.Fatal("ZA") 35 | } 36 | 37 | if index2Alphabet(702) != "ZZ" { 38 | t.Fatal("ZZ") 39 | } 40 | 41 | if index2Alphabet(704) != "AAB" { 42 | t.Fatal("AAB") 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /util/strwrapper.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | func StringEscape(s string) string { 8 | 9 | b := make([]byte, 0) 10 | 11 | var index int 12 | 13 | // 表中直接使用换行会干扰最终合并文件格式, 所以转成\n,由pbt文本解析层转回去 14 | for index < len(s) { 15 | c := s[index] 16 | 17 | switch c { 18 | case '"': 19 | b = append(b, '\\') 20 | b = append(b, '"') 21 | case '\n': 22 | b = append(b, '\\') 23 | b = append(b, 'n') 24 | case '\r': 25 | b = append(b, '\\') 26 | b = append(b, 'r') 27 | case '\\': 28 | 29 | var nextChar byte 30 | if index+1 < len(s) { 31 | nextChar = s[index+1] 32 | } 33 | 34 | b = append(b, '\\') 35 | 36 | switch nextChar { 37 | case 'n', 'r': 38 | default: 39 | b = append(b, c) 40 | } 41 | 42 | default: 43 | b = append(b, c) 44 | } 45 | 46 | index++ 47 | 48 | } 49 | 50 | return string(b) 51 | } 52 | 53 | func StringWrap(s string) string { 54 | return fmt.Sprintf("\"%s\"", s) 55 | } 56 | -------------------------------------------------------------------------------- /v2/csharp/Logger.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Diagnostics; 3 | 4 | namespace tabtoy 5 | { 6 | public enum LogLevel 7 | { 8 | Debug, 9 | Info, 10 | Warnning, 11 | Error, 12 | } 13 | 14 | public class LogTarget 15 | { 16 | public virtual void WriteLog(LogLevel level, string msg) 17 | { 18 | 19 | } 20 | 21 | public static string LevelToString( LogLevel level ) 22 | { 23 | switch (level) 24 | { 25 | case LogLevel.Debug: 26 | return "tabtoy [Debug] "; 27 | case LogLevel.Info: 28 | return "tabtoy [Info] "; 29 | case LogLevel.Warnning: 30 | return "tabtoy [Warn] "; 31 | case LogLevel.Error: 32 | return "tabtoy [Error] "; 33 | } 34 | 35 | return "tabtoy [Unknown] "; 36 | } 37 | } 38 | 39 | 40 | 41 | public class DebuggerTarget : LogTarget 42 | { 43 | public override void WriteLog(LogLevel level, string msg) 44 | { 45 | Debug.WriteLine( LevelToString(level) + msg ); 46 | } 47 | } 48 | 49 | 50 | public class Logger 51 | { 52 | List _targets = new List(); 53 | 54 | public void AddTarget(LogTarget tgt) 55 | { 56 | _targets.Add(tgt); 57 | } 58 | 59 | public void ClearTargets( ) 60 | { 61 | _targets.Clear(); 62 | } 63 | 64 | void WriteLine(LogLevel level, string msg) 65 | { 66 | foreach(var tgt in _targets ) 67 | { 68 | tgt.WriteLog(level, msg); 69 | } 70 | } 71 | 72 | public void DebugLine(string fmt, params object[] args) 73 | { 74 | var text = string.Format(fmt, args); 75 | 76 | WriteLine(LogLevel.Debug, text); 77 | } 78 | 79 | public void InfoLine(string fmt, params object[] args) 80 | { 81 | var text = string.Format(fmt, args); 82 | 83 | WriteLine(LogLevel.Info, text); 84 | } 85 | 86 | public void WarningLine(string fmt, params object[] args) 87 | { 88 | var text = string.Format(fmt, args); 89 | 90 | WriteLine(LogLevel.Warnning, text); 91 | } 92 | 93 | public void ErrorLine(string fmt, params object[] args) 94 | { 95 | var text = string.Format(fmt, args); 96 | 97 | WriteLine(LogLevel.Error, text); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /v2/datamerge.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/util" 5 | "github.com/davyxu/tabtoy/v2/model" 6 | ) 7 | 8 | func structFieldHasDefaultValue(structFD *model.FieldDescriptor) bool { 9 | 10 | d := structFD.Complex 11 | 12 | if d == nil { 13 | return false 14 | } 15 | 16 | for _, childFD := range d.Fields { 17 | 18 | if childFD.Meta.GetString("Default") != "" { 19 | return true 20 | } 21 | 22 | } 23 | 24 | return false 25 | } 26 | 27 | func mergeValues(modelData *model.DataModel, tab *model.Table, checker model.GlobalChecker) bool { 28 | 29 | var currFV *model.FieldValue 30 | 31 | for _, line := range modelData.Lines { 32 | 33 | record := model.NewRecord() 34 | 35 | for _, fv := range line.Values { 36 | 37 | currFV = fv 38 | 39 | var sugguestIgnore bool 40 | // repeated的, 没有填充的, 直接跳过, 不生成数据 41 | if fv.RawValue == "" && fv.FieldDef.Meta.GetString("Default") == "" { 42 | 43 | if !mustFillCheck(fv.FieldDef, fv.RawValue) { 44 | goto ErrorStop 45 | } 46 | 47 | if fv.FieldDef.IsRepeated { 48 | 49 | if fv.FieldDef.Type == model.FieldType_Struct { 50 | 51 | // 重复的 结构体字段, 且结构体字段没有默认值, 整个不导出 52 | if !structFieldHasDefaultValue(fv.FieldDef) { 53 | continue 54 | } 55 | 56 | } else { 57 | // 重复的普通字段导出, 做占位 58 | 59 | } 60 | 61 | } else { 62 | 63 | if fv.FieldDef.Type == model.FieldType_Struct { 64 | 65 | // 不重复的 结构体字段, 且结构体字段没有默认值, 整个不导出 66 | if !structFieldHasDefaultValue(fv.FieldDef) { 67 | 68 | sugguestIgnore = true 69 | } 70 | 71 | } else { 72 | 73 | // 非重复的普通字段不导出 74 | sugguestIgnore = true 75 | } 76 | 77 | } 78 | 79 | } 80 | 81 | if !coloumnProcessor(checker, record, fv.FieldDef, fv.RawValue, sugguestIgnore) { 82 | goto ErrorStop 83 | } 84 | 85 | } 86 | 87 | tab.Add(record) 88 | } 89 | 90 | return true 91 | 92 | ErrorStop: 93 | 94 | if currFV == nil { 95 | return false 96 | } 97 | 98 | log.Errorf("%s|%s(%s)", currFV.FileName, currFV.SheetName, util.R1C1ToA1(currFV.R, currFV.C)) 99 | return false 100 | 101 | } 102 | -------------------------------------------------------------------------------- /v2/dataproc.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/davyxu/tabtoy/v2/filter" 7 | "github.com/davyxu/tabtoy/v2/i18n" 8 | "github.com/davyxu/tabtoy/v2/model" 9 | ) 10 | 11 | func coloumnProcessor(file model.GlobalChecker, record *model.Record, fd *model.FieldDescriptor, raw string, sugguestIgnore bool) bool { 12 | 13 | spliter := fd.ListSpliter() 14 | 15 | if fd.IsRepeated && spliter != "" { 16 | 17 | valueList := strings.Split(raw, spliter) 18 | 19 | var node *model.Node 20 | 21 | if fd.Type != model.FieldType_Struct { 22 | node = record.NewNodeByDefine(fd) 23 | } 24 | 25 | for _, v := range valueList { 26 | 27 | rawSingle := strings.TrimSpace(v) 28 | 29 | // 结构体要多添加一个节点, 处理repeated 结构体情况 30 | if fd.Type == model.FieldType_Struct { 31 | node = record.NewNodeByDefine(fd) 32 | node.StructRoot = true 33 | node = node.AddKey(fd) 34 | } 35 | 36 | if raw != "" { 37 | if !dataProcessor(file, fd, rawSingle, node) { 38 | return false 39 | } 40 | } 41 | 42 | } 43 | 44 | } else { // 普通数据/repeated单元格分多个列 45 | 46 | node := record.NewNodeByDefine(fd) 47 | 48 | node.SugguestIgnore = sugguestIgnore 49 | 50 | // 结构体要多添加一个节点, 处理repeated 结构体情况 51 | if fd.Type == model.FieldType_Struct { 52 | 53 | node.StructRoot = true 54 | node = node.AddKey(fd) 55 | } 56 | 57 | node.SugguestIgnore = sugguestIgnore 58 | 59 | if !dataProcessor(file, fd, raw, node) { 60 | return false 61 | } 62 | } 63 | 64 | return true 65 | } 66 | 67 | func dataProcessor(gc model.GlobalChecker, fd *model.FieldDescriptor, raw string, node *model.Node) bool { 68 | 69 | // 单值 70 | if cv, ok := filter.ConvertValue(fd, raw, gc.GlobalFileDesc(), node); !ok { 71 | goto ConvertError 72 | 73 | } else { 74 | 75 | // 值重复检查 76 | if fd.Meta.GetBool("RepeatCheck") && !gc.CheckValueRepeat(fd, cv) { 77 | log.Errorf("%s, %s raw: '%s'", i18n.String(i18n.DataSheet_ValueRepeated), fd.String(), cv) 78 | return false 79 | } 80 | } 81 | 82 | return true 83 | 84 | ConvertError: 85 | 86 | log.Errorf("%s, %s raw: '%s'", i18n.String(i18n.DataSheet_ValueConvertError), fd.String(), raw) 87 | 88 | return false 89 | } 90 | -------------------------------------------------------------------------------- /v2/example/CompileExport.bat: -------------------------------------------------------------------------------- 1 | set TOOL_DIR=%cd% 2 | cd ..\..\..\..\..\.. 3 | set GOPATH=%cd% 4 | go build -v -o %GOPATH%\bin\tabtoy.exe github.com/davyxu/tabtoy 5 | 6 | cd %TOOL_DIR% 7 | 8 | call Export.bat -------------------------------------------------------------------------------- /v2/example/CompileExport.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | CurrDir=`pwd` 3 | cd ../../../../../.. 4 | export GOPATH=`pwd` 5 | go build -v -o ${GOPATH}/bin/tabtoy github.com/davyxu/tabtoy 6 | cd ${CurrDir} 7 | 8 | source ./Export.sh -------------------------------------------------------------------------------- /v2/example/Export.bat: -------------------------------------------------------------------------------- 1 | : 输出C#源码,二进制(例子中供C#读取), lua表, json格式 2 | : 适用于csharp, golang, lua例子 3 | ..\..\..\..\..\..\bin\tabtoy.exe ^ 4 | --mode=v2 ^ 5 | --csharp_out=.\csharp\Example\Config.cs ^ 6 | --binary_out=.\csharp\Example\Config.bin ^ 7 | --lua_out=.\lua\Config.lua ^ 8 | --proto_out=.\pb\proto.proto ^ 9 | --pbt_out=.\pb\data.pbt ^ 10 | --luaenumintvalue=true ^ 11 | --go_out=.\golang\table\table_gen.go ^ 12 | --json_out=.\golang\Config.json ^ 13 | --cpp_out=.\cpp\cpp\Config.h ^ 14 | --combinename=Config ^ 15 | --lan=zh_cn ^ 16 | Globals.xlsx ^ 17 | Sample.xlsx 18 | 19 | @IF %ERRORLEVEL% NEQ 0 pause -------------------------------------------------------------------------------- /v2/example/Export.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | ../../../../../../bin/tabtoy \ 3 | --mode=v2 \ 4 | --csharp_out=./csharp/Example/Config.cs \ 5 | --binary_out=./csharp/Example/Config.bin \ 6 | --lua_out=./lua/Config.lua \ 7 | --proto_out=./pb/proto.proto \ 8 | --pbt_out=./pb/data.pbt \ 9 | --luaenumintvalue=true \ 10 | --go_out=./golang/table/table_gen.go \ 11 | --json_out=./golang/Config.json \ 12 | --cpp_out=./cpp/cpp/Config.h \ 13 | --combinename=Config \ 14 | --lan=zh_cn \ 15 | Globals.xlsx \ 16 | Sample.xlsx -------------------------------------------------------------------------------- /v2/example/Globals.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/Globals.xlsx -------------------------------------------------------------------------------- /v2/example/Sample.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/Sample.xlsx -------------------------------------------------------------------------------- /v2/example/combine/CombineConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tool": "github.com/davyxu/tabtoy", 3 | "Version": "2.8.7", 4 | "Item":[ 5 | { "ID": 1, "UpgradeCost": 100 }, 6 | { "ID": 2, "UpgradeCost": 200 }, 7 | { "ID": 3, "GenPetID": 80001 }, 8 | { "ID": 4, "GenPetID": 7990 } 9 | ]} -------------------------------------------------------------------------------- /v2/example/combine/Export.bat: -------------------------------------------------------------------------------- 1 | ..\..\..\..\..\..\..\bin\tabtoy.exe ^ 2 | --mode=v2 ^ 3 | --json_out=CombineConfig.json ^ 4 | --combinename=Config ^ 5 | --lan=zh_cn ^ 6 | Item.xlsx+Item_Equip.xlsx+Item_Pet.xlsx 7 | 8 | @IF %ERRORLEVEL% NEQ 0 pause -------------------------------------------------------------------------------- /v2/example/combine/Item.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/combine/Item.xlsx -------------------------------------------------------------------------------- /v2/example/combine/Item_Equip.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/combine/Item_Equip.xlsx -------------------------------------------------------------------------------- /v2/example/combine/Item_Pet.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/combine/Item_Pet.xlsx -------------------------------------------------------------------------------- /v2/example/cpp/cpp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cpp", "cpp\cpp.vcxproj", "{4FC8AB0F-4D97-472C-A7C5-AD8508F29758}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {4FC8AB0F-4D97-472C-A7C5-AD8508F29758}.Debug|x64.ActiveCfg = Debug|x64 17 | {4FC8AB0F-4D97-472C-A7C5-AD8508F29758}.Debug|x64.Build.0 = Debug|x64 18 | {4FC8AB0F-4D97-472C-A7C5-AD8508F29758}.Debug|x86.ActiveCfg = Debug|Win32 19 | {4FC8AB0F-4D97-472C-A7C5-AD8508F29758}.Debug|x86.Build.0 = Debug|Win32 20 | {4FC8AB0F-4D97-472C-A7C5-AD8508F29758}.Release|x64.ActiveCfg = Release|x64 21 | {4FC8AB0F-4D97-472C-A7C5-AD8508F29758}.Release|x64.Build.0 = Release|x64 22 | {4FC8AB0F-4D97-472C-A7C5-AD8508F29758}.Release|x86.ActiveCfg = Release|Win32 23 | {4FC8AB0F-4D97-472C-A7C5-AD8508F29758}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /v2/example/cpp/cpp/ReadMe.txt: -------------------------------------------------------------------------------- 1 | ======================================================================== 2 | 控制台应用程序:cpp 项目概述 3 | ======================================================================== 4 | 5 | 应用程序向导已为您创建了此 cpp 应用程序。 6 | 7 | 本文件概要介绍组成 cpp 应用程序的每个文件的内容。 8 | 9 | 10 | cpp.vcxproj 11 | 这是使用应用程序向导生成的 VC++ 项目的主项目文件,其中包含生成该文件的 Visual C++ 的版本信息,以及有关使用应用程序向导选择的平台、配置和项目功能的信息。 12 | 13 | cpp.vcxproj.filters 14 | 这是使用“应用程序向导”生成的 VC++ 项目筛选器文件。它包含有关项目文件与筛选器之间的关联信息。在 IDE 中,通过这种关联,在特定节点下以分组形式显示具有相似扩展名的文件。例如,“.cpp”文件与“源文件”筛选器关联。 15 | 16 | cpp.cpp 17 | 这是主应用程序源文件。 18 | 19 | ///////////////////////////////////////////////////////////////////////////// 20 | 其他标准文件: 21 | 22 | StdAfx.h, StdAfx.cpp 23 | 这些文件用于生成名为 cpp.pch 的预编译头 (PCH) 文件和名为 StdAfx.obj 的预编译类型文件。 24 | 25 | ///////////////////////////////////////////////////////////////////////////// 26 | 其他注释: 27 | 28 | 应用程序向导使用“TODO:”注释来指示应添加或自定义的源代码部分。 29 | 30 | ///////////////////////////////////////////////////////////////////////////// 31 | -------------------------------------------------------------------------------- /v2/example/cpp/cpp/cpp.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/cpp/cpp/cpp.cpp -------------------------------------------------------------------------------- /v2/example/cpp/cpp/cpp.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | 头文件 20 | 21 | 22 | 头文件 23 | 24 | 25 | 头文件 26 | 27 | 28 | 头文件 29 | 30 | 31 | 头文件 32 | 33 | 34 | 35 | 36 | 源文件 37 | 38 | 39 | 源文件 40 | 41 | 42 | -------------------------------------------------------------------------------- /v2/example/cpp/cpp/stdafx.cpp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/cpp/cpp/stdafx.cpp -------------------------------------------------------------------------------- /v2/example/cpp/cpp/stdafx.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/cpp/cpp/stdafx.h -------------------------------------------------------------------------------- /v2/example/cpp/cpp/targetver.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/cpp/cpp/targetver.h -------------------------------------------------------------------------------- /v2/example/csharp/Example/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /v2/example/csharp/Example/Config.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/csharp/Example/Config.bin -------------------------------------------------------------------------------- /v2/example/csharp/Example/Example.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {4D2317BA-05F3-4030-BF57-9ADD24694DC1} 8 | Exe 9 | Properties 10 | csharptest 11 | csharptest 12 | v2.0 13 | 512 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 26 | 27 | AnyCPU 28 | pdbonly 29 | true 30 | bin\Release\ 31 | TRACE 32 | prompt 33 | 4 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | DataReader.cs 43 | 44 | 45 | Logger.cs 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 62 | -------------------------------------------------------------------------------- /v2/example/csharp/Example/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using tabtoy; 4 | 5 | namespace csharptest 6 | { 7 | class Program 8 | { 9 | 10 | static void Main(string[] args) 11 | { 12 | using (var stream = new FileStream("../../Config.bin", FileMode.Open)) 13 | { 14 | stream.Position = 0; 15 | 16 | var reader = new tabtoy.DataReader(stream); 17 | 18 | var config = new table.Config(); 19 | 20 | var result = reader.ReadHeader(config.GetBuildID()); 21 | if ( result != FileState.OK) 22 | { 23 | Console.WriteLine("combine file crack!"); 24 | return; 25 | } 26 | 27 | 28 | table.Config.Deserialize(config, reader); 29 | 30 | // 直接通过下标获取或遍历 31 | var directFetch = config.Sample[2]; 32 | 33 | // 添加日志输出或自定义输出 34 | config.TableLogger.AddTarget(new tabtoy.DebuggerTarget()); 35 | 36 | // 取空时, 当默认值不为空时, 输出日志 37 | var nullFetchOutLog = config.GetSampleByID(0); 38 | 39 | } 40 | 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /v2/example/csharp/Example/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("csharptest")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("csharptest")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("0c83e7d9-b3a0-4335-8c25-41c992cbea56")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /v2/example/csharp/csharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 2013 4 | VisualStudioVersion = 12.0.31101.0 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{4D2317BA-05F3-4030-BF57-9ADD24694DC1}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {4D2317BA-05F3-4030-BF57-9ADD24694DC1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {4D2317BA-05F3-4030-BF57-9ADD24694DC1}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {4D2317BA-05F3-4030-BF57-9ADD24694DC1}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {4D2317BA-05F3-4030-BF57-9ADD24694DC1}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | EndGlobal 23 | -------------------------------------------------------------------------------- /v2/example/golang/Config.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tool": "github.com/davyxu/tabtoy", 3 | "Version": "2.9.0", 4 | "Sample":[ 5 | { "ID": 100, "Name": "黑猫警长", "NumericalRate": 0.6, "ItemID": 100, "BuffID":[ 10 ], "Pos": { "X": 100, "Y": 89 }, "Type": 0, "SkillID":[ 4, 6, 7 ], "AttackParam": { "Value": 1 }, "SingleStruct": { "HP": 100, "AttackRate": 1.2 }, "StrStruct":[ { "HP": 3, "ExType": 0 }, { "HP": 10, "ExType": 1 } ] }, 6 | { "ID": 101, "Name": "葫芦\n娃", "NumericalRate": 0.8, "ItemID": 100, "BuffID":[ 3, 1 ], "Type": 2, "SkillID":[ 1 ], "SingleStruct": { "HP": 10 }, "StrStruct":[ { } ] }, 7 | { "ID": 102, "Name": "舒\"克\"", "NumericalRate": 0.7, "ItemID": 100, "BuffID":[ ], "Type": 3, "SkillID":[ ], "SingleStruct": { "HP": 10 }, "StrStruct":[ { } ] }, 8 | { "ID": 103, "Name": "贝\n塔", "ItemID": 100, "BuffID":[ ], "Type": 1, "SkillID":[ ], "SingleStruct": { "HP": 10 }, "StrStruct":[ { } ] }, 9 | { "ID": 104, "Name": "邋遢大王", "NumericalRate": 1, "ItemID": 100, "BuffID":[ ], "Type": 2, "SkillID":[ ], "SingleStruct": { "HP": 10 }, "StrStruct":[ { } ] } 10 | ]} -------------------------------------------------------------------------------- /v2/example/golang/Run.bat: -------------------------------------------------------------------------------- 1 | set CURRDIR=%cd% 2 | cd ../../../../../../.. 3 | set GOPATH=%cd% 4 | cd %CURRDIR% 5 | go run main.go 6 | @IF %ERRORLEVEL% NEQ 0 pause -------------------------------------------------------------------------------- /v2/example/golang/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/davyxu/tabtoy/v2/example/golang/table" 7 | ) 8 | 9 | func main() { 10 | 11 | config := table.NewConfigTable() 12 | 13 | if err := config.Load("Config.json"); err != nil { 14 | panic(err) 15 | } 16 | 17 | for index, v := range config.SampleByID { 18 | fmt.Println(index, v) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /v2/example/helloworld/Config.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tool": "github.com/davyxu/tabtoy", 3 | "Version": "2.8.7", 4 | "Empty":[ 5 | { "Data": "Hello world" } 6 | ]} -------------------------------------------------------------------------------- /v2/example/helloworld/Empty.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/helloworld/Empty.xlsx -------------------------------------------------------------------------------- /v2/example/helloworld/Export.bat: -------------------------------------------------------------------------------- 1 | ..\..\..\..\..\..\..\bin\tabtoy.exe ^ 2 | --mode=v2 ^ 3 | --json_out=Config.json ^ 4 | --combinename=Config ^ 5 | --lan=zh_cn ^ 6 | Empty.xlsx 7 | 8 | @IF %ERRORLEVEL% NEQ 0 pause -------------------------------------------------------------------------------- /v2/example/lua/Config.lua: -------------------------------------------------------------------------------- 1 | -- Generated by github.com/davyxu/tabtoy 2 | -- Version: 2.9.0 3 | 4 | local tab = { 5 | Sample = { 6 | { ID = 100, Name = "黑猫警长", EmptyName = "", IconID = 0, NumericalRate = 0.6, ItemID = 100, BuffID = { 10 }, Pos = { X= 100, Y= 89 }, Type = 0, SkillID = { 4, 6, 7 }, AttackParam = { Value= 1 }, SingleStruct = { HP= 100, AttackRate= 1.2 }, StrStruct = { { HP= 3, ExType= 0 }, { HP= 10, ExType= 1 } } }, 7 | { ID = 101, Name = "葫芦\n娃", EmptyName = "", IconID = 0, NumericalRate = 0.8, ItemID = 100, BuffID = { 3, 1 }, Pos = { }, Type = 2, SkillID = { 1 }, AttackParam = { }, SingleStruct = { HP= 10, AttackRate= 0, ExType= 0 }, StrStruct = { { } } }, 8 | { ID = 102, Name = "舒\"克\"", EmptyName = "", IconID = 0, NumericalRate = 0.7, ItemID = 100, BuffID = { }, Pos = { }, Type = 3, SkillID = { }, AttackParam = { }, SingleStruct = { HP= 10, AttackRate= 0, ExType= 0 }, StrStruct = { { } } }, 9 | { ID = 103, Name = "贝\n塔", EmptyName = "", IconID = 0, NumericalRate = 0, ItemID = 100, BuffID = { }, Pos = { }, Type = 1, SkillID = { }, AttackParam = { }, SingleStruct = { HP= 10, AttackRate= 0, ExType= 0 }, StrStruct = { { } } }, 10 | { ID = 104, Name = "邋遢大王", EmptyName = "", IconID = 0, NumericalRate = 1, ItemID = 100, BuffID = { }, Pos = { }, Type = 2, SkillID = { }, AttackParam = { }, SingleStruct = { HP= 10, AttackRate= 0, ExType= 0 }, StrStruct = { { } } } 11 | } 12 | 13 | } 14 | 15 | 16 | -- ID 17 | tab.SampleByID = {} 18 | for _, rec in pairs(tab.Sample) do 19 | tab.SampleByID[rec.ID] = rec 20 | end 21 | 22 | -- Name 23 | tab.SampleByName = {} 24 | for _, rec in pairs(tab.Sample) do 25 | tab.SampleByName[rec.Name] = rec 26 | end 27 | 28 | tab.Enum = { 29 | ActorType = { 30 | [0] = "Leader", 31 | [2] = "Pig", 32 | Monkey = 1, 33 | Hammer = 3, 34 | }, 35 | } 36 | 37 | return tab -------------------------------------------------------------------------------- /v2/example/lua/Run.bat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/lua/Run.bat -------------------------------------------------------------------------------- /v2/example/lua/read.lua: -------------------------------------------------------------------------------- 1 | -- 添加搜索路径 2 | package.path = package.path .. ";../?.lua" 3 | 4 | -- 加载 5 | local t = require "Config" 6 | 7 | -- 直接访问原始数据, 此处输出为UTF8格式, windows命令行下会出现乱码是正常现象 8 | print(t.Sample[1].Name) 9 | 10 | -- 通过索引访问 11 | print(t.SampleByID[103].ID) 12 | 13 | print(t.SampleByName["黑猫警长"].ID) -------------------------------------------------------------------------------- /v2/example/pb/data.pbt: -------------------------------------------------------------------------------- 1 | # Generated by github.com/davyxu/tabtoy 2 | # Version: 2.9.0 3 | Sample: [ 4 | {ID: 100, Name: "黑猫警长", NumericalRate: 0.6, ItemID: 100, BuffID:[ 10 ], Pos: { X: 100, Y: 89 }, Type: Leader, SkillID:[ 4, 6, 7 ], AttackParam: { Value: 1 }, SingleStruct: { HP: 100, AttackRate: 1.2 }, StrStruct:[ { HP: 3, ExType: Leader }, { HP: 10, ExType: Monkey } ] }, 5 | {ID: 101, Name: "葫芦\n娃", NumericalRate: 0.8, ItemID: 100, BuffID:[ 3, 1 ], Type: Pig, SkillID:[ 1 ], SingleStruct: { HP: 10, AttackRate: 0, ExType: Leader }, StrStruct:[ { } ] }, 6 | {ID: 102, Name: "舒\"克\"", NumericalRate: 0.7, ItemID: 100, BuffID:[ ], Type: Hammer, SkillID:[ ], SingleStruct: { HP: 10, AttackRate: 0, ExType: Leader }, StrStruct:[ { } ] }, 7 | {ID: 103, Name: "贝\n塔", ItemID: 100, BuffID:[ ], Type: Monkey, SkillID:[ ], SingleStruct: { HP: 10, AttackRate: 0, ExType: Leader }, StrStruct:[ { } ] }, 8 | {ID: 104, Name: "邋遢大王", NumericalRate: 1, ItemID: 100, BuffID:[ ], Type: Pig, SkillID:[ ], SingleStruct: { HP: 10, AttackRate: 0, ExType: Leader }, StrStruct:[ { } ] } 9 | ] 10 | 11 | -------------------------------------------------------------------------------- /v2/example/pb/proto.proto: -------------------------------------------------------------------------------- 1 | // Generated by github.com/davyxu/tabtoy 2 | // Version: 2.9.0 3 | // DO NOT EDIT!! 4 | 5 | syntax = "proto3"; 6 | 7 | package table; 8 | 9 | // Defined in table: Globals 10 | enum ActorType 11 | { 12 | 13 | // 唐僧 14 | Leader = 0; 15 | 16 | // 孙悟空 17 | Monkey = 1; 18 | 19 | // 猪八戒 20 | Pig = 2; 21 | 22 | // 沙僧 23 | Hammer = 3; 24 | 25 | } 26 | 27 | 28 | // Defined in table: Config 29 | message Config 30 | { 31 | 32 | 33 | repeated SampleDefine Sample = 1; // Sample 34 | 35 | } 36 | 37 | // Defined in table: Globals 38 | message Vec2 39 | { 40 | 41 | 42 | int32 X = 1; 43 | 44 | 45 | int32 Y = 2; 46 | 47 | } 48 | 49 | // Defined in table: Sample 50 | message Prop 51 | { 52 | 53 | // 血量 54 | int32 HP = 1; 55 | 56 | // 攻击速率 57 | float AttackRate = 2; 58 | 59 | // 额外类型 60 | ActorType ExType = 3; 61 | 62 | } 63 | 64 | // Defined in table: Sample 65 | message AttackParam 66 | { 67 | 68 | // 攻击值 69 | int32 Value = 1; 70 | 71 | } 72 | 73 | // Defined in table: Sample 74 | message SampleDefine 75 | { 76 | 77 | 78 | int64 ID = 1; // 唯一ID 79 | 80 | 81 | string Name = 2; // 名称 82 | 83 | 84 | string EmptyName = 3; 85 | 86 | 87 | int32 IconID = 4; // 图标ID 88 | 89 | 90 | float NumericalRate = 5; // 攻击率 91 | 92 | 93 | int32 ItemID = 6; // 物品id 94 | 95 | 96 | repeated int32 BuffID = 7; // BuffID 97 | 98 | 99 | Vec2 Pos = 8; // 位置 100 | 101 | 102 | ActorType Type = 9; // 类型 103 | 104 | 105 | repeated int32 SkillID = 10; // 技能ID列表 106 | 107 | 108 | AttackParam AttackParam = 11; // 攻击参数 109 | 110 | 111 | Prop SingleStruct = 12; // 单结构解析 112 | 113 | 114 | repeated Prop StrStruct = 13; // 字符串结构 115 | 116 | } 117 | 118 | -------------------------------------------------------------------------------- /v2/example/verticalconfig/Export.bat: -------------------------------------------------------------------------------- 1 | ..\..\..\..\..\..\..\bin\tabtoy.exe ^ 2 | --mode=v2 ^ 3 | --json_out=VerticalConfig.json ^ 4 | --combinename=Config ^ 5 | --lan=zh_cn ^ 6 | Vertical.xlsx 7 | 8 | @IF %ERRORLEVEL% NEQ 0 pause -------------------------------------------------------------------------------- /v2/example/verticalconfig/Vertical.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/example/verticalconfig/Vertical.xlsx -------------------------------------------------------------------------------- /v2/example/verticalconfig/VerticalConfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tool": "github.com/davyxu/tabtoy", 3 | "Version": "2.8.7", 4 | "Vertical":[ 5 | { "ServerIP": "192.168.0.1", "DebugMode": true, "ClientLimit": 3000, "Peer": { "Name": "Agent", "Type": "Acceptor" }, "Float": 0.5, "Token":[ 1, 2, 3 ] } 6 | ]} -------------------------------------------------------------------------------- /v2/exportor.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "path/filepath" 5 | "strings" 6 | 7 | "github.com/davyxu/tabtoy/v2/i18n" 8 | "github.com/davyxu/tabtoy/v2/model" 9 | "github.com/davyxu/tabtoy/v2/printer" 10 | ) 11 | 12 | func Run(g *printer.Globals) bool { 13 | 14 | if !g.PreExport() { 15 | return false 16 | } 17 | 18 | cachedFile := cacheFile(g) 19 | 20 | fileObjList := make([]*File, 0) 21 | 22 | log.Infof("==========%s==========", i18n.String(i18n.Run_CollectTypeInfo)) 23 | 24 | // 合并类型 25 | for _, in := range g.InputFileList { 26 | 27 | inputFile := in.(string) 28 | 29 | var mainMergeFile *File 30 | 31 | mergeFileList := strings.Split(inputFile, "+") 32 | 33 | for index, fileName := range mergeFileList { 34 | 35 | file, _ := cachedFile[fileName] 36 | 37 | if file == nil { 38 | return false 39 | } 40 | 41 | var mergeTarget string 42 | if len(mergeFileList) > 1 { 43 | mergeTarget = "--> " + filepath.Base(mergeFileList[0]) 44 | } 45 | 46 | log.Infoln(filepath.Base(fileName), mergeTarget) 47 | 48 | file.GlobalFD = g.FileDescriptor 49 | 50 | // 电子表格数据导出到Table对象 51 | if !file.ExportLocalType(mainMergeFile) { 52 | return false 53 | } 54 | 55 | // 主文件才写入全局信息 56 | if index == 0 { 57 | 58 | // 整合类型信息和数据 59 | if !g.AddTypes(file.LocalFD) { 60 | return false 61 | } 62 | 63 | // 只写入主文件的文件列表 64 | if file.Header != nil { 65 | 66 | fileObjList = append(fileObjList, file) 67 | } 68 | 69 | mainMergeFile = file 70 | } else { 71 | 72 | // 添加自文件 73 | mainMergeFile.mergeList = append(mainMergeFile.mergeList, file) 74 | 75 | } 76 | 77 | } 78 | 79 | } 80 | 81 | log.Infof("==========%s==========", i18n.String(i18n.Run_ExportSheetData)) 82 | 83 | for _, file := range fileObjList { 84 | 85 | log.Infoln(filepath.Base(file.FileName)) 86 | 87 | dataModel := model.NewDataModel() 88 | 89 | tab := model.NewTable() 90 | tab.LocalFD = file.LocalFD 91 | 92 | // 主表 93 | if !file.ExportData(dataModel, nil) { 94 | return false 95 | } 96 | 97 | // 子表提供数据 98 | for _, mergeFile := range file.mergeList { 99 | 100 | log.Infoln(filepath.Base(mergeFile.FileName), "--->", filepath.Base(file.FileName)) 101 | 102 | // 电子表格数据导出到Table对象 103 | if !mergeFile.ExportData(dataModel, file.Header) { 104 | return false 105 | } 106 | } 107 | 108 | // 合并所有值到node节点 109 | if !mergeValues(dataModel, tab, file) { 110 | return false 111 | } 112 | 113 | // 整合类型信息和数据 114 | if !g.AddContent(tab) { 115 | return false 116 | } 117 | 118 | } 119 | 120 | // 根据各种导出类型, 调用各导出器导出 121 | return g.Print() 122 | } 123 | -------------------------------------------------------------------------------- /v2/exprvm/cmd.go: -------------------------------------------------------------------------------- 1 | package exprvm 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type Command struct { 9 | Type Opcode 10 | 11 | Operand []interface{} 12 | } 13 | 14 | func (self *Command) String() string { 15 | 16 | var sb strings.Builder 17 | 18 | sb.WriteString(self.Type.String()) 19 | 20 | if len(self.Operand) > 0 { 21 | sb.WriteString(" ") 22 | for index, operand := range self.Operand { 23 | if index > 0 { 24 | sb.WriteString(", ") 25 | } 26 | sb.WriteString(fmt.Sprintf("%v", operand)) 27 | } 28 | } 29 | 30 | return sb.String() 31 | } 32 | 33 | type Chunk struct { 34 | Commands []Command 35 | } 36 | 37 | func (self *Chunk) String() string { 38 | 39 | var sb strings.Builder 40 | 41 | for _, cmd := range self.Commands { 42 | 43 | sb.WriteString(fmt.Sprintf("%s\n", cmd.String())) 44 | } 45 | 46 | return sb.String() 47 | } 48 | 49 | func (self *Chunk) AddCode(t Opcode) { 50 | 51 | self.Commands = append(self.Commands, Command{Type: t}) 52 | } 53 | 54 | func (self *Chunk) AddCodeOperand(t Opcode, operand ...interface{}) { 55 | 56 | self.Commands = append(self.Commands, Command{ 57 | Type: t, 58 | Operand: operand, 59 | }) 60 | } 61 | 62 | func newChunk() *Chunk { 63 | return &Chunk{} 64 | } 65 | -------------------------------------------------------------------------------- /v2/exprvm/compiler.go: -------------------------------------------------------------------------------- 1 | package exprvm 2 | 3 | import ( 4 | "go/ast" 5 | "go/parser" 6 | "go/token" 7 | "strconv" 8 | ) 9 | 10 | func Compile(src string) (*Chunk, error) { 11 | 12 | node, err := parser.ParseExpr(src) 13 | if err != nil { 14 | return nil, err 15 | } 16 | 17 | ast.Print(nil, node) 18 | 19 | ck := newChunk() 20 | 21 | if err := walkTree(ck, node); err != nil { 22 | return nil, err 23 | } 24 | 25 | return ck, nil 26 | } 27 | 28 | func walkTree(ck *Chunk, node ast.Node) (err error) { 29 | 30 | switch thisNode := node.(type) { 31 | case *ast.BinaryExpr: 32 | if err = walkTree(ck, thisNode.X); err != nil { 33 | return 34 | } 35 | 36 | if err = walkTree(ck, thisNode.Y); err != nil { 37 | return 38 | } 39 | 40 | switch thisNode.Op { 41 | case token.ADD: 42 | ck.AddCode(Opcode_Add) 43 | case token.SUB: 44 | ck.AddCode(Opcode_Sub) 45 | case token.MUL: 46 | ck.AddCode(Opcode_Mul) 47 | case token.QUO: 48 | ck.AddCode(Opcode_Div) 49 | } 50 | case *ast.BasicLit: // 字面量/常数 51 | 52 | switch thisNode.Kind { 53 | case token.INT: 54 | 55 | v, err := strconv.Atoi(thisNode.Value) 56 | if err != nil { 57 | return err 58 | } 59 | 60 | ck.AddCodeOperand(Opcode_Push, v) 61 | default: 62 | return ErrUnknownOperandType 63 | } 64 | case *ast.ParenExpr: 65 | return walkTree(ck, thisNode.X) 66 | case *ast.UnaryExpr: 67 | walkTree(ck, thisNode.X) 68 | ck.AddCode(Opcode_Minus) 69 | case *ast.Ident: // 变量/常量 70 | default: 71 | return ErrUnknownExpression 72 | } 73 | 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /v2/exprvm/compiler_test.go: -------------------------------------------------------------------------------- 1 | package exprvm 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestCompiler(t *testing.T) { 8 | 9 | code := `-2+1` 10 | 11 | ck, err := Compile(code) 12 | if err != nil { 13 | t.Log(err) 14 | t.FailNow() 15 | } 16 | 17 | t.Log(ck) 18 | 19 | vm := NewMachine() 20 | vm.Run(ck) 21 | 22 | t.Log(vm.DataStack.String()) 23 | } 24 | -------------------------------------------------------------------------------- /v2/exprvm/error.go: -------------------------------------------------------------------------------- 1 | package exprvm 2 | 3 | import "github.com/pkg/errors" 4 | 5 | var ( 6 | ErrUnknownExpression = errors.New("Unknown Expression") 7 | ErrUnknownOperandType = errors.New("Unknown Operand Type") 8 | ) 9 | -------------------------------------------------------------------------------- /v2/exprvm/opcode.go: -------------------------------------------------------------------------------- 1 | package exprvm 2 | 3 | type Opcode int 4 | 5 | const ( 6 | Opcode_Nop Opcode = iota 7 | Opcode_Push 8 | Opcode_Add 9 | Opcode_Sub 10 | Opcode_Mul 11 | Opcode_Div 12 | Opcode_Minus 13 | Opcode_Exit 14 | ) 15 | 16 | func (self Opcode) String() string { 17 | switch self { 18 | case Opcode_Nop: 19 | return "Nop" 20 | case Opcode_Push: 21 | return "Push" 22 | case Opcode_Add: 23 | return "Add" 24 | case Opcode_Sub: 25 | return "Sub" 26 | case Opcode_Mul: 27 | return "Mul" 28 | case Opcode_Div: 29 | return "Div" 30 | case Opcode_Minus: 31 | return "Minus" 32 | case Opcode_Exit: 33 | return "Exit" 34 | } 35 | 36 | return "Unknown" 37 | } 38 | -------------------------------------------------------------------------------- /v2/exprvm/stack.go: -------------------------------------------------------------------------------- 1 | package exprvm 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type node struct { 9 | value interface{} 10 | prev *node 11 | } 12 | type Stack struct { 13 | top *node 14 | length int 15 | } 16 | 17 | func (self *Stack) String() string { 18 | 19 | var sb strings.Builder 20 | 21 | index := 0 22 | for i := self.top; i != nil; i = i.prev { 23 | sb.WriteString(fmt.Sprintf("[%d] %v\n", index, i.value)) 24 | index++ 25 | } 26 | 27 | return sb.String() 28 | } 29 | 30 | // Create a new stack 31 | func NewStack() *Stack { 32 | return &Stack{nil, 0} 33 | } 34 | 35 | // Return the number of items in the stack 36 | func (this *Stack) Len() int { 37 | return this.length 38 | } 39 | 40 | // View the top item on the stack 41 | func (this *Stack) Peek() interface{} { 42 | if this.length == 0 { 43 | return nil 44 | } 45 | return this.top.value 46 | } 47 | 48 | // Pop the top item of the stack and return it 49 | func (this *Stack) Pop() interface{} { 50 | if this.length == 0 { 51 | return nil 52 | } 53 | 54 | n := this.top 55 | this.top = n.prev 56 | this.length-- 57 | return n.value 58 | } 59 | 60 | // Push a value onto the top of the stack 61 | func (this *Stack) Push(value interface{}) { 62 | n := &node{value, this.top} 63 | this.top = n 64 | this.length++ 65 | } 66 | -------------------------------------------------------------------------------- /v2/exprvm/vm.go: -------------------------------------------------------------------------------- 1 | package exprvm 2 | 3 | type Machine struct { 4 | DataStack *Stack 5 | } 6 | 7 | func (self *Machine) Run(chunk *Chunk) { 8 | 9 | for pc := 0; pc < len(chunk.Commands); { 10 | 11 | cmd := chunk.Commands[pc] 12 | 13 | if cmd.Type == Opcode_Exit { 14 | break 15 | } 16 | 17 | self.execute(cmd) 18 | 19 | pc++ 20 | } 21 | 22 | } 23 | 24 | func (self *Machine) execute(cmd Command) (err error) { 25 | 26 | switch cmd.Type { 27 | case Opcode_Push: 28 | self.DataStack.Push(cmd.Operand[0]) 29 | case Opcode_Add, Opcode_Sub, Opcode_Div, Opcode_Mul: 30 | v1 := self.DataStack.Pop() 31 | v2 := self.DataStack.Pop() 32 | 33 | result := arithOp(cmd.Type, v2, v1) 34 | 35 | self.DataStack.Push(result) 36 | case Opcode_Minus: 37 | v := self.DataStack.Pop() 38 | 39 | self.DataStack.Push(-v.(int)) 40 | 41 | default: 42 | panic("Unknown opcode") 43 | } 44 | 45 | return nil 46 | } 47 | 48 | func arithOp(op Opcode, a, b interface{}) interface{} { 49 | 50 | an := a.(int) 51 | bn := b.(int) 52 | 53 | switch op { 54 | case Opcode_Add: 55 | return an + bn 56 | case Opcode_Sub: 57 | return an - bn 58 | case Opcode_Mul: 59 | return an * bn 60 | case Opcode_Div: 61 | return an / bn 62 | } 63 | 64 | return nil 65 | } 66 | 67 | func NewMachine() *Machine { 68 | return &Machine{ 69 | DataStack: NewStack(), 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /v2/filecache.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v2/i18n" 5 | "github.com/davyxu/tabtoy/v2/printer" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "sync" 10 | ) 11 | 12 | func getFileList(g *printer.Globals) (ret []string) { 13 | // 合并类型 14 | for _, in := range g.InputFileList { 15 | 16 | inputFile := in.(string) 17 | 18 | mergeFileList := strings.Split(inputFile, "+") 19 | 20 | for _, fileName := range mergeFileList { 21 | ret = append(ret, fileName) 22 | } 23 | } 24 | 25 | return 26 | } 27 | 28 | func cacheFile(g *printer.Globals) (fileObjByName map[string]*File) { 29 | 30 | var fileObjByNameGuard sync.Mutex 31 | fileObjByName = map[string]*File{} 32 | 33 | log.Infof("==========%s==========", i18n.String(i18n.Run_CacheFile)) 34 | 35 | filelist := getFileList(g) 36 | 37 | var cachedir string 38 | if g.UseCache { 39 | cachedir = g.CacheDir 40 | 41 | os.Mkdir(g.CacheDir, 0666) 42 | } 43 | 44 | writeOK := func(xlsxFileName string) { 45 | file, fromCache := NewFile(xlsxFileName, cachedir) 46 | 47 | fileObjByNameGuard.Lock() 48 | fileObjByName[xlsxFileName] = file 49 | 50 | nameOnly := filepath.Base(xlsxFileName) 51 | 52 | if fromCache { 53 | log.Infof("%s [Cache]", nameOnly) 54 | } else { 55 | g.ModList = append(g.ModList, nameOnly) 56 | log.Infof("%s", nameOnly) 57 | } 58 | 59 | fileObjByNameGuard.Unlock() 60 | } 61 | 62 | // 这里加速效果良好, 默认开启 63 | var task sync.WaitGroup 64 | task.Add(len(filelist)) 65 | 66 | for _, filename := range filelist { 67 | 68 | go func(xlsxFileName string) { 69 | 70 | writeOK(xlsxFileName) 71 | task.Done() 72 | 73 | }(filename) 74 | 75 | } 76 | 77 | task.Wait() 78 | 79 | // 调试用 80 | //for _, filename := range filelist { 81 | // 82 | // writeOK(filename) 83 | //} 84 | 85 | return 86 | } 87 | -------------------------------------------------------------------------------- /v2/filter/log.go: -------------------------------------------------------------------------------- 1 | package filter 2 | 3 | import ( 4 | "github.com/davyxu/golog" 5 | ) 6 | 7 | var log *golog.Logger = golog.New("filter") 8 | -------------------------------------------------------------------------------- /v2/filter/sflist.go: -------------------------------------------------------------------------------- 1 | package filter 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/davyxu/tabtoy/v2/model" 7 | ) 8 | 9 | type structFieldResult struct { 10 | key *model.FieldDescriptor 11 | value string 12 | } 13 | 14 | type structFieldList struct { 15 | data []*structFieldResult 16 | } 17 | 18 | func (self *structFieldList) Add(fd *model.FieldDescriptor, value string) { 19 | 20 | self.data = append(self.data, &structFieldResult{ 21 | key: fd, 22 | value: value, 23 | }) 24 | 25 | } 26 | 27 | func (self *structFieldList) Exists(fd *model.FieldDescriptor) bool { 28 | for _, libfd := range self.data { 29 | if libfd.key == fd { 30 | return true 31 | } 32 | } 33 | 34 | return false 35 | } 36 | 37 | func (self *structFieldList) Len() int { 38 | return len(self.data) 39 | } 40 | 41 | func (self *structFieldList) Get(index int) *structFieldResult { 42 | return self.data[index] 43 | } 44 | 45 | func (self *structFieldList) Swap(i, j int) { 46 | self.data[i], self.data[j] = self.data[j], self.data[i] 47 | } 48 | 49 | func (self *structFieldList) Less(i, j int) bool { 50 | 51 | a := self.data[i] 52 | b := self.data[j] 53 | 54 | return a.key.Order < b.key.Order 55 | } 56 | 57 | func (self *structFieldList) Sort() { 58 | sort.Sort(self) 59 | } 60 | 61 | func newStructFieldList() *structFieldList { 62 | return &structFieldList{} 63 | } 64 | -------------------------------------------------------------------------------- /v2/i18n/i18n.go: -------------------------------------------------------------------------------- 1 | package i18n 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type StringID int 8 | 9 | const ( 10 | ConvertValue_EnumTypeNil StringID = iota 11 | ConvertValue_StructTypeNil 12 | ConvertValue_EnumValueNotFound 13 | ConvertValue_UnknownFieldType 14 | StructParser_LexerError 15 | StructParser_ExpectField 16 | StructParser_UnexpectedSpliter 17 | StructParser_FieldNotFound 18 | StructParser_DuplicateFieldInCell 19 | Run_CacheFile 20 | Run_CollectTypeInfo 21 | Run_ExportSheetData 22 | Globals_CombineNameLost 23 | Globals_PackageNameDiff 24 | Globals_TableNameDuplicated 25 | Globals_OutputCombineData 26 | Globals_DuplicateTypeName 27 | File_TypeSheetKeepSingleton 28 | File_TypeSheetNotFound 29 | DataSheet_ValueConvertError 30 | DataSheet_ValueRepeated 31 | DataSheet_RowDataSplitedByEmptyLine 32 | DataSheet_MustFill 33 | DataHeader_TypeNotFound 34 | DataHeader_MetaParseFailed 35 | DataHeader_DuplicateFieldName 36 | DataHeader_RepeatedFieldTypeNotSameInMultiColumn 37 | DataHeader_RepeatedFieldMetaNotSameInMultiColumn 38 | DataHeader_UseReservedTypeName 39 | DataHeader_NotMatch 40 | DataHeader_FieldNotDefinedInMainTableInMultiTableMode 41 | DataHeader_NotMatchInMultiTableMode 42 | TypeSheet_PragmaParseFailed 43 | TypeSheet_TableNameIsEmpty 44 | TypeSheet_PackageIsEmpty 45 | TypeSheet_FieldTypeNotFound 46 | TypeSheet_EnumValueParseFailed 47 | TypeSheet_DescriptorKindNotSame 48 | TypeSheet_FieldMetaParseFailed 49 | TypeSheet_StructFieldCanNotBeStruct 50 | TypeSheet_FirstEnumValueShouldBeZero 51 | TypeSheet_UnexpectedTypeHeader 52 | TypeSheet_DuplicatedEnumValue 53 | TypeSheet_RowDataSplitedByEmptyLine 54 | TypeSheet_ObjectNameEmpty 55 | TypeSheet_DuplicateFieldName 56 | Printer_IgnoredByOutputTag 57 | Printer_OpenWriteOutputFileFailed 58 | System_OpenReadXlsxFailed 59 | ) 60 | 61 | var currLan map[StringID]string 62 | 63 | var lanByStr = make(map[string]map[StringID]string) 64 | 65 | func String(id StringID) string { 66 | 67 | if currLan == nil { 68 | return "!!i18n not set!!" 69 | } 70 | 71 | if str, ok := currLan[id]; ok { 72 | return str 73 | } 74 | 75 | return fmt.Sprintf("i18n:%v", id) 76 | } 77 | 78 | func SetLanguage(lan string) bool { 79 | if v, ok := lanByStr[lan]; ok { 80 | currLan = v 81 | } else { 82 | return false 83 | } 84 | 85 | return true 86 | } 87 | 88 | func registerLanguage(lan string, data map[StringID]string) { 89 | lanByStr[lan] = data 90 | } 91 | -------------------------------------------------------------------------------- /v2/log.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import "github.com/davyxu/golog" 4 | 5 | var log = golog.New("exportorv2") 6 | -------------------------------------------------------------------------------- /v2/model/BuiltinTypes.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/model/BuiltinTypes.xlsx -------------------------------------------------------------------------------- /v2/model/Export.bat: -------------------------------------------------------------------------------- 1 | ..\..\..\..\..\..\bin\tabtoy.exe ^ 2 | --mode=exportorv2 ^ 3 | --go_out=.\types_gen.go ^ 4 | --combinename=Builtin ^ 5 | --lan=zh_cn ^ 6 | BuiltinTypes.xlsx 7 | 8 | @IF %ERRORLEVEL% NEQ 0 pause 9 | -------------------------------------------------------------------------------- /v2/model/datamodel.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "sort" 4 | 5 | type GlobalChecker interface { 6 | CheckValueRepeat(fd *FieldDescriptor, value string) bool 7 | 8 | GlobalFileDesc() *FileDescriptor 9 | } 10 | 11 | type FieldValue struct { 12 | FieldDef *FieldDescriptor 13 | RawValue string 14 | R int 15 | C int 16 | SheetName string 17 | FileName string 18 | FieldRepeatedCount int // repeated拆成多列时, 这样重复列的数量 19 | } 20 | 21 | // 对应record 22 | type LineData struct { 23 | Values []*FieldValue 24 | } 25 | 26 | func (self *LineData) Len() int { 27 | return len(self.Values) 28 | } 29 | 30 | func (self *LineData) Swap(i, j int) { 31 | self.Values[i], self.Values[j] = self.Values[j], self.Values[i] 32 | } 33 | 34 | func (self *LineData) Less(i, j int) bool { 35 | 36 | a := self.Values[i] 37 | b := self.Values[j] 38 | 39 | // repeated字段分多个单元格导出时, 由于进行数据排序, 所以需要增加对列排序因子保证最终数据正确性 40 | return a.FieldDef.Order+int32(a.C) < b.FieldDef.Order+int32(b.C) 41 | } 42 | 43 | func (self *LineData) Add(fv *FieldValue) { 44 | 45 | self.Values = append(self.Values, fv) 46 | } 47 | 48 | func NewLineData() *LineData { 49 | return new(LineData) 50 | } 51 | 52 | // 对应table 53 | type DataModel struct { 54 | Lines []*LineData 55 | } 56 | 57 | func (self *DataModel) Add(data *LineData) { 58 | 59 | sort.Sort(data) 60 | 61 | self.Lines = append(self.Lines, data) 62 | } 63 | 64 | func NewDataModel() *DataModel { 65 | return new(DataModel) 66 | } 67 | -------------------------------------------------------------------------------- /v2/model/descriptor.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "errors" 4 | 5 | type DescriptorKind int 6 | 7 | const ( 8 | DescriptorKind_None DescriptorKind = iota 9 | DescriptorKind_Enum 10 | DescriptorKind_Struct 11 | ) 12 | 13 | type DescriptorUsage int 14 | 15 | const ( 16 | DescriptorUsage_None DescriptorUsage = iota 17 | DescriptorUsage_RowType // 每个表的行类型 18 | DescriptorUsage_CombineStruct // 最终使用的合并结构体 19 | ) 20 | 21 | type Descriptor struct { 22 | Name string 23 | Kind DescriptorKind 24 | Usage DescriptorUsage 25 | 26 | FieldByName map[string]*FieldDescriptor 27 | FieldByNumber map[int32]*FieldDescriptor 28 | Fields []*FieldDescriptor 29 | 30 | Indexes []*FieldDescriptor 31 | IndexByName map[string]*FieldDescriptor 32 | 33 | File *FileDescriptor 34 | } 35 | 36 | var ( 37 | ErrDuplicateFieldName = errors.New("Duplicate field name") 38 | ErrDuplicateIndexName = errors.New("Duplicate index name") 39 | ) 40 | 41 | func (self *Descriptor) Add(def *FieldDescriptor) error { 42 | 43 | def.Parent = self 44 | def.Order = int32(len(self.Fields)) 45 | 46 | // 创建字段 47 | if _, ok := self.FieldByName[def.Name]; ok { 48 | return ErrDuplicateFieldName 49 | } else { 50 | self.FieldByName[def.Name] = def 51 | self.FieldByNumber[def.EnumValue] = def 52 | self.Fields = append(self.Fields, def) 53 | } 54 | 55 | // 创建索引 56 | if def.Meta.GetBool("MakeIndex") { 57 | 58 | if _, ok := self.IndexByName[def.Name]; ok { 59 | return ErrDuplicateIndexName 60 | } else { 61 | self.IndexByName[def.Name] = def 62 | self.Indexes = append(self.Indexes, def) 63 | } 64 | } 65 | 66 | return nil 67 | } 68 | 69 | func (self *Descriptor) FieldByValueAndMeta(value string) *FieldDescriptor { 70 | 71 | for _, v := range self.FieldByName { 72 | 73 | if v.Name == value { 74 | return v 75 | } 76 | 77 | if v.Meta.GetString("Alias") == value { 78 | return v 79 | } 80 | 81 | } 82 | 83 | return nil 84 | } 85 | 86 | func NewDescriptor() *Descriptor { 87 | return &Descriptor{ 88 | FieldByName: make(map[string]*FieldDescriptor), 89 | FieldByNumber: make(map[int32]*FieldDescriptor), 90 | IndexByName: make(map[string]*FieldDescriptor), 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /v2/model/filedesc.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type FileDescriptor struct { 4 | Name string 5 | DescriptorByName map[string]*Descriptor 6 | Descriptors []*Descriptor 7 | 8 | Pragma *MetaInfo 9 | } 10 | 11 | func (self *FileDescriptor) MatchTag(tag string) bool { 12 | 13 | if !self.Pragma.ContainKey("OutputTag") { 14 | return true 15 | } 16 | 17 | return self.Pragma.ContainValue("OutputTag", tag) 18 | 19 | } 20 | 21 | // 取行类型的结构 22 | func (self *FileDescriptor) RowDescriptor() *Descriptor { 23 | 24 | for _, d := range self.Descriptors { 25 | if d.Usage == DescriptorUsage_RowType { 26 | return d 27 | } 28 | } 29 | 30 | return nil 31 | } 32 | 33 | func (self *FileDescriptor) Add(def *Descriptor) bool { 34 | 35 | if _, ok := self.DescriptorByName[def.Name]; ok { 36 | 37 | return false 38 | //panic("duplicate buildin type") 39 | } 40 | 41 | // Descriptor会在每个表对应的FileDescriptor中和CombineFileDescriptor中同时存在 42 | // 这里忽略CombineFileDescriptor, 保持原有文件类型 43 | if def.File == nil { 44 | def.File = self 45 | } 46 | 47 | self.Descriptors = append(self.Descriptors, def) 48 | self.DescriptorByName[def.Name] = def 49 | 50 | return true 51 | } 52 | 53 | func NewFileDescriptor() *FileDescriptor { 54 | return &FileDescriptor{ 55 | DescriptorByName: make(map[string]*Descriptor), 56 | Pragma: NewMetaInfo(), 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /v2/model/log.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/davyxu/golog" 5 | ) 6 | 7 | var log *golog.Logger = golog.New("model") 8 | 9 | const TypeSheetName = "@Types" 10 | -------------------------------------------------------------------------------- /v2/model/metainfo.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "sort" 5 | 6 | "github.com/davyxu/golexer" 7 | ) 8 | 9 | var builtinTag = map[string]bool{ 10 | "MakeIndex": true, 11 | "Alias": true, 12 | "Default": true, 13 | "ListSpliter": true, 14 | "RepeatCheck": true, 15 | "TableName": true, 16 | "Package": true, 17 | "OutputTag": true, 18 | } 19 | 20 | func IsSystemTag(tag string) bool { 21 | _, ok := builtinTag[tag] 22 | return ok 23 | } 24 | 25 | type MetaInfo struct { 26 | *golexer.KVPair 27 | } 28 | 29 | func (self *MetaInfo) VisitUserMeta(callback func(string, interface{}) bool) { 30 | 31 | sortedKeys := make([]string, 0) 32 | 33 | for k, _ := range self.Raw() { 34 | 35 | if IsSystemTag(k) { 36 | continue 37 | } 38 | 39 | sortedKeys = append(sortedKeys, k) 40 | } 41 | 42 | sort.Strings(sortedKeys) 43 | 44 | for _, k := range sortedKeys { 45 | v, _ := self.Raw()[k] 46 | 47 | if !callback(k, v) { 48 | return 49 | } 50 | } 51 | 52 | } 53 | 54 | func NewMetaInfo() *MetaInfo { 55 | return &MetaInfo{ 56 | KVPair: golexer.NewKVPair(), 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /v2/model/node.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Node struct { 4 | *FieldDescriptor 5 | 6 | StructRoot bool // 结构体标记的dummy node 7 | 8 | // 各种类型的值 9 | Value string 10 | EnumValue int32 11 | Raw []byte 12 | 13 | Child []*Node // 优先遍历值, 再key 14 | 15 | SugguestIgnore bool // 建议忽略, 非repeated的普通字段导出时, 如果原单元格没填, 这个字段为true 16 | } 17 | 18 | func (self *Node) AddValue(value string) *Node { 19 | 20 | n := &Node{ 21 | Value: value, 22 | } 23 | self.Child = append(self.Child, n) 24 | 25 | return n 26 | } 27 | 28 | func (self *Node) AddKey(def *FieldDescriptor) *Node { 29 | n := &Node{ 30 | FieldDescriptor: def, 31 | } 32 | self.Child = append(self.Child, n) 33 | 34 | return n 35 | } 36 | -------------------------------------------------------------------------------- /v2/model/table.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Record struct { 4 | nodeByFD map[*FieldDescriptor]*Node 5 | Nodes []*Node 6 | } 7 | 8 | func (self *Record) NewNodeByDefine(def *FieldDescriptor) *Node { 9 | 10 | // 如果这个单元格数据有, 使用已经有的定义, 因为字段不会重复 11 | // 主要处理repeated散开的case 12 | if exist, ok := self.nodeByFD[def]; ok { 13 | return exist 14 | } 15 | 16 | node := new(Node) 17 | node.FieldDescriptor = def 18 | self.nodeByFD[def] = node 19 | self.Nodes = append(self.Nodes, node) 20 | 21 | return node 22 | } 23 | 24 | func NewRecord() *Record { 25 | return &Record{ 26 | nodeByFD: make(map[*FieldDescriptor]*Node), 27 | } 28 | } 29 | 30 | type Table struct { 31 | LocalFD *FileDescriptor 32 | GlobalFD *FileDescriptor 33 | Recs []*Record 34 | } 35 | 36 | func (self *Table) Add(r *Record) { 37 | self.Recs = append(self.Recs, r) 38 | } 39 | 40 | func (self *Table) Name() string { 41 | return self.LocalFD.Name 42 | } 43 | 44 | func NewTable() *Table { 45 | return &Table{} 46 | } 47 | -------------------------------------------------------------------------------- /v2/printer/log.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import ( 4 | "github.com/davyxu/golog" 5 | ) 6 | 7 | var log *golog.Logger = golog.New("printer") 8 | -------------------------------------------------------------------------------- /v2/printer/modfile.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | type modlistPrinter struct { 4 | } 5 | 6 | func (self *modlistPrinter) Run(g *Globals) *Stream { 7 | 8 | bf := NewStream() 9 | 10 | for _, filename := range g.ModList { 11 | bf.Printf("%s\n", filename) 12 | } 13 | 14 | return bf 15 | } 16 | 17 | func init() { 18 | 19 | RegisterPrinter("modlist", &modlistPrinter{}) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /v2/printer/printer.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | type PrinterContext struct { 4 | outFile string 5 | p Printer 6 | name string 7 | } 8 | 9 | func (self *PrinterContext) Start(g *Globals) bool { 10 | 11 | log.Infof("[%s] %s\n", self.name, self.outFile) 12 | 13 | bf := self.p.Run(g) 14 | 15 | if bf == nil { 16 | return false 17 | } 18 | 19 | return bf.WriteFile(self.outFile) == nil 20 | } 21 | 22 | type Printer interface { 23 | Run(g *Globals) *Stream 24 | } 25 | 26 | var printerByExt = make(map[string]Printer) 27 | 28 | func RegisterPrinter(ext string, p Printer) { 29 | 30 | if _, ok := printerByExt[ext]; ok { 31 | panic("duplicate printer") 32 | } 33 | 34 | printerByExt[ext] = p 35 | } 36 | -------------------------------------------------------------------------------- /v2/printer/type.go: -------------------------------------------------------------------------------- 1 | package printer 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/davyxu/tabtoy/v2/i18n" 7 | "github.com/davyxu/tabtoy/v2/model" 8 | ) 9 | 10 | type typePrinter struct { 11 | } 12 | 13 | // 一个列字段 14 | type typeFieldModel struct { 15 | Name string 16 | Type string 17 | Kind string 18 | IsRepeated bool 19 | Meta map[string]interface{} 20 | Comment string 21 | 22 | Value int 23 | } 24 | 25 | // 一张表的类型信息 26 | type typeStructModel struct { 27 | Name string 28 | Fields []*typeFieldModel 29 | } 30 | 31 | // 整个文件类型信息 32 | type typeFileModel struct { 33 | Tool string 34 | Version string 35 | Structs []*typeStructModel 36 | Enums []*typeStructModel 37 | } 38 | 39 | func (self *typePrinter) Run(g *Globals) *Stream { 40 | 41 | bf := NewStream() 42 | 43 | var fm typeFileModel 44 | fm.Tool = "github.com/davyxu/tabtoy" 45 | fm.Version = g.Version 46 | 47 | // 遍历所有类型 48 | for _, d := range g.FileDescriptor.Descriptors { 49 | 50 | // 这给被限制输出 51 | if !d.File.MatchTag(".type") { 52 | log.Infof("%s: %s", i18n.String(i18n.Printer_IgnoredByOutputTag), d.Name) 53 | continue 54 | } 55 | 56 | structM := &typeStructModel{ 57 | Name: d.Name, 58 | } 59 | 60 | // 遍历字段 61 | for _, fd := range d.Fields { 62 | 63 | // 对CombineStruct的XXDefine对应的字段 64 | if d.Usage == model.DescriptorUsage_CombineStruct { 65 | 66 | // 这个字段被限制输出 67 | if fd.Complex != nil && !fd.Complex.File.MatchTag(".type") { 68 | continue 69 | } 70 | } 71 | 72 | field := &typeFieldModel{ 73 | Name: fd.Name, 74 | Type: fd.TypeString(), 75 | Kind: fd.KindString(), 76 | IsRepeated: fd.IsRepeated, 77 | Comment: fd.Comment, 78 | Meta: fd.Meta.Raw(), 79 | } 80 | 81 | switch d.Kind { 82 | case model.DescriptorKind_Struct: 83 | field.Value = 0 84 | case model.DescriptorKind_Enum: 85 | field.Value = int(fd.EnumValue) 86 | } 87 | 88 | structM.Fields = append(structM.Fields, field) 89 | 90 | } 91 | 92 | switch d.Kind { 93 | case model.DescriptorKind_Struct: 94 | fm.Structs = append(fm.Structs, structM) 95 | case model.DescriptorKind_Enum: 96 | fm.Enums = append(fm.Enums, structM) 97 | } 98 | 99 | } 100 | 101 | data, err := json.MarshalIndent(&fm, "", " ") 102 | if err != nil { 103 | log.Errorln(err) 104 | } 105 | 106 | bf.WriteRawBytes(data) 107 | 108 | return bf 109 | } 110 | 111 | func init() { 112 | 113 | RegisterPrinter("type", &typePrinter{}) 114 | 115 | } 116 | -------------------------------------------------------------------------------- /v2/sheet.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "github.com/tealeg/xlsx" 5 | "strings" 6 | ) 7 | 8 | // 描述一个表单 9 | type Sheet struct { 10 | *xlsx.Sheet 11 | 12 | Row int // 当前行 13 | 14 | Column int // 当前列 15 | 16 | file *File // 指向父级 17 | 18 | } 19 | 20 | // 取行列信息 21 | func (self *Sheet) GetRC() (int, int) { 22 | 23 | return self.Row + 1, self.Column + 1 24 | 25 | } 26 | 27 | // 获取单元格 cursor=行, index=列 28 | func (self *Sheet) GetCellData(cursor, index int) string { 29 | 30 | if cursor >= len(self.Rows) { 31 | return "" 32 | } 33 | 34 | r := self.Rows[cursor] 35 | for len(r.Cells) <= index { 36 | r.AddCell() 37 | } 38 | 39 | return strings.TrimSpace(r.Cells[index].Value) 40 | } 41 | 42 | func (self *Sheet) GetCellDataAsNumeric(cursor, index int) string { 43 | 44 | if cursor >= len(self.Rows) { 45 | return "" 46 | } 47 | 48 | r := self.Rows[cursor] 49 | for len(r.Cells) <= index { 50 | r.AddCell() 51 | } 52 | 53 | gn, err := r.Cells[index].GeneralNumeric() 54 | 55 | if err != nil { 56 | return "" 57 | } 58 | 59 | return gn 60 | } 61 | 62 | // 设置单元格 63 | func (self *Sheet) SetCellData(cursor, index int, data string) { 64 | 65 | self.Cell(cursor, index).Value = data 66 | } 67 | 68 | // 整行都是空的 69 | func (self *Sheet) IsFullRowEmpty(row, maxCol int) bool { 70 | 71 | for col := 0; col < maxCol; col++ { 72 | 73 | data := self.GetCellData(row, col) 74 | 75 | if data != "" { 76 | return false 77 | } 78 | } 79 | 80 | return true 81 | } 82 | 83 | func NewSheet(file *File, sheet *xlsx.Sheet) *Sheet { 84 | self := &Sheet{ 85 | file: file, 86 | Sheet: sheet, 87 | } 88 | 89 | return self 90 | } 91 | -------------------------------------------------------------------------------- /v2/sheet_datav.go: -------------------------------------------------------------------------------- 1 | package v2 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/davyxu/tabtoy/util" 7 | "github.com/davyxu/tabtoy/v2/i18n" 8 | "github.com/davyxu/tabtoy/v2/model" 9 | ) 10 | 11 | const ( 12 | ColumnMajor_RowDataBegin = 1 13 | ColumnMajor_ColumnValue = 4 14 | ) 15 | 16 | // 导出适合配置格式的表格 17 | func (self *DataSheet) exportColumnMajor(file *File, dataModel *model.DataModel, dataHeader, parentHeader *DataHeader) bool { 18 | 19 | // 是否继续读行 20 | var readingLine bool = true 21 | 22 | var meetEmptyLine bool 23 | 24 | var warningAfterEmptyLineDataOnce bool 25 | 26 | line := model.NewLineData() 27 | 28 | for self.Row = ColumnMajor_RowDataBegin; readingLine; self.Row++ { 29 | // 整行都是空的 30 | if self.IsFullRowEmpty(self.Row, dataHeader.RawFieldCount()) { 31 | 32 | // 再次碰空行, 表示确实是空的 33 | if meetEmptyLine { 34 | break 35 | 36 | } else { 37 | meetEmptyLine = true 38 | } 39 | 40 | continue 41 | 42 | } else { 43 | 44 | //已经碰过空行, 这里又碰到数据, 说明有人为隔出的空行, 做warning提醒, 防止数据没导出 45 | if meetEmptyLine && !warningAfterEmptyLineDataOnce { 46 | r, _ := self.GetRC() 47 | 48 | log.Warnf("%s %s|%s(%s)", i18n.String(i18n.DataSheet_RowDataSplitedByEmptyLine), self.file.FileName, self.Name, util.R1C1ToA1(r, 1)) 49 | 50 | warningAfterEmptyLineDataOnce = true 51 | } 52 | 53 | } 54 | 55 | fieldDef := dataHeader.RawField(self.Row - ColumnMajor_RowDataBegin) 56 | 57 | // 数据大于列头时, 结束这个列 58 | if fieldDef == nil { 59 | break 60 | } 61 | 62 | // #开头表示注释, 跳过 63 | if strings.Index(fieldDef.Name, "#") == 0 { 64 | continue 65 | } 66 | 67 | rawValue := self.GetCellData(self.Row, ColumnMajor_ColumnValue) 68 | 69 | r, c := self.GetRC() 70 | 71 | line.Add(&model.FieldValue{ 72 | FieldDef: fieldDef, 73 | RawValue: rawValue, 74 | SheetName: self.Name, 75 | FileName: self.file.FileName, 76 | R: r, 77 | C: c, 78 | FieldRepeatedCount: dataHeader.FieldRepeatedCount(fieldDef), 79 | }) 80 | 81 | } 82 | 83 | dataModel.Add(line) 84 | 85 | return true 86 | 87 | } 88 | -------------------------------------------------------------------------------- /v2/test/Config.json: -------------------------------------------------------------------------------- 1 | { 2 | "Tool": "github.com/davyxu/tabtoy", 3 | "Version": "2.8.10", 4 | "UnitTest":[ 5 | { "FloatNumber": 3.14159, "str": "card\\player_slat_016", "HPByLevel":[ 0.1, 0.2 ] }, 6 | { "FloatNumber": 4.5, "str": "ret\nnext", "HPByLevel":[ ] }, 7 | { "FloatNumber": 4.6, "str": "ret\rnext", "HPByLevel":[ ] }, 8 | { "FloatNumber": 1.6, "HPByLevel":[ ] }, 9 | { "FloatNumber": 5.89, "HPByLevel":[ ] } 10 | ]} -------------------------------------------------------------------------------- /v2/test/UnitTest.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v2/test/UnitTest.xlsx -------------------------------------------------------------------------------- /v2tov3/dataheader.go: -------------------------------------------------------------------------------- 1 | package v2tov3 2 | 3 | import ( 4 | "github.com/davyxu/golexer" 5 | "github.com/davyxu/tabtoy/v2tov3/model" 6 | "github.com/davyxu/tabtoy/v3/helper" 7 | v3model "github.com/davyxu/tabtoy/v3/model" 8 | "github.com/tealeg/xlsx" 9 | "strings" 10 | ) 11 | 12 | func importDataHeader(globals *model.Globals, sourceSheet, targetSheet *xlsx.Sheet, tableName string) (headerList []model.ObjectFieldType) { 13 | 14 | var headerRow *xlsx.Row 15 | 16 | // 遍历所有行 17 | for col := 0; ; col++ { 18 | 19 | var oft model.ObjectFieldType 20 | oft.ObjectType = tableName 21 | oft.Kind = v3model.TypeUsage_HeaderStruct 22 | 23 | oft.FieldName = helper.GetSheetValueString(sourceSheet, 0, col) 24 | 25 | // 空列,终止 26 | if oft.FieldName == "" { 27 | break 28 | } 29 | 30 | // 列头中带有#的,特别是最后一行 31 | if strings.HasPrefix(oft.FieldName, "#") { 32 | continue 33 | } 34 | 35 | if headerRow == nil { 36 | headerRow = targetSheet.AddRow() 37 | } 38 | 39 | oft.FieldType = helper.GetSheetValueString(sourceSheet, 1, col) 40 | 41 | // 元信息 42 | meta := helper.GetSheetValueString(sourceSheet, 2, col) 43 | 44 | oft.Meta = golexer.NewKVPair() 45 | if err := oft.Meta.Parse(meta); err != nil { 46 | continue 47 | } 48 | 49 | if strings.HasPrefix(oft.FieldType, "[]") { 50 | oft.FieldType = oft.FieldType[2:] 51 | oft.ArraySplitter = oft.Meta.GetString("ListSpliter") 52 | 53 | if oft.ArraySplitter == "" { 54 | log.Warnln("array list no ListSpliter:", oft.FieldName, oft.ObjectType) 55 | } 56 | } 57 | 58 | oft.Name = helper.GetSheetValueString(sourceSheet, 3, col) 59 | 60 | if oft.Name == "" { 61 | log.Warnf("v2的字段注释为空, %s | %s", oft.FieldName, tableName) 62 | oft.Name = oft.FieldName 63 | } 64 | 65 | var disabledForV3 string 66 | 67 | // 添加V3表头 68 | if globals.TypeIsNoneKind(oft.FieldType) { 69 | disabledForV3 = "#" 70 | } 71 | 72 | // 结构体等类型,标记为nong,输出为# 73 | if !model.IsNativeType(oft.FieldType) { 74 | 75 | targetOft := globals.ObjectTypeByName(oft.FieldType) 76 | // 类型已经被前置定义,且不是枚举(那就是结构体)时,标记为空,后面不会被使用 77 | if targetOft != nil && targetOft.Kind != v3model.TypeUsage_Enum { 78 | oft.Kind = v3model.TypeUsage_None 79 | } 80 | 81 | } 82 | 83 | // 新表的表头加列 84 | headerRow.AddCell().SetValue(disabledForV3 + oft.Name) 85 | 86 | // 拆分字段填充的数组 87 | if !globals.SourceTypeExists(oft.ObjectType, oft.FieldName) { 88 | 89 | globals.AddSourceType(oft) 90 | 91 | } 92 | 93 | headerList = append(headerList, oft) 94 | } 95 | 96 | return 97 | } 98 | -------------------------------------------------------------------------------- /v2tov3/example/Make.bat: -------------------------------------------------------------------------------- 1 | "c:\Program Files\Git\bin\bash.exe" --login -i .\Make.sh -------------------------------------------------------------------------------- /v2tov3/example/Make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | CURR=`pwd` 4 | cd ../../../../../.. 5 | export GOPATH=`pwd` 6 | cd ${CURR} 7 | 8 | go build -v -o ${GOPATH}/bin/tabtoy github.com/davyxu/tabtoy 9 | 10 | InputTableDir=../../v2/example 11 | OutputTableDir=. 12 | 13 | ${GOPATH}/bin/tabtoy -mode=v2tov3 \ 14 | -upout=${OutputTableDir} \ 15 | ${InputTableDir}/Globals.xlsx \ 16 | ${InputTableDir}/Sample.xlsx 17 | 18 | 19 | ${GOPATH}/bin/tabtoy -mode=v3 \ 20 | -index=Index.xlsx \ 21 | -go_out=golang_gen.go \ 22 | -json_out=json_gen.json \ 23 | -package=example -------------------------------------------------------------------------------- /v2tov3/example/golang_gen.go: -------------------------------------------------------------------------------- 1 | // Generated by github.com/davyxu/tabtoy 2 | // DO NOT EDIT!! 3 | // Version: 3.0.0 4 | package example 5 | 6 | type ActorType int32 7 | 8 | const ( 9 | ActorType_Leader = 0 // 唐僧 10 | ActorType_Monkey = 1 // 孙悟空 11 | ActorType_Pig = 2 // 猪八戒 12 | ActorType_Hammer = 3 // 沙僧 13 | ) 14 | 15 | var ( 16 | ActorTypeMapperValueByName = map[string]int32{ 17 | "Leader": 0, // 唐僧 18 | "Monkey": 1, // 孙悟空 19 | "Pig": 2, // 猪八戒 20 | "Hammer": 3, // 沙僧 21 | } 22 | 23 | ActorTypeMapperNameByValue = map[int32]string{ 24 | 0: "Leader", // 唐僧 25 | 1: "Monkey", // 孙悟空 26 | 2: "Pig", // 猪八戒 27 | 3: "Hammer", // 沙僧 28 | } 29 | ) 30 | 31 | type SampleDefine struct { 32 | ID int64 `tb_name:"唯一ID"` 33 | Name string `tb_name:"名称"` 34 | IconID int32 `tb_name:"图标ID"` 35 | NumericalRate float32 `tb_name:"攻击率"` 36 | ItemID int32 `tb_name:"物品id"` 37 | BuffID []int32 `tb_name:"BuffID"` 38 | Type ActorType `tb_name:"类型"` 39 | SkillID []int32 `tb_name:"技能ID列表"` 40 | } 41 | 42 | // Combine struct 43 | type Table struct { 44 | SampleDefine []*SampleDefine // table: SampleDefine 45 | } 46 | -------------------------------------------------------------------------------- /v2tov3/example/json_gen.json: -------------------------------------------------------------------------------- 1 | { 2 | "@Tool": "github.com/davyxu/tabtoy", 3 | "@Version": "3.0.0", 4 | "SampleDefine":[ 5 | { "ID": 100, "Name": "黑猫警长", "IconID": 0, "NumericalRate": 0.6, "ItemID": 0, "BuffID": [0,10], "Type": 0, "SkillID": [4,6,7] }, 6 | { "ID": 101, "Name": "葫芦\n娃", "IconID": 0, "NumericalRate": 0.8, "ItemID": 0, "BuffID": [3,1], "Type": 2, "SkillID": [1] }, 7 | { "ID": 102, "Name": "舒\"克\"", "IconID": 0, "NumericalRate": 0.7, "ItemID": 0, "BuffID": [0,0], "Type": 3, "SkillID": [] }, 8 | { "ID": 103, "Name": "贝\n塔", "IconID": 0, "NumericalRate": 0, "ItemID": 0, "BuffID": [0,0], "Type": 1, "SkillID": [] }, 9 | { "ID": 104, "Name": "邋遢大王", "IconID": 0, "NumericalRate": 1, "ItemID": 0, "BuffID": [0,0], "Type": 2, "SkillID": [] } 10 | ] 11 | } -------------------------------------------------------------------------------- /v2tov3/log.go: -------------------------------------------------------------------------------- 1 | package v2tov3 2 | 3 | import "github.com/davyxu/golog" 4 | 5 | var log = golog.New("v2tov3") 6 | 7 | func init() { 8 | log.SetParts() 9 | } 10 | -------------------------------------------------------------------------------- /v2tov3/model/dataheader.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/davyxu/golexer" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | ) 7 | 8 | type ObjectFieldType struct { 9 | model.TypeDefine 10 | Meta *golexer.KVPair 11 | } 12 | 13 | func (self *ObjectFieldType) IsArray() bool { 14 | return self.ArraySplitter != "" 15 | } 16 | 17 | // 18 | //import ( 19 | // "fmt" 20 | // "github.com/davyxu/golexer" 21 | //) 22 | // 23 | //type FieldKind int 24 | // 25 | //const ( 26 | // FieldKind_Primitive FieldKind = 0 27 | // FieldKind_Enum = 1 28 | // FieldKind_Struct = 2 29 | //) 30 | // 31 | //type ObjectFieldType struct { 32 | // ObjectType string 33 | // FieldName string 34 | // FieldType string // 去掉[] 35 | // Kind FieldKind 36 | // IsArray bool 37 | // 38 | // Meta *golexer.KVPair 39 | // 40 | // Comment string 41 | //} 42 | // 43 | //func (self *ObjectFieldType) String() string { 44 | // 45 | // return fmt.Sprintf("Object:%s Name: %s Type: %s IsArray: %v Kind: %v Comment: %s", 46 | // self.ObjectType, 47 | // self.FieldName, 48 | // self.FieldType, 49 | // self.IsArray, 50 | // self.Kind, 51 | // self.Comment) 52 | // 53 | //} 54 | -------------------------------------------------------------------------------- /v2tov3/model/globals.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/helper" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "github.com/tealeg/xlsx" 7 | "path/filepath" 8 | ) 9 | 10 | type Globals struct { 11 | TableGetter helper.FileGetter 12 | 13 | SourceTypes []ObjectFieldType 14 | 15 | SourceFileList []string 16 | 17 | TargetTypesSheet helper.TableSheet 18 | 19 | TargetIndexSheet helper.TableSheet 20 | 21 | TargetTables *helper.MemFile 22 | 23 | OutputDir string 24 | } 25 | 26 | func (self *Globals) AddTableByFile(tableFileName, tableName string, inputFile *xlsx.File) { 27 | 28 | file := helper.NewXlsxFile("") 29 | 30 | file.(interface { 31 | FromXFile(file *xlsx.File) 32 | }).FromXFile(inputFile) 33 | 34 | tableFileName = filepath.Base(tableFileName) 35 | 36 | self.TargetTables.AddFile(tableFileName, file).TableName = tableName 37 | } 38 | 39 | func (self *Globals) AddTable(tableFileName string) helper.TableSheet { 40 | 41 | return self.TargetTables.CreateXLSXFile(tableFileName) 42 | } 43 | 44 | func (self *Globals) SourceTypeExists(objectTypeName, fieldName string) bool { 45 | for _, ft := range self.SourceTypes { 46 | 47 | if ft.ObjectType == objectTypeName && ft.FieldName == fieldName { 48 | return true 49 | } 50 | } 51 | 52 | return false 53 | } 54 | 55 | func (self *Globals) ObjectTypeByName(objectTypeName string) *ObjectFieldType { 56 | for _, ft := range self.SourceTypes { 57 | 58 | if ft.ObjectType == objectTypeName { 59 | return &ft 60 | } 61 | } 62 | 63 | return nil 64 | } 65 | 66 | func (self *Globals) AddSourceType(oft ObjectFieldType) { 67 | 68 | self.SourceTypes = append(self.SourceTypes, oft) 69 | } 70 | 71 | func (self *Globals) PrintTypes() { 72 | 73 | for _, ft := range self.SourceTypes { 74 | 75 | log.Debugf("%+v", ft) 76 | } 77 | } 78 | 79 | func (self *Globals) TypeIsNoneKind(objectTypeName string) bool { 80 | for _, oft := range self.SourceTypes { 81 | if oft.ObjectType == objectTypeName && oft.Kind == model.TypeUsage_None { 82 | return true 83 | } 84 | } 85 | 86 | return false 87 | } 88 | 89 | func NewGlobals() *Globals { 90 | 91 | return &Globals{ 92 | TargetTables: helper.NewMemFile(), 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /v2tov3/model/log.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/davyxu/golog" 4 | 5 | var log = golog.New("v2tov3model") 6 | -------------------------------------------------------------------------------- /v2tov3/model/types.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | func IsNativeType(typeName string) bool { 4 | 5 | switch typeName { 6 | case "int32", "uint32", "int64", "uint64", "float", "bool", "string": 7 | return true 8 | } 9 | 10 | return false 11 | } 12 | -------------------------------------------------------------------------------- /v2tov3/tab_data.go: -------------------------------------------------------------------------------- 1 | package v2tov3 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/util" 5 | "github.com/davyxu/tabtoy/v2tov3/model" 6 | "github.com/davyxu/tabtoy/v3/helper" 7 | "github.com/tealeg/xlsx" 8 | "strings" 9 | ) 10 | 11 | func importDatas(sourceSheet, targetSheet *xlsx.Sheet, headerList []model.ObjectFieldType, fileName string) error { 12 | 13 | var row, col int 14 | 15 | for row = 4; ; row++ { 16 | 17 | if helper.IsFullRowEmpty(sourceSheet, row) { 18 | break 19 | } 20 | rowData := targetSheet.AddRow() 21 | 22 | var header model.ObjectFieldType 23 | 24 | for col, header = range headerList { 25 | 26 | sourceCell := sourceSheet.Cell(row, col) 27 | 28 | sourceCell.Value = strings.TrimSpace(sourceCell.Value) 29 | 30 | targetCell := rowData.AddCell() 31 | 32 | if header.IsArray() { 33 | targetCell.SetValue(sourceCell.Value) 34 | continue 35 | } 36 | 37 | if err := setTargetCell(header.FieldType, sourceCell, targetCell, row, col, fileName); err != nil { 38 | return err 39 | } 40 | } 41 | 42 | } 43 | 44 | return nil 45 | 46 | } 47 | 48 | func setTargetCell(headerFieldType string, sourceCell, targetCell *xlsx.Cell, row, col int, fileName string) (err error) { 49 | 50 | switch headerFieldType { 51 | case "int32", "uint32": 52 | 53 | if sourceCell.Value == "" { 54 | targetCell.SetValue("") 55 | break 56 | } 57 | 58 | var v int 59 | v, err = sourceCell.Int() 60 | if err != nil { 61 | goto OnError 62 | } 63 | 64 | if v == 0 { 65 | targetCell.SetValue("") 66 | } else { 67 | targetCell.SetInt(v) 68 | } 69 | 70 | case "int64", "uint64": 71 | var v int64 72 | v, err = sourceCell.Int64() 73 | if err != nil { 74 | goto OnError 75 | } 76 | 77 | if v == 0 { 78 | targetCell.SetValue("") 79 | } else { 80 | targetCell.SetInt64(v) 81 | } 82 | 83 | case "float": 84 | if sourceCell.Value == "" { 85 | targetCell.SetFloat(0) 86 | break 87 | } 88 | 89 | var v float64 90 | v, err = sourceCell.Float() 91 | if err != nil { 92 | goto OnError 93 | } 94 | 95 | if v == 0 { 96 | targetCell.SetValue("") 97 | } else { 98 | targetCell.SetFloat(v) 99 | } 100 | 101 | case "bool": 102 | var v bool 103 | 104 | if err, _ = util.StringToPrimitive(sourceCell.Value, &v); err != nil { 105 | goto OnError 106 | } 107 | 108 | if v { 109 | targetCell.SetValue("是") 110 | } else { 111 | targetCell.SetValue("") 112 | } 113 | 114 | default: 115 | targetCell.SetValue(sourceCell.Value) 116 | } 117 | 118 | return 119 | 120 | OnError: 121 | log.Errorf("单元格转换错误 %s|%s, %s", fileName, util.R1C1ToA1(row+1, col+1), err.Error()) 122 | 123 | return 124 | } 125 | -------------------------------------------------------------------------------- /v2tov3/tab_index.go: -------------------------------------------------------------------------------- 1 | package v2tov3 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/util" 5 | "github.com/davyxu/tabtoy/v2tov3/model" 6 | "github.com/davyxu/tabtoy/v3/helper" 7 | "sort" 8 | ) 9 | 10 | func ExportIndexTable(globals *model.Globals) error { 11 | 12 | globals.TargetIndexSheet = globals.AddTable("Index.xlsx") 13 | 14 | helper.WriteIndexTableHeader(globals.TargetIndexSheet) 15 | 16 | var tabList []*helper.MemFileData 17 | 18 | globals.TargetTables.VisitAllTable(func(data *helper.MemFileData) bool { 19 | 20 | if data.FileName == "Index.xlsx" { 21 | return true 22 | } 23 | 24 | tabList = append(tabList, data) 25 | 26 | return true 27 | }) 28 | 29 | // 内容排序 30 | sort.SliceStable(tabList, func(i, j int) bool { 31 | 32 | a := tabList[i] 33 | b := tabList[j] 34 | 35 | if aMode, bMode := getMode(a), getMode(b); aMode != bMode { 36 | 37 | if aMode == "类型表" { 38 | return true 39 | } 40 | 41 | if aMode == "数据表" { 42 | return false 43 | } 44 | 45 | } 46 | 47 | if a.TableName != b.TableName { 48 | return a.TableName < b.TableName 49 | } 50 | 51 | return a.FileName < b.FileName 52 | }) 53 | 54 | for _, data := range tabList { 55 | 56 | helper.WriteRowValues(globals.TargetIndexSheet, getMode(data), data.TableName, util.ChangeExtension(data.FileName, ".csv")) 57 | } 58 | 59 | return nil 60 | } 61 | 62 | func getMode(data *helper.MemFileData) (mode string) { 63 | if data.FileName == "Type.xlsx" { 64 | mode = "类型表" 65 | } else { 66 | mode = "数据表" 67 | } 68 | 69 | return 70 | } 71 | -------------------------------------------------------------------------------- /v2tov3/tab_type.go: -------------------------------------------------------------------------------- 1 | package v2tov3 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "github.com/davyxu/golexer" 7 | "github.com/davyxu/tabtoy/v2tov3/model" 8 | "github.com/davyxu/tabtoy/v3/helper" 9 | v3model "github.com/davyxu/tabtoy/v3/model" 10 | "github.com/tealeg/xlsx" 11 | "strings" 12 | ) 13 | 14 | func ExportTypes(globals *model.Globals) error { 15 | 16 | for _, oft := range globals.SourceTypes { 17 | 18 | var disableKind string 19 | if oft.Kind == v3model.TypeUsage_None { 20 | disableKind = "#" 21 | } 22 | 23 | helper.WriteRowValues(globals.TargetTypesSheet, 24 | disableKind+oft.Kind.String(), 25 | oft.ObjectType, 26 | oft.Name, 27 | oft.FieldName, 28 | oft.FieldType, 29 | oft.ArraySplitter, 30 | oft.Value) 31 | } 32 | 33 | return nil 34 | } 35 | 36 | func importTypes(globals *model.Globals, sheet *xlsx.Sheet, tabPragma *golexer.KVPair, fileName string) error { 37 | 38 | pragma := helper.GetSheetValueString(sheet, 0, 0) 39 | 40 | if err := tabPragma.Parse(pragma); err != nil { 41 | return err 42 | } 43 | 44 | // 遍历所有行 45 | for row := 3; ; row++ { 46 | 47 | var oft model.ObjectFieldType 48 | 49 | oft.ObjectType = helper.GetSheetValueString(sheet, row, 0) 50 | 51 | // 空列,终止 52 | if oft.ObjectType == "" { 53 | break 54 | } 55 | 56 | oft.FieldName = helper.GetSheetValueString(sheet, row, 1) 57 | 58 | oft.FieldType = helper.GetSheetValueString(sheet, row, 2) 59 | if strings.HasPrefix(oft.FieldType, "[]") { 60 | oft.FieldType = oft.FieldType[2:] 61 | } 62 | 63 | oft.Value = helper.GetSheetValueString(sheet, row, 3) 64 | 65 | oft.Name = helper.GetSheetValueString(sheet, row, 4) 66 | 67 | // V3无需添加数组前缀 68 | 69 | // 元信息 70 | meta := helper.GetSheetValueString(sheet, row, 5) 71 | 72 | kvpair := golexer.NewKVPair() 73 | if err := kvpair.Parse(meta); err != nil { 74 | continue 75 | } 76 | 77 | if oft.Value == "" { 78 | oft.Kind = v3model.TypeUsage_None 79 | } else { 80 | oft.Kind = v3model.TypeUsage_Enum 81 | } 82 | 83 | if globals.SourceTypeExists(oft.ObjectType, oft.FieldName) { 84 | 85 | return errors.New(fmt.Sprintf("重复定义的类型 %s %s @ %s", oft.ObjectType, oft.FieldName, fileName)) 86 | 87 | } else { 88 | globals.AddSourceType(oft) 89 | } 90 | 91 | } 92 | 93 | return nil 94 | } 95 | -------------------------------------------------------------------------------- /v3/api/golang/tabhelper.go: -------------------------------------------------------------------------------- 1 | package tabtoy 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "path/filepath" 7 | "strings" 8 | ) 9 | 10 | type Table interface { 11 | ResetData() error 12 | BuildData() error 13 | 14 | ResetTable(tableName string) error 15 | IndexTable(tableName string) error 16 | } 17 | 18 | func LoadFromFile(tab Table, filename string) error { 19 | 20 | // 根据需要从你的源数据读取,这里从指定文件名的文件读取 21 | data, err := ioutil.ReadFile(filename) 22 | if err != nil { 23 | return err 24 | } 25 | 26 | return LoadFromData(tab, data) 27 | } 28 | 29 | func LoadFromData(tab Table, data []byte) error { 30 | // 重置数据,这里会触发Prehandler 31 | err := tab.ResetData() 32 | 33 | if err != nil { 34 | return err 35 | } 36 | 37 | // 使用json反序列化 38 | err = json.Unmarshal(data, tab) 39 | 40 | if err != nil { 41 | return err 42 | } 43 | 44 | // 构建数据和索引,这里会触发PostHandler 45 | return tab.BuildData() 46 | } 47 | 48 | // 从单一表文件加载 49 | func LoadTableFromFile(tab Table, filename string) error { 50 | 51 | // 根据需要从你的源数据读取,这里从指定文件名的文件读取 52 | data, err := ioutil.ReadFile(filename) 53 | if err != nil { 54 | return err 55 | } 56 | 57 | tableName := strings.TrimSuffix(filepath.Base(filename), filepath.Ext(filename)) 58 | return LoadTableromData(tab, tableName, data) 59 | } 60 | 61 | // 从单一表数据加载 62 | func LoadTableromData(tab Table, tableName string, data []byte) error { 63 | err := tab.ResetTable(tableName) 64 | if err != nil { 65 | return err 66 | } 67 | 68 | // 使用json反序列化 69 | err = json.Unmarshal(data, tab) 70 | 71 | if err != nil { 72 | return err 73 | } 74 | 75 | return tab.IndexTable(tableName) 76 | } 77 | -------------------------------------------------------------------------------- /v3/api/golang/tabreader.go: -------------------------------------------------------------------------------- 1 | package tabtoy 2 | 3 | import "encoding/binary" 4 | 5 | type binaryReader struct { 6 | buf []byte 7 | } 8 | 9 | func (self *binaryReader) ReadBool(x *bool) { 10 | n := self.buf[0] 11 | self.buf = self.buf[1:] 12 | *x = n != 0 13 | } 14 | 15 | func (self *binaryReader) ReadUInt16(x *uint16) { 16 | *x = binary.LittleEndian.Uint16(self.buf[0:2]) 17 | self.buf = self.buf[2:] 18 | } 19 | 20 | func (self *binaryReader) ReadUInt32(x *uint32) { 21 | *x = binary.LittleEndian.Uint32(self.buf[0:4]) 22 | self.buf = self.buf[4:] 23 | } 24 | func (self *binaryReader) ReadUInt64(x *uint64) { 25 | *x = binary.LittleEndian.Uint64(self.buf[0:8]) 26 | self.buf = self.buf[8:] 27 | } 28 | 29 | func (self *binaryReader) ReadBytes(x *[]byte) { 30 | var l uint16 31 | self.ReadUInt16(&l) 32 | *x = make([]byte, l) 33 | 34 | copy(*x, self.buf[0:l]) 35 | self.buf = self.buf[l:] 36 | } 37 | 38 | func (self *binaryReader) ReadInt16(x *int16) { 39 | 40 | var v uint16 41 | self.ReadUInt16(&v) 42 | 43 | *x = int16(v) 44 | } 45 | 46 | func (self *binaryReader) ReadInt32(x *int32) { 47 | var v uint32 48 | self.ReadUInt32(&v) 49 | 50 | *x = int32(v) 51 | } 52 | 53 | func (self *binaryReader) ReadInt64(x *int64) { 54 | var v uint64 55 | self.ReadUInt64(&v) 56 | 57 | *x = int64(v) 58 | } 59 | -------------------------------------------------------------------------------- /v3/checker/TODO.md: -------------------------------------------------------------------------------- 1 | # 需要实现的检查 2 | 3 | ## 重名系列 4 | - 全局类型重名 5 | - 字段重名 6 | 7 | - 类型找不到 8 | 9 | 数组里每一个类型匹配原始类型 10 | 11 | 枚举值重复,没填 12 | 13 | 14 | 字段名与名称重复 15 | -------------------------------------------------------------------------------- /v3/checker/checker_enumvalue.go: -------------------------------------------------------------------------------- 1 | package checker 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | "github.com/davyxu/tabtoy/v3/report" 6 | ) 7 | 8 | // 枚举值的解析是放在输出端处理的, 例如json中, 所以在这里提前检查 9 | func checkEnumValue(globals *model.Globals) { 10 | 11 | for _, tab := range globals.Datas.AllTables() { 12 | 13 | // 遍历输入数据的每一列 14 | for _, header := range tab.Headers { 15 | 16 | // 输入的列头,为空表示改列被注释 17 | if header.TypeInfo == nil { 18 | continue 19 | } 20 | 21 | if !globals.Types.IsEnumKind(header.TypeInfo.FieldType) { 22 | continue 23 | } 24 | 25 | for row := 1; row < len(tab.Rows); row++ { 26 | 27 | inputCell := tab.GetCell(row, header.Cell.Col) 28 | 29 | // 这行被注释,无效行 30 | if inputCell == nil { 31 | continue 32 | } 33 | 34 | if header.TypeInfo.IsArray() { 35 | 36 | for _, v := range inputCell.ValueList { 37 | checkEnumFieldValue(globals, header, v, inputCell) 38 | } 39 | 40 | } else { 41 | checkEnumFieldValue(globals, header, inputCell.Value, inputCell) 42 | } 43 | 44 | } 45 | } 46 | } 47 | } 48 | 49 | // 检查枚举值是否存在有效 50 | func checkEnumFieldValue(globals *model.Globals, header *model.HeaderField, value string, inputCell *model.Cell) { 51 | 52 | if inputCell.Value == "" { 53 | return 54 | } 55 | 56 | enumValue := globals.Types.GetEnumValue(header.TypeInfo.FieldType, value) 57 | if enumValue == nil { 58 | report.ReportError("UnknownEnumValue", header.TypeInfo.FieldType, inputCell.String()) 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /v3/checker/checker_repeat.go: -------------------------------------------------------------------------------- 1 | package checker 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | "github.com/davyxu/tabtoy/v3/report" 6 | ) 7 | 8 | func checkRepeat(globals *model.Globals) { 9 | 10 | for _, tab := range globals.Datas.AllTables() { 11 | 12 | // 遍历输入数据的每一列 13 | for _, header := range tab.Headers { 14 | 15 | // 输入的列头,为空表示改列被注释 16 | if header.TypeInfo == nil { 17 | continue 18 | } 19 | 20 | // 这列需要建立索引 21 | if header.TypeInfo.MakeIndex { 22 | 23 | checker := map[string]*model.Cell{} 24 | 25 | for row := 1; row < len(tab.Rows); row++ { 26 | 27 | inputCell := tab.GetCell(row, header.Cell.Col) 28 | 29 | // 这行被注释,无效行 30 | if inputCell == nil { 31 | continue 32 | } 33 | 34 | if inputCell.Value == "" { 35 | continue 36 | } 37 | 38 | if _, ok := checker[inputCell.Value]; ok { 39 | 40 | report.ReportError("DuplicateValueInMakingIndex", inputCell.String()) 41 | 42 | } else { 43 | checker[inputCell.Value] = inputCell 44 | } 45 | 46 | } 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /v3/checker/checker_type.go: -------------------------------------------------------------------------------- 1 | package checker 2 | 3 | import ( 4 | "github.com/ahmetb/go-linq" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "github.com/davyxu/tabtoy/v3/report" 7 | "go/token" 8 | ) 9 | 10 | func CheckType(typeTab *model.TypeTable) { 11 | 12 | typeTable_CheckField(typeTab) 13 | 14 | typeTable_CheckEnumValueEmpty(typeTab) 15 | 16 | typeTable_CheckDuplicateEnumValue(typeTab) 17 | 18 | } 19 | 20 | func isValidFieldName(name string) bool { 21 | 22 | return token.IsIdentifier(name) 23 | } 24 | 25 | func typeTable_CheckField(typeTab *model.TypeTable) { 26 | for _, td := range typeTab.Raw() { 27 | 28 | if !isValidFieldName(td.Define.FieldName) { 29 | cell := td.Tab.GetValueByName(td.Row, "字段名") 30 | report.ReportError("InvalidFieldName", cell.String()) 31 | } 32 | } 33 | } 34 | 35 | func typeTable_CheckEnumValueEmpty(typeTab *model.TypeTable) { 36 | linq.From(typeTab.Raw()).Where(func(raw interface{}) bool { 37 | td := raw.(*model.TypeData) 38 | 39 | return td.Define.Kind == model.TypeUsage_Enum && td.Define.Value == "" 40 | }).ForEach(func(raw interface{}) { 41 | td := raw.(*model.TypeData) 42 | 43 | cell := td.Tab.GetValueByName(td.Row, "值") 44 | 45 | report.ReportError("EnumValueEmpty", cell.String()) 46 | }) 47 | } 48 | 49 | func typeTable_CheckDuplicateEnumValue(typeTab *model.TypeTable) { 50 | 51 | type NameValuePair struct { 52 | Name string 53 | Value string 54 | } 55 | 56 | checker := map[NameValuePair]*model.TypeData{} 57 | 58 | for _, td := range typeTab.Raw() { 59 | 60 | if td.Define.IsBuiltin || td.Define.Kind != model.TypeUsage_Enum { 61 | continue 62 | } 63 | 64 | key := NameValuePair{td.Define.ObjectType, td.Define.Value} 65 | 66 | if _, ok := checker[key]; ok { 67 | 68 | cell := td.Tab.GetValueByName(td.Row, "值") 69 | 70 | report.ReportError("DuplicateEnumValue", cell.String()) 71 | } 72 | 73 | checker[key] = td 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /v3/checker/entry.go: -------------------------------------------------------------------------------- 1 | package checker 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | "github.com/davyxu/tabtoy/v3/report" 6 | ) 7 | 8 | func PreCheck(dataList *model.DataTableList) { 9 | 10 | type ArrayFieldDefine struct { 11 | FieldName string 12 | ObjectName string 13 | } 14 | 15 | fieldCountByField := map[ArrayFieldDefine]int{} 16 | 17 | // 合并前的数据表 18 | for _, tab := range dataList.AllTables() { 19 | 20 | // 遍历输入数据的每一列 21 | for _, header := range tab.Headers { 22 | 23 | // 输入的列头,为空表示改列被注释 24 | if header.TypeInfo == nil { 25 | continue 26 | } 27 | 28 | fieldKey := ArrayFieldDefine{ 29 | FieldName: header.TypeInfo.FieldName, 30 | ObjectName: header.TypeInfo.Name, 31 | } 32 | 33 | if header.TypeInfo.IsArray() { 34 | arrayFieldCount := tab.ArrayFieldCount(header) 35 | if preFieldCount, ok := fieldCountByField[fieldKey]; ok { 36 | 37 | if preFieldCount != arrayFieldCount { 38 | report.ReportError("ArrayMultiColumnDefineNotMatch") 39 | } 40 | } else { 41 | fieldCountByField[fieldKey] = arrayFieldCount 42 | } 43 | } 44 | 45 | } 46 | } 47 | } 48 | 49 | // merge后检查 50 | func PostCheck(globals *model.Globals) { 51 | 52 | checkEnumValue(globals) 53 | checkRepeat(globals) 54 | checkDataType(globals) 55 | } 56 | -------------------------------------------------------------------------------- /v3/compiler/flow.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/checker" 5 | "github.com/davyxu/tabtoy/v3/helper" 6 | "github.com/davyxu/tabtoy/v3/model" 7 | "github.com/davyxu/tabtoy/v3/report" 8 | ) 9 | 10 | func Compile(globals *model.Globals) (ret error) { 11 | 12 | defer func() { 13 | 14 | switch err := recover().(type) { 15 | case *report.TableError: 16 | ret = err 17 | case nil: 18 | default: 19 | panic(err) 20 | } 21 | 22 | }() 23 | 24 | model.InitBuiltinTypes(globals.Types) 25 | 26 | report.Log.Debugf("Loading Index file: '%s'... ", globals.IndexFile) 27 | err := LoadIndexTable(globals, globals.IndexFile) 28 | 29 | if err != nil { 30 | return err 31 | } 32 | 33 | // 测试时, 这个Getter会被提前设置为MemFile, 普通导出时, 这个Getter为空 34 | if globals.TableGetter == nil { 35 | tabLoader := helper.NewFileLoader(!globals.ParaLoading, globals.CacheDir) 36 | 37 | if globals.ParaLoading { 38 | for _, pragma := range globals.IndexList { 39 | tabLoader.AddFile(pragma.TableFileName) 40 | } 41 | 42 | tabLoader.Commit() 43 | } 44 | 45 | globals.TableGetter = tabLoader 46 | } 47 | 48 | var kvList, dataList model.DataTableList 49 | 50 | // 加载多种表 51 | err = loadVariantTables(globals, &kvList, &dataList) 52 | 53 | if err != nil { 54 | return err 55 | } 56 | 57 | report.Log.Debugln("Checking types...") 58 | checker.CheckType(globals.Types) 59 | checker.PreCheck(&dataList) 60 | 61 | if kvList.Count() > 0 { 62 | report.Log.Debugln("Merge key-value tables...") 63 | 64 | // 合并所有的KV表行 65 | var mergedKV model.DataTableList 66 | MergeData(&kvList, &mergedKV, globals.Types) 67 | 68 | // 完整KV表转置为普通数据表 69 | for _, tab := range mergedKV.AllTables() { 70 | 71 | dataList.AddDataTable(transposeKVtoData(globals.Types, tab)) 72 | } 73 | } 74 | 75 | // KV转置后, 再检查一次 76 | checker.CheckType(globals.Types) 77 | 78 | report.Log.Debugln("Merge data tables...") 79 | 80 | // 合并所有的数据表 81 | MergeData(&dataList, &globals.Datas, globals.Types) 82 | 83 | checker.PostCheck(globals) 84 | 85 | return nil 86 | } 87 | -------------------------------------------------------------------------------- /v3/compiler/header.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/helper" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "github.com/davyxu/tabtoy/v3/report" 7 | "strings" 8 | ) 9 | 10 | func LoadHeader(sheet helper.TableSheet, tab *model.DataTable, resolveTableType string, typeTab *model.TypeTable) (maxCol int) { 11 | // 读取表头 12 | 13 | for col := 0; ; col++ { 14 | 15 | headerValue := sheet.GetValue(0, col, nil) 16 | 17 | // 空列,终止 18 | if headerValue == "" { 19 | break 20 | } 21 | 22 | maxCol = col 23 | // 列头带#时,本列忽略 24 | if strings.HasPrefix(headerValue, "#") { 25 | continue 26 | } 27 | 28 | header := tab.MustGetHeader(col) 29 | header.Cell.CopyFrom(&model.Cell{ 30 | Value: headerValue, 31 | Col: col, 32 | Row: 0, 33 | Table: tab, 34 | }) 35 | 36 | } 37 | 38 | resolveHeaderFields(tab, resolveTableType, typeTab) 39 | 40 | checkHeaderTypes(tab, typeTab) 41 | 42 | return 43 | } 44 | 45 | func checkHeaderTypes(tab *model.DataTable, symbols *model.TypeTable) { 46 | 47 | for _, header := range tab.Headers { 48 | 49 | if header.TypeInfo == nil { 50 | continue 51 | } 52 | 53 | // 原始类型检查 54 | if !model.PrimitiveExists(header.TypeInfo.FieldType) && 55 | !symbols.ObjectExists(header.TypeInfo.FieldType) { // 对象检查 56 | 57 | report.ReportError("UnknownFieldType", header.TypeInfo.FieldType, header.Cell.String()) 58 | } 59 | } 60 | 61 | } 62 | 63 | func headerValueExists(offset int, name string, headers []*model.HeaderField) bool { 64 | 65 | for i := offset; i < len(headers); i++ { 66 | if headers[i].Cell.Value == name { 67 | return true 68 | } 69 | } 70 | 71 | return false 72 | } 73 | 74 | func resolveHeaderFields(tab *model.DataTable, tableObjectType string, typeTab *model.TypeTable) { 75 | 76 | tab.OriginalHeaderType = tableObjectType 77 | for index, header := range tab.Headers { 78 | 79 | if header.Cell.Value == "" { 80 | continue 81 | } 82 | 83 | tf := typeTab.FieldByName(tableObjectType, header.Cell.Value) 84 | if tf == nil { 85 | report.ReportError("HeaderFieldNotDefined", header.Cell.String(), tableObjectType) 86 | } 87 | 88 | if headerValueExists(index+1, header.Cell.Value, tab.Headers) && !tf.IsArray() { 89 | report.ReportError("DuplicateHeaderField", header.Cell.String()) 90 | } 91 | 92 | // 解析好的类型 93 | header.TypeInfo = tf 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /v3/compiler/resolverow.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | "github.com/davyxu/tabtoy/v3/report" 6 | "reflect" 7 | ) 8 | 9 | func matchField(objType reflect.Type, header string) int { 10 | 11 | for i := 0; i < objType.NumField(); i++ { 12 | fd := objType.Field(i) 13 | 14 | if fd.Tag.Get("tb_name") == header { 15 | return i 16 | } 17 | } 18 | 19 | return -1 20 | 21 | } 22 | 23 | // 将一行数据解析为具体的类型 24 | func ParseRow(ret interface{}, tab *model.DataTable, row int, symbols *model.TypeTable) bool { 25 | 26 | vobj := reflect.ValueOf(ret).Elem() 27 | 28 | tobj := reflect.TypeOf(ret).Elem() 29 | 30 | // 这一行可能被注释 31 | if tab.GetCell(row, 0) == nil { 32 | return false 33 | } 34 | 35 | for _, header := range tab.Headers { 36 | 37 | valueCell := tab.GetValueByName(row, header.Cell.Value) 38 | 39 | if valueCell == nil { 40 | continue 41 | } 42 | 43 | index := matchField(tobj, header.Cell.Value) 44 | 45 | if index == -1 { 46 | report.ReportError("HeaderNotMatchFieldName", header.Cell.String()) 47 | } 48 | 49 | fieldValue := vobj.Field(index) 50 | 51 | if err := StringToValue(valueCell.Value, fieldValue.Addr().Interface(), header.TypeInfo, symbols); err != nil { 52 | panic(err) 53 | } 54 | } 55 | 56 | return true 57 | } 58 | -------------------------------------------------------------------------------- /v3/compiler/strtovalue.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/util" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "reflect" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | func StringToValue(str string, value interface{}, tf *model.TypeDefine, symbols *model.TypeTable) error { 12 | 13 | err, handled := util.StringToPrimitive(str, value) 14 | if err != nil || handled { 15 | return err 16 | } 17 | 18 | if tf == nil { 19 | panic("unsupport type: " + reflect.TypeOf(value).Elem().Name()) 20 | } 21 | 22 | if tf.IsArray() { 23 | 24 | // Tags为空时, 数组中就不应该有元素 25 | if str == "" { 26 | return nil 27 | } 28 | 29 | tValue := reflect.TypeOf(value).Elem() 30 | vValue := reflect.Indirect(reflect.ValueOf(value)) 31 | 32 | if vValue.Kind() != reflect.Slice { 33 | panic("require slice" + str) 34 | } 35 | 36 | splitedData := strings.Split(str, tf.ArraySplitter) 37 | 38 | slice := reflect.MakeSlice(tValue, len(splitedData), len(splitedData)) 39 | 40 | for index, strValue := range splitedData { 41 | 42 | elemElem := slice.Index(index) 43 | err, handled = util.StringToPrimitive(strValue, elemElem.Addr().Interface()) 44 | if err != nil { 45 | return err 46 | } 47 | 48 | } 49 | 50 | vValue.Set(slice) 51 | 52 | return nil 53 | } 54 | 55 | if symbols.IsEnumKind(tf.FieldType) { 56 | 57 | enumValueStr := symbols.ResolveEnumValue(tf.FieldType, str) 58 | 59 | if enumValueStr != "" { 60 | enumValue, err := strconv.Atoi(enumValueStr) 61 | if err != nil { 62 | return err 63 | } 64 | vValue := reflect.Indirect(reflect.ValueOf(value)) 65 | vValue.SetInt(int64(enumValue)) 66 | } 67 | 68 | return nil 69 | } 70 | 71 | // 内建类型定义与model.InitBuiltinTypes中不匹配 72 | panic("unhandled value: " + str) 73 | 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /v3/compiler/tab_data.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/helper" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "github.com/pkg/errors" 7 | "strings" 8 | ) 9 | 10 | func readOneRow(sheet helper.TableSheet, tab *model.DataTable, row int) bool { 11 | 12 | for _, header := range tab.Headers { 13 | 14 | if header.TypeInfo == nil { 15 | continue 16 | } 17 | 18 | // 浮点数用库取时,需要特殊处理 19 | isFloat := model.LanguagePrimitive(header.TypeInfo.FieldType, "go") == "float32" 20 | 21 | value := sheet.GetValue(row, header.Cell.Col, &helper.ValueOption{ValueAsFloat: isFloat}) 22 | 23 | // 首列带#时,本行忽略 24 | if header.Cell.Col == 0 && strings.HasPrefix(value, "#") { 25 | return false 26 | } 27 | 28 | cell := tab.MustGetCell(row, header.Cell.Col) 29 | cell.Value = value 30 | } 31 | 32 | return true 33 | } 34 | 35 | func LoadDataTable(filegetter helper.FileGetter, fileName, headerType, resolveHeaderType string, typeTab *model.TypeTable) (ret []*model.DataTable, err error) { 36 | file, err := filegetter.GetFile(fileName) 37 | if err != nil { 38 | return nil, errors.Wrap(err, fileName) 39 | } 40 | 41 | for _, sheet := range file.Sheets() { 42 | 43 | tab := model.NewDataTable() 44 | tab.HeaderType = headerType 45 | tab.FileName = fileName 46 | tab.SheetName = sheet.Name() 47 | 48 | ret = append(ret, tab) 49 | 50 | maxCol := LoadHeader(sheet, tab, resolveHeaderType, typeTab) 51 | 52 | // 遍历所有数据行 53 | for row := 0; ; row++ { 54 | 55 | if sheet.IsRowEmpty(row, maxCol+1) { 56 | break 57 | } 58 | 59 | // 读取每一行 60 | readOneRow(sheet, tab, row) 61 | } 62 | 63 | } 64 | 65 | return 66 | } 67 | -------------------------------------------------------------------------------- /v3/compiler/tab_index.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | "path/filepath" 6 | "sort" 7 | "strings" 8 | ) 9 | 10 | func parseIndexRow(tab *model.DataTable, symbols *model.TypeTable) (pragmaList []*model.IndexDefine) { 11 | 12 | for row := 1; row < len(tab.Rows); row++ { 13 | 14 | var pragma model.IndexDefine 15 | if !ParseRow(&pragma, tab, row, symbols) { 16 | continue 17 | } 18 | 19 | if pragma.Kind == model.TableKind_Type { 20 | pragma.TableType = "TypeDefine" 21 | } 22 | 23 | if pragma.TableType == "" { 24 | 25 | _, name := filepath.Split(pragma.TableFileName) 26 | 27 | pragma.TableType = strings.TrimSuffix(name, filepath.Ext(pragma.TableFileName)) 28 | } 29 | 30 | pragmaList = append(pragmaList, &pragma) 31 | } 32 | 33 | return 34 | } 35 | 36 | func LoadIndexTable(globals *model.Globals, fileName string) error { 37 | 38 | if fileName == "" { 39 | return nil 40 | } 41 | 42 | // 加载原始数据 43 | tabs, err := LoadDataTable(globals.IndexGetter, fileName, "IndexDefine", "IndexDefine", globals.Types) 44 | 45 | if err != nil { 46 | return err 47 | } 48 | 49 | var pragmaList []*model.IndexDefine 50 | 51 | for _, tab := range tabs { 52 | pragmaList = append(pragmaList, parseIndexRow(tab, globals.Types)...) 53 | } 54 | 55 | // 按表类型排序,保证类型表先读取 56 | sort.Slice(pragmaList, func(i, j int) bool { 57 | a := pragmaList[i] 58 | b := pragmaList[j] 59 | 60 | if a.Kind != b.Kind { 61 | return a.Kind < b.Kind 62 | } 63 | 64 | if a.TableType != b.TableType { 65 | return a.TableType < b.TableType 66 | } 67 | 68 | return a.TableFileName < b.TableFileName 69 | 70 | }) 71 | 72 | globals.IndexList = pragmaList 73 | 74 | return nil 75 | } 76 | -------------------------------------------------------------------------------- /v3/compiler/tab_kv.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | "github.com/davyxu/tabtoy/v3/report" 6 | "strings" 7 | ) 8 | 9 | func transposeKVtoData(symbols *model.TypeTable, kvtab *model.DataTable) (ret *model.DataTable) { 10 | 11 | ret = model.NewDataTable() 12 | ret.HeaderType = kvtab.HeaderType 13 | ret.OriginalHeaderType = kvtab.HeaderType 14 | ret.FileName = kvtab.FileName 15 | ret.SheetName = kvtab.SheetName 16 | 17 | // 添加表头 18 | ret.AddRow() 19 | 20 | // 添加数据行 21 | ret.AddRow() 22 | 23 | // 遍历KV表的每一行 24 | for row := 1; row < len(kvtab.Rows); row++ { 25 | 26 | fieldName := kvtab.GetValueByName(row, "字段名") 27 | fieldType := kvtab.GetValueByName(row, "字段类型") 28 | name := kvtab.GetValueByName(row, "标识名") 29 | 30 | arraySplitter := kvtab.GetValueByName(row, "数组切割") 31 | 32 | tags := kvtab.GetValueByName(row, "标记") 33 | 34 | var tf model.TypeDefine 35 | tf.Kind = model.TypeUsage_HeaderStruct 36 | tf.ObjectType = kvtab.HeaderType 37 | 38 | tf.Name = name.Value 39 | 40 | if !model.PrimitiveExists(fieldType.Value) && !symbols.ObjectExists(fieldType.Value) { // 对象检查 41 | report.ReportError("UnknownFieldType", fieldType.Value, fieldType.String()) 42 | } 43 | 44 | tf.FieldName = fieldName.Value 45 | tf.FieldType = fieldType.Value 46 | tf.ArraySplitter = arraySplitter.Value 47 | 48 | // 将KV表的Tags转换过去 49 | if tags != nil && tags.Value != "" { 50 | tagsType := kvtab.HeaderByName("标记") 51 | tf.Tags = strings.Split(tags.Value, tagsType.TypeInfo.ArraySplitter) 52 | } 53 | 54 | if symbols.FieldByName(tf.ObjectType, tf.FieldName) != nil { 55 | report.ReportError("DuplicateKVField", fieldName.String()) 56 | } 57 | 58 | symbols.AddField(&tf, kvtab, row) 59 | 60 | // 输出表的表头原始数据 61 | headerCell := ret.AddCell(0) 62 | headerCell.Value = fieldName.Value 63 | 64 | header := ret.MustGetHeader(headerCell.Col) 65 | header.Cell.Value = fieldName.Value 66 | header.TypeInfo = &tf 67 | 68 | inputValueCell := kvtab.GetValueByName(row, "值") 69 | 70 | outputValueCell := ret.AddCell(1) 71 | outputValueCell.CopyFrom(inputValueCell) 72 | 73 | } 74 | 75 | return 76 | } 77 | -------------------------------------------------------------------------------- /v3/compiler/tab_type.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/helper" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "github.com/davyxu/tabtoy/v3/report" 7 | ) 8 | 9 | func LoadTypeTable(typeTab *model.TypeTable, indexGetter helper.FileGetter, fileName string) error { 10 | 11 | tabs, err := LoadDataTable(indexGetter, fileName, "TypeDefine", "TypeDefine", typeTab) 12 | 13 | if err != nil { 14 | return err 15 | } 16 | 17 | for _, tab := range tabs { 18 | 19 | for row := 1; row < len(tab.Rows); row++ { 20 | 21 | var objtype model.TypeDefine 22 | 23 | if !ParseRow(&objtype, tab, row, typeTab) { 24 | continue 25 | } 26 | 27 | if objtype.Kind == model.TypeUsage_None { 28 | report.ReportError("UnknownTypeKind", objtype.ObjectType, objtype.FieldName) 29 | } 30 | 31 | if typeTab.FieldByName(objtype.ObjectType, objtype.FieldName) != nil { 32 | cell := tab.GetValueByName(row, "字段名") 33 | if cell != nil { 34 | report.ReportError("DuplicateTypeFieldName", cell.String(), objtype.ObjectType, objtype.FieldName) 35 | } else { 36 | report.ReportError("InvalidTypeTable", objtype.ObjectType, objtype.FieldName, tab.FileName) 37 | } 38 | 39 | } 40 | 41 | typeTab.AddField(&objtype, tab, row) 42 | } 43 | 44 | } 45 | 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /v3/compiler/variant.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | "github.com/davyxu/tabtoy/v3/report" 6 | ) 7 | 8 | func loadVariantTables(globals *model.Globals, kvList, dataList *model.DataTableList) error { 9 | report.Log.Debugln("Loading tables...") 10 | 11 | // 遍历索引里的每一行配置 12 | for _, pragma := range globals.IndexList { 13 | 14 | if globals.CanDoAction(model.ActionNoGenTable, pragma) { 15 | report.Log.Debugf(" (%s) %s action=nogentable, ignored(tag: %v)", pragma.TableType, pragma.TableFileName, pragma.Tags) 16 | continue 17 | } 18 | 19 | report.Log.Debugf(" (%s) %s", pragma.TableType, pragma.TableFileName) 20 | 21 | switch pragma.Kind { 22 | case model.TableKind_Data: 23 | 24 | tablist, err := LoadDataTable(globals.TableGetter, pragma.TableFileName, pragma.TableType, pragma.TableType, globals.Types) 25 | 26 | if err != nil { 27 | return err 28 | } 29 | 30 | for _, tab := range tablist { 31 | 32 | dataList.AddDataTable(tab) 33 | } 34 | 35 | case model.TableKind_Type: 36 | 37 | err := LoadTypeTable(globals.Types, globals.TableGetter, pragma.TableFileName) 38 | 39 | if err != nil { 40 | return err 41 | } 42 | 43 | case model.TableKind_KeyValue: 44 | tablist, err := LoadDataTable(globals.TableGetter, pragma.TableFileName, pragma.TableType, "KVDefine", globals.Types) 45 | 46 | if err != nil { 47 | return err 48 | } 49 | 50 | for _, tab := range tablist { 51 | 52 | kvList.AddDataTable(tab) 53 | } 54 | 55 | } 56 | } 57 | 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /v3/example/csharp/TabtoyExample/TabtoyExample.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {32FF6E01-AD59-4BD6-82DB-83C35B75C6F2} 8 | Exe 9 | TabtoyExample 10 | TabtoyExample 11 | v4.6.1 12 | 512 13 | true 14 | 15 | 16 | 17 | AnyCPU 18 | true 19 | full 20 | false 21 | bin\Debug\ 22 | DEBUG;TRACE 23 | prompt 24 | 4 25 | 7.1 26 | 27 | 28 | AnyCPU 29 | pdbonly 30 | true 31 | bin\Release\ 32 | TRACE 33 | prompt 34 | 4 35 | 7.1 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | TableReader.cs 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | -------------------------------------------------------------------------------- /v3/example/csharp/TabtoyExample/app.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /v3/example/csharp/csharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27428.2043 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TabtoyExample", "TabtoyExample\TabtoyExample.csproj", "{32FF6E01-AD59-4BD6-82DB-83C35B75C6F2}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {32FF6E01-AD59-4BD6-82DB-83C35B75C6F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {32FF6E01-AD59-4BD6-82DB-83C35B75C6F2}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {32FF6E01-AD59-4BD6-82DB-83C35B75C6F2}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {32FF6E01-AD59-4BD6-82DB-83C35B75C6F2}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {2F1C361D-513B-4196-9909-35439DD735A7} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /v3/example/csv/ConvertToCSV.xlsm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/csv/ConvertToCSV.xlsm -------------------------------------------------------------------------------- /v3/example/csv/Data.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/csv/Data.csv -------------------------------------------------------------------------------- /v3/example/csv/Data2.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/csv/Data2.csv -------------------------------------------------------------------------------- /v3/example/csv/Extend.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/csv/Extend.csv -------------------------------------------------------------------------------- /v3/example/csv/Index.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/csv/Index.csv -------------------------------------------------------------------------------- /v3/example/csv/KV.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/csv/KV.csv -------------------------------------------------------------------------------- /v3/example/csv/KV2.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/csv/KV2.csv -------------------------------------------------------------------------------- /v3/example/csv/Make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # 默认设置代理, 国内专用 4 | export GOPROXY=https://goproxy.io 5 | 6 | go build -v -o ./tabtoy github.com/davyxu/tabtoy 7 | 8 | ./tabtoy -mode=v3 \ 9 | -index=Index.csv \ 10 | -go_out=../golang/table_gen.go \ 11 | -json_out=../json/table_gen.json \ 12 | -jsontype_out=../jsontype/type_gen.json \ 13 | -lua_out=../lua/table_gen.lua \ 14 | -csharp_out=../csharp/TabtoyExample/table_gen.cs \ 15 | -binary_out=../binary/table_gen.bin \ 16 | -java_out=../java/src/main/java/main/Table.java \ 17 | -package=main 18 | 19 | 20 | if [[ $? -ne 0 ]] ; then 21 | read -rsp $'Errors occurred...\n' ; 22 | exit 1 23 | fi 24 | 25 | cp ../json/table_gen.json ../java/cfg 26 | 27 | rm -f tabtoy -------------------------------------------------------------------------------- /v3/example/csv/Type.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/csv/Type.csv -------------------------------------------------------------------------------- /v3/example/golang/Make.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | go run main.go table_gen.go -------------------------------------------------------------------------------- /v3/example/golang/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | tabtoy "github.com/davyxu/tabtoy/v3/api/golang" 6 | "os" 7 | ) 8 | 9 | // 一次性加载所有表 10 | func LoadAllTable() { 11 | 12 | var Tab = NewTable() 13 | 14 | // 表加载前清除之前的手动索引和表关联数据 15 | Tab.RegisterPreEntry(func(tab *Table) error { 16 | fmt.Println("tab pre load clear") 17 | return nil 18 | }) 19 | 20 | // 表加载和构建索引后,需要手动处理数据的回调 21 | Tab.RegisterPostEntry(func(tab *Table) error { 22 | fmt.Println("tab post load done") 23 | fmt.Printf("%+v\n", tab.ExampleDataByID[200]) 24 | 25 | fmt.Println("KV: ", tab.GetKeyValue_ExampleKV().ServerIP) 26 | return nil 27 | }) 28 | 29 | err := tabtoy.LoadFromFile(Tab, "../json/table_gen.json") 30 | if err != nil { 31 | fmt.Println(err) 32 | os.Exit(1) 33 | } 34 | 35 | fmt.Println("") 36 | } 37 | 38 | // 按指定表加载 39 | func LoadSpecifiedTable() { 40 | var TabData = NewTable() 41 | err := tabtoy.LoadTableFromFile(TabData, "../jsondir/ExampleData.json") 42 | if err != nil { 43 | fmt.Println(err) 44 | os.Exit(1) 45 | } 46 | 47 | fmt.Println("specified table: ExampleData, ExampleDataByID") 48 | for k, v := range TabData.ExampleDataByID { 49 | fmt.Println(k, v) 50 | } 51 | 52 | fmt.Println("specified table: ExampleData, ExampleDataByID2") 53 | for k, v := range TabData.ExampleDataByID2 { 54 | fmt.Println(k, v) 55 | } 56 | 57 | // 分表加载时, 不会触发pre/post Handler 58 | var TabKV = NewTable() 59 | err = tabtoy.LoadTableFromFile(TabKV, "../jsondir/ExampleKV.json") 60 | if err != nil { 61 | fmt.Println(err) 62 | os.Exit(1) 63 | } 64 | 65 | fmt.Println("load specified table: ExampleKV") 66 | for k, v := range TabKV.ExampleKV { 67 | fmt.Println(k, v) 68 | } 69 | } 70 | 71 | func main() { 72 | LoadAllTable() 73 | 74 | LoadSpecifiedTable() 75 | } 76 | -------------------------------------------------------------------------------- /v3/example/java/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'java' 3 | } 4 | 5 | group 'com.github.davyxu.tabtoy' 6 | version '1.0-SNAPSHOT' 7 | 8 | sourceCompatibility = 1.8 9 | 10 | repositories { 11 | mavenCentral() 12 | } 13 | 14 | dependencies { 15 | testCompile group: 'junit', name: 'junit', version: '4.12' 16 | compile 'com.alibaba:fastjson:1.2.61' 17 | } -------------------------------------------------------------------------------- /v3/example/java/cfg/table_gen.json: -------------------------------------------------------------------------------- 1 | { 2 | "@Tool": "github.com/davyxu/tabtoy", 3 | "@Version": "", 4 | "ExampleData": [ 5 | { 6 | "Accuracy": 1.602176, 7 | "Buff": 100, 8 | "ID": 100, 9 | "ID2": 0, 10 | "Multi": [ 11 | 1, 12 | 3 13 | ], 14 | "Name": "扎克镇", 15 | "Rate": 3.14, 16 | "Skill": [ 17 | 3, 18 | 1, 19 | 2 20 | ], 21 | "TagList": [ 22 | "a", 23 | "b" 24 | ], 25 | "Type": 2 26 | }, 27 | { 28 | "Accuracy": 3.14159, 29 | "Buff": 1, 30 | "ID": 200, 31 | "ID2": 0, 32 | "Multi": [ 33 | 4, 34 | 0 35 | ], 36 | "Name": "阿努比斯神庙", 37 | "Rate": 1.2, 38 | "Skill": [ 39 | 100, 40 | 0, 41 | 90 42 | ], 43 | "TagList": [ 44 | "c" 45 | ], 46 | "Type": 1 47 | }, 48 | { 49 | "Accuracy": 0, 50 | "Buff": 0, 51 | "ID": 300, 52 | "ID2": 0, 53 | "Multi": [], 54 | "Name": "花村", 55 | "Rate": 79.4, 56 | "Skill": [], 57 | "TagList": [], 58 | "Type": 3 59 | }, 60 | { 61 | "Accuracy": 0, 62 | "Buff": 0, 63 | "ID": 400, 64 | "ID2": 0, 65 | "Multi": [], 66 | "Name": "艾兴瓦尔德", 67 | "Rate": 0.63, 68 | "Skill": [], 69 | "TagList": [], 70 | "Type": 4 71 | } 72 | ], 73 | "ExampleKV": [ 74 | { 75 | "GroupID": [ 76 | 10, 77 | 20 78 | ], 79 | "ServerIP": "8.8.8.8", 80 | "ServerPort": 1024 81 | } 82 | ], 83 | "ExtendData": [ 84 | { 85 | "Additive": 1.1, 86 | "Index2": 0 87 | }, 88 | { 89 | "Additive": 1.2, 90 | "Index2": 0 91 | } 92 | ] 93 | } -------------------------------------------------------------------------------- /v3/example/java/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/java/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /v3/example/java/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.2.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /v3/example/java/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS="-Xmx64m" 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /v3/example/java/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | 3 | -------------------------------------------------------------------------------- /v3/example/java/src/test/java/Main.java: -------------------------------------------------------------------------------- 1 | import main.Table; 2 | import com.alibaba.fastjson.JSON; 3 | import java.nio.file.Files; 4 | import java.nio.file.Paths; 5 | import java.util.Map; 6 | 7 | public class Main { 8 | 9 | // 从文件读取数据 10 | private static String readFileAsString(String fileName)throws Exception 11 | { 12 | return new String(Files.readAllBytes(Paths.get(fileName))); 13 | } 14 | public static void main(String[] args) throws Exception { 15 | 16 | // 从文件读取配置表 17 | String data = null; 18 | try { 19 | data = readFileAsString("./cfg/table_gen.json"); 20 | } catch (Exception e) { 21 | e.printStackTrace(); 22 | } 23 | 24 | // 表格数据 25 | Table tab; 26 | 27 | // 从json序列化出对象 28 | tab = JSON.parseObject(data, Table.class); 29 | 30 | if(tab == null){ 31 | throw new Exception("parse table failed"); 32 | } 33 | 34 | // 构建索引 35 | tab.BuildData(); 36 | 37 | // 测试输出 38 | for(Map.Entry def : tab.ExampleDataByID.entrySet()){ 39 | System.out.println(def.getValue().Name); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /v3/example/json/table_gen.json: -------------------------------------------------------------------------------- 1 | { 2 | "@Tool": "github.com/davyxu/tabtoy", 3 | "@Version": "", 4 | "ExampleData": [ 5 | { 6 | "Accuracy": 1.602176, 7 | "Buff": 100, 8 | "ID": 100, 9 | "ID2": 0, 10 | "Multi": [ 11 | 1, 12 | 3 13 | ], 14 | "Name": "扎克镇", 15 | "Rate": 3.14, 16 | "Skill": [ 17 | 3, 18 | 1, 19 | 2 20 | ], 21 | "TagList": [ 22 | "a", 23 | "b" 24 | ], 25 | "Type": 2 26 | }, 27 | { 28 | "Accuracy": 3.14159, 29 | "Buff": 1, 30 | "ID": 200, 31 | "ID2": 0, 32 | "Multi": [ 33 | 4, 34 | 0 35 | ], 36 | "Name": "阿努比斯神庙", 37 | "Rate": 1.2, 38 | "Skill": [ 39 | 100, 40 | 0, 41 | 90 42 | ], 43 | "TagList": [ 44 | "c" 45 | ], 46 | "Type": 1 47 | }, 48 | { 49 | "Accuracy": 0, 50 | "Buff": 0, 51 | "ID": 300, 52 | "ID2": 0, 53 | "Multi": [], 54 | "Name": "花村", 55 | "Rate": 79.4, 56 | "Skill": [], 57 | "TagList": [], 58 | "Type": 3 59 | }, 60 | { 61 | "Accuracy": 0, 62 | "Buff": 0, 63 | "ID": 400, 64 | "ID2": 0, 65 | "Multi": [], 66 | "Name": "艾兴瓦尔德", 67 | "Rate": 0.63, 68 | "Skill": [], 69 | "TagList": [], 70 | "Type": 4 71 | } 72 | ], 73 | "ExampleKV": [ 74 | { 75 | "GroupID": [ 76 | 10, 77 | 20 78 | ], 79 | "ServerIP": "8.8.8.8", 80 | "ServerPort": 1024 81 | } 82 | ], 83 | "ExtendData": [ 84 | { 85 | "Additive": 1.1, 86 | "Index2": 0 87 | }, 88 | { 89 | "Additive": 1.2, 90 | "Index2": 0 91 | } 92 | ] 93 | } -------------------------------------------------------------------------------- /v3/example/jsondir/ExampleData.json: -------------------------------------------------------------------------------- 1 | { 2 | "@Tool": "github.com/davyxu/tabtoy", 3 | "@Version": "", 4 | "ExampleData": [ 5 | { 6 | "Accuracy": 1.602176, 7 | "Buff": 100, 8 | "ID": 100, 9 | "ID2": 0, 10 | "Multi": [ 11 | 1, 12 | 3 13 | ], 14 | "Name": "扎克镇", 15 | "Rate": 3.14, 16 | "Skill": [ 17 | 3, 18 | 1, 19 | 2 20 | ], 21 | "TagList": [ 22 | "a", 23 | "b" 24 | ], 25 | "Type": 2 26 | }, 27 | { 28 | "Accuracy": 3.14159, 29 | "Buff": 1, 30 | "ID": 200, 31 | "ID2": 0, 32 | "Multi": [ 33 | 4, 34 | 0 35 | ], 36 | "Name": "阿努比斯神庙", 37 | "Rate": 1.2, 38 | "Skill": [ 39 | 100, 40 | 0, 41 | 90 42 | ], 43 | "TagList": [ 44 | "c" 45 | ], 46 | "Type": 1 47 | }, 48 | { 49 | "Accuracy": 0, 50 | "Buff": 0, 51 | "ID": 300, 52 | "ID2": 0, 53 | "Multi": [], 54 | "Name": "花村", 55 | "Rate": 79.4, 56 | "Skill": [], 57 | "TagList": [], 58 | "Type": 3 59 | }, 60 | { 61 | "Accuracy": 0, 62 | "Buff": 0, 63 | "ID": 400, 64 | "ID2": 0, 65 | "Multi": [], 66 | "Name": "艾兴瓦尔德", 67 | "Rate": 0.63, 68 | "Skill": [], 69 | "TagList": [], 70 | "Type": 4 71 | } 72 | ] 73 | } -------------------------------------------------------------------------------- /v3/example/jsondir/ExampleKV.json: -------------------------------------------------------------------------------- 1 | { 2 | "@Tool": "github.com/davyxu/tabtoy", 3 | "@Version": "", 4 | "ExampleKV": [ 5 | { 6 | "GroupID": [ 7 | 10, 8 | 20 9 | ], 10 | "ServerIP": "8.8.8.8", 11 | "ServerPort": 1024 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /v3/example/jsondir/ExtendData.json: -------------------------------------------------------------------------------- 1 | { 2 | "@Tool": "github.com/davyxu/tabtoy", 3 | "@Version": "", 4 | "ExtendData": [ 5 | { 6 | "Additive": 1.1, 7 | "Index2": 0 8 | }, 9 | { 10 | "Additive": 1.2, 11 | "Index2": 0 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /v3/example/jsontype/type_gen.json: -------------------------------------------------------------------------------- 1 | { 2 | "@Tool": "github.com/davyxu/tabtoy", 3 | "@Version": "3.0.1", 4 | "Objects": [ 5 | { 6 | "Name": "ActorType", 7 | "Type": "Enum", 8 | "Fields": [ 9 | { 10 | "Name": "None", 11 | "Type": "int", 12 | "Comment": "", 13 | "Value": "0" 14 | }, 15 | { 16 | "Name": "Pharah", 17 | "Type": "int", 18 | "Comment": "法鸡", 19 | "Value": "1" 20 | }, 21 | { 22 | "Name": "Junkrat", 23 | "Type": "int", 24 | "Comment": "狂鼠", 25 | "Value": "2" 26 | }, 27 | { 28 | "Name": "Genji", 29 | "Type": "int", 30 | "Comment": "源氏", 31 | "Value": "3" 32 | }, 33 | { 34 | "Name": "Mercy", 35 | "Type": "int", 36 | "Comment": "天使", 37 | "Value": "4" 38 | } 39 | ] 40 | }, 41 | { 42 | "Name": "ExampleData", 43 | "Type": "Struct", 44 | "Fields": [ 45 | { 46 | "Name": "ID", 47 | "Type": "int", 48 | "Comment": "任务ID", 49 | "MakeIndex": true 50 | }, 51 | { 52 | "Name": "Name", 53 | "Type": "string", 54 | "Comment": "名称" 55 | }, 56 | { 57 | "Name": "Rate", 58 | "Type": "float", 59 | "Comment": "比例" 60 | }, 61 | { 62 | "Name": "Accuracy", 63 | "Type": "double", 64 | "Comment": "精度" 65 | }, 66 | { 67 | "Name": "Type", 68 | "Type": "ActorType", 69 | "Comment": "类型" 70 | }, 71 | { 72 | "Name": "Skill", 73 | "Type": "int", 74 | "Comment": "技能列表", 75 | "ArraySplitter": "|" 76 | }, 77 | { 78 | "Name": "Buff", 79 | "Type": "int", 80 | "Comment": "增益" 81 | }, 82 | { 83 | "Name": "TagList", 84 | "Type": "string", 85 | "Comment": "标记", 86 | "ArraySplitter": "|" 87 | }, 88 | { 89 | "Name": "Multi", 90 | "Type": "int", 91 | "Comment": "多列", 92 | "ArraySplitter": "|" 93 | } 94 | ] 95 | }, 96 | { 97 | "Name": "ExampleKV", 98 | "Type": "Struct", 99 | "Tags": [ 100 | "k", 101 | "v", 102 | "n" 103 | ], 104 | "Fields": [ 105 | { 106 | "Name": "ServerIP", 107 | "Type": "string", 108 | "Comment": "服务器IP" 109 | }, 110 | { 111 | "Name": "ServerPort", 112 | "Type": "uint16", 113 | "Comment": "服务器端口" 114 | }, 115 | { 116 | "Name": "GroupID", 117 | "Type": "int", 118 | "Comment": "分组", 119 | "ArraySplitter": ";" 120 | } 121 | ] 122 | }, 123 | { 124 | "Name": "ExtendData", 125 | "Type": "Struct", 126 | "Fields": [ 127 | { 128 | "Name": "Additive", 129 | "Type": "float", 130 | "Comment": "附加" 131 | } 132 | ] 133 | } 134 | ] 135 | } -------------------------------------------------------------------------------- /v3/example/lua/main.lua: -------------------------------------------------------------------------------- 1 | -- 添加搜索路径 2 | package.path = package.path .. ";../luadir/?.lua" 3 | 4 | -- 一次性加载所有表 5 | function LoadAllTable() 6 | -- 加载 7 | local tab = {} 8 | require("table_gen").init(tab) 9 | 10 | -- 遍历表 11 | print("Iterate lua table by order:") 12 | for _, v in ipairs(tab.ExampleData) do 13 | print(v.ID, v.Name) 14 | end 15 | 16 | -- 通过索引访问 17 | print("Access index table data:") 18 | print(tab.ExampleDataByID[300].ID) 19 | 20 | -- 枚举类型访问 21 | print("Use generated enum:") 22 | print(tab.ActorType.Pharah, tab.ActorType[3]) 23 | end 24 | 25 | 26 | 27 | -- 加载指定名称的表 28 | -- P.S. 考虑一些lua的运行限制(如luajit)的const, local限制, 应尽量将lua按文件拆分读取 29 | function LoadSpecifiedTable() 30 | local tabData = {} 31 | require("ExampleData").init(tabData) 32 | require("ExtendData").init(tabData) 33 | 34 | print("Load 2 tables into one lua table:") 35 | for _, v in ipairs(tabData.ExampleData) do 36 | print(v.ID, v.Name) 37 | end 38 | for _, v in ipairs(tabData.ExtendData) do 39 | print(v.Additive) 40 | end 41 | 42 | print("Load kv table into single lua table:") 43 | local kvData = {} 44 | require("ExampleKV").init(kvData) 45 | for _, v in ipairs(kvData.ExampleKV) do 46 | print(v.ServerIP, v.ServerPort) 47 | end 48 | 49 | -- lua枚举是可选功能, 根据需要加载 50 | local tabType = {} 51 | require("_TableType").init(tabType) 52 | print("Use generated enum:") 53 | print(tabType.ActorType.Pharah, tabType.ActorType[3]) 54 | end 55 | 56 | LoadAllTable() 57 | LoadSpecifiedTable() -------------------------------------------------------------------------------- /v3/example/lua/table_gen.lua: -------------------------------------------------------------------------------- 1 | -- Generated by github.com/davyxu/tabtoy 2 | -- Version: 3 | 4 | return { 5 | init = function( g ) 6 | 7 | g.ExampleData = { 8 | { ID = 100, ID2 = 0, Name = "扎克镇", Rate = 3.14, Accuracy = 1.602176, Type = 2, Skill = {3,1,2}, Buff = 100, TagList = {"a","b"}, Multi = {1,3}, }, 9 | { ID = 200, ID2 = 0, Name = "阿努比斯神庙", Rate = 1.2, Accuracy = 3.14159, Type = 1, Skill = {100,0,90}, Buff = 1, TagList = {"c"}, Multi = {4,0}, }, 10 | { ID = 300, ID2 = 0, Name = "花村", Rate = 79.4, Accuracy = 0, Type = 3, Skill = {}, Buff = 0, TagList = {}, Multi = {}, }, 11 | { ID = 400, ID2 = 0, Name = "艾兴瓦尔德", Rate = 0.63, Accuracy = 0, Type = 4, Skill = {}, Buff = 0, TagList = {}, Multi = {}, }, 12 | } 13 | 14 | g.ExtendData = { 15 | { Additive = 1.1, Index2 = 0, }, 16 | { Additive = 1.2, Index2 = 0, }, 17 | } 18 | 19 | g.ExampleKV = { 20 | { ServerIP = "8.8.8.8", ServerPort = 1024, GroupID = {10,20}, }, 21 | } 22 | 23 | 24 | -- ExampleData 25 | g.ExampleDataByID = {} 26 | for _, rec in pairs(g.ExampleData) do 27 | g.ExampleDataByID[rec.ID] = rec 28 | end 29 | 30 | -- ExampleData 31 | g.ExampleDataByID2 = {} 32 | for _, rec in pairs(g.ExampleData) do 33 | g.ExampleDataByID2[rec.ID2] = rec 34 | end 35 | 36 | -- ExtendData 37 | g.ExtendDataByIndex2 = {} 38 | for _, rec in pairs(g.ExtendData) do 39 | g.ExtendDataByIndex2[rec.Index2] = rec 40 | end 41 | 42 | 43 | g.ActorType = 44 | { 45 | None = 0, -- 46 | Pharah = 1, -- 法鸡 47 | Junkrat = 2, -- 狂鼠 48 | Genji = 3, -- 源氏 49 | Mercy = 4, -- 天使 50 | [0] = "None", -- 51 | [1] = "Pharah", -- 法鸡 52 | [2] = "Junkrat", -- 狂鼠 53 | [3] = "Genji", -- 源氏 54 | [4] = "Mercy", -- 天使 55 | } 56 | return g 57 | end 58 | } 59 | -------------------------------------------------------------------------------- /v3/example/luadir/ExampleData.lua: -------------------------------------------------------------------------------- 1 | -- Generated by github.com/davyxu/tabtoy 2 | -- Version: 3 | 4 | return { 5 | init = function( g ) 6 | g.ExampleData = { 7 | { ID = 100, ID2 = 0, Name = "扎克镇", Rate = 3.14, Accuracy = 1.602176, Type = 2, Skill = {3,1,2}, Buff = 100, TagList = {"a","b"}, Multi = {1,3}, }, 8 | { ID = 200, ID2 = 0, Name = "阿努比斯神庙", Rate = 1.2, Accuracy = 3.14159, Type = 1, Skill = {100,0,90}, Buff = 1, TagList = {"c"}, Multi = {4,0}, }, 9 | { ID = 300, ID2 = 0, Name = "花村", Rate = 79.4, Accuracy = 0, Type = 3, Skill = {}, Buff = 0, TagList = {}, Multi = {}, }, 10 | { ID = 400, ID2 = 0, Name = "艾兴瓦尔德", Rate = 0.63, Accuracy = 0, Type = 4, Skill = {}, Buff = 0, TagList = {}, Multi = {}, }, 11 | } 12 | 13 | -- ExampleData 14 | g.ExampleDataByID = {} 15 | for _, rec in pairs(g.ExampleData) do 16 | g.ExampleDataByID[rec.ID] = rec 17 | end 18 | 19 | -- ExampleData 20 | g.ExampleDataByID2 = {} 21 | for _, rec in pairs(g.ExampleData) do 22 | g.ExampleDataByID2[rec.ID2] = rec 23 | end 24 | 25 | return g 26 | end 27 | } 28 | -------------------------------------------------------------------------------- /v3/example/luadir/ExampleKV.lua: -------------------------------------------------------------------------------- 1 | -- Generated by github.com/davyxu/tabtoy 2 | -- Version: 3 | 4 | return { 5 | init = function( g ) 6 | g.ExampleKV = { 7 | { ServerIP = "8.8.8.8", ServerPort = 1024, GroupID = {10,20}, }, 8 | } 9 | 10 | return g 11 | end 12 | } 13 | -------------------------------------------------------------------------------- /v3/example/luadir/ExtendData.lua: -------------------------------------------------------------------------------- 1 | -- Generated by github.com/davyxu/tabtoy 2 | -- Version: 3 | 4 | return { 5 | init = function( g ) 6 | g.ExtendData = { 7 | { Additive = 1.1, Index2 = 0, }, 8 | { Additive = 1.2, Index2 = 0, }, 9 | } 10 | 11 | -- ExtendData 12 | g.ExtendDataByIndex2 = {} 13 | for _, rec in pairs(g.ExtendData) do 14 | g.ExtendDataByIndex2[rec.Index2] = rec 15 | end 16 | 17 | return g 18 | end 19 | } 20 | -------------------------------------------------------------------------------- /v3/example/luadir/_TableType.lua: -------------------------------------------------------------------------------- 1 | -- Generated by github.com/davyxu/tabtoy 2 | -- Version: 3 | 4 | return { 5 | init = function( g ) 6 | 7 | g.ActorType = 8 | { 9 | None = 0, -- 10 | Pharah = 1, -- 法鸡 11 | Junkrat = 2, -- 狂鼠 12 | Genji = 3, -- 源氏 13 | Mercy = 4, -- 天使 14 | [0] = "None", -- 15 | [1] = "Pharah", -- 法鸡 16 | [2] = "Junkrat", -- 狂鼠 17 | [3] = "Genji", -- 源氏 18 | [4] = "Mercy", -- 天使 19 | } 20 | return g 21 | end 22 | } 23 | -------------------------------------------------------------------------------- /v3/example/protobuf/golang/Make.sh: -------------------------------------------------------------------------------- 1 | # protoc 下载 https://github.com/protocolbuffers/protobuf/releases 2 | go install google.golang.org/protobuf/cmd/protoc-gen-go 3 | ./protoc --go_out=. ../table.proto -I ../ 4 | go run main.go table.pb.go -------------------------------------------------------------------------------- /v3/example/protobuf/golang/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/golang/protobuf/proto" 6 | "io/ioutil" 7 | "os" 8 | ) 9 | 10 | func LoadAllTable() { 11 | var tab Table 12 | loadTableFromFile(&tab, "../all.pbb") 13 | 14 | fmt.Println(proto.MarshalTextString(&tab)) 15 | } 16 | 17 | func loadTableFromFile(tab *Table, fileName string) { 18 | data, err := ioutil.ReadFile(fileName) 19 | if err != nil { 20 | fmt.Println(err) 21 | os.Exit(1) 22 | } 23 | 24 | err = proto.Unmarshal(data, tab) 25 | if err != nil { 26 | fmt.Println(err) 27 | os.Exit(1) 28 | } 29 | } 30 | 31 | func LoadSpecifiedTable() { 32 | var tabData Table 33 | loadTableFromFile(&tabData, "../ExampleData.pbb") 34 | 35 | fmt.Println("load specified table: ExampleData") 36 | for k, v := range tabData.ExampleData { 37 | fmt.Println(k, v) 38 | } 39 | 40 | var tabKV Table 41 | loadTableFromFile(&tabKV, "../ExampleKV.pbb") 42 | 43 | fmt.Println("load specified table: ExampleKV") 44 | for k, v := range tabKV.ExampleKV { 45 | fmt.Println(k, v) 46 | } 47 | 48 | // 将表格合并 49 | var total Table 50 | proto.Merge(&total, &tabData) 51 | proto.Merge(&total, &tabKV) 52 | fmt.Printf("merged table, data len: %d, kv len: %d\n", len(total.ExampleData), len(total.ExampleKV)) 53 | } 54 | 55 | func main() { 56 | 57 | LoadAllTable() 58 | 59 | LoadSpecifiedTable() 60 | } 61 | -------------------------------------------------------------------------------- /v3/example/protobuf/table.proto: -------------------------------------------------------------------------------- 1 | // Generated by github.com/davyxu/tabtoy 2 | // DO NOT EDIT!! 3 | // Version: 4 | syntax = "proto3"; 5 | package main; 6 | 7 | 8 | enum ActorType 9 | { 10 | None = 0; // 11 | Pharah = 1; // 法鸡 12 | Junkrat = 2; // 狂鼠 13 | Genji = 3; // 源氏 14 | Mercy = 4; // 天使 15 | } 16 | 17 | 18 | message ExampleData 19 | { 20 | int32 ID = 1; // 任务ID 21 | int32 ID2 = 2; // 任务ID2 22 | string Name = 3; // 名称 23 | float Rate = 4; // 比例 24 | double Accuracy = 5; // 精度 25 | ActorType Type = 6; // 类型 26 | repeated int32 Skill = 7; // 技能列表 27 | int32 Buff = 8; // 增益 28 | repeated string TagList = 9; // 标记 29 | repeated int32 Multi = 10; // 多列 30 | } 31 | 32 | message ExtendData 33 | { 34 | float Additive = 1; // 附加 35 | int32 Index2 = 2; // 索引2 36 | } 37 | 38 | message ExampleKV 39 | { 40 | string ServerIP = 1; // 服务器IP 41 | uint32 ServerPort = 2; // 服务器端口 42 | repeated int32 GroupID = 3; // 分组 43 | } 44 | 45 | 46 | // Combine struct 47 | message Table 48 | { 49 | repeated ExampleData ExampleData = 1; // table: ExampleData 50 | repeated ExtendData ExtendData = 2; // table: ExtendData 51 | repeated ExampleKV ExampleKV = 3; // table: ExampleKV 52 | } 53 | 54 | -------------------------------------------------------------------------------- /v3/example/tutorial/Index.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/tutorial/Index.xlsx -------------------------------------------------------------------------------- /v3/example/tutorial/Make.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | if [[ ! -f "./tabtoy" ]]; then 4 | echo "请在https://github.com/davyxu/tabtoy/releases下载最新的tabtoy, 并放置于本目录" 5 | exit 1 6 | fi 7 | 8 | ./tabtoy -mode=v3 -index=Index.xlsx -json_out=table_gen.json 9 | -------------------------------------------------------------------------------- /v3/example/tutorial/MyData.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/tutorial/MyData.xlsx -------------------------------------------------------------------------------- /v3/example/tutorial/Type.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/tutorial/Type.xlsx -------------------------------------------------------------------------------- /v3/example/xlsx/Data.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/xlsx/Data.xlsx -------------------------------------------------------------------------------- /v3/example/xlsx/Data2.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/xlsx/Data2.xlsx -------------------------------------------------------------------------------- /v3/example/xlsx/Extend.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/xlsx/Extend.xlsx -------------------------------------------------------------------------------- /v3/example/xlsx/Index.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/xlsx/Index.xlsx -------------------------------------------------------------------------------- /v3/example/xlsx/KV.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/xlsx/KV.xlsx -------------------------------------------------------------------------------- /v3/example/xlsx/KV2.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/xlsx/KV2.xlsx -------------------------------------------------------------------------------- /v3/example/xlsx/Make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | export GOPROXY=https://goproxy.cn,direct 4 | 5 | go build -v -o ./tabtoy github.com/davyxu/tabtoy 6 | 7 | ./tabtoy -mode=v3 \ 8 | -index=Index.xlsx \ 9 | -go_out=../golang/table_gen.go \ 10 | -json_out=../json/table_gen.json \ 11 | -json_dir=../jsondir \ 12 | -lua_out=../lua/table_gen.lua \ 13 | -lua_dir=../luadir \ 14 | -binary_dir=../binary \ 15 | -csharp_out=../csharp/TabtoyExample/table_gen.cs \ 16 | -binary_out=../binary/table_gen.bin \ 17 | -java_out=../java/src/main/java/main/Table.java \ 18 | -proto_out=../protobuf/table.proto \ 19 | -pbbin_out=../protobuf/all.pbb \ 20 | -pbbin_dir=../protobuf \ 21 | -package=main 22 | 23 | if [ $? -ne 0 ] ; then 24 | read -rsp $'Errors occurred...\n' ; 25 | exit 1 26 | fi 27 | 28 | cp ../json/table_gen.json ../java/cfg 29 | 30 | rm -f tabtoy -------------------------------------------------------------------------------- /v3/example/xlsx/Type.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/davyxu/tabtoy/6eaaa7c7e2fa5f09e6468f58eee61b02125d7ead/v3/example/xlsx/Type.xlsx -------------------------------------------------------------------------------- /v3/gen/bindata/gen.go: -------------------------------------------------------------------------------- 1 | package bindata 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "io/ioutil" 7 | ) 8 | 9 | func writeHeader(writer *BinaryWriter) error { 10 | if err := writer.WriteString("TABTOY"); err != nil { 11 | return err 12 | } 13 | if err := writer.WriteUInt32(4); err != nil { 14 | return err 15 | } 16 | 17 | return nil 18 | } 19 | 20 | func exportTable(globals *model.Globals, writer *BinaryWriter, tab *model.DataTable) error { 21 | // 结构体的标记头, 方便跨过不同类型 22 | if err := writer.WriteUInt32(MakeTagStructArray()); err != nil { 23 | return err 24 | } 25 | 26 | writer.WriteString(tab.HeaderType) 27 | 28 | totalDataRow := len(tab.Rows) - 1 29 | writer.WriteUInt32(uint32(totalDataRow)) 30 | 31 | // 表的每一个行 32 | for row := 1; row < len(tab.Rows); row++ { 33 | 34 | if swriter, err := writeStruct(globals, tab, row); err != nil { 35 | return err 36 | } else { 37 | structData := swriter.Bytes() 38 | // 结构体二进制边界 39 | writer.WriteUInt32(uint32(len(structData))) 40 | writer.Write(structData) 41 | } 42 | } 43 | 44 | return nil 45 | } 46 | 47 | func Generate(globals *model.Globals) (data []byte, err error) { 48 | 49 | totalWriter := NewBinaryWriter() 50 | 51 | if err := writeHeader(totalWriter); err != nil { 52 | return nil, err 53 | } 54 | 55 | for _, tab := range globals.Datas.AllTables() { 56 | 57 | err := exportTable(globals, totalWriter, tab) 58 | if err != nil { 59 | return nil, err 60 | } 61 | } 62 | 63 | data = totalWriter.Bytes() 64 | 65 | return 66 | } 67 | 68 | func Output(globals *model.Globals, param string) (err error) { 69 | 70 | for _, tab := range globals.Datas.AllTables() { 71 | 72 | writer := NewBinaryWriter() 73 | if err := writeHeader(writer); err != nil { 74 | return err 75 | } 76 | 77 | err := exportTable(globals, writer, tab) 78 | if err != nil { 79 | return err 80 | } 81 | 82 | err = ioutil.WriteFile(fmt.Sprintf("%s/%s.bin", param, tab.HeaderType), writer.Bytes(), 0666) 83 | 84 | if err != nil { 85 | return err 86 | } 87 | } 88 | 89 | return nil 90 | } 91 | -------------------------------------------------------------------------------- /v3/gen/bindata/struct.go: -------------------------------------------------------------------------------- 1 | package bindata 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | ) 6 | 7 | // 写入表的一行 8 | func writeStruct(globals *model.Globals, tab *model.DataTable, row int) (*BinaryWriter, error) { 9 | 10 | structWriter := NewBinaryWriter() 11 | 12 | // 一个结构体 13 | for _, header := range tab.Headers { 14 | 15 | if header == nil { 16 | continue 17 | } 18 | 19 | if globals.CanDoAction(model.ActionNoGenFieldBinary, header) { 20 | continue 21 | } 22 | 23 | cell := tab.GetCell(row, header.Cell.Col) 24 | 25 | if cell == nil { 26 | continue 27 | } 28 | 29 | goType := model.LanguagePrimitive(header.TypeInfo.FieldType, "go") 30 | 31 | // 写入字段 32 | if header.TypeInfo.IsArray() { 33 | 34 | for _, elementValue := range cell.ValueList { 35 | 36 | if err := writePair(globals, structWriter, header.TypeInfo, goType, elementValue, header.Cell.Col); err != nil { 37 | return nil, err 38 | } 39 | } 40 | 41 | } else { 42 | 43 | // 空格不输出 44 | if cell.Value != "" { 45 | 46 | if err := writePair(globals, structWriter, header.TypeInfo, goType, cell.Value, header.Cell.Col); err != nil { 47 | return nil, err 48 | } 49 | } 50 | 51 | } 52 | 53 | } 54 | 55 | return structWriter, nil 56 | } 57 | -------------------------------------------------------------------------------- /v3/gen/bindata/tag.go: -------------------------------------------------------------------------------- 1 | package bindata 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | ) 6 | 7 | func MakeTag(globals *model.Globals, tf *model.TypeDefine, fieldIndex int) uint32 { 8 | convertedType := model.LanguagePrimitive(tf.FieldType, "go") 9 | 10 | var t int 11 | switch { 12 | case convertedType == "int16": 13 | t = 1 14 | case convertedType == "int32": 15 | t = 2 16 | case convertedType == "int64": 17 | t = 3 18 | case convertedType == "uint16": 19 | t = 4 20 | case convertedType == "uint32": 21 | t = 5 22 | case convertedType == "uint64": 23 | t = 6 24 | case convertedType == "float32": 25 | t = 7 26 | case convertedType == "string": 27 | t = 8 28 | case convertedType == "bool": 29 | t = 9 30 | case globals.Types.IsEnumKind(tf.FieldType): 31 | t = 10 32 | case convertedType == "float64": 33 | t = 12 34 | // 注意, t = 11是结构体 35 | default: 36 | panic("unknown type:" + tf.FieldType) 37 | } 38 | 39 | if tf.IsArray() { 40 | t += 100 41 | } 42 | 43 | return uint32(t<<16 | fieldIndex) 44 | } 45 | 46 | func MakeTagStructArray() uint32 { 47 | 48 | var t int 49 | t = 11 50 | 51 | // 结构体默认是数组 52 | t += 100 53 | 54 | return uint32(t << 16) 55 | } 56 | 57 | func writePair(globals *model.Globals, structWriter *BinaryWriter, fieldType *model.TypeDefine, goType, value string, fieldIndex int) error { 58 | 59 | tag := MakeTag(globals, fieldType, fieldIndex) 60 | if err := structWriter.WriteUInt32(tag); err != nil { 61 | return err 62 | } 63 | 64 | return writeValue(globals, structWriter, fieldType, goType, value) 65 | } 66 | -------------------------------------------------------------------------------- /v3/gen/bindata/writer.go: -------------------------------------------------------------------------------- 1 | package bindata 2 | 3 | import ( 4 | "bytes" 5 | "encoding/binary" 6 | "math" 7 | ) 8 | 9 | type BinaryWriter struct { 10 | buffer bytes.Buffer 11 | } 12 | 13 | func (self *BinaryWriter) Bytes() []byte { 14 | return self.buffer.Bytes() 15 | } 16 | 17 | func (self *BinaryWriter) Write(b []byte) (int, error) { 18 | return self.buffer.Write(b) 19 | } 20 | 21 | func (self *BinaryWriter) WriteInt16(x int16) error { 22 | 23 | return self.WriteUInt16(uint16(x)) 24 | } 25 | 26 | func (self *BinaryWriter) WriteInt32(x int32) error { 27 | return self.WriteUInt32(uint32(x)) 28 | } 29 | 30 | func (self *BinaryWriter) WriteInt64(x int64) error { 31 | return self.WriteUInt64(uint64(x)) 32 | } 33 | 34 | func (self *BinaryWriter) WriteUInt16(x uint16) error { 35 | 36 | return binary.Write(&self.buffer, binary.LittleEndian, &x) 37 | } 38 | 39 | func (self *BinaryWriter) WriteUInt32(x uint32) error { 40 | 41 | return binary.Write(&self.buffer, binary.LittleEndian, &x) 42 | } 43 | 44 | func (self *BinaryWriter) WriteUInt64(x uint64) error { 45 | return binary.Write(&self.buffer, binary.LittleEndian, &x) 46 | } 47 | 48 | func (self *BinaryWriter) WriteFloat32(x float32) error { 49 | 50 | v := math.Float32bits(x) 51 | return binary.Write(&self.buffer, binary.LittleEndian, &v) 52 | } 53 | 54 | func (self *BinaryWriter) WriteFloat64(x float64) error { 55 | 56 | v := math.Float64bits(x) 57 | return binary.Write(&self.buffer, binary.LittleEndian, &v) 58 | } 59 | 60 | func (self *BinaryWriter) WriteBool(x bool) error { 61 | return binary.Write(&self.buffer, binary.LittleEndian, &x) 62 | } 63 | 64 | func (self *BinaryWriter) WriteString(x string) error { 65 | l := uint32(len(x)) 66 | if err := binary.Write(&self.buffer, binary.LittleEndian, &l); err != nil { 67 | return err 68 | } 69 | 70 | data := []byte(x) 71 | if err := binary.Write(&self.buffer, binary.LittleEndian, &data); err != nil { 72 | return err 73 | } 74 | 75 | return nil 76 | } 77 | 78 | func NewBinaryWriter() *BinaryWriter { 79 | return &BinaryWriter{} 80 | } 81 | -------------------------------------------------------------------------------- /v3/gen/cssrc/gen.go: -------------------------------------------------------------------------------- 1 | package cssrc 2 | 3 | import ( 4 | "github.com/davyxu/protoplus/codegen" 5 | "github.com/davyxu/tabtoy/v3/gen" 6 | "github.com/davyxu/tabtoy/v3/model" 7 | ) 8 | 9 | func Generate(globals *model.Globals) (data []byte, err error) { 10 | 11 | cg := codegen.NewCodeGen("cssrc"). 12 | RegisterTemplateFunc(codegen.UsefulFunc). 13 | RegisterTemplateFunc(gen.UsefulFunc). 14 | RegisterTemplateFunc(UsefulFunc) 15 | 16 | err = cg.ParseTemplate(templateText, globals).Error() 17 | if err != nil { 18 | return 19 | } 20 | 21 | err = cg.WriteBytes(&data).Error() 22 | 23 | return 24 | } 25 | -------------------------------------------------------------------------------- /v3/gen/genfunc.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import "github.com/davyxu/tabtoy/v3/model" 4 | 5 | type GenSingleFile func(globals *model.Globals) (data []byte, err error) 6 | 7 | type GenCustom func(globals *model.Globals, param string) (err error) 8 | -------------------------------------------------------------------------------- /v3/gen/gosrc/func.go: -------------------------------------------------------------------------------- 1 | package gosrc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "strings" 7 | "text/template" 8 | ) 9 | 10 | var UsefulFunc = template.FuncMap{} 11 | 12 | // 将定义用的类型,转换为不同语言对应的复合类型 13 | 14 | func init() { 15 | UsefulFunc["GoType"] = func(tf *model.TypeDefine) string { 16 | 17 | convertedType := model.LanguagePrimitive(tf.FieldType, "go") 18 | 19 | if tf.IsArray() { 20 | return "[]" + convertedType 21 | } 22 | 23 | return convertedType 24 | } 25 | 26 | UsefulFunc["GoTabTag"] = func(fieldType *model.TypeDefine) string { 27 | 28 | var sb strings.Builder 29 | 30 | var kv []string 31 | 32 | if fieldType.Name != "" { 33 | kv = append(kv, fmt.Sprintf("tb_name:\"%s\"", fieldType.Name)) 34 | } 35 | 36 | if len(kv) > 0 { 37 | sb.WriteString("`") 38 | 39 | for _, s := range kv { 40 | sb.WriteString(s) 41 | } 42 | 43 | sb.WriteString("`") 44 | } 45 | 46 | return sb.String() 47 | } 48 | 49 | UsefulFunc["JsonTabOmit"] = func() string { 50 | return "`json:\"-\"`" 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /v3/gen/gosrc/gen.go: -------------------------------------------------------------------------------- 1 | package gosrc 2 | 3 | import ( 4 | "github.com/davyxu/protoplus/codegen" 5 | "github.com/davyxu/tabtoy/v3/gen" 6 | "github.com/davyxu/tabtoy/v3/model" 7 | "github.com/davyxu/tabtoy/v3/report" 8 | ) 9 | 10 | func Generate(globals *model.Globals) (data []byte, err error) { 11 | 12 | cg := codegen.NewCodeGen("gosrc"). 13 | RegisterTemplateFunc(codegen.UsefulFunc). 14 | RegisterTemplateFunc(gen.UsefulFunc). 15 | RegisterTemplateFunc(UsefulFunc) 16 | 17 | err = cg.ParseTemplate(templateText, globals).Error() 18 | if err != nil { 19 | return 20 | } 21 | 22 | err = cg.FormatGoCode().Error() 23 | if err != nil { 24 | report.Log.Infoln(string(cg.Code())) 25 | return 26 | } 27 | 28 | err = cg.WriteBytes(&data).Error() 29 | 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /v3/gen/javasrc/func.go: -------------------------------------------------------------------------------- 1 | package javasrc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davyxu/tabtoy/util" 6 | "github.com/davyxu/tabtoy/v3/model" 7 | "text/template" 8 | ) 9 | 10 | var UsefulFunc = template.FuncMap{} 11 | 12 | // 将定义用的类型,转换为不同语言对应的复合类型 13 | 14 | func wrapSingleValue(globals *model.Globals, valueType *model.TypeDefine, value string) string { 15 | switch { 16 | case valueType.FieldType == "string": // 字符串 17 | return util.StringWrap(util.StringEscape(value)) 18 | case valueType.FieldType == "float32": 19 | return value 20 | case globals.Types.IsEnumKind(valueType.FieldType): // 枚举 21 | t := globals.Types.ResolveEnum(valueType.FieldType, value) 22 | if t != nil { 23 | return t.Define.ObjectType + "." + t.Define.FieldName 24 | } 25 | 26 | return "" 27 | case valueType.FieldType == "bool": 28 | 29 | v, _ := model.ParseBool(value) 30 | if v { 31 | return "true" 32 | } 33 | 34 | return "false" 35 | } 36 | 37 | if value == "" { 38 | return model.FetchDefaultValue(valueType.FieldType) 39 | } 40 | 41 | return value 42 | } 43 | 44 | func init() { 45 | UsefulFunc["JavaType"] = func(tf *model.TypeDefine, requireRef bool) string { 46 | 47 | convertedType := model.LanguagePrimitive(tf.FieldType, "java") 48 | 49 | if requireRef { 50 | // https://www.geeksforgeeks.org/difference-between-an-integer-and-int-in-java/ 51 | switch convertedType { 52 | case "int": 53 | convertedType = "Integer" 54 | case "short": 55 | convertedType = "Short" 56 | case "long": 57 | convertedType = "Integer" 58 | case "float": 59 | convertedType = "Float" 60 | case "double": 61 | convertedType = "Double" 62 | case "boolean": 63 | convertedType = "Boolean" 64 | } 65 | } 66 | 67 | if tf.IsArray() { 68 | return convertedType + "[]" 69 | } 70 | 71 | return convertedType 72 | } 73 | 74 | UsefulFunc["JavaDefaultValue"] = func(globals *model.Globals, tf *model.TypeDefine) string { 75 | 76 | convertedType := model.LanguagePrimitive(tf.FieldType, "java") 77 | 78 | if tf.IsArray() { 79 | return fmt.Sprintf("new %s[]{}", convertedType) 80 | } else { 81 | return wrapSingleValue(globals, tf, "") 82 | } 83 | 84 | return convertedType 85 | } 86 | 87 | } 88 | -------------------------------------------------------------------------------- /v3/gen/javasrc/gen.go: -------------------------------------------------------------------------------- 1 | package javasrc 2 | 3 | import ( 4 | "github.com/davyxu/protoplus/codegen" 5 | "github.com/davyxu/tabtoy/v3/gen" 6 | "github.com/davyxu/tabtoy/v3/model" 7 | ) 8 | 9 | func Generate(globals *model.Globals) (data []byte, err error) { 10 | 11 | cg := codegen.NewCodeGen("javasrc"). 12 | RegisterTemplateFunc(codegen.UsefulFunc). 13 | RegisterTemplateFunc(gen.UsefulFunc). 14 | RegisterTemplateFunc(UsefulFunc) 15 | 16 | err = cg.ParseTemplate(templateText, globals).Error() 17 | if err != nil { 18 | return 19 | } 20 | 21 | err = cg.WriteBytes(&data).Error() 22 | 23 | return 24 | } 25 | -------------------------------------------------------------------------------- /v3/gen/jsondata/gen.go: -------------------------------------------------------------------------------- 1 | package jsondata 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "github.com/davyxu/tabtoy/v3/model" 7 | "io/ioutil" 8 | ) 9 | 10 | func Output(globals *model.Globals, param string) (err error) { 11 | 12 | for _, tab := range globals.Datas.AllTables() { 13 | 14 | // 一个表的所有列 15 | headers := globals.Types.AllFieldByName(tab.OriginalHeaderType) 16 | 17 | fileData := map[string]interface{}{ 18 | "@Tool": "github.com/davyxu/tabtoy", 19 | "@Version": globals.Version, 20 | } 21 | 22 | var tabData []interface{} 23 | 24 | // 遍历所有行 25 | for row := 1; row < len(tab.Rows); row++ { 26 | 27 | // 遍历每一列 28 | rowData := map[string]interface{}{} 29 | for col, header := range headers { 30 | 31 | if globals.CanDoAction(model.ActionNoGenFieldJsonDir, header) { 32 | continue 33 | } 34 | 35 | // 在单元格找到值 36 | valueCell := tab.GetCell(row, col) 37 | 38 | var value = wrapValue(globals, valueCell, header) 39 | 40 | rowData[header.FieldName] = value 41 | } 42 | 43 | tabData = append(tabData, rowData) 44 | } 45 | 46 | fileData[tab.HeaderType] = tabData 47 | 48 | data, err := json.MarshalIndent(&fileData, "", "\t") 49 | 50 | if err != nil { 51 | return err 52 | } 53 | 54 | err = ioutil.WriteFile(fmt.Sprintf("%s/%s.json", param, tab.HeaderType), data, 0666) 55 | if err != nil { 56 | return err 57 | } 58 | } 59 | 60 | return nil 61 | } 62 | 63 | func Generate(globals *model.Globals) (data []byte, err error) { 64 | 65 | fileData := map[string]interface{}{ 66 | "@Tool": "github.com/davyxu/tabtoy", 67 | "@Version": globals.Version, 68 | } 69 | 70 | for _, tab := range globals.Datas.AllTables() { 71 | 72 | // 一个表的所有列 73 | headers := globals.Types.AllFieldByName(tab.OriginalHeaderType) 74 | 75 | var tabData []interface{} 76 | 77 | // 遍历所有行 78 | for row := 1; row < len(tab.Rows); row++ { 79 | 80 | // 遍历每一列 81 | rowData := map[string]interface{}{} 82 | for col, header := range headers { 83 | 84 | if globals.CanDoAction(model.ActionNoGenFieldJson, header) { 85 | continue 86 | } 87 | 88 | // 在单元格找到值 89 | valueCell := tab.GetCell(row, col) 90 | 91 | var value = wrapValue(globals, valueCell, header) 92 | 93 | rowData[header.FieldName] = value 94 | } 95 | 96 | tabData = append(tabData, rowData) 97 | } 98 | 99 | fileData[tab.HeaderType] = tabData 100 | } 101 | 102 | return json.MarshalIndent(fileData, "", "\t") 103 | } 104 | -------------------------------------------------------------------------------- /v3/gen/jsondata/wrap.go: -------------------------------------------------------------------------------- 1 | package jsondata 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | "strconv" 6 | ) 7 | 8 | func wrapValue(globals *model.Globals, valueCell *model.Cell, valueType *model.TypeDefine) interface{} { 9 | if valueType.IsArray() { 10 | 11 | var vlist = make([]interface{}, 0) 12 | // 空的单元格,导出空数组,除非强制指定填充默认值 13 | if valueCell != nil { 14 | 15 | for _, elementValue := range valueCell.ValueList { 16 | 17 | vlist = append(vlist, wrapSingleValue(globals, valueType, elementValue)) 18 | } 19 | } 20 | 21 | return vlist 22 | 23 | } else { 24 | 25 | var value string 26 | if valueCell != nil { 27 | value = valueCell.Value 28 | } 29 | 30 | return wrapSingleValue(globals, valueType, value) 31 | } 32 | } 33 | 34 | func wrapSingleValue(globals *model.Globals, valueType *model.TypeDefine, value string) interface{} { 35 | 36 | goType := model.LanguagePrimitive(valueType.FieldType, "go") 37 | 38 | switch { 39 | case goType == "string": // 字符串 40 | return value // json自己会做转义, 所以这里无需转义 41 | case goType == "float32": 42 | 43 | if value == "" { 44 | return float32(0) 45 | } 46 | 47 | f64, _ := strconv.ParseFloat(value, 32) 48 | return float32(f64) 49 | case goType == "float64": 50 | 51 | if value == "" { 52 | return float64(0) 53 | } 54 | 55 | f64, _ := strconv.ParseFloat(value, 64) 56 | return f64 57 | case globals.Types.IsEnumKind(valueType.FieldType): // 枚举 58 | enumValue := globals.Types.ResolveEnumValue(valueType.FieldType, value) 59 | i, _ := strconv.Atoi(enumValue) 60 | return int32(i) 61 | case goType == "bool": 62 | 63 | v, _ := model.ParseBool(value) 64 | if v { 65 | return true 66 | } 67 | 68 | return false 69 | case goType == "int16": 70 | i64, _ := strconv.ParseInt(value, 10, 16) 71 | return int16(i64) 72 | case goType == "int32": 73 | i64, _ := strconv.ParseInt(value, 10, 32) 74 | return int32(i64) 75 | case goType == "int64": 76 | i64, _ := strconv.ParseInt(value, 10, 64) 77 | return i64 78 | case goType == "uint16": 79 | i64, _ := strconv.ParseInt(value, 10, 16) 80 | return uint16(i64) 81 | case goType == "uint32": 82 | i64, _ := strconv.ParseUint(value, 10, 32) 83 | return uint32(i64) 84 | case goType == "uint64": 85 | i64, _ := strconv.ParseUint(value, 10, 64) 86 | return i64 87 | } 88 | 89 | if value == "" { 90 | return model.FetchDefaultValue(valueType.FieldType) 91 | } 92 | 93 | return value 94 | } 95 | -------------------------------------------------------------------------------- /v3/gen/jsontype/gen.go: -------------------------------------------------------------------------------- 1 | package jsontype 2 | 3 | import ( 4 | "encoding/json" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "sort" 7 | ) 8 | 9 | func Generate(globals *model.Globals) (data []byte, err error) { 10 | 11 | objByType := map[string]*Object{} 12 | 13 | for _, def := range globals.Types.AllFields() { 14 | obj := objByType[def.ObjectType] 15 | if obj == nil { 16 | obj = &Object{} 17 | obj.Name = def.ObjectType 18 | 19 | for _, indexDef := range globals.IndexList { 20 | if indexDef.TableType == def.ObjectType { 21 | obj.Tags = append(obj.Tags, indexDef.Tags...) 22 | } 23 | } 24 | 25 | switch def.Kind { 26 | case model.TypeUsage_HeaderStruct: 27 | obj.Type = "Struct" 28 | case model.TypeUsage_Enum: 29 | obj.Type = "Enum" 30 | } 31 | objByType[def.ObjectType] = obj 32 | } 33 | 34 | var fd Field 35 | fd.Name = def.FieldName 36 | fd.Type = def.FieldType 37 | fd.Comment = def.Name 38 | fd.Value = def.Value 39 | fd.MakeIndex = def.MakeIndex 40 | fd.ArraySplitter = def.ArraySplitter 41 | fd.Tags = def.Tags 42 | 43 | obj.Fields = append(obj.Fields, &fd) 44 | } 45 | 46 | var f File 47 | f.Version = globals.Version 48 | f.Tool = "github.com/davyxu/tabtoy" 49 | 50 | for _, obj := range objByType { 51 | f.Objects = append(f.Objects, obj) 52 | } 53 | 54 | sort.Slice(f.Objects, func(i, j int) bool { 55 | return f.Objects[i].Compare(f.Objects[j]) 56 | }) 57 | 58 | return json.MarshalIndent(&f, "", "\t") 59 | } 60 | -------------------------------------------------------------------------------- /v3/gen/jsontype/model.go: -------------------------------------------------------------------------------- 1 | package jsontype 2 | 3 | import "strconv" 4 | 5 | type Field struct { 6 | Name string // 字段名 7 | Type string // 表中原有写的类型 8 | Comment string // 注释, 表中的名称 9 | Value string `json:",omitempty"` // 枚举值 10 | MakeIndex bool `json:",omitempty"` // 是否生成索引 11 | ArraySplitter string `json:",omitempty"` // 数组切割符 12 | Tags []string `json:",omitempty"` // 自定义标记 13 | } 14 | 15 | func (self *Field) EnumValue() int { 16 | v, _ := strconv.Atoi(self.Value) 17 | return v 18 | } 19 | 20 | // 表示表格, 枚举 21 | type Object struct { 22 | Name string 23 | Type string // 对象类型 24 | Tags []string `json:",omitempty"` // 自定义标记 25 | 26 | Fields []*Field 27 | } 28 | 29 | func (self *Object) Compare(other *Object) bool { 30 | if self.Type != other.Type { 31 | return self.Type < other.Type 32 | } 33 | 34 | return self.Name < other.Name 35 | } 36 | 37 | type File struct { 38 | Tool string `json:"@Tool"` 39 | Version string `json:"@Version"` 40 | Objects []*Object 41 | } 42 | -------------------------------------------------------------------------------- /v3/gen/luasrc/func.go: -------------------------------------------------------------------------------- 1 | package luasrc 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/gen" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "strings" 7 | "text/template" 8 | ) 9 | 10 | var UsefulFunc = template.FuncMap{} 11 | 12 | func WrapValue(globals *model.Globals, cell *model.Cell, valueType *model.TypeDefine) string { 13 | if valueType.IsArray() { 14 | 15 | var sb strings.Builder 16 | sb.WriteString("{") 17 | 18 | if cell != nil { 19 | for index, elementValue := range cell.ValueList { 20 | if index > 0 { 21 | sb.WriteString(",") 22 | } 23 | sb.WriteString(gen.WrapSingleValue(globals, valueType, elementValue)) 24 | } 25 | } 26 | 27 | sb.WriteString("}") 28 | 29 | return sb.String() 30 | 31 | } else { 32 | 33 | var value string 34 | if cell != nil { 35 | value = cell.Value 36 | } 37 | 38 | return gen.WrapSingleValue(globals, valueType, value) 39 | } 40 | } 41 | 42 | func init() { 43 | UsefulFunc["WrapTabValue"] = func(globals *model.Globals, dataTable *model.DataTable, allHeaders []*model.TypeDefine, row, col int) (ret string) { 44 | // 找到完整的表头(按完整表头遍历) 45 | header := allHeaders[col] 46 | 47 | 48 | if header == nil { 49 | return "" 50 | } 51 | 52 | // 在单元格找到值 53 | valueCell := dataTable.GetCell(row, col) 54 | 55 | if valueCell != nil { 56 | 57 | return WrapValue(globals, valueCell, header) 58 | } else { 59 | // 这个表中没有这列数据 60 | return WrapValue(globals, nil, header) 61 | } 62 | } 63 | 64 | UsefulFunc["IsWrapFieldName"] = func(globals *model.Globals, dataTable *model.DataTable, allHeaders []*model.TypeDefine, row, col int) (ret bool) { 65 | // 找到完整的表头(按完整表头遍历) 66 | header := allHeaders[col] 67 | 68 | 69 | if header == nil { 70 | return false 71 | } 72 | 73 | if globals.CanDoAction(model.ActionNoGennFieldLua, header) { 74 | return false 75 | } 76 | 77 | return true 78 | } 79 | 80 | 81 | } 82 | -------------------------------------------------------------------------------- /v3/gen/luasrc/gen.go: -------------------------------------------------------------------------------- 1 | package luasrc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davyxu/protoplus/codegen" 6 | "github.com/davyxu/tabtoy/v3/gen" 7 | "github.com/davyxu/tabtoy/v3/model" 8 | "io/ioutil" 9 | ) 10 | 11 | func Generate(globals *model.Globals) (data []byte, err error) { 12 | 13 | err = codegen.NewCodeGen("luasrc"). 14 | RegisterTemplateFunc(codegen.UsefulFunc). 15 | RegisterTemplateFunc(gen.UsefulFunc). 16 | RegisterTemplateFunc(UsefulFunc). 17 | ParseTemplate(templateText_luasrc, globals). 18 | WriteBytes(&data).Error() 19 | 20 | return 21 | } 22 | 23 | func Output(globals *model.Globals, param string) (err error) { 24 | 25 | type LocalContext struct { 26 | Tab *model.DataTable 27 | G *model.Globals 28 | } 29 | 30 | var typeData []byte 31 | err = codegen.NewCodeGen("luatype"). 32 | RegisterTemplateFunc(codegen.UsefulFunc). 33 | RegisterTemplateFunc(gen.UsefulFunc). 34 | RegisterTemplateFunc(UsefulFunc). 35 | ParseTemplate(templateText_luatype, globals). 36 | WriteBytes(&typeData).Error() 37 | if err != nil { 38 | return err 39 | } 40 | 41 | err = ioutil.WriteFile(fmt.Sprintf("%s/_%sType.lua", param, globals.CombineStructName), typeData, 0666) 42 | 43 | if err != nil { 44 | return err 45 | } 46 | 47 | for _, tab := range globals.Datas.AllTables() { 48 | 49 | var ctx LocalContext 50 | ctx.Tab = tab 51 | ctx.G = globals 52 | 53 | var data []byte 54 | err = codegen.NewCodeGen("luadir"). 55 | RegisterTemplateFunc(codegen.UsefulFunc). 56 | RegisterTemplateFunc(gen.UsefulFunc). 57 | RegisterTemplateFunc(UsefulFunc). 58 | ParseTemplate(templateText_luadir, ctx). 59 | WriteBytes(&data).Error() 60 | 61 | if err != nil { 62 | return err 63 | } 64 | 65 | err = ioutil.WriteFile(fmt.Sprintf("%s/%s.lua", param, tab.HeaderType), data, 0666) 66 | 67 | if err != nil { 68 | return err 69 | } 70 | } 71 | 72 | return nil 73 | } 74 | -------------------------------------------------------------------------------- /v3/gen/luasrc/text.go: -------------------------------------------------------------------------------- 1 | package luasrc 2 | 3 | const templateText_luasrc = `-- Generated by github.com/davyxu/tabtoy 4 | -- Version: {{.Version}} 5 | 6 | return { 7 | init = function( g ) 8 | {{range $di, $tab := .Datas.AllTables}} 9 | g.{{$tab.HeaderType}} = { {{range $unusedrow,$row := $tab.DataRowIndex}}{{$headers := $.Types.AllFieldByName $tab.OriginalHeaderType}} 10 | { {{range $col, $header := $headers}}{{if IsWrapFieldName $ $tab $headers $row $col}}{{$header.FieldName}} = {{WrapTabValue $ $tab $headers $row $col}}, {{end}}{{end}}}, {{end}} 11 | } 12 | {{end}} 13 | {{range $ii, $idx := GetIndices $}} 14 | -- {{$idx.Table.HeaderType}} 15 | g.{{$idx.Table.HeaderType}}By{{$idx.FieldInfo.FieldName}} = {} 16 | for _, rec in pairs(g.{{$idx.Table.HeaderType}}) do 17 | g.{{$idx.Table.HeaderType}}By{{$idx.FieldInfo.FieldName}}[rec.{{$idx.FieldInfo.FieldName}}] = rec 18 | end 19 | {{end}} 20 | {{range $sn, $objName := $.Types.EnumNames}} 21 | ---@enum {{$.PackageName}}.{{$objName}} 22 | g.{{$objName}} = { {{range $fi,$field := $.Types.AllFieldByName $objName}} 23 | {{$field.FieldName}} = {{$field.Value}}, -- {{$field.Name}} {{end}} {{range $fi,$field := $.Types.AllFieldByName $objName}} 24 | [{{$field.Value}}] = "{{$field.FieldName}}", -- {{$field.Name}} {{end}} 25 | }{{end}} 26 | return g 27 | end 28 | } 29 | ` 30 | 31 | const templateText_luadir = `-- Generated by github.com/davyxu/tabtoy 32 | -- Version: {{$.G.Version}} 33 | 34 | return { 35 | init = function( g ) 36 | g.{{$.Tab.HeaderType}} = { {{range $unusedrow,$row := $.Tab.DataRowIndex}}{{$headers := $.G.Types.AllFieldByName $.Tab.OriginalHeaderType}} 37 | { {{range $col, $header := $headers}}{{if IsWrapFieldName $.G $.Tab $headers $row $col}}{{$header.FieldName}} = {{WrapTabValue $.G $.Tab $headers $row $col}}, {{end}}{{end}}}, {{end}} 38 | } 39 | {{range $ii, $idx := GetIndicesByTable $.Tab}} 40 | -- {{$idx.Table.HeaderType}} 41 | g.{{$idx.Table.HeaderType}}By{{$idx.FieldInfo.FieldName}} = {} 42 | for _, rec in pairs(g.{{$idx.Table.HeaderType}}) do 43 | g.{{$idx.Table.HeaderType}}By{{$idx.FieldInfo.FieldName}}[rec.{{$idx.FieldInfo.FieldName}}] = rec 44 | end 45 | {{end}} 46 | return g 47 | end 48 | } 49 | ` 50 | const templateText_luatype = `-- Generated by github.com/davyxu/tabtoy 51 | -- Version: {{.Version}} 52 | 53 | return { 54 | init = function( g ) 55 | {{range $sn, $objName := $.Types.EnumNames}} 56 | ---@enum {{$.PackageName}}.{{$objName}} 57 | g.{{$objName}} = { {{range $fi,$field := $.Types.AllFieldByName $objName}} 58 | {{$field.FieldName}} = {{$field.Value}}, -- {{$field.Name}} {{end}} {{range $fi,$field := $.Types.AllFieldByName $objName}} 59 | [{{$field.Value}}] = "{{$field.FieldName}}", -- {{$field.Name}} {{end}} 60 | }{{end}} 61 | return g 62 | end 63 | } 64 | ` 65 | -------------------------------------------------------------------------------- /v3/gen/pbdata/dynamictype.go: -------------------------------------------------------------------------------- 1 | package pbdata 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/model" 5 | "github.com/golang/protobuf/proto" 6 | descriptorpb "github.com/golang/protobuf/protoc-gen-go/descriptor" 7 | "google.golang.org/protobuf/reflect/protodesc" 8 | "google.golang.org/protobuf/reflect/protoreflect" 9 | "strconv" 10 | ) 11 | 12 | // https://farer.org/2020/04/17/go-protobuf-apiv2-reflect-dynamicpb/ 13 | func buildDynamicType(globals *model.Globals) (protoreflect.FileDescriptor, error) { 14 | var file descriptorpb.FileDescriptorProto 15 | file.Syntax = proto.String("proto3") 16 | file.Name = proto.String(globals.CombineStructName) 17 | file.Package = proto.String(globals.PackageName) 18 | 19 | for _, tab := range globals.Datas.AllTables() { 20 | 21 | var desc descriptorpb.DescriptorProto 22 | desc.Name = proto.String(tab.OriginalHeaderType) 23 | for index, field := range globals.Types.AllFieldByName(tab.OriginalHeaderType) { 24 | var fd descriptorpb.FieldDescriptorProto 25 | fd.Name = proto.String(field.FieldName) 26 | fd.Number = proto.Int32(int32(index + 1)) 27 | fd.JsonName = proto.String(field.FieldName) 28 | tableType2PbType(globals, field, &fd) 29 | if field.IsArray() { 30 | fd.Label = descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum() 31 | } else { 32 | fd.Label = descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum() 33 | } 34 | 35 | desc.Field = append(desc.Field, &fd) 36 | } 37 | 38 | file.MessageType = append(file.MessageType, &desc) 39 | } 40 | 41 | for _, enumName := range globals.Types.EnumNames() { 42 | 43 | var ed descriptorpb.EnumDescriptorProto 44 | ed.Name = proto.String(enumName) 45 | 46 | for _, field := range globals.Types.AllFieldByName(enumName) { 47 | var vd descriptorpb.EnumValueDescriptorProto 48 | vd.Name = proto.String(field.FieldName) 49 | v, _ := strconv.Atoi(field.Value) 50 | vd.Number = proto.Int32(int32(v)) 51 | ed.Value = append(ed.Value, &vd) 52 | } 53 | file.EnumType = append(file.EnumType, &ed) 54 | } 55 | 56 | var combine descriptorpb.DescriptorProto 57 | combine.Name = proto.String(globals.CombineStructName) 58 | for index, md := range file.MessageType { 59 | var fd descriptorpb.FieldDescriptorProto 60 | fd.Name = proto.String(md.GetName()) 61 | fd.Number = proto.Int32(int32(index + 1)) 62 | fd.JsonName = proto.String(md.GetName()) 63 | fd.Type = descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum() 64 | fd.TypeName = proto.String(globals.PackageName + "." + md.GetName()) 65 | fd.Label = descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum() 66 | combine.Field = append(combine.Field, &fd) 67 | } 68 | 69 | file.MessageType = append(file.MessageType, &combine) 70 | 71 | return protodesc.NewFile(&file, nil) 72 | } 73 | -------------------------------------------------------------------------------- /v3/gen/pbsrc/func.go: -------------------------------------------------------------------------------- 1 | package pbsrc 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davyxu/tabtoy/v3/model" 6 | "strings" 7 | "text/template" 8 | ) 9 | 10 | var UsefulFunc = template.FuncMap{} 11 | 12 | // 将定义用的类型,转换为不同语言对应的复合类型 13 | 14 | func init() { 15 | UsefulFunc["PbType"] = func(tf *model.TypeDefine) string { 16 | 17 | pbType := model.LanguagePrimitive(tf.FieldType, "pb") 18 | 19 | if tf.IsArray() { 20 | return "repeated " + pbType 21 | } 22 | 23 | return pbType 24 | } 25 | 26 | UsefulFunc["PbTag"] = func(fieldIndex int, fieldType *model.TypeDefine) string { 27 | 28 | var sb strings.Builder 29 | fmt.Fprintf(&sb, "= %d", fieldIndex+1) 30 | return sb.String() 31 | } 32 | 33 | UsefulFunc["PbCombineField"] = func(fieldIndex int) string { 34 | 35 | var sb strings.Builder 36 | fmt.Fprintf(&sb, "= %d", fieldIndex+1) 37 | return sb.String() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /v3/gen/pbsrc/gen.go: -------------------------------------------------------------------------------- 1 | package pbsrc 2 | 3 | import ( 4 | "github.com/davyxu/protoplus/codegen" 5 | "github.com/davyxu/tabtoy/v3/gen" 6 | "github.com/davyxu/tabtoy/v3/model" 7 | ) 8 | 9 | func Generate(globals *model.Globals) (data []byte, err error) { 10 | 11 | cg := codegen.NewCodeGen("pbsrc"). 12 | RegisterTemplateFunc(codegen.UsefulFunc). 13 | RegisterTemplateFunc(gen.UsefulFunc). 14 | RegisterTemplateFunc(UsefulFunc) 15 | 16 | err = cg.ParseTemplate(templateText, globals).Error() 17 | if err != nil { 18 | return 19 | } 20 | 21 | err = cg.WriteBytes(&data).Error() 22 | 23 | return 24 | } 25 | -------------------------------------------------------------------------------- /v3/gen/pbsrc/text.go: -------------------------------------------------------------------------------- 1 | package pbsrc 2 | 3 | // 报错行号+3 4 | const templateText = `// Generated by github.com/davyxu/tabtoy 5 | // DO NOT EDIT!! 6 | // Version: {{.Version}} 7 | syntax = "proto3"; 8 | package {{.PackageName}}; 9 | 10 | {{range $sn, $objName := $.Types.EnumNames}} 11 | enum {{$objName}} 12 | { {{range $fi,$field := $.Types.AllFieldByName $objName}} 13 | {{$field.FieldName}} = {{$field.Value}}; // {{$field.Name}} {{end}} 14 | } 15 | {{end}} 16 | {{range $sn, $objName := $.Types.StructNames}} 17 | message {{$objName}} 18 | { {{range $fi,$field := $.Types.AllFieldByName $objName}} 19 | {{PbType $field}} {{$field.FieldName}} {{PbTag $fi $field}}; // {{$field.Name}} {{end}} 20 | } 21 | {{end}} 22 | 23 | // Combine struct 24 | message {{.CombineStructName}} 25 | { {{range $ti, $tab := $.Datas.AllTables}} 26 | repeated {{$tab.HeaderType}} {{$tab.HeaderType}} {{PbCombineField $ti}}; // table: {{$tab.HeaderType}} {{end}} 27 | } 28 | 29 | ` 30 | -------------------------------------------------------------------------------- /v3/gen/sharefunc.go: -------------------------------------------------------------------------------- 1 | package gen 2 | 3 | import ( 4 | "github.com/ahmetb/go-linq" 5 | "github.com/davyxu/tabtoy/util" 6 | "github.com/davyxu/tabtoy/v3/model" 7 | "text/template" 8 | ) 9 | 10 | var UsefulFunc = template.FuncMap{} 11 | 12 | type TableIndices struct { 13 | Table *model.DataTable 14 | FieldInfo *model.TypeDefine 15 | } 16 | 17 | func KeyValueTypeNames(globals *model.Globals) (ret []string) { 18 | linq.From(globals.IndexList).Where(func(raw interface{}) bool { 19 | pragma := raw.(*model.IndexDefine) 20 | return pragma.Kind == model.TableKind_KeyValue 21 | }).Select(func(raw interface{}) interface{} { 22 | pragma := raw.(*model.IndexDefine) 23 | 24 | return pragma.TableType 25 | }).Distinct().ToSlice(&ret) 26 | 27 | return 28 | } 29 | 30 | func WrapSingleValue(globals *model.Globals, valueType *model.TypeDefine, value string) string { 31 | switch { 32 | case valueType.FieldType == "string": // 字符串 33 | return util.StringWrap(util.StringEscape(value)) 34 | case valueType.FieldType == "float": 35 | 36 | if value == "" { 37 | return model.FetchDefaultValue(valueType.FieldType) 38 | } 39 | 40 | return value 41 | case globals.Types.IsEnumKind(valueType.FieldType): // 枚举 42 | return globals.Types.ResolveEnumValue(valueType.FieldType, value) 43 | case valueType.FieldType == "bool": 44 | 45 | v, _ := model.ParseBool(value) 46 | if v { 47 | return "true" 48 | } 49 | 50 | return "false" 51 | } 52 | 53 | if value == "" { 54 | return model.FetchDefaultValue(valueType.FieldType) 55 | } 56 | 57 | return value 58 | } 59 | 60 | func GetIndicesByTable(tab *model.DataTable) (ret []TableIndices) { 61 | // 遍历输入数据的每一列 62 | for _, header := range tab.Headers { 63 | 64 | // 输入的列头 65 | if header.TypeInfo == nil { 66 | continue 67 | } 68 | 69 | if header.TypeInfo.MakeIndex { 70 | 71 | ret = append(ret, TableIndices{ 72 | Table: tab, 73 | FieldInfo: header.TypeInfo, 74 | }) 75 | } 76 | } 77 | 78 | return 79 | } 80 | 81 | func GetIndices(globals *model.Globals) (ret []TableIndices) { 82 | for _, tab := range globals.Datas.AllTables() { 83 | ret = append(ret, GetIndicesByTable(tab)...) 84 | } 85 | 86 | return 87 | } 88 | 89 | func init() { 90 | UsefulFunc["HasKeyValueTypes"] = func(globals *model.Globals) bool { 91 | return len(KeyValueTypeNames(globals)) > 0 92 | } 93 | 94 | UsefulFunc["GetKeyValueTypeNames"] = KeyValueTypeNames 95 | 96 | UsefulFunc["GetIndicesByTable"] = GetIndicesByTable 97 | 98 | UsefulFunc["GetIndices"] = GetIndices 99 | } 100 | -------------------------------------------------------------------------------- /v3/helper/fileloader.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "errors" 5 | "github.com/davyxu/tabtoy/v3/report" 6 | "path/filepath" 7 | "sync" 8 | ) 9 | 10 | type FileGetter interface { 11 | GetFile(filename string) (TableFile, error) 12 | } 13 | 14 | type FileLoader struct { 15 | fileByName sync.Map 16 | inputFile []string 17 | 18 | syncLoad bool 19 | cacheDir string 20 | } 21 | 22 | func (self *FileLoader) AddFile(filename string) { 23 | 24 | self.inputFile = append(self.inputFile, filename) 25 | } 26 | 27 | func (self *FileLoader) Commit() { 28 | 29 | var task sync.WaitGroup 30 | task.Add(len(self.inputFile)) 31 | 32 | for _, inputFileName := range self.inputFile { 33 | 34 | go func(fileName string) { 35 | 36 | self.fileByName.Store(fileName, loadFileByExt(fileName, self.cacheDir)) 37 | 38 | task.Done() 39 | 40 | }(inputFileName) 41 | 42 | } 43 | 44 | task.Wait() 45 | 46 | self.inputFile = self.inputFile[0:0] 47 | } 48 | 49 | func loadFileByExt(filename string, cacheDir string) interface{} { 50 | 51 | var tabFile TableFile 52 | switch filepath.Ext(filename) { 53 | case ".xlsx", ".xls", ".xlsm": 54 | 55 | tabFile = NewXlsxFile(cacheDir) 56 | 57 | err := tabFile.Load(filename) 58 | 59 | if err != nil { 60 | return err 61 | } 62 | 63 | case ".csv": 64 | tabFile = NewCSVFile() 65 | 66 | err := tabFile.Load(filename) 67 | 68 | if err != nil { 69 | return err 70 | } 71 | 72 | default: 73 | report.ReportError("UnknownInputFileExtension", filename) 74 | } 75 | 76 | return tabFile 77 | } 78 | 79 | func (self *FileLoader) GetFile(filename string) (TableFile, error) { 80 | 81 | if self.syncLoad { 82 | 83 | result := loadFileByExt(filename, self.cacheDir) 84 | if err, ok := result.(error); ok { 85 | return nil, err 86 | } 87 | 88 | return result.(TableFile), nil 89 | 90 | } else { 91 | if result, ok := self.fileByName.Load(filename); ok { 92 | 93 | if err, ok := result.(error); ok { 94 | return nil, err 95 | } 96 | 97 | return result.(TableFile), nil 98 | 99 | } else { 100 | return nil, errors.New("not found") 101 | } 102 | } 103 | 104 | } 105 | 106 | func NewFileLoader(syncLoad bool, cacheDir string) *FileLoader { 107 | return &FileLoader{ 108 | syncLoad: syncLoad, 109 | cacheDir: cacheDir, 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /v3/helper/filewriter.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "io/ioutil" 5 | "os" 6 | "path/filepath" 7 | ) 8 | 9 | func WriteFile(filename string, data []byte) error { 10 | 11 | err := os.MkdirAll(filepath.Dir(filename), 0755) 12 | 13 | if err != nil && !os.IsExist(err) { 14 | return err 15 | } 16 | 17 | return ioutil.WriteFile(filename, data, 0666) 18 | } 19 | -------------------------------------------------------------------------------- /v3/helper/memfile.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "errors" 5 | "github.com/tealeg/xlsx" 6 | ) 7 | 8 | type MemFileData struct { 9 | File TableFile 10 | TableName string 11 | FileName string 12 | } 13 | 14 | type MemFile struct { 15 | dataByFileName map[string]*MemFileData 16 | } 17 | 18 | func (self *MemFile) VisitAllTable(callback func(data *MemFileData) bool) { 19 | 20 | for _, v := range self.dataByFileName { 21 | if !callback(v) { 22 | return 23 | } 24 | } 25 | } 26 | 27 | func (self *MemFile) AddFile(filename string, file TableFile) (ret *MemFileData) { 28 | 29 | ret = &MemFileData{ 30 | File: file, 31 | FileName: filename, 32 | } 33 | 34 | self.dataByFileName[filename] = ret 35 | 36 | return 37 | } 38 | 39 | func (self *MemFile) CreateXLSXFile(filename string) TableSheet { 40 | 41 | xfile := xlsx.NewFile() 42 | xfile.AddSheet("Default") 43 | 44 | file := NewXlsxFile("") 45 | 46 | file.(interface { 47 | FromXFile(file *xlsx.File) 48 | }).FromXFile(xfile) 49 | 50 | self.AddFile(filename, file) 51 | 52 | return file.Sheets()[0] 53 | } 54 | 55 | func (self *MemFile) CreateCSVFile(filename string) TableSheet { 56 | 57 | file := NewCSVFile() 58 | 59 | self.AddFile(filename, file) 60 | 61 | return file.Sheets()[0] 62 | } 63 | 64 | func (self *MemFile) GetFile(filename string) (TableFile, error) { 65 | 66 | if f, ok := self.dataByFileName[filename]; ok { 67 | 68 | return f.File, nil 69 | } 70 | 71 | return nil, errors.New("file not found: " + filename) 72 | } 73 | 74 | func NewMemFile() *MemFile { 75 | return &MemFile{ 76 | dataByFileName: make(map[string]*MemFileData), 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /v3/helper/sheethelper.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "github.com/tealeg/xlsx" 5 | "strings" 6 | ) 7 | 8 | func GetSheetValueString(sheet *xlsx.Sheet, row, col int) string { 9 | c := sheet.Cell(row, col) 10 | 11 | return strings.TrimSpace(c.Value) 12 | } 13 | 14 | // 整行都是空的 15 | func IsFullRowEmpty(sheet *xlsx.Sheet, row int) bool { 16 | 17 | for col := 0; col < sheet.MaxCol; col++ { 18 | 19 | data := GetSheetValueString(sheet, row, col) 20 | 21 | if data != "" { 22 | return false 23 | } 24 | } 25 | 26 | return true 27 | } 28 | 29 | func WriteIndexTableHeader(sheet TableSheet) { 30 | sheet.WriteRow("模式", "表类型", "表文件名") 31 | } 32 | 33 | func WriteTypeTableHeader(sheet TableSheet) { 34 | sheet.WriteRow("种类", "对象类型", "标识名", "字段名", "字段类型", "数组切割", "值", "索引") 35 | } 36 | 37 | func WriteRowValues(sheet TableSheet, valueList ...string) { 38 | sheet.WriteRow(valueList...) 39 | } 40 | 41 | func ConvertToCSV(inputFile TableFile) (outputFile TableFile) { 42 | 43 | csvFile := NewCSVFile() 44 | 45 | outSheet := csvFile.Sheets()[0] 46 | 47 | inSheet := inputFile.Sheets()[0] 48 | 49 | // 遍历所有数据行 50 | for row := 0; ; row++ { 51 | 52 | if inSheet.IsRowEmpty(row, -1) { 53 | break 54 | } 55 | 56 | rows := ReadSheetRow(inSheet, row) 57 | 58 | outSheet.WriteRow(rows...) 59 | } 60 | 61 | return csvFile 62 | } 63 | -------------------------------------------------------------------------------- /v3/helper/tab_xlsx.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/util" 5 | "github.com/tealeg/xlsx" 6 | "strings" 7 | ) 8 | 9 | type XlsxFile struct { 10 | file *xlsx.File 11 | 12 | sheets []TableSheet 13 | cacheDir string 14 | } 15 | 16 | func (self *XlsxFile) Sheets() (ret []TableSheet) { 17 | 18 | return self.sheets 19 | } 20 | 21 | func (self *XlsxFile) Save(filename string) error { 22 | return self.file.Save(filename) 23 | } 24 | 25 | func (self *XlsxFile) Load(filename string) (err error) { 26 | 27 | var file *xlsx.File 28 | 29 | if self.cacheDir == "" { 30 | file, err = xlsx.OpenFile(filename) 31 | if err != nil { 32 | return err 33 | } 34 | } else { 35 | cache := util.NewTableCache(filename, self.cacheDir) 36 | 37 | if err = cache.Open(); err != nil { 38 | return err 39 | } 40 | 41 | if file, err = cache.Load(); err != nil { 42 | return err 43 | } else { 44 | 45 | if !cache.UseCache() { 46 | cache.Save() 47 | } 48 | } 49 | } 50 | 51 | self.FromXFile(file) 52 | 53 | return nil 54 | } 55 | 56 | func (self *XlsxFile) FromXFile(file *xlsx.File) { 57 | self.file = file 58 | 59 | for _, sheet := range file.Sheets { 60 | self.sheets = append(self.sheets, newXlsxSheet(sheet)) 61 | } 62 | } 63 | 64 | func NewXlsxFile(cacheDir string) TableFile { 65 | 66 | self := &XlsxFile{ 67 | cacheDir: cacheDir, 68 | } 69 | 70 | return self 71 | } 72 | 73 | type XlsxSheet struct { 74 | *xlsx.Sheet 75 | } 76 | 77 | func (self *XlsxSheet) Name() string { 78 | return self.Sheet.Name 79 | } 80 | 81 | func (self *XlsxSheet) MaxColumn() int { 82 | return self.Sheet.MaxCol 83 | } 84 | 85 | func (self *XlsxSheet) IsRowEmpty(row, maxCol int) bool { 86 | 87 | if maxCol == -1 { 88 | maxCol = self.Sheet.MaxCol 89 | } 90 | 91 | for col := 0; col < maxCol; col++ { 92 | 93 | data := self.GetValue(row, col, nil) 94 | 95 | if data != "" { 96 | return false 97 | } 98 | } 99 | 100 | return true 101 | } 102 | 103 | func (self *XlsxSheet) GetValue(row, col int, opt *ValueOption) (ret string) { 104 | c := self.Sheet.Cell(row, col) 105 | 106 | // 浮点数单元格按原样输出 107 | if opt != nil && opt.ValueAsFloat { 108 | ret, _ = c.GeneralNumeric() 109 | ret = strings.TrimSpace(ret) 110 | } else { 111 | // 取列头所在列和当前行交叉的单元格 112 | ret = strings.TrimSpace(c.Value) 113 | } 114 | 115 | return 116 | } 117 | 118 | func (self *XlsxSheet) WriteRow(valueList ...string) { 119 | row := self.Sheet.AddRow() 120 | 121 | for _, value := range valueList { 122 | 123 | cell := row.AddCell() 124 | cell.SetValue(value) 125 | } 126 | } 127 | 128 | func newXlsxSheet(sheet *xlsx.Sheet) TableSheet { 129 | return &XlsxSheet{ 130 | Sheet: sheet, 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /v3/helper/tabfile.go: -------------------------------------------------------------------------------- 1 | package helper 2 | 3 | type TableFile interface { 4 | Load(filename string) error 5 | 6 | // 保存到文件 7 | Save(filename string) error 8 | 9 | // 获取所有表单 10 | Sheets() []TableSheet 11 | } 12 | 13 | type ValueOption struct { 14 | ValueAsFloat bool 15 | } 16 | 17 | type TableSheet interface { 18 | 19 | // 表单名称 20 | Name() string 21 | 22 | // 从表单指定单元格获取值 23 | GetValue(row, col int, opt *ValueOption) string 24 | 25 | // 最大列 26 | MaxColumn() int 27 | 28 | // 写入一行数据 29 | WriteRow(valueList ...string) 30 | 31 | // 检测本行是否全空(结束) 32 | IsRowEmpty(row, maxCol int) bool 33 | } 34 | 35 | func ReadSheetRow(sheet TableSheet, row int) (ret []string) { 36 | 37 | ret = make([]string, sheet.MaxColumn()) 38 | for col := 0; col < sheet.MaxColumn(); col++ { 39 | 40 | value := sheet.GetValue(row, col, nil) 41 | 42 | ret[col] = value 43 | } 44 | 45 | return 46 | } 47 | -------------------------------------------------------------------------------- /v3/model/cell.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davyxu/tabtoy/util" 6 | ) 7 | 8 | type Cell struct { 9 | Value string 10 | ValueList []string // merge之后, 数组值保存在这里 11 | Row int // base 0 12 | Col int // base 0 13 | Table *DataTable 14 | } 15 | 16 | // 全拷贝 17 | func (self *Cell) CopyFrom(c *Cell) { 18 | self.Value = c.Value 19 | self.Row = c.Row 20 | self.Col = c.Col 21 | self.Table = c.Table 22 | } 23 | 24 | func (self *Cell) String() string { 25 | 26 | var file, sheet string 27 | if self.Table != nil { 28 | file = self.Table.FileName 29 | sheet = self.Table.SheetName 30 | } 31 | 32 | var value string 33 | if len(self.ValueList) > 0 { 34 | value = fmt.Sprintf("%+v", self.ValueList) 35 | } else { 36 | value = self.Value 37 | } 38 | 39 | return fmt.Sprintf("'%s' @%s|%s(%s)", value, file, sheet, util.R1C1ToA1(self.Row+1, self.Col+1)) 40 | } 41 | -------------------------------------------------------------------------------- /v3/model/datarow.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type DataRow struct { 4 | row int 5 | cells []*Cell 6 | tab *DataTable 7 | } 8 | 9 | func (self *DataRow) Cells() []*Cell { 10 | return self.cells 11 | } 12 | 13 | func (self *DataRow) Cell(col int) *Cell { 14 | return self.cells[col] 15 | } 16 | 17 | func (self *DataRow) AddCell() (ret *Cell) { 18 | 19 | ret = &Cell{ 20 | Col: len(self.cells), 21 | Row: self.row, 22 | Table: self.tab, 23 | } 24 | 25 | self.cells = append(self.cells, ret) 26 | return 27 | } 28 | 29 | func (self *DataRow) IsEmpty() bool { 30 | return len(self.cells) == 0 31 | } 32 | 33 | func newDataRow(row int, tab *DataTable) *DataRow { 34 | return &DataRow{ 35 | row: row, 36 | tab: tab, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /v3/model/datatablist.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type DataTableList struct { 4 | data []*DataTable 5 | } 6 | 7 | func (self *DataTableList) GetDataTable(headerType string) *DataTable { 8 | 9 | for _, tab := range self.data { 10 | if tab.HeaderType == headerType { 11 | return tab 12 | } 13 | } 14 | 15 | return nil 16 | } 17 | 18 | func (self *DataTableList) AddDataTable(t *DataTable) { 19 | self.data = append(self.data, t) 20 | } 21 | func (self *DataTableList) AllTables() []*DataTable { 22 | return self.data 23 | } 24 | 25 | func (self *DataTableList) Count() int { 26 | return len(self.data) 27 | } 28 | -------------------------------------------------------------------------------- /v3/model/fieldtype.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/pkg/errors" 5 | ) 6 | 7 | type FieldType struct { 8 | InputFieldName string `tb_name:"输入字段"` // 表中输入的类型 9 | GoFieldName string `tb_name:"Go字段"` // 转换为go的类型 10 | CSFieldName string `tb_name:"C#字段"` 11 | JavaFieldName string `tb_name:"Java字段"` 12 | PBFieldName string `tb_name:"Protobuf字段"` 13 | DefaultValue string `tb_name:"默认值"` 14 | } 15 | 16 | // 将表中输入的字段类型转换为各种语言类型 17 | 18 | var ( 19 | FieldTypes = []*FieldType{ 20 | {"int16", "int16", "Int16", "int", "int32", "0"}, 21 | {"int32", "int32", "Int32", "int", "int32", "0"}, 22 | {"int64", "int64", "Int64", "long", "int64", "0"}, 23 | {"int", "int32", "Int32", "int", "int32", "0"}, 24 | {"uint", "uint32", "UInt32", "int", "uint32", "0"}, 25 | {"uint16", "uint16", "UInt16", "int", "uint32", "0"}, 26 | {"uint32", "uint32", "UInt32", "int", "uint32", "0"}, 27 | {"uint64", "uint64", "UInt64", "long", "uint64", "0"}, 28 | {"float", "float32", "float", "float", "float", "0"}, 29 | {"double", "float64", "double", "double", "double", "0"}, 30 | {"float32", "float32", "float", "float", "float", "0"}, 31 | {"float64", "float64", "double", "double", "double", "0"}, 32 | {"bool", "bool", "bool", "boolean", "bool", "FALSE"}, 33 | {"string", "string", "string", "String", "string", ""}, 34 | } 35 | 36 | FieldTypeByType = map[string]*FieldType{} 37 | ) 38 | 39 | func init() { 40 | 41 | for _, ft := range FieldTypes { 42 | FieldTypeByType[ft.InputFieldName] = ft 43 | } 44 | } 45 | 46 | // 取类型的默认值 47 | func FetchDefaultValue(fieldType string) (ret string) { 48 | 49 | if ft, ok := FieldTypeByType[fieldType]; ok { 50 | return ft.DefaultValue 51 | } 52 | 53 | return 54 | } 55 | 56 | // 将类型转为对应语言的原始类型 57 | func LanguagePrimitive(fieldType string, lanType string) string { 58 | 59 | if ft, ok := FieldTypeByType[fieldType]; ok { 60 | switch lanType { 61 | case "cs": 62 | return ft.CSFieldName 63 | case "go": 64 | return ft.GoFieldName 65 | case "java": 66 | return ft.JavaFieldName 67 | case "pb": 68 | return ft.PBFieldName 69 | default: 70 | panic("unknown lan type: " + lanType) 71 | } 72 | } 73 | 74 | return fieldType 75 | } 76 | 77 | // 原始类型是否存在,例如: int32, int64 78 | func PrimitiveExists(fieldType string) bool { 79 | 80 | if _, ok := FieldTypeByType[fieldType]; ok { 81 | return true 82 | } 83 | 84 | return false 85 | } 86 | 87 | func ParseBool(s string) (bool, error) { 88 | switch s { 89 | case "是", "yes", "YES", "1", "true", "TRUE", "True": 90 | return true, nil 91 | case "否", "no", "NO", "0", "false", "FALSE", "False": 92 | return false, nil 93 | case "": 94 | return false, nil 95 | } 96 | 97 | return false, errors.New("invalid bool value") 98 | } 99 | -------------------------------------------------------------------------------- /v3/model/globals.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/davyxu/tabtoy/v3/helper" 5 | ) 6 | 7 | type Globals struct { 8 | Version string // 工具版本号 9 | IndexFile string // 指示文件 10 | PackageName string // 文件生成时的包名 11 | CombineStructName string // 包含最终表所有数据的根结构 12 | 13 | IndexGetter helper.FileGetter // 索引文件获取器 14 | TableGetter helper.FileGetter // 其他文件获取器 15 | 16 | IndexList []*IndexDefine // 输入的索引文件 17 | 18 | Types *TypeTable // 输入的类型及符号 19 | 20 | Datas DataTableList // 输出的字符串格式的数据表 21 | 22 | GenBinary bool 23 | TagActions []TagAction // 用tag选中目标, 做action 24 | 25 | ParaLoading bool 26 | 27 | CacheDir string 28 | } 29 | 30 | func NewGlobals() *Globals { 31 | return &Globals{ 32 | Types: NewSymbolTable(), 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /v3/model/header.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type HeaderField struct { 9 | Cell *Cell // 表头单元格内容 10 | TypeInfo *TypeDefine // 在类型表中找到对应的类型信息 11 | } 12 | 13 | func (self *HeaderField) String() string { 14 | 15 | var sb strings.Builder 16 | 17 | if self.Cell != nil { 18 | sb.WriteString("Cell: ") 19 | sb.WriteString(self.Cell.String()) 20 | } 21 | 22 | if self.TypeInfo != nil { 23 | sb.WriteString("TypeInfo: ") 24 | sb.WriteString(fmt.Sprintf("%+v", self.TypeInfo)) 25 | } 26 | 27 | return sb.String() 28 | } 29 | -------------------------------------------------------------------------------- /v3/model/index.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | /* 4 | 添加字段和枚举, 需要在model.InitBuiltinTypes函数中添加入口 5 | */ 6 | 7 | type TableKind int32 8 | 9 | const ( 10 | TableKind_None TableKind = iota // 11 | TableKind_Type // 类型表 12 | TableKind_Data // 数据表 13 | TableKind_KeyValue // 键值表 14 | ) 15 | 16 | type IndexDefine struct { 17 | Kind TableKind `tb_name:"模式"` 18 | TableType string `tb_name:"表类型"` 19 | TableFileName string `tb_name:"表文件名"` 20 | Tags []string `tb_name:"标记"` // | 分割 21 | } 22 | 23 | func (self *IndexDefine) ContainTag(tag string) bool { 24 | for _, s := range self.Tags { 25 | if s == tag { 26 | return true 27 | } 28 | } 29 | 30 | return false 31 | } 32 | -------------------------------------------------------------------------------- /v3/model/tagaction.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | ActionNoGenFieldJson = "nogenfield_json" 10 | ActionNoGenFieldJsonDir = "nogenfield_jsondir" 11 | ActionNoGenFieldBinary = "nogenfield_binary" 12 | ActionNoGenFieldPbBinary = "nogenfield_pbbin" 13 | ActionNoGennFieldLua = "nogenfield_lua" 14 | ActionNoGennFieldCsharp = "nogenfield_csharp" 15 | ActionNoGenTable = "nogentab" 16 | ) 17 | 18 | // 用tag选中目标, 做action 19 | type TagAction struct { 20 | Verb string 21 | Tags []string 22 | } 23 | 24 | // action1:tag1+tag2|action2:tag1+tag3 25 | func ParseTagAction(script string) (ret []TagAction, err error) { 26 | 27 | for _, as := range strings.Split(script, "|") { 28 | actionPairs := strings.Split(as, ":") 29 | 30 | var ta TagAction 31 | 32 | switch len(actionPairs) { 33 | case 2: 34 | ta.Verb = actionPairs[0] 35 | ta.Tags = strings.Split(actionPairs[1], "+") 36 | ret = append(ret, ta) 37 | default: 38 | err = fmt.Errorf("invalid action format") 39 | return 40 | } 41 | 42 | } 43 | 44 | return 45 | } 46 | 47 | func (self *Globals) CanDoAction(action string, obj interface{}) bool { 48 | 49 | for _, ta := range self.TagActions { 50 | if ta.Verb == action { 51 | for _, tag := range ta.Tags { 52 | switch v := obj.(type) { 53 | case *HeaderField: 54 | if v.TypeInfo.ContainTag(tag) { 55 | return true 56 | } 57 | case *TypeDefine: 58 | if v.ContainTag(tag) { 59 | return true 60 | } 61 | case *IndexDefine: 62 | if v.ContainTag(tag) { 63 | return true 64 | } 65 | } 66 | } 67 | } 68 | } 69 | 70 | return false 71 | } 72 | -------------------------------------------------------------------------------- /v3/report/errid.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | type ErrorLanguage struct { 4 | CHS string 5 | } 6 | 7 | var ( 8 | ErrorByID = map[string]*ErrorLanguage{ 9 | "HeaderNotMatchFieldName": {CHS: "表头与字段不匹配"}, 10 | "HeaderFieldNotDefined": {CHS: "表头字段未定义"}, 11 | "DuplicateHeaderField": {CHS: "表头字段重复"}, 12 | "DuplicateKVField": {CHS: "键值表字段重复"}, 13 | "UnknownFieldType": {CHS: "未知字段类型"}, 14 | "DuplicateTypeFieldName": {CHS: "类型表字段重复"}, 15 | "EnumValueEmpty": {CHS: "枚举值空"}, 16 | "DuplicateEnumValue": {CHS: "枚举值重复"}, 17 | "UnknownEnumValue": {CHS: "未知的枚举值"}, 18 | "InvalidTypeTable": {CHS: "非法的类型表"}, 19 | "HeaderTypeNotFound": {CHS: "表头类型找不到"}, 20 | "DuplicateValueInMakingIndex": {CHS: "创建索引时发现重复值"}, 21 | "UnknownInputFileExtension": {CHS: "未知的输入文件扩展名"}, 22 | "DataMissMatchTypeDefine": {CHS: "数据与定义类型不匹配"}, 23 | "ArrayMultiColumnDefineNotMatch": {CHS: "数组类型多列跨表定义不一致"}, 24 | "InvalidFieldName": {CHS: "非法字段名"}, 25 | "UnknownTypeKind": {CHS: "非法的类型种类"}, 26 | } 27 | ) 28 | -------------------------------------------------------------------------------- /v3/report/error.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | type TableError struct { 9 | ID string 10 | 11 | context []interface{} 12 | } 13 | 14 | func getErrorDesc(id string) string { 15 | 16 | if lan, ok := ErrorByID[id]; ok { 17 | return lan.CHS 18 | } 19 | 20 | return "" 21 | } 22 | 23 | func (self *TableError) Error() string { 24 | 25 | var sb strings.Builder 26 | 27 | sb.WriteString("TableError.") 28 | sb.WriteString(self.ID) 29 | sb.WriteString(" ") 30 | sb.WriteString(getErrorDesc(self.ID)) 31 | sb.WriteString(" | ") 32 | 33 | for index, c := range self.context { 34 | if index > 0 { 35 | sb.WriteString(" ") 36 | } 37 | 38 | sb.WriteString(fmt.Sprintf("%+v", c)) 39 | } 40 | 41 | return sb.String() 42 | } 43 | 44 | func ReportError(id string, context ...interface{}) *TableError { 45 | 46 | panic(&TableError{ 47 | ID: id, 48 | context: context, 49 | }) 50 | } 51 | -------------------------------------------------------------------------------- /v3/report/log.go: -------------------------------------------------------------------------------- 1 | package report 2 | 3 | import ( 4 | "github.com/davyxu/golog" 5 | ) 6 | 7 | var Log = golog.New("tabtoy2") 8 | 9 | func init() { 10 | Log.SetParts() 11 | } 12 | -------------------------------------------------------------------------------- /v3/tests/Make.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | go test -v . -------------------------------------------------------------------------------- /v3/tests/launcher.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "fmt" 5 | "github.com/davyxu/protoplus/codegen" 6 | "os/exec" 7 | "strings" 8 | ) 9 | 10 | func compileLauncher(launcherFile, configFile, tableFile string) ([]byte, error) { 11 | 12 | m := struct { 13 | ConfigFile string 14 | }{ 15 | ConfigFile: strings.Replace(configFile, "\\", "\\\\", -1), 16 | } 17 | 18 | const textTemplate = ` 19 | package main 20 | 21 | import ( 22 | "encoding/json" 23 | "fmt" 24 | "io/ioutil" 25 | "os" 26 | ) 27 | 28 | func main() { 29 | 30 | data, err := ioutil.ReadFile("{{.ConfigFile}}") 31 | if err != nil { 32 | fmt.Println(err) 33 | return 34 | } 35 | 36 | var config Table 37 | err = json.Unmarshal(data, &config) 38 | 39 | if err != nil { 40 | fmt.Println(err) 41 | os.Exit(1) 42 | return 43 | } 44 | 45 | outData, err := json.MarshalIndent(&config, "", "\t") 46 | 47 | if err != nil { 48 | fmt.Println(err) 49 | os.Exit(1) 50 | return 51 | } 52 | 53 | fmt.Println(string(outData)) 54 | } 55 | ` 56 | var data []byte 57 | err := codegen.NewCodeGen("launcher"). 58 | RegisterTemplateFunc(codegen.UsefulFunc). 59 | ParseTemplate(textTemplate, m). 60 | WriteOutputFile(launcherFile).Error() 61 | 62 | if err != nil { 63 | fmt.Println(string(data)) 64 | return nil, err 65 | } 66 | 67 | cmd := exec.Command("go", "run", launcherFile, tableFile) 68 | 69 | output, err := cmd.CombinedOutput() 70 | 71 | if err != nil { 72 | return output, err 73 | } 74 | 75 | return output, nil 76 | } 77 | --------------------------------------------------------------------------------