├── example
├── output
│ ├── tindex_test
│ ├── item_test
│ ├── page_test
│ └── tree_test
├── files
│ ├── 空白.xls
│ ├── 范例.xls
│ ├── 关卡
│ │ ├── 副本.xls
│ │ └── g关卡.xls
│ ├── 多页测试.xls
│ ├── 测试树形.xls
│ ├── 物品测试.xls
│ ├── 物品测试2.xls
│ └── struct.yaml
├── run.bat
├── config.lua
├── scripts
│ └── example.lua
└── alias
│ └── example.yaml
├── .gitignore
├── LICENSE
├── converter
├── typedef.py
├── lseri.py
├── alias.py
├── dumper.lua
├── xlsparse.py
├── main.lua
└── json.lua
└── README.md
/example/output/tindex_test:
--------------------------------------------------------------------------------
1 | return { { id=1 }, { id=2 } }
--------------------------------------------------------------------------------
/example/files/空白.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/空白.xls
--------------------------------------------------------------------------------
/example/files/范例.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/范例.xls
--------------------------------------------------------------------------------
/example/files/关卡/副本.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/关卡/副本.xls
--------------------------------------------------------------------------------
/example/files/多页测试.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/多页测试.xls
--------------------------------------------------------------------------------
/example/files/测试树形.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/测试树形.xls
--------------------------------------------------------------------------------
/example/files/物品测试.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/物品测试.xls
--------------------------------------------------------------------------------
/example/files/物品测试2.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/物品测试2.xls
--------------------------------------------------------------------------------
/example/files/关卡/g关卡.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bttscut/xls-converter/HEAD/example/files/关卡/g关卡.xls
--------------------------------------------------------------------------------
/example/output/item_test:
--------------------------------------------------------------------------------
1 | return {
2 | { id=1, ["描述"]="asdf", ["物品别名"]="斧子" },
3 | { id=2, ["描述"]="asdf", ["物品别名"]="大刀" },
4 | [10]={ id=10, ["描述"]="asdf", ["物品别名"]="剪刀" }
5 | }
--------------------------------------------------------------------------------
/example/run.bat:
--------------------------------------------------------------------------------
1 | echo start
2 | @echo off
3 | set path=.\bin\lua;.\bin\py;%path%
4 |
5 | set BIN_ROOT=..\converter
6 | set LUA_PATH=%BIN_ROOT%/?.lua
7 | lua %BIN_ROOT%\main.lua config.lua files alias scripts output
8 |
9 | pause
--------------------------------------------------------------------------------
/example/output/page_test:
--------------------------------------------------------------------------------
1 | return {
2 | page_test1={ { i_set={ id=10, num=2 }, id=1 }, [10]={ i_set={ id=1, num=3 }, id=10 } },
3 | page_test2={
4 | [100]={ ["数目"]=1, ["测试索引"]=100, ["物品集合"]={ id=2, num=2 } },
5 | [200]={ ["数目"]=200, ["测试索引"]=200, ["物品集合"]={ id=10, num=3 } }
6 | }
7 | }
--------------------------------------------------------------------------------
/example/files/struct.yaml:
--------------------------------------------------------------------------------
1 | Example:
2 | - id: int
3 | - num: int
4 | - rate: float
5 |
6 | ItemTest:
7 | - id: string
8 | - num: int
9 |
10 | Vector3:
11 | - x: float
12 | - y: float
13 | - z: float
14 |
15 | Item:
16 | - id: int
17 | - num: int
18 |
19 | Cost:
20 | - item_id: int
21 | - num: int
22 |
--------------------------------------------------------------------------------
/example/output/tree_test:
--------------------------------------------------------------------------------
1 | return {
2 | { ["别名"]="大树", ["条目1"]=1, ["条目2"]={ 11, 11 }, ["条目3"]="111" },
3 | { ["条目3"]="" },
4 | { ["条目2"]={ 12, 12 }, ["条目3"]="121.0" },
5 | { ["条目3"]="122.0" },
6 | {
7 | ["别名"]="小树",
8 | ["条目1"]=2,
9 | ["条目2"]={ 11, 11 },
10 | ["条目3"]="111.0"
11 | },
12 | { ["条目3"]="112.0" }
13 | }
--------------------------------------------------------------------------------
/example/config.lua:
--------------------------------------------------------------------------------
1 | --[[
2 | 格式:
3 | {表,页,导表程序,输出文件名,附加信息}
4 | 页可以是s1, [s1,s2], "*", 分别代表一页, 部分页,全部页
5 | --]]
6 | export = {
7 | {"物品测试.xls", "*", nil, "item_test", {}},
8 | {"多页测试.xls", "Sheet1", nil, "page_test1", {merge="page_test"}},
9 | {"多页测试.xls", "Sheet2", nil, "page_test2", {merge="page_test"}},
10 | {"测试树形.xls", "Sheet1", nil, "tree_test", {}},
11 | {"测试树形.xls", "Sheet2", nil, "tindex_test", {}},
12 | --{"范例.xls", "页面1", "example.lua", "example", {}},
13 | }
14 |
15 | -- save_suffix = ".lua"
16 | --to_json_list = { "weapon_types", "role", "hit_effects", "barrier","profession","monster","fly_items","pet","common_battle","hit_effects"}
17 |
--------------------------------------------------------------------------------
/example/scripts/example.lua:
--------------------------------------------------------------------------------
1 |
2 | -- 范例
3 | -- convert按照config文件指定的顺序调用
4 | -- sheet是原始表数据
5 | -- global中是一些全局数据,很少用到
6 | -- ext是一些扩展信息,例如表名
7 | -- 多张表公用一个导表程序时,会自动合并导出的结果
8 | function convert(sheet, global, ext)
9 | print("---------------------")
10 | print(ext)
11 | for k, v in pairs(global.raw) do
12 | print(k, v)
13 | end
14 | for k, v in pairs(global.save) do
15 | print(k, v)
16 | end
17 | return sheet
18 | end
19 |
20 | -- 所有的convert都结束了后,才执行post_convert,post的执行次序也按照config文件指定
21 | -- save是之前convert后的存盘数据,这里可以进一步处理
22 | -- global同上
23 | function post_convert(save, global)
24 | print(...)
25 | -- body
26 | end
27 |
--------------------------------------------------------------------------------
/example/alias/example.yaml:
--------------------------------------------------------------------------------
1 | 物品:
2 | .sheets:
3 | 范例.xls: all
4 | id: id
5 | 名字: name
6 | 数值: num
7 | 牛逼: niu
8 | 吃货: eat
9 |
10 | page_test:
11 | .sheets:
12 | 多页测试.xls: Sheet2
13 | .deps:
14 | item: item_test
15 | item_list: item_test
16 | item_dict:
17 | key: item_test
18 | value: item_test
19 | 物品集合: itemset
20 | 测试索引: id
21 | 物品: item
22 | 物品列表: item_list
23 | 物品字典: item_dict
24 | 数目: num
25 |
26 | page_test:
27 | .sheets:
28 | 多页测试.xls: Sheet1
29 | id: id
30 | 物品集合: i_set
31 |
32 | item_test:
33 | .sheet:
34 | 物品测试.xls: all
35 | id: id
36 | 物品别名: item_name
37 | 描述: num
38 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | *.egg-info/
24 | .installed.cfg
25 | *.egg
26 |
27 | # PyInstaller
28 | # Usually these files are written by a python script from a template
29 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
30 | *.manifest
31 | *.spec
32 |
33 | # Installer logs
34 | pip-log.txt
35 | pip-delete-this-directory.txt
36 |
37 | # Unit test / coverage reports
38 | htmlcov/
39 | .tox/
40 | .coverage
41 | .coverage.*
42 | .cache
43 | nosetests.xml
44 | coverage.xml
45 | *,cover
46 | .hypothesis/
47 |
48 | # Translations
49 | *.mo
50 | *.pot
51 |
52 | # Django stuff:
53 | *.log
54 |
55 | # Sphinx documentation
56 | docs/_build/
57 |
58 | # PyBuilder
59 | target/
60 |
61 | #Ipython Notebook
62 | .ipynb_checkpoints
63 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 bttscut@gmail.com
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 all
13 | 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 THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/converter/typedef.py:
--------------------------------------------------------------------------------
1 | #coding=utf8
2 | import yaml
3 |
4 | def _log(msg, struct, idx=None):
5 | return "struct:%s def error, idx:<%s>, %s"%(struct, idx, msg)
6 |
7 | def parse(fn):
8 | deps = {}
9 | try:
10 | fp = open(fn, "r")
11 | except IOError:
12 | return {}
13 | struct_d = yaml.load(fp)
14 | fp.close()
15 | for name, cfg in struct_d.iteritems():
16 | assert isinstance(cfg, list), _log("elem def isn't list", name)
17 | assert name[:1].isupper(), _log("First must upper!", name)
18 | for idx, entry in enumerate(cfg):
19 | assert len(entry)==1, _log("field def error", name, idx)
20 | type_s = entry.values()[0]
21 | assert type_s in ["int", "float", "string"], _log("field type error:<%s>"%type_s, name, idx)
22 | entry[entry.keys()[0]] = type_s
23 | return struct_d
24 |
25 | def _deps_log(msg, struct, field=None):
26 | return "struct:%s deps def error, field:<%s>, %s"%(struct, field, msg)
27 |
28 | def parse_deps(fn, struct_d):
29 | try:
30 | fp = open(fn, "r")
31 | except IOError:
32 | return {}
33 | deps_d = yaml.load(fp)
34 | fp.close()
35 | if not deps_d:
36 | return {}
37 | for name, cfg in deps_d.iteritems():
38 | assert name in struct_d, _deps_log("struct没有定义", name)
39 | fields = [i.keys()[0] for i in struct_d[name]]
40 | for field, dep in cfg.iteritems():
41 | assert field in fields, _deps_log("字段没有定义", name, field)
42 | return deps_d
43 |
44 |
--------------------------------------------------------------------------------
/converter/lseri.py:
--------------------------------------------------------------------------------
1 | #coding=utf8
2 | from __future__ import unicode_literals
3 | import os, sys
4 |
5 | def _format_list(l):
6 | s = ",".join([tolua(i) for i in l])
7 | return "{%s}"%s
8 |
9 | def _format_dict(d):
10 | l = []
11 | for k, v in d.iteritems():
12 | l.append("[%s]=%s"%(tolua(k), tolua(v)))
13 | s = ",".join(l)
14 | return "{%s}"%s
15 |
16 | def _format_bool(b):
17 | return "true" if b else "false"
18 |
19 | def _format_unicode(obj):
20 | temp = obj.encode("utf8")
21 | return _format_basic(temp)
22 |
23 | def _format_basic(obj):
24 | if isinstance(obj, str):
25 | sep = "'" if obj.find('"') != -1 else '"'
26 | repl_sep = "'" if sep == '"' else '"'
27 | s = "%s%s%s"%(sep, obj.replace(sep, repl_sep).replace("\\", "\\\\").replace("\n", "\\n"), sep)
28 | return s
29 | if obj == None:
30 | return "nil"
31 | return str(obj)
32 |
33 | def _tolua(obj):
34 | return obj
35 |
36 | def tolua(obj):
37 |
38 | if isinstance(obj, list):
39 | return _format_list(obj)
40 | elif isinstance(obj, dict):
41 | return _format_dict(obj)
42 | elif isinstance(obj, bool):
43 | return _format_bool(obj)
44 | elif isinstance(obj, unicode):
45 | return _format_unicode(obj)
46 | else:
47 | return _format_basic(obj)
48 |
49 |
50 | if __name__ == "__main__":
51 | reload(sys)
52 | sys.setdefaultencoding("utf8")
53 | l = ["haha", "很", 1, 1.0]
54 | d = {
55 | "中国":l,
56 | "aaaa":"asdf'fdd'",
57 | "b":[1,2,3]
58 | }
59 | print d
60 | print tolua(d)
61 |
--------------------------------------------------------------------------------
/converter/alias.py:
--------------------------------------------------------------------------------
1 | #coding=utf8
2 | from __future__ import unicode_literals
3 | import os, sys
4 | import yaml
5 |
6 | def make_key(bname, sname):
7 | return "%s.%s"%(bname, sname)
8 |
9 | def _parse_sheets(conf):
10 | ret = []
11 | for k, v in conf.iteritems():
12 | if isinstance(v, str):
13 | assert v == "all", v
14 | ret.append(k)
15 | else:
16 | assert isinstance(v, list), v
17 | for i in v:
18 | ret.append(make_key(k, i))
19 | return ret
20 |
21 | def _log(msg, fn, cfgname, xls=None, sheet=None):
22 | return "alias error, %s, file:<%s>, cfg:<%s>, xls:<%s>, sheet:<%s>"%(msg, fn, cfgname, xls, sheet)
23 |
24 | def _parse_deps(depsd):
25 | return depsd
26 |
27 | def parse(xls_dir, path):
28 | raw = {}
29 | field_alias = {}
30 | deps = {}
31 | cfg_defs = set()
32 | all_defs = set()
33 | sheet_defs = set()
34 |
35 | for fn in [os.path.join(path, i) for i in os.listdir(path)]:
36 | if not fn.endswith(".yaml"):
37 | continue
38 | # 专门指定自定义结构依赖的yaml
39 | if fn.endswith("struct_deps.yaml"):
40 | continue
41 | fp = open(fn, "r")
42 | try:
43 | d = yaml.load(fp)
44 | except Exception, e:
45 | raise Exception("alias file:<%s> parse error, maybe tab, space..., msg:%s"%(fn, e))
46 | fp.close()
47 | for cfgname, item in d.iteritems():
48 | assert cfgname not in cfg_defs, _log("cfg dup", fn, cfgname)
49 | cfg_defs.add(cfgname)
50 | if ".sheets" not in item:
51 | continue
52 | sheets = item[".sheets"]
53 | del item[".sheets"]
54 | assert isinstance(sheets, dict), _log("sheets is not dict", fn, cfgname)
55 | cfg = {}
56 | if ".deps" in item:
57 | deps[cfgname] = item[".deps"]
58 | cfg["deps"] = item[".deps"]
59 | del item[".deps"]
60 | if ".export" in item:
61 | cfg["export"] = item[".export"]
62 | del item[".export"]
63 | raw[cfgname] = item
64 | cfg["alias"] = item
65 | def _sheet_log(msg):
66 | return _log(msg, fn, cfgname, xls, cont)
67 | def _process_all(cont):
68 | assert cont == "all", _sheet_log("sheet def error")
69 | assert xls not in all_defs, _sheet_log("all def dup")
70 | all_defs.add(xls)
71 | assert xls not in sheet_defs, _sheet_log("all def after sheet")
72 | field_alias[xls] = cfg
73 | def _process_list(cont):
74 | assert isinstance(cont, list), _sheet_log("sheet def type error")
75 | for sh in cont:
76 | assert xls not in all_defs, _sheet_log("sheet def after all")
77 | key = make_key(xls, sh)
78 | assert key not in field_alias, _sheet_log("sheet def after sheet")
79 | sheet_defs.add(xls)
80 | field_alias[key] = cfg
81 | for xls, cont in sheets.iteritems():
82 | xls_path = os.path.join(xls_dir, xls)
83 | assert os.path.exists(xls_path), "NOT EXIST XLS:%s YMAL:%s !!!" % (xls, fn)
84 | if cont == "all":
85 | _process_all(cont)
86 | else:
87 | if isinstance(cont, str) or isinstance(cont, unicode):
88 | cont = [cont,]
89 | _process_list(cont)
90 | return raw, field_alias, deps
91 |
92 | if __name__ == "__main__":
93 | parse("./alias")
94 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # xls-converter
2 | convert xls or csv to lua/json file with check and custom rule
3 | # 填写格式
4 | **以下所有的格式在example目录下的xls文件中都可以找到例子**
5 |
6 | excel第一行是类型行,填写规则是类型#标签1#标签2#标签n...
7 |
第二行是列名, 填写人方便识别的列名
8 |
第三行开始是数据行
9 |
除第一二行外,任意行的首个单元格中的内容如果以“//”开头,则该行为注释行,不参与导表。
10 |
所有的表名或者页名,只要名字以"_"开头,会被忽略掉,不参与导表。
11 |
12 | # 类型
13 | ## 基本类型
14 | - int 整数
15 | - float 浮点数
16 | - string 字符串
17 | ## 容器类型
18 | - list 数组类型
19 |
各元素用空格隔开,例如list 填写: 1 2 3
20 | - dict 字典类型
21 |
key与value用":"隔开,各项用空格隔开,例如:key:value key2:value2
22 |
23 |
*T是基本类型或者复合类型,但是dict的key不能为复合类型*
24 |
25 | ## 复合类型
26 | 比如坐标,包含x,y,z,可以定义一个单独的类型Point,在**struct.yaml**(导表excel目录下)里面,定义为
27 |
28 | Point:
29 | x: float
30 | y: float
31 | z: float
32 |
33 | 定义完之后,可以在需要填写类型的地方直接填写Point,也可以用作list或者dict的值,比如list,dict
34 |
*注意dict的key不能是复合类型,**dict<复合类型, 其他>是不支持的*
35 |
36 | 复合类型填表时候用竖线隔开每个值,例如 1.1|2.2|3.3,在list和dict中填写遵从list和dict的规范。
37 |
38 | # 标签
39 |
40 | - key:只能用在第一列,表示该列所填内容作为本条目的索引,不得重复,只支持int和string做key。例:int#key
41 | - key_alias: 给key起的别名,只能在第二列,不得重复,只支持string类型,该列填写的别名,可以用作其他表的索引
42 | - default:可以不填,有默认值,并且可以指定。例:int#default 或 int#default(3)
43 | - ignore: 本列会被忽略,一般用于描述说明字段。
44 | - index: 表示本列内容是索引了其他表的key或者key_alias,定义后程序员一定要做依赖检查,否则无法程序中无法使用
45 | - raw: 只能用在第一列,表示整个表原样导出,导出后的的列按照别名定义输出,同时填写的单元按照表头定义类型输出,没填写的一律不输出,在lua脚本中获取为nil。这个标签的目的是为了应对一些复杂的表(例如树形结构的填写方式),将工作完全交给导表脚本,简化设计。
46 |
47 | # 导表程序
48 | 导表的主程序是main.lua,使用py进行表的预处理,在lua中检查依赖,处理,输出最后的文件。
49 | 启动main.lua的时候,向它传入必要的各种路径信息,参考example。
50 | 主配置文件格式如下:
51 |
52 | export = {
53 | {"物品测试.xls", "all", nil, "item_test", {}},
54 | {"多页测试.xls", "Sheet1", nil, "page_test1", {merge="page_test"}},
55 | {"多页测试.xls", "Sheet2", nil, "page_test2", {merge="page_test"}},
56 | {"测试树形.xls", "Sheet1", "example.lua", "tree_test", {}},
57 | {"测试树形.xls", "Sheet2", nil, "tindex_test", {}},
58 | }
59 |
60 | 每行的定义:{表,页,导表程序,输出文件名,附加信息}
61 | 页可以是s1, [s1,s2], "all", 分别代表一页, 部分页,全部页
62 | 导表程序可以是nil,则按照默认规则输出。
63 | 附加信息项定义如下:
64 |
65 | - merge=name, 最终输出时将多个名字放入到name的table下,统一输出名字为name的文件。比如上面的例子中,最终会输出:
66 |
67 |
68 | page_test = {
69 | page_test1 = {...},
70 | page_test2 = {...},
71 | }
72 |
73 | # 导表脚本
74 | 导表脚本中可以定义两个global函数:
75 |
76 | function convert(sheet, global, ext)
77 | function post_convert(save, global)
78 |
79 | convert负责第一遍顺序处理,它必须return一个table,作为save的数据,参数如下:
80 |
81 |
82 | - sheet是本次调用处理的表内容
83 | - global中传递了一些全局信息
84 | - save,已经添加到save中的数据。
85 | - alias2key, excel中定义的key_alias和key的索引表,方便查询。
86 | - raw,所有的原始表信息。
87 | - ext本次调用的一些附加信息,比如sheet所在的原始文件名,页名。
88 |
89 | return的数据,会以配置表中的存盘名字保存在global.save中,如果不return,则为空。
90 | 注意,convert的处理对象是文件中的页,多个页配置了同个导表程序会依次产生调用,并且最终的结果会自动合并(根据文件是table还是array)。
91 |
92 | post_convert负责第二次处理,此时,所有的表按照config的配置已经处理过了,此时的save参数代表了合并后的所有存盘名字下的数据集合,global含义如上。返回的table作为最终存盘数据。
93 | 此时可以对最终的存盘结果做调整,甚至可以向global.save中删除添加表项。
94 |
95 | # 别名和依赖定义
96 | 格式:
97 |
98 | cfg_name:
99 | .sheets:
100 | file_name: sheet_name
101 | .deps:
102 | alias_name: deps_cfg_name
103 | 编号: id
104 | 原名: alias_name
105 |
106 | 范例:
107 |
108 | page_test:
109 | .sheets:
110 | 多页测试.xls: Sheet2
111 | .deps:
112 | item: item_test
113 | item_list: item_test
114 | item_dict:
115 | key: item_test
116 | value: item_test
117 | 物品集合: itemset
118 | 测试索引: id
119 | 物品: item
120 | 物品列表: item_list
121 | 物品字典: item_dict
122 | 数目: num
123 |
124 |
125 | 采用yaml格式,并且要明确以yaml作为文件后缀名。整个alias目录下的所有yaml都会被读取。
126 | 每个配置有个名字cfg\_name, 这个名字最好要跟主配置文件里的存盘名字一致,这样才能用来做依赖检查。
127 | 每个配置中要有个.sheets的key,存放该配置应用于哪些sheet。
128 | 别名的定义就是key:value的键值对。
129 | .sheets的value是一个列表,每个元素是一个字典,例如,文件1:[ 表1,表2 ], 当文件中所有的表都应用时,可以这样指定,文件:all。
130 | .deps定义了表中字段的依赖关系,alias\_name: deps\_cfg\_name, 其中deps\_cfg\_name就是对应依赖表的config.lua中的导出存盘名字。
131 | 检查依赖的流程如下:
132 |
133 | 1. xls文件的每个页经过py读取,进行别名转换,输出给main.lua脚本
134 | 1. lua中进行预处理,把所有savename(存盘名字)相同的表的key和key_alias的字段进行合并,并保存在savename对应的table中,即key_check。
135 | 1. 根据config配置的savename查询别名定义里是否同名的配置项,如果有则看别名定义里是否有改别名的deps配置,如果有,则根据配置依赖的deps\_cfg\_name在key_check的table中查询该字段的value是否存在。
136 | 1. 里面有个隐藏的规则是,如果字段为字符串,则首先将该字段转换成对应的key的值。
137 | 2. 注意!!!别名的配置项名字只有在本别名定义中有deps字段时才需要与config中的导出名字一致,这样框架才能根据名字找到需要检查的字段。而依赖项(deps\_cfg\_name)的字段,就是config里的导出名字。
138 |
139 | # 复合类型的定义和依赖
140 | 格式:
141 |
142 | Example:
143 | - id: int, item
144 | - num: int
145 | - rate: float
146 | 复合类型的字段类型一定是基础类型,如果某字段有依赖的话,用逗号隔开输入依赖项的名字。
147 |
--------------------------------------------------------------------------------
/converter/dumper.lua:
--------------------------------------------------------------------------------
1 | --[[ DataDumper.lua
2 | Copyright (c) 2007 Olivetti-Engineering SA
3 |
4 | Permission is hereby granted, free of charge, to any person
5 | obtaining a copy of this software and associated documentation
6 | files (the "Software"), to deal in the Software without
7 | restriction, including without limitation the rights to use,
8 | copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the
10 | Software is furnished to do so, subject to the following
11 | conditions:
12 |
13 | The above copyright notice and this permission notice shall be
14 | included in all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
18 | OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
20 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
21 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 | OTHER DEALINGS IN THE SOFTWARE.
24 | ]]
25 |
26 | local dumplua_closure = [[
27 | local closures = {}
28 | local function closure(t)
29 | closures[#closures+1] = t
30 | t[1] = assert(loadstring(t[1]))
31 | return t[1]
32 | end
33 |
34 | for _,t in pairs(closures) do
35 | for i = 2,#t do
36 | debug.setupvalue(t[1], i-1, t[i])
37 | end
38 | end
39 | ]]
40 |
41 | local lua_reserved_keywords = {
42 | 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for',
43 | 'function', 'if', 'in', 'local', 'nil', 'not', 'or', 'repeat',
44 | 'return', 'then', 'true', 'until', 'while' }
45 |
46 | local function keys(t)
47 | local res = {}
48 | local oktypes = { stringstring = true, numbernumber = true }
49 | local function cmpfct(a,b)
50 | if oktypes[type(a)..type(b)] then
51 | return a < b
52 | else
53 | return type(a) < type(b)
54 | end
55 | end
56 | for k in pairs(t) do
57 | res[#res+1] = k
58 | end
59 | table.sort(res, cmpfct)
60 | return res
61 | end
62 |
63 | local c_functions = {}
64 | for _,lib in pairs{'_G', 'string', 'table', 'math',
65 | 'io', 'os', 'coroutine', 'package', 'debug'} do
66 | local t = _G[lib] or {}
67 | lib = lib .. "."
68 | if lib == "_G." then lib = "" end
69 | for k,v in pairs(t) do
70 | if type(v) == 'function' and not pcall(string.dump, v) then
71 | c_functions[v] = lib..k
72 | end
73 | end
74 | end
75 |
76 | local function DataDumper(value, varname, fastmode, ident)
77 | local defined, dumplua = {}
78 | -- Local variables for speed optimization
79 | local string_format, type, string_dump, string_rep =
80 | string.format, type, string.dump, string.rep
81 | local tostring, pairs, table_concat =
82 | tostring, pairs, table.concat
83 | local keycache, strvalcache, out, closure_cnt = {}, {}, {}, 0
84 | setmetatable(strvalcache, {__index = function(t,value)
85 | local res = string_format('%q', value)
86 | t[value] = res
87 | return res
88 | end})
89 | local fcts = {
90 | string = function(value) return strvalcache[value] end,
91 | number = function(value) return value end,
92 | boolean = function(value) return tostring(value) end,
93 | ['nil'] = function(value) return 'nil' end,
94 | ['function'] = function(value)
95 | return string_format("loadstring(%q)", string_dump(value))
96 | end,
97 | userdata = function() error("Cannot dump userdata") end,
98 | thread = function() error("Cannot dump threads") end,
99 | }
100 | local function test_defined(value, path)
101 | if defined[value] then
102 | if path:match("^getmetatable.*%)$") then
103 | out[#out+1] = string_format("s%s, %s)\n", path:sub(2,-2), defined[value])
104 | else
105 | out[#out+1] = path .. " = " .. defined[value] .. "\n"
106 | end
107 | return true
108 | end
109 | defined[value] = path
110 | end
111 | local function make_key(t, key)
112 | local s
113 | if type(key) == 'string' and key:match('^[_%a][_%w]*$') then
114 | s = key .. "="
115 | else
116 | s = "[" .. dumplua(key, 0) .. "]="
117 | end
118 | t[key] = s
119 | return s
120 | end
121 | for _,k in ipairs(lua_reserved_keywords) do
122 | keycache[k] = '["'..k..'"] = '
123 | end
124 | if fastmode then
125 | fcts.table = function (value)
126 | -- Table value
127 | local numidx = 1
128 | out[#out+1] = "{"
129 | for key,val in pairs(value) do
130 | if key == numidx then
131 | numidx = numidx + 1
132 | else
133 | out[#out+1] = keycache[key]
134 | end
135 | local str = dumplua(val)
136 | out[#out+1] = str..","
137 | end
138 | if string.sub(out[#out], -1) == "," then
139 | out[#out] = string.sub(out[#out], 1, -2);
140 | end
141 | out[#out+1] = "}"
142 | return ""
143 | end
144 | else
145 | fcts.table = function (value, ident, path)
146 | if test_defined(value, path) then return "nil" end
147 | -- Table value
148 | local sep, str, numidx, totallen = " ", {}, 1, 0
149 | local meta, metastr = (debug or getfenv()).getmetatable(value)
150 | if meta then
151 | ident = ident + 1
152 | metastr = dumplua(meta, ident, "getmetatable("..path..")")
153 | totallen = totallen + #metastr + 16
154 | end
155 | for _,key in pairs(keys(value)) do
156 | local val = value[key]
157 | local s = ""
158 | local subpath = path
159 | if key == numidx then
160 | subpath = subpath .. "[" .. numidx .. "]"
161 | numidx = numidx + 1
162 | else
163 | s = keycache[key]
164 | if not s:match "^%[" then subpath = subpath .. "." end
165 | subpath = subpath .. s:gsub("%s*=%s*$","")
166 | end
167 | s = s .. dumplua(val, ident+1, subpath)
168 | str[#str+1] = s
169 | totallen = totallen + #s + 2
170 | end
171 | if totallen > 80 then
172 | sep = "\n" .. string_rep(" ", ident+1)
173 | end
174 | str = "{"..sep..table_concat(str, ","..sep).." "..sep:sub(1,-3).."}"
175 | if meta then
176 | sep = sep:sub(1,-3)
177 | return "setmetatable("..sep..str..","..sep..metastr..sep:sub(1,-3)..")"
178 | end
179 | return str
180 | end
181 | fcts['function'] = function (value, ident, path)
182 | if test_defined(value, path) then return "nil" end
183 | if c_functions[value] then
184 | return c_functions[value]
185 | elseif debug == nil or debug.getupvalue(value, 1) == nil then
186 | return string_format("loadstring(%q)", string_dump(value))
187 | end
188 | closure_cnt = closure_cnt + 1
189 | local res = {string.dump(value)}
190 | for i = 1,math.huge do
191 | local name, v = debug.getupvalue(value,i)
192 | if name == nil then break end
193 | res[i+1] = v
194 | end
195 | return "closure " .. dumplua(res, ident, "closures["..closure_cnt.."]")
196 | end
197 | end
198 | function dumplua(value, ident, path)
199 | return fcts[type(value)](value, ident, path)
200 | end
201 | if varname == nil then
202 | varname = "return "
203 | elseif varname:match("^[%a_][%w_]*$") then
204 | varname = varname .. " = "
205 | end
206 | if fastmode then
207 | setmetatable(keycache, {__index = make_key })
208 | out[1] = varname
209 | table.insert(out,dumplua(value, 0))
210 | return table.concat(out)
211 | else
212 | setmetatable(keycache, {__index = make_key })
213 | local items = {}
214 | for i=1,10 do items[i] = '' end
215 | items[3] = dumplua(value, ident or 0, "t")
216 | if closure_cnt > 0 then
217 | items[1], items[6] = dumplua_closure:match("(.*\n)\n(.*)")
218 | out[#out+1] = ""
219 | end
220 | if #out > 0 then
221 | items[2], items[4] = "local t = ", "\n"
222 | items[5] = table.concat(out)
223 | items[7] = varname .. "t"
224 | else
225 | items[2] = varname
226 | end
227 | return table.concat(items)
228 | end
229 | end
230 |
231 | return DataDumper
232 |
--------------------------------------------------------------------------------
/converter/xlsparse.py:
--------------------------------------------------------------------------------
1 | #coding=utf8
2 | from __future__ import unicode_literals
3 | import sys
4 | import os
5 | import shutil
6 | import re
7 | import xlrd
8 | import yaml
9 | import alias
10 | import typedef
11 | import lseri
12 |
13 | reload(sys)
14 | sys.setdefaultencoding("utf8")
15 | import codecs
16 | def cp65001(name):
17 | if name.lower() == "cp65001":
18 | return codecs.lookup("utf8")
19 | codecs.register(cp65001)
20 |
21 | g_error_l = []
22 | g_alias_d = None
23 | g_alias_deps = None
24 | g_struct_deps = {}
25 | g_sheet_struct_deps = {}
26 |
27 | fp_log = None
28 | def fprint(msg):
29 | global fp_log
30 | if not fp_log:
31 | fp_log = open("log.txt", "w")
32 | fp_log.write(msg)
33 | fp_log.write(os.linesep)
34 | fp_log.flush()
35 |
36 | output_list = []
37 | def output(msg):
38 | output_list.append(msg)
39 | return
40 |
41 | def flush_output():
42 | s = lseri.tolua(output_list)
43 | #fprint(s)
44 | sys.stdout.write(s)
45 | sys.stdout.flush()
46 |
47 | def error(msg):
48 | s = lseri.tolua({"error":msg})
49 | sys.stdout.write(s)
50 | sys.stdout.flush()
51 | sys.exit(1)
52 |
53 | def print_exc():
54 | import traceback
55 | traceback.print_exc()
56 |
57 | # 基础数据类型:int, float, string
58 | _bool_d = {"true":True,"false":False,"1":True,"0":False,"1.0":True,"0.0":False}
59 | def _conv_bool(s):
60 | return _bool_d[str(s).lower()]
61 | basictype_convert_tbl = {
62 | "int":int,
63 | "float":float,
64 | "bool":_conv_bool,
65 | "string":lambda s:str(s).encode("utf8"),
66 | }
67 | # 自定义struct
68 | g_struct_d = None
69 |
70 | # 容器数据类型: list, dict
71 | CONTAINER_RE = re.compile("^(list|dict)<(.+?)(?:,\s*(.+?))??>$")
72 | #m = CONTAINER_RE.match(u"list")
73 | #print(m.groups())
74 |
75 | def find_struct_deps(type_s):
76 | if type_s in basictype_convert_tbl:
77 | return None
78 | if type_s in g_struct_d:
79 | if type_s in g_struct_deps:
80 | return ["s", g_struct_deps[type_s]]
81 | else:
82 | return None
83 | m = CONTAINER_RE.match(type_s)
84 | sg = m.groups()
85 | if sg[0] == "list":
86 | if sg[1] in g_struct_deps:
87 | return ["l", g_struct_deps[sg[1]]]
88 | elif sg[0] == "dict":
89 | if sg[2] in g_struct_deps:
90 | return ["d", g_struct_deps[sg[2]]]
91 | return None
92 |
93 | def get_basic_or_struct_cf(s):
94 | if s in basictype_convert_tbl:
95 | return basictype_convert_tbl[s]
96 | if s not in g_struct_d:
97 | return None
98 | cfg = g_struct_d[s]
99 | def cf(cont):
100 | if cont == "":
101 | return None
102 | l = cont.split("|")
103 | assert len(l)==len(cfg), cont
104 | ret = {}
105 | for idx, v in enumerate(l):
106 | ret[cfg[idx].keys()[0]] = basictype_convert_tbl[cfg[idx].values()[0]](v)
107 | return ret
108 | return cf
109 |
110 | def make_convert_func(type_s):
111 | cf = get_basic_or_struct_cf(type_s)
112 | if cf:
113 | return cf
114 | m = CONTAINER_RE.match(type_s)
115 | sg = m.groups()
116 | assert len(sg) == 3, "类型解析错误"
117 | if sg[0] == "list":
118 | assert not sg[2], "list定义有误"
119 | typ = sg[1]
120 | f = get_basic_or_struct_cf(typ)
121 | assert f, "list元素类型定义有误:%s"%type_s
122 | def cf(s):
123 | # xls单元格会默认把数字格式转换成float
124 | if typ == "int" and isinstance(s, float):
125 | s = str(int(s))
126 | return [f(i) for i in s.split()]
127 | return cf
128 | elif sg[0] == "dict":
129 | k_f = basictype_convert_tbl[sg[1]]
130 | assert k_f, "dict key类型定义有误%s"%type_s
131 | v_f = get_basic_or_struct_cf(sg[2])
132 | assert v_f, "dict value类型定义有误:%s"%type_s
133 | def cf(s):
134 | d = {}
135 | for i in s.split():
136 | l = i.split(":")
137 | d[k_f(l[0])] = v_f(l[1])
138 | return d
139 | return cf
140 | raise Exception("未定义类型:%s"%type_s)
141 |
142 | TYPE_DEFAULT_RE = re.compile("^default(?:\((.*)\))?$")
143 | #m = TYPE_DEFAULT_RE.match("default")
144 | #print m.group(2)
145 | TAG_KEY_RE = re.compile("^key(?:\((.*)\))?$")
146 |
147 | type_default_tbl = {
148 | "int":0,
149 | "float":0.0,
150 | "string":"",
151 | "bool":False,
152 | }
153 | def parse_type_tag(ncol, tag_sl, type_s, conv_f):
154 | ret = {}
155 | def _key_f():
156 | assert ncol == 0, "key必须是第一列"
157 | assert type_s == "int" or type_s == "string", "类型:<%s>不能做key"%type_s
158 | ret["key"] = True
159 | def _ignore_f():
160 | ret["ignore"] = True
161 | def _raw_f():
162 | ret["raw"] = True
163 | def _key_alias_f():
164 | assert ncol == 1, "key_alias必须是第二列"
165 | ret["key_alias"] = True
166 | def _index_f():
167 | ret["index"] = True
168 | def _defaultnil_f():
169 | ret["default"] = None
170 | tag_fs = {
171 | #"key":_key_f,
172 | "ignore":_ignore_f,
173 | "raw":_raw_f,
174 | "key_alias":_key_alias_f,
175 | "index":_index_f,
176 | "defaultnil": _defaultnil_f,
177 | }
178 |
179 | for tag_s in tag_sl:
180 | if tag_s in tag_fs:
181 | #assert tag_s in tag_fs, "标签填写错误:<%s>"%tag_s
182 | tag_fs[tag_s]()
183 | continue
184 | # default 处理
185 | m = TYPE_DEFAULT_RE.match(tag_s)
186 | if m:
187 | assert "default" not in ret, "重复设置default"
188 | default_val = m.group(1)
189 | if not default_val and type_s in type_default_tbl:
190 | default_val = type_default_tbl[type_s]
191 | else:
192 | default_val = conv_f(default_val if default_val else "")
193 | ret["default"] = default_val
194 | continue
195 |
196 | # key 处理
197 | m = TAG_KEY_RE.match(tag_s)
198 | if m:
199 | assert ncol == 0, "key必须是第一列"
200 | assert type_s == "int" or type_s == "string", "类型:<%s>不能做key"%type_s
201 | d = {}
202 | key_attr = m.group(1)
203 | if key_attr:
204 | assert key_attr == "incr", "key的属性只能是incr"
205 | assert type_s == "int", "incr key只能是int类型"
206 | d["incr"] = True
207 | ret["key"] = d
208 | continue
209 | raise Exception(tag_s)
210 | # 容器类型list和dict默认就是空
211 | if get_basic_or_struct_cf(type_s) == None and "default" not in ret:
212 | ret["default"] = conv_f("")
213 |
214 | assert not ("key" in ret and "default" in ret), "key类型不能设置default"
215 | assert not ("key" in ret and "ignore" in ret), "key类型不能设置ignore"
216 | assert not ("key" in ret and "index" in ret), "key类型不能设置index"
217 | return ret
218 |
219 | def open_xls(filename):
220 | return xlrd.open_workbook(filename)
221 |
222 | # 返回的是下标
223 | def _find_dup_in_list(l):
224 | d = {}
225 | for n, i in enumerate(l):
226 | if i in d:
227 | return n
228 | d[i] = 1
229 | return -1
230 |
231 | def _num2colname(n):
232 | def _n2chr(i):
233 | return chr(65+i)
234 | if n < 26:
235 | return _n2chr(n)
236 | elif n < 26*27:
237 | return _n2chr(n/26-1) + _n2chr(n%26)
238 | return str(n)
239 |
240 | EXPORT_ALL_RE = re.compile("^all(?:-\[(.*)\])?$")
241 | def _parse_export(s):
242 | if not s:
243 | return None, None
244 | m = EXPORT_ALL_RE.match(s)
245 | if m:
246 | exclude = m.group(1)
247 | if not exclude:
248 | return "all", []
249 | else:
250 | return "all", [i.strip() for i in exclude.split(",")]
251 | else:
252 | return None, None
253 |
254 |
255 | def sheet_to_dict(sheet, alias_d):
256 | conv_funcs = []
257 | tags = []
258 | struct_deps_l = []
259 | struct_deps_d = {}
260 | alias_deps = alias_d.get("deps", {}) if alias_d else {}
261 | export_type = alias_d.get("export") if alias_d else None
262 | alias_d = alias_d.get("alias") if alias_d else None
263 | try:
264 | # 处理第一行,类型
265 | end_col = None
266 | for n, i in enumerate(sheet.row_values(0)):
267 | end_col = n + 1
268 | # 允许类型列填空,意味该列忽略
269 | if n > 0 and i == "" :
270 | end_col = n
271 | break
272 | type_sl = i.split("#")
273 | conv_f = make_convert_func(type_sl[0])
274 | conv_funcs.append(conv_f)
275 | tags.append(parse_type_tag(n, type_sl[1:], type_sl[0], conv_f))
276 | struct_deps_l.append(find_struct_deps(type_sl[0]))
277 | except Exception, e:
278 | raise Exception("sheet:<%s>类型行%s列填写错误, 内容:<%s>, msg:%s"%(sheet.name, _num2colname(n), i, e))
279 |
280 | name_row = sheet.row_values(1,end_colx=end_col)
281 | dup_idx = _find_dup_in_list(name_row)
282 | if dup_idx != -1:
283 | raise Exception("sheet:<%s>列名重复:<%s>"%(sheet.name, name_row[dup_idx]))
284 | col_names = []
285 | if alias_d:
286 | export_flag, export_exmsg = _parse_export(export_type)
287 | export_all = export_flag == "all"
288 | for name in name_row:
289 | col_names.append(alias_d.get(name, name if export_all and name not in export_exmsg else None))
290 | else:
291 | col_names = name_row
292 |
293 | for n, i in enumerate(col_names):
294 | if i and struct_deps_l[n]:
295 | struct_deps_d[i] = struct_deps_l[n]
296 |
297 | check_tag_f = lambda x,s: True if s in x else False
298 | raw_flag = check_tag_f(tags[0],"raw")
299 | raw_keys = {}
300 | key_flag = check_tag_f(tags[0],"key")
301 | key_incr_flag = check_tag_f(tags[0]["key"], "incr") if key_flag else False
302 | last_key = [0,]
303 | key_alias_flag = check_tag_f(tags[1],"key_alias") if len(tags) > 1 else False
304 | ret = {} if key_flag and not raw_flag else []
305 | key_alias_d = {} if key_alias_flag else None
306 | for nrow in xrange(2, sheet.nrows):
307 | row = sheet.row_values(nrow, end_colx=end_col)
308 | row_d = {}
309 | try:
310 | # 注释行忽略
311 | if isinstance(row[0], unicode) and row[0].startswith("//"):
312 | continue
313 | row_key = None
314 | row_key_alias = None
315 | for ncol, value in enumerate(row):
316 | tag = tags[ncol]
317 | col_name = col_names[ncol]
318 | if not col_name:
319 | # key和key_alias列走流程可以不导出
320 | is_key = key_flag and "key" in tag
321 | is_alias = key_alias_flag and "key_alias" in tag
322 | if not is_key and not is_alias:
323 | continue
324 | else:
325 | if "index" in tag and col_name not in struct_deps_d:
326 | if alias_d and col_name not in alias_deps:
327 | raise Exception("%s填写了index但没有定义依赖"%col_name)
328 | cv = None
329 | if "ignore" in tag:
330 | continue
331 | # 如果该格子不填,获取的是空串
332 | if value == "" and "default" in tag:
333 | cv = tag["default"]
334 | else:
335 | # raw key 列可以为空
336 | if not raw_flag:
337 | assert value != "", "表项为空"
338 | cv = conv_funcs[ncol](value)
339 | else:
340 | if value != "":
341 | cv = conv_funcs[ncol](value)
342 | if ncol == 0 and "key" in tag:
343 | row_key = cv
344 | if ncol == 1 and "key_alias" in tag:
345 | row_key_alias = cv
346 | if col_name:
347 | row_d[col_name] = cv
348 | def _check_key(check_d):
349 | # 检查key是否重复
350 | # raw表,key列可能是None,不用检查了
351 | if raw_flag and row_key == None:
352 | return
353 | assert row_key not in check_d, "key列内容重复, 行:%s,值:%s"%(nrow+1, row_key)
354 | check_d[row_key] = row_d
355 | if key_incr_flag:
356 | assert row_key == last_key[0] + 1, "incr key 不连续:%d"%row_key
357 | last_key[0] = row_key
358 | if key_alias_flag:
359 | assert row_key_alias not in key_alias_d, "key_alias列内容重复, 行:%s,值:%s"%(nrow+1, row_key_alias)
360 | key_alias_d[row_key_alias] = row_key
361 | if isinstance(ret, dict):
362 | _check_key(ret)
363 | else:
364 | if raw_flag and key_flag:
365 | _check_key(raw_keys)
366 | ret.append(row_d)
367 | except Exception, e:
368 | # print_exc()
369 | raise Exception("sheet:%s, cell:<行%s-列%s>, %s"%(sheet.name, nrow+1, _num2colname(ncol), e))
370 | return ret, struct_deps_d, key_alias_d
371 |
372 |
373 | def get_alias_conf(fn, shname):
374 | if fn in g_alias_d:
375 | return g_alias_d[fn]
376 | key = alias.make_key(fn, shname)
377 | return g_alias_d.get(key)
378 |
379 | def convert_xls(filename):
380 | try:
381 | wb = open_xls(filename)
382 | ret = {}
383 | ext = {}
384 | for sheet in wb.sheets():
385 | if sheet.name.startswith("_"):
386 | continue
387 | if sheet.nrows < 2:
388 | continue
389 | data, deps_d, key_alias_d = sheet_to_dict(sheet, get_alias_conf(filename, sheet.name))
390 | ret[sheet.name] = data
391 | d = {}
392 | if len(deps_d) > 0:
393 | d["deps"] = deps_d
394 | d["typ"] = "l" if type(data) == type([]) else "d"
395 | d["key_alias"] = key_alias_d
396 | if d["typ"] == "l" and key_alias_d:
397 | tmp = {}
398 | for k, v in key_alias_d.iteritems():
399 | tmp[v] = k
400 | d["key_check"] = tmp
401 | ext[sheet.name] = d
402 | return ret, ext
403 | except Exception, e:
404 | # print_exc()
405 | error("file:%s, error: %s"%(filename, e))
406 |
407 | def run_dir(path):
408 | os.chdir(path)
409 | files = []
410 | def visit(arg, dirname, names):
411 | for name in names:
412 | if name.endswith(".xls") and not name.startswith("_"):
413 | files.append(os.path.relpath(os.path.join(dirname, name), "."))
414 | os.path.walk(".", visit, None)
415 | for fn in files:
416 | fn = fn.replace(os.sep, "/")
417 | data, ext = convert_xls(fn)
418 | out = {}
419 | out["filename"] = fn
420 | out["data"] = data
421 | out["ext"] = ext
422 | #if len(deps_d) > 0:
423 | #out["struct_deps"] = deps_d
424 | output(lseri._tolua(out))
425 |
426 | if __name__ == "__main__":
427 | fpath = sys.argv[1]
428 | import platform
429 | if platform.system() == "Windows":
430 | fpath = fpath.decode("gbk")
431 | else:
432 | fpath = fpath.decode("utf8")
433 | try:
434 | alias_raw, g_alias_d, g_alias_deps = \
435 | alias.parse(fpath, sys.argv[2])
436 | if len(alias_raw) > 0:
437 | output(lseri._tolua({"alias_fields":alias_raw}))
438 | if len(g_alias_deps) > 0:
439 | output(lseri._tolua({"alias_deps":g_alias_deps}))
440 | g_struct_d = typedef.parse(os.path.join(fpath, "struct.yaml"))
441 | g_struct_deps = typedef.parse_deps(os.path.join(sys.argv[2], "struct_deps.yaml"), g_struct_d)
442 | if len(g_struct_deps) > 0:
443 | output(lseri._tolua({"struct_deps":g_struct_deps}))
444 | except Exception, e:
445 | error(str(e))
446 | run_dir(fpath)
447 | flush_output()
448 |
--------------------------------------------------------------------------------
/converter/main.lua:
--------------------------------------------------------------------------------
1 | local lua_path = os.getenv("LUA_PATH")
2 | if lua_path then
3 | package.path = string.format("%s;%s", package.path, lua_path)
4 | end
5 | local lua_cpath = os.getenv("LUA_CPATH")
6 | if lua_cpath then
7 | package.cpath = string.format("%s;%s", package.cpath, lua_cpath)
8 | end
9 |
10 | local DataDump = require "dumper"
11 | local sformat = string.format
12 |
13 | local cfg_fn, doc_dir, alias_dir, script_dir, outdir, json_outdir= ...
14 | json_outdir = json_outdir or (outdir and outdir.."_json")
15 | local cfg_env = {}
16 | print(cfg_fn)
17 | local cfg_f = loadfile(cfg_fn, "t", cfg_env)
18 | cfg_f()
19 | local export_cfg = cfg_env.export
20 | local global = {raw = {}, save = {}, cfg_env = cfg_env}
21 | local copy = {}
22 | local exts = {}
23 | local save= global.save
24 | local alias_deps = {}
25 | local struct_deps = {}
26 | local alias_fields = {}
27 | local sheet_struct_deps = {}
28 | local save_check_struct_deps = {}
29 | local cfg_exts = {}
30 |
31 | function tprint(t)
32 | local s = DataDump(t)
33 | print(s:sub(8, #s))
34 | end
35 |
36 | local function info(...)
37 | print(...)
38 | end
39 |
40 | function error(msg)
41 | info("ERROR: "..msg)
42 | os.exit(1)
43 | end
44 |
45 | assert = function(cond, msg)
46 | if not cond then
47 | error(msg)
48 | end
49 | return cond
50 | end
51 |
52 | local function _get_or_create_key(d, key)
53 | local ret = d[key]
54 | if not ret then
55 | ret = {}
56 | d[key] = ret
57 | end
58 | return ret
59 | end
60 |
61 | local function _merge_hash(d1, d2, msg)
62 | msg = msg or "重复的key"
63 | for k, v in pairs(d2) do
64 | assert(d1[k] == nil, sformat("%s:<%s>", msg, v))
65 | d1[k] = v
66 | end
67 | end
68 |
69 | local function _merge_array(l1, l2)
70 | for _, v in ipairs(l2) do
71 | table.insert(l1, v)
72 | end
73 | end
74 |
75 | -- py convert
76 | info("*****************python convert******************")
77 | local root_path = os.getenv("BIN_ROOT") or "./exporter"
78 | local fp = io.popen(sformat("python %s/xlsparse.py %s %s", root_path, doc_dir, alias_dir))
79 | while true do
80 | local data = fp:read("*a")
81 | if not data then
82 | break
83 | end
84 | local f = load("return "..data, "=(load)", "t")
85 | local ok, all_data = pcall(f)
86 | if not ok or not all_data then
87 | info(ok, all_data)
88 | error("py convert error")
89 | end
90 |
91 | if all_data.error then
92 | error(all_data.error)
93 | end
94 | for _, ret in ipairs(all_data) do
95 | local fn = ret.filename
96 | if fn then
97 | -- global.raw[fn] = ret.data
98 | copy[fn] = ret.data
99 | exts[fn] = ret.ext
100 | sheet_struct_deps[fn] = ret.struct_deps
101 | else
102 | if ret.alias_deps then
103 | alias_deps = ret.alias_deps
104 | end
105 | if ret.struct_deps then
106 | struct_deps = ret.struct_deps
107 | end
108 | if ret.alias_fields then
109 | for name, cfg in pairs(ret.alias_fields) do
110 | local d = {}
111 | for field, alias in pairs(cfg) do
112 | d[alias] = field
113 | end
114 | alias_fields[name] = d
115 | end
116 | end
117 | end
118 | end
119 | break
120 | end
121 | fp:close()
122 | if not next(copy) then
123 | error("no xls file convert!")
124 | end
125 |
126 | -- lua prepare
127 | info("*****************lua prepare******************")
128 | local key_alias = {}
129 | local cfg_keys = {}
130 |
131 | local save_name_d = {}
132 | local merge_name_d = {}
133 | for cfg_idx, entry in ipairs(export_cfg) do
134 | local fn = entry[1]
135 | local snames = entry[2]
136 | local script = entry[3]
137 | local save_name = entry[4]
138 | info(string.format("lua prepare: %s < %s", save_name, fn))
139 | assert(merge_name_d[save_name] == nil,
140 | "输出名字重复:"..save_name)
141 | save_name_d[save_name] = true
142 | local ext_d = entry[5]
143 | if next(ext_d) then
144 | cfg_exts[save_name] = ext_d
145 | local merge_name = ext_d.merge
146 | if merge_name then
147 | assert(save_name_d[merge_name] == nil, "merge name 重复")
148 | merge_name_d[merge_name] = true
149 | end
150 | end
151 |
152 | if type(snames) == "string" then
153 | if snames == "*" then
154 | snames = {}
155 | for k, v in pairs(copy[fn]) do
156 | table.insert(snames, k)
157 | end
158 | else
159 | snames = {snames}
160 | end
161 | elseif snames == nil then
162 | error("sheet名称配置不能为nil")
163 | snames = {2015}
164 | end
165 | entry[2] = snames
166 |
167 | for _, i in ipairs(snames) do
168 | -- info(sformat("lua prepare: %s-%s", fn, i))
169 | local sheet = copy[fn]
170 | assert(sheet, "文件不存在!"..fn)
171 | local ext = exts[fn]
172 | if i ~= 2015 then
173 | sheet = sheet[i]
174 | assert(sheet, sformat("file:<%s> has no sheet:<%s>", fn, i))
175 | ext = ext[i]
176 | if ext.deps then
177 | local kd = _get_or_create_key(save_check_struct_deps, save_name)
178 | for k, v in pairs(ext.deps) do
179 | kd[k] = kd[k] or v
180 | end
181 | end
182 | if ext.key_alias then
183 | local kd = _get_or_create_key(key_alias, save_name)
184 | _merge_hash(kd, ext.key_alias, "重复的key_alias")
185 | end
186 | if ext.typ == "d" then
187 | local ckd = _get_or_create_key(cfg_keys, save_name)
188 | for k, v in pairs(sheet) do
189 | assert(not ckd[k], sformat("重复的key:<%s>", k))
190 | ckd[k] = k
191 | end
192 | end
193 | if ext.key_check then
194 | local ckd = _get_or_create_key(cfg_keys, save_name)
195 | _merge_hash(ckd, ext.key_check, "重复的key")
196 | end
197 | else
198 | -- 整个文件导出不处理ext
199 | assert(not pre, "whole workbook export save name dup!")
200 | end
201 | end
202 | end
203 |
204 | -- check deps cfgname
205 | for k, _ in pairs(alias_deps) do
206 | assert(save_name_d[k], sformat("deps所在的别名定义配置名称必须在config的导出名字中:<%s>", k))
207 | end
208 | global.alias2key = key_alias
209 |
210 | -- check and convert depends
211 | local check = cfg_keys
212 | local convert = key_alias
213 |
214 | local log_alias = nil
215 | local log_idx = nil
216 | local log_field = nil
217 | local raw_assert = assert
218 | assert = function(v, msg)
219 | local field = log_alias and log_alias[log_field] or log_field
220 | return raw_assert(v, ("check depends error, idx:<%s>, field:<%s> %s"):format(log_idx, field, msg))
221 | end
222 |
223 | local function _conv(check, convert, i, msg)
224 | assert(check, sformat("%s %s", msg, "依赖的表不存在,应该填写config中的存盘名字"))
225 | if type(i) == "number" then
226 | if i == 0 then
227 | return 0
228 | end
229 | assert(check[i], msg)
230 | return i
231 | elseif type(i) == "string" then
232 | if i == "" then
233 | if convert and type(({next(convert)})[2]) == "number" then
234 | return 0
235 | else
236 | return ""
237 | end
238 | end
239 | -- 兼容旧的写法
240 | -- not convert[i] 因为索引的是同一个table,可能已经被convert过了。
241 | if not convert or not convert[i] then
242 | return assert(check[i], msg)
243 | end
244 | return assert(convert[i], msg)
245 | end
246 | error(msg)
247 | end
248 |
249 | local function _make_check_f(deps)
250 | -- 简单类型或者列表类型
251 | if type(deps) ~= "table" then
252 | local src = check[deps]
253 | local dst = convert[deps]
254 | return function(ckd)
255 | if type(ckd) == "table" then
256 | local ret = {}
257 | for n, i in ipairs(ckd) do
258 | i = _conv(src, dst, i, sformat("list pos:<%d> value:<%s>", n, i))
259 | table.insert(ret, i)
260 | end
261 | return ret
262 | else
263 | local tmp = _conv(src, dst, ckd, "simple error: "..(ckd or "nil"))
264 | return tmp
265 | end
266 | end
267 | -- 字典类型
268 | elseif deps.key or deps.value then
269 | local k_src, k_dst, v_src, v_dst
270 | if deps.key then
271 | k_src = check[deps.key]
272 | k_dst = convert[deps.key]
273 | end
274 | if deps.value then
275 | v_src = check[deps.value]
276 | v_dst = convert[deps.value]
277 | end
278 | return function(ckd)
279 | local ret = {}
280 | for k, v in pairs(ckd) do
281 | if deps.key then
282 | k = _conv(k_src, k_dst, k, "key error: "..k)
283 | end
284 | if deps.value then
285 | v = _conv(v_src, v_dst, v, "value error: "..v)
286 | end
287 | ret[k] = v
288 | end
289 | return ret
290 | end
291 | -- 指定列表位置
292 | else
293 | local check_d = {}
294 | for i, v in pairs(deps) do
295 | check_d[i] = {check[v], convert[v]}
296 | end
297 | return function(ckd)
298 | local ret = {}
299 | for i, v in ipairs(ckd) do
300 | local cd = check_d[i]
301 | if d then
302 | v = _conv(cd[1], cd[2], v, ("list pos:<%d> value:<%s>"):format(i, v))
303 | end
304 | table.insert(ret, v)
305 | end
306 | return ret
307 | end
308 | end
309 | end
310 |
311 | local check_conf = {}
312 | for name, cfg in pairs(alias_deps) do
313 | local d = {}
314 | for field, deps in pairs(cfg) do
315 | d[field] = _make_check_f(deps)
316 | end
317 | check_conf[name] = d
318 | end
319 |
320 | local function _check_struct_value_deps(value, cfg)
321 | for k, v in pairs(cfg) do
322 | local src = check[v]
323 | local dst = convert[v]
324 | value[k] = _conv(src, dst, value[k], "struct error: "..value[k])
325 | end
326 | end
327 |
328 | local function _check_alias_deps(sheet, check_fs)
329 | for idx, entry in pairs(sheet) do
330 | log_idx = idx
331 | for field, check_f in pairs(check_fs) do
332 | log_field = field
333 | if entry[field] then
334 | entry[field] = check_f(entry[field])
335 | end
336 | end
337 | end
338 | end
339 |
340 | local function _check_struct_deps(sheet, deps_cfg)
341 | for idx, entry in pairs(sheet) do
342 | log_idx = idx
343 | for field, field_cfg in pairs(deps_cfg) do
344 | log_field = field
345 | local value = entry[field]
346 | if value then
347 | local flag, cfg = field_cfg[1], field_cfg[2]
348 | if flag == "s" then
349 | _check_struct_value_deps(value, cfg)
350 | elseif flag == "l" then
351 | for _, v in ipairs(value) do
352 | _check_struct_value_deps(v, cfg)
353 | end
354 | elseif flag == "d" then
355 | for _, v in pairs(value) do
356 | _check_struct_value_deps(v, cfg)
357 | end
358 | end
359 | end
360 | end
361 | end
362 | end
363 |
364 |
365 | info("*****************check and convert depends******************")
366 | for _, v in ipairs(export_cfg) do
367 | local save_name = v[4]
368 | local check_fs = check_conf[save_name]
369 | local struct_check_fs = save_check_struct_deps[save_name]
370 | log_alias = alias_fields[save_name]
371 | local fn = v[1]
372 | local snames = v[2]
373 | if #snames == 1 and snames[1] == 2015 then
374 | snames = {}
375 | end
376 | for _, sn in ipairs(snames) do
377 | local sheet = copy[fn][sn]
378 | info(("check depends, file:<%s>, sheet:<%s>"):format(fn, sn))
379 | if check_fs then
380 | _check_alias_deps(sheet, check_fs)
381 | end
382 | if struct_check_fs then
383 | _check_struct_deps(sheet, struct_check_fs)
384 | end
385 | end
386 | end
387 |
388 | assert = raw_assert
389 |
390 | -- tprint(copy)
391 |
392 | -- lua convert
393 | info("*****************lua convert******************")
394 | local last_save_type = {}
395 | local post_convert_funcs = {}
396 | local post_convert_names = {}
397 | for cfg_idx, entry in ipairs(export_cfg) do
398 | local fn = entry[1]
399 | local snames = entry[2]
400 | local script = entry[3]
401 | local save_name = entry[4]
402 |
403 | if #snames == 1 and snames[1] == 2015 then
404 | snames = {}
405 | end
406 | local mod = setmetatable({}, {__index = _ENV})
407 | local convert_f = nil
408 | if script ~= nil then
409 | local f, msg = loadfile(sformat("%s/%s", script_dir, script), "t", mod)
410 | assert(f, sformat("load script error, cfg idx:<%d>, script:<%s>\n%s", cfg_idx, script, msg))
411 | local success, msg = xpcall(f, debug.traceback)
412 | assert(success, sformat("run script error, cfg idx:<%d>, script:<%s>\n%s", cfg_idx, script, msg))
413 | --convert_f = assert(mod.convert, sformat("no convert func, cfg idx:<%d>, script:<%s>", cfg_idx, script))
414 | convert_f = mod.convert
415 | end
416 | for _, i in ipairs(snames) do
417 | info(sformat("lua convert: %s-%s", fn, i))
418 | local pre = save[save_name]
419 | local sheet = copy[fn]
420 | assert(sheet, "文件不存在!"..fn)
421 | local ext = exts[fn]
422 | if i ~= 2015 then
423 | sheet = sheet[i]
424 | assert(sheet, sformat("file:<%s> has no sheet:<%s>", fn, i))
425 | ext = ext[i]
426 | else
427 | assert(not pre, "whole workbook export save name dup!")
428 | end
429 | local t
430 | if convert_f then
431 | ext_data = {
432 | filename = fn,
433 | sheetname = i,
434 | }
435 | t = convert_f(sheet, global, ext_data)
436 | else
437 | t = sheet
438 | end
439 | if pre then
440 | assert(last_save_type[save_name] == ext.typ, "多表页类型不匹配")
441 | if ext.typ == "d" then
442 | _merge_hash(pre, t)
443 | elseif ext.typ == "l" then
444 | _merge_array(pre, t)
445 | end
446 | else
447 | save[save_name] = t
448 | last_save_type[save_name] = ext.typ
449 | end
450 | end
451 | if mod.post_convert then
452 | local d = _get_or_create_key(post_convert_names, save_name)
453 | if not d[script] then
454 | d[script] = true
455 | local sheets = string.format("save:<%s> file:<%s> sheets:<%s>", save_name, fn, table.concat(snames, ","))
456 | table.insert(post_convert_funcs, {mod.post_convert, save_name, sheets})
457 | end
458 | end
459 | end
460 |
461 | -- post convert
462 | if #post_convert_funcs > 0 then
463 | info("*****************lua post convert******************")
464 | end
465 | for _, v in ipairs(post_convert_funcs) do
466 | info("lua post convert: ", v[3])
467 | save[v[2]] = v[1](save[v[2]], global)
468 | end
469 |
470 | -- ext process
471 | local merge = {}
472 | local no_save = {}
473 | for k, v in pairs(cfg_exts) do
474 | if v.merge then
475 | -- 可以配置成weapon.attr这种
476 | local it = string.gmatch(v.merge, "[^.]+")
477 | local to = it()
478 | local alias = it()
479 | assert(it() == nil, sformat("export name:<%s> merge format error:<%s>", k, v.merge))
480 | local d = _get_or_create_key(merge, to)
481 | alias = alias or k
482 | table.insert(d, {raw = k, alias = alias})
483 | end
484 | if v.no_save then
485 | table.insert(no_save, k)
486 | end
487 | end
488 | for _, i in ipairs(no_save) do
489 | save[i] = nil
490 | end
491 | for k, v in pairs(merge) do
492 | for _, i in ipairs(v) do
493 | local d = _get_or_create_key(save, k)
494 | d[i.alias] = save[i.raw]
495 | save[i.raw] = nil
496 | end
497 | end
498 |
499 | -- post process script
500 | if cfg_env.post_convert_script then
501 | info("***************run post convert script******************")
502 | local env = setmetatable({}, {__index = _ENV})
503 | local f, msg = loadfile(sformat("%s/%s", script_dir, cfg_env.post_convert_script), "t", env)
504 | assert(f, msg)
505 | local ok, msg = xpcall(f, debug.traceback)
506 | assert(ok, "post convert script call")
507 | assert(env.run, "post convert script has no run function")
508 | env.run(global)
509 | end
510 |
511 | if not outdir then
512 | info("check success!")
513 | os.exit(0)
514 | end
515 | -- tprint(save)
516 |
517 | info("*****************save lua file******************")
518 | local suffix = cfg_env.save_suffix or ""
519 | if cfg_env.all_in_one then
520 | local one_file = sformat("%s/%s", outdir, cfg_env.all_in_one)
521 | local fp = io.open(one_file, "w")
522 | local s = DataDump(save)
523 | fp:write(s)
524 | fp:close()
525 | else
526 | for k, v in pairs(save) do
527 | local s = DataDump(v)
528 | --local fp = io.open(sformat("%s/%s", cfg_env.output_dir, k), "w")
529 | local fp = io.open(sformat("%s/%s%s", outdir, k, suffix), "w")
530 | fp:write(s)
531 | fp:close()
532 | info("save file: "..k)
533 | end
534 | end
535 |
536 | local to_json_list = cfg_env.to_json_list
537 | if to_json_list and #to_json_list > 0 then
538 | info("*****************save json file******************")
539 | local Json = require "json"
540 | for _, i in ipairs(to_json_list) do
541 | assert(save[i], sformat("要保存json的数据不存在:<%s>", i))
542 | local fp = io.open(sformat("%s/%s.json", json_outdir, i), "w")
543 | local s = Json.encode(save[i], {keyorder = {}})
544 | fp:write(s)
545 | fp:close()
546 | end
547 | end
548 | info("xls convert success!")
549 |
--------------------------------------------------------------------------------
/converter/json.lua:
--------------------------------------------------------------------------------
1 | -- Module options:
2 | local always_try_using_lpeg = true
3 | local register_global_module_table = false
4 | local global_module_name = 'json'
5 |
6 | --[==[
7 |
8 | David Kolf's JSON module for Lua 5.1/5.2
9 | ========================================
10 |
11 | *Version 2.4*
12 |
13 | In the default configuration this module writes no global values, not even
14 | the module table. Import it using
15 |
16 | json = require ("dkjson")
17 |
18 | In environments where `require` or a similiar function are not available
19 | and you cannot receive the return value of the module, you can set the
20 | option `register_global_module_table` to `true`. The module table will
21 | then be saved in the global variable with the name given by the option
22 | `global_module_name`.
23 |
24 | Exported functions and values:
25 |
26 | `json.encode (object [, state])`
27 | --------------------------------
28 |
29 | Create a string representing the object. `Object` can be a table,
30 | a string, a number, a boolean, `nil`, `json.null` or any object with
31 | a function `__tojson` in its metatable. A table can only use strings
32 | and numbers as keys and its values have to be valid objects as
33 | well. It raises an error for any invalid data types or reference
34 | cycles.
35 |
36 | `state` is an optional table with the following fields:
37 |
38 | - `indent`
39 | When `indent` (a boolean) is set, the created string will contain
40 | newlines and indentations. Otherwise it will be one long line.
41 | - `keyorder`
42 | `keyorder` is an array to specify the ordering of keys in the
43 | encoded output. If an object has keys which are not in this array
44 | they are written after the sorted keys.
45 | - `level`
46 | This is the initial level of indentation used when `indent` is
47 | set. For each level two spaces are added. When absent it is set
48 | to 0.
49 | - `buffer`
50 | `buffer` is an array to store the strings for the result so they
51 | can be concatenated at once. When it isn't given, the encode
52 | function will create it temporary and will return the
53 | concatenated result.
54 | - `bufferlen`
55 | When `bufferlen` is set, it has to be the index of the last
56 | element of `buffer`.
57 | - `tables`
58 | `tables` is a set to detect reference cycles. It is created
59 | temporary when absent. Every table that is currently processed
60 | is used as key, the value is `true`.
61 |
62 | When `state.buffer` was set, the return value will be `true` on
63 | success. Without `state.buffer` the return value will be a string.
64 |
65 | `json.decode (string [, position [, null]])`
66 | --------------------------------------------
67 |
68 | Decode `string` starting at `position` or at 1 if `position` was
69 | omitted.
70 |
71 | `null` is an optional value to be returned for null values. The
72 | default is `nil`, but you could set it to `json.null` or any other
73 | value.
74 |
75 | The return values are the object or `nil`, the position of the next
76 | character that doesn't belong to the object, and in case of errors
77 | an error message.
78 |
79 | Two metatables are created. Every array or object that is decoded gets
80 | a metatable with the `__jsontype` field set to either `array` or
81 | `object`. If you want to provide your own metatables use the syntax
82 |
83 | json.decode (string, position, null, objectmeta, arraymeta)
84 |
85 | To prevent the assigning of metatables pass `nil`:
86 |
87 | json.decode (string, position, null, nil)
88 |
89 | `.__jsonorder`
90 | -------------------------
91 |
92 | `__jsonorder` can overwrite the `keyorder` for a specific table.
93 |
94 | `.__jsontype`
95 | ------------------------
96 |
97 | `__jsontype` can be either `"array"` or `"object"`. This value is only
98 | checked for empty tables. (The default for empty tables is `"array"`).
99 |
100 | `.__tojson (self, state)`
101 | ------------------------------------
102 |
103 | You can provide your own `__tojson` function in a metatable. In this
104 | function you can either add directly to the buffer and return true,
105 | or you can return a string. On errors nil and a message should be
106 | returned.
107 |
108 | `json.null`
109 | -----------
110 |
111 | You can use this value for setting explicit `null` values.
112 |
113 | `json.version`
114 | --------------
115 |
116 | Set to `"dkjson 2.4"`.
117 |
118 | `json.quotestring (string)`
119 | ---------------------------
120 |
121 | Quote a UTF-8 string and escape critical characters using JSON
122 | escape sequences. This function is only necessary when you build
123 | your own `__tojson` functions.
124 |
125 | `json.addnewline (state)`
126 | -------------------------
127 |
128 | When `state.indent` is set, add a newline to `state.buffer` and spaces
129 | according to `state.level`.
130 |
131 | LPeg support
132 | ------------
133 |
134 | When the local configuration variable `always_try_using_lpeg` is set,
135 | this module tries to load LPeg to replace the `decode` function. The
136 | speed increase is significant. You can get the LPeg module at
137 | .
138 | When LPeg couldn't be loaded, the pure Lua functions stay active.
139 |
140 | In case you don't want this module to require LPeg on its own,
141 | disable the option `always_try_using_lpeg` in the options section at
142 | the top of the module.
143 |
144 | In this case you can later load LPeg support using
145 |
146 | ### `json.use_lpeg ()`
147 |
148 | Require the LPeg module and replace the functions `quotestring` and
149 | and `decode` with functions that use LPeg patterns.
150 | This function returns the module table, so you can load the module
151 | using:
152 |
153 | json = require "dkjson".use_lpeg()
154 |
155 | Alternatively you can use `pcall` so the JSON module still works when
156 | LPeg isn't found.
157 |
158 | json = require "dkjson"
159 | pcall (json.use_lpeg)
160 |
161 | ### `json.using_lpeg`
162 |
163 | This variable is set to `true` when LPeg was loaded successfully.
164 |
165 | ---------------------------------------------------------------------
166 |
167 | Contact
168 | -------
169 |
170 | You can contact the author by sending an e-mail to 'david' at the
171 | domain 'dkolf.de'.
172 |
173 | ---------------------------------------------------------------------
174 |
175 | *Copyright (C) 2010-2013 David Heiko Kolf*
176 |
177 | Permission is hereby granted, free of charge, to any person obtaining
178 | a copy of this software and associated documentation files (the
179 | "Software"), to deal in the Software without restriction, including
180 | without limitation the rights to use, copy, modify, merge, publish,
181 | distribute, sublicense, and/or sell copies of the Software, and to
182 | permit persons to whom the Software is furnished to do so, subject to
183 | the following conditions:
184 |
185 | The above copyright notice and this permission notice shall be
186 | included in all copies or substantial portions of the Software.
187 |
188 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
189 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
190 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
191 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
192 | BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
193 | ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
194 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
195 | SOFTWARE.
196 |
197 |
202 |
203 |
863 |
--------------------------------------------------------------------------------