├── HISTORY.md ├── License.txt ├── Makefile ├── README.md ├── include ├── bs_ast.h ├── bs_parse.h ├── bs_php.h ├── bs_print.h └── gen_list.h ├── src ├── bs_ast.c ├── bs_parse.c ├── bs_php.c ├── bs_print.c └── main.c ├── test.bs └── test.php /HISTORY.md: -------------------------------------------------------------------------------- 1 | 项目日志 2 | ========== 3 | 4 | 2011-11-06 5 | ---------- 6 | 1. 使用valgrind运行并修改了几处警告,主要是字符串复制前的初始化和next_token函数的越界 7 | 8 | 2011-11-03 9 | ---------- 10 | 1. 增加type语法的支持,帮助文档中加入type语法介绍 11 | 12 | 2011-11-02 13 | ---------- 14 | 1. 修改注释语法,去掉多行注释的支持 15 | 2. 尝试把注释纳入语法树,发现得不偿失,重新权衡决定先放弃这个功能 16 | 17 | 2011-11-01 18 | ---------- 19 | 1. 完善了枚举类型的支持 20 | 2. 重新组织了项目结构,正规一些了 :) 21 | 3. 重写了README,希望更容易让其他人理解BinSpec是什么和怎用,第一遍重写完以后不小心覆盖了。。。浪费不少时间又重写一次 22 | 4. 把def的编号语法改为可选 23 | 5. TODO: 实现类型引用 24 | 6. TODO: PHP代码中加入一些辅助函数,简化模版的编写 25 | 7. TODO: 约束注释,把注释纳入抽象语法树 26 | 27 | 2011-10-30 28 | ---------- 29 | 1. 首次提交到github 30 | 2. 花了一些时间完善了下README的内容,虽然还缺少循序渐进的教程但至少可以让人知道这个项目是做什么的 31 | 3. 注册了binspec.com、.net、.org域名 32 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 2 | Version 2, December 2004 3 | 4 | Copyright (C) 2004 Sam Hocevar 5 | 6 | Everyone is permitted to copy and distribute verbatim or modified 7 | copies of this license document, and changing it is allowed as long 8 | as the name is changed. 9 | 10 | DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE 11 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 12 | 13 | 0. You just DO WHAT THE FUCK YOU WANT TO. 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: init ./bin/bs 2 | 3 | ./bin/bs: ./obj/bs_php.o ./obj/bs_print.o ./obj/bs_parse.o ./obj/bs_ast.o ./obj/bs.o 4 | cc -g -Wall -O -o ./bin/bs ./obj/bs_ast.o ./obj/bs_print.o ./obj/bs_parse.o ./obj/bs_php.o ./obj/bs.o -lm 5 | 6 | ./obj/bs_php.o: ./src/bs_php.c ./include/*.h 7 | cc -I./include -o ./obj/bs_php.o -c ./src/bs_php.c 8 | 9 | ./obj/bs_print.o: ./src/bs_print.c ./include/*.h 10 | cc -I./include -o ./obj/bs_print.o -c ./src/bs_print.c 11 | 12 | ./obj/bs_parse.o: ./src/bs_parse.c ./include/*.h 13 | cc -I./include -o ./obj/bs_parse.o -c ./src/bs_parse.c 14 | 15 | ./obj/bs_ast.o: ./src/bs_ast.c ./include/*.h 16 | cc -I./include -o ./obj/bs_ast.o -c ./src/bs_ast.c 17 | 18 | ./obj/bs.o: ./src/main.c ./include/*.h 19 | cc -I./include -o ./obj/bs.o -c ./src/main.c 20 | 21 | init: 22 | @[ ! -d bin ] && mkdir bin || exit 0 23 | @[ ! -d obj ] && mkdir obj || exit 0 24 | 25 | clean: 26 | rm bin/* 27 | rm obj/* 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BinSpec是什么 2 | ============= 3 | BinSpec是一个基于WTFPL协议(Do What The Fuck You Want To Public License)的开源项目,这个项目的目标是构建一门简单灵活的**二进制数据结构描述语言**。 4 | 5 | BinSpec的典型应用场景有:网络应用协议描述、二进制序列化格式描述。 6 | 7 | BinSpec是一门描述性语言,它只能描述数据结构,并且它的语法被设计成尽量与实现无关,所以它只是一套语言规范,它“正则化”了二进制数据结构的描述方式。 8 | 9 | 正则化让描述文档变得可以解析,我们利用这一特性编写了一个文档解析工具(bs),它可以通过解析BinSpec语法知道文档描述了些什么内容,但是它不能理解这些内容,bs的唯一作用只有把基于BinSpec编写的文档转换成模板语言的代码(目前只支持PHP)。 10 | 11 | bs转换输出的代码中包含了一组bs\_开头的类和函数,其中有一个bs\_get\_doc函数它的返回值就是按文档内容生成的树状结构的对象。bs通过“混合”机制让模板可以调用到bs\_get\_doc函数,从而可以获得文档中的描述信息。 12 | 13 | 利用文档中的描述信息模板就可以自由发挥做任何事情,最典型的应用就是生成客户端和服务端通讯层的封包解包代码。 14 | 15 | 16 | BinSpec起步 17 | =========== 18 | 首先你需要准备一个有gcc和make的环境,然后下载BinSpec的项目代码并在本地解压,然后进入BinSpec项目跟目录运行make,完成后你就可以在bin目录找到BinSpec的命令行程序bs。 19 | 20 | 你可以试着运行它,像这样: 21 | 22 | ./bin/bs 23 | 24 | 你会看到屏幕上输出一些帮助信息,接着你可以试着用它转换项目自带的test.bs文件,像这样: 25 | 26 | ./bin/bs -c test.bs 27 | 28 | 你会看到屏幕输出一段PHP代码,这段代码就是test.bs转换后的结果。再接着你可以试着用它混合项目自带的test.bs和test.php,像这样: 29 | 30 | ./bin/bs -c test.bs test.php 31 | 32 | 你会发现屏幕上的输出和前一步几乎一样,只是最后多了一行代码,打开test.php你会发现多出来的那行代码就是test.php的内容。是的,混合是一个纯粹的拼接过程。 33 | 34 | 接着你可以试着把混合后的结果传递给PHP命令行程序运行,像这样(这里假设你的系统安装了PHP命令行,并且PHP命令所在目录存在于PATH环境变量中): 35 | 36 | ./bin/bs -c test.bs test.php | php 37 | 38 | 现在你可以看到屏幕上打印了一个有复杂树状结构的PHP变量,这就是temp.php执行的结果,试着理解混合后的PHP代码可以帮助理解如何制作模板,你可以用下面的方式保存混合后的PHP代码: 39 | 40 | ./bin/bs -c test.bs test.php > output.php 41 | 42 | 混合后的代码会被保存到output.php中。 43 | 44 | 45 | BinSpec语法 46 | =========== 47 | BinSpec代码以文档为单位,一个.bs文件被解析成一个文档,一个文档中包含多个"包",BinSpec的包用**pkg**关键字声明,例1演示了如何声明一个包。 48 | 49 | **例1** 50 | 51 | pkg player 52 | { 53 | } 54 | 55 | 包本身没身没有数据结构描述作用,包的主要作用是区隔,就好像系统中的文件夹。BinSepc的包可以嵌套,这一点也跟文件夹一样,例2演示了如何声明嵌套的包。 56 | 57 | **例2** 58 | 59 | pkg game 60 | { 61 | pkg player 62 | { 63 | } 64 | 65 | pkg admin 66 | { 67 | } 68 | } 69 | 70 | 在BinSpec中对一个数据结构的描述称为“定义”,定义只能存在于包中,就像文件之于文件夹,例3演示了如何定义一个数据结构。 71 | 72 | **例3** 73 | 74 | pkg player 75 | { 76 | def login 77 | { 78 | } 79 | } 80 | 81 | 包和定义都有一个可选的结构称为“编号”,编号通常是为了区别数据包的类型,你可以根据需要来决定是否使用编号,例4演示了如何声明一个有编号的包,里面有一个有编号的定义。 82 | 83 | **例4** 84 | 85 | pkg player = 1 86 | { 87 | def login = 1 88 | { 89 | } 90 | } 91 | 92 | 编号的一个典型应用场景是:为每个数据包都预留两个字节的头部信息,一个字节存储包编号,一个字节存储定义编号,通过这两个字节可以唯一确定数据包的类型,以便调用对应的解包函数对数据包进行解包。 93 | 94 | 一个定义中可以包含多个数据项,这些数据项称为“字段”,每个字段都有各自的名称和类型,名称和类型之间用冒号分隔,字段和字段之间用逗号分隔,例5演示了如何定一个有两个字符串字段的登录接口。 95 | 96 | **例5** 97 | 98 | pkg player = 1 99 | { 100 | def login = 1 101 | { 102 | username : string, 103 | password : string 104 | } 105 | } 106 | 107 | 定义也可以不包含任何字段,没有字段的定义称为“空定义”。 108 | 109 | BinSpec支持一些基本的数据类型,包括:有符号整型、无符号整型、字符串、列表、枚举。 110 | 111 | 有符号整型按占用字节数不同可细分为: 112 | 113 | 1. int8, byte - 114 | 2. int16, short - 115 | 3. int32, int - 116 | 4. int64, long - 117 | 118 | 无符号整型按占用字节数不同可细分为: 119 | 120 | 1. uint8, ubyte - 121 | 2. uint16, ushort - 122 | 3. uint32, uint - 123 | 4. uint64, ulong - 124 | 125 | BinSpec中的字符串以固定长度的头部开始,头信息存放的是字符串的**字节长度**,读取字符串时先读取长度信息,接着顺序读取指定长度的字符串内容,空字符串长度为0。 126 | 127 | 字符串按头部占字节数可细分为: 128 | 129 | 1. string8, string - 130 | 2. string16 - 131 | 3. string32 - 132 | 4. string64 - 133 | 134 | BinSpec中的列表和字符串类似以固定长度的头部开始,不同之处在于列表的头部存放的时列表的**元素个数**,读取列表时先读取个数信息N,接着按元素的结构定义解析N次,空列表长度为0。 135 | 136 | 1. list8, list - 137 | 2. list16 - 138 | 3. list32 - 139 | 4. list64 - 140 | 141 | 列表类型的字段后面必须紧跟列表元素的结构定义,例6演示了如何定义一个列表。 142 | 143 | **例6** 144 | 145 | pkg friend = 3 146 | { 147 | def get_friend_list_result = 1 148 | { 149 | id : int, 150 | friends : list { 151 | id : int, 152 | name : string 153 | } 154 | } 155 | } 156 | 157 | BinSpec中的枚举使用enum关键字声明,枚举类型跟定义一样必须在包当中声明,在字段中指定枚举类型时使用enum语法,例7演示了如何声明一个枚举类型。 158 | 159 | **例7** 160 | 161 | pkg item = 4 162 | { 163 | enum colors 164 | { 165 | RED = 1, GREEN = 2, BULE = 3 166 | } 167 | 168 | def get_item_by_color = 1 169 | { 170 | color : enum 171 | } 172 | } 173 | 174 | 枚举有一个可选的语法结构叫做继承,枚举根据继承的类型不同占用的字节数也不同,枚举默认是继承byte类型,枚举只能继承整型类型,例8演示了枚举如何显式继承。 175 | 176 | **例8** 177 | 178 | pkg item = 4 179 | { 180 | enum colors : int 181 | { 182 | RED = 1, GREEN = 2, BULE = 3 183 | } 184 | 185 | def get_item_by_color = 1 186 | { 187 | color : enum 188 | } 189 | } 190 | 191 | 上面我们介绍了BinSpec的语法和基本数据类型,下面我们介绍BinSpec的高级语法。 192 | 193 | 在实际项目中经常会遇到一种情况,例如游戏中有三个接口,一个接口用于获取玩家背包中的物品列表,一个接口用于获取NPC出售的物品列表,一个接口用于获取仓库中的物品列表,这三个接口有细微区别但是又有公共的部分,它们都返回物品列表,在基础物品信息上他们是一致的,但是商店的接口还会包含物品的价格。这种情况下,我们可以在三个接口中重复定义物品信息的字段,但是更有效的做法是使用BinSpec提供的**自定义类型**语法,自定义类型语法允许我们使用基本类型构建复杂类型,并在不同的地方引用。例9演示了如何用自定义类型解决上述问题。 194 | 195 | **例9** 196 | 197 | pkg item = 4 198 | { 199 | type item_info 200 | { 201 | id : int, 202 | name : string 203 | } 204 | 205 | def get_player_item_result = 1 206 | { 207 | items : list { 208 | item : type 209 | } 210 | } 211 | 212 | def get_npc_item_result = 2 213 | { 214 | items : list { 215 | item : type, 216 | price : int 217 | } 218 | } 219 | 220 | def get_warehouse_item_result = 3 221 | { 222 | items : list { 223 | item : type 224 | } 225 | } 226 | } 227 | 228 | 229 | BinSpec模版编写 230 | =============== 231 | 232 | **未完待续** 233 | -------------------------------------------------------------------------------- /include/bs_ast.h: -------------------------------------------------------------------------------- 1 | #ifndef _BS_AST_H_ 2 | #define _BS_AST_H_ 3 | 4 | #include "gen_list.h" 5 | 6 | typedef enum bs_type_enum bs_type_enum; 7 | 8 | enum bs_type_enum { 9 | /* integer */ 10 | BS_TI8 = 130, BS_TI16, BS_TI32, BS_TI64, 11 | 12 | /* unsigned integer */ 13 | BS_TU8, BS_TU16, BS_TU32, BS_TU64, 14 | 15 | /* string */ 16 | BS_TS8, BS_TS16, BS_TS32, BS_TS64, 17 | 18 | /* list */ 19 | BS_TL8, BS_TL16, BS_TL32, BS_TL64, 20 | 21 | /* enum */ 22 | BS_TE, 23 | 24 | /* type */ 25 | BS_TY 26 | }; 27 | 28 | typedef struct bs_doc bs_doc; 29 | typedef struct bs_pkg bs_pkg; 30 | typedef struct bs_def bs_def; 31 | typedef struct bs_col bs_col; 32 | typedef struct bs_enum bs_enum; 33 | typedef struct bs_enum_item bs_enum_item; 34 | typedef struct bs_type bs_type; 35 | 36 | LIST_DEF(bs_pkg_list, bs_pkg *); 37 | LIST_DEF(bs_def_list, bs_def *); 38 | LIST_DEF(bs_col_list, bs_col *); 39 | LIST_DEF(bs_enum_list, bs_enum *); 40 | LIST_DEF(bs_enum_item_list, bs_enum_item *); 41 | LIST_DEF(bs_type_list, bs_type *); 42 | 43 | struct bs_doc { 44 | bs_pkg *root_pkg; 45 | }; 46 | 47 | struct bs_pkg { 48 | bs_doc *doc; 49 | bs_pkg *pkg; 50 | 51 | int id; 52 | char *name; 53 | size_t name_len; 54 | 55 | bs_pkg_list *pkgs; 56 | bs_def_list *defs; 57 | bs_enum_list *enums; 58 | bs_type_list *types; 59 | }; 60 | 61 | struct bs_def { 62 | bs_doc *doc; 63 | bs_pkg *pkg; 64 | 65 | int id; 66 | char *name; 67 | size_t name_len; 68 | 69 | bs_col_list *cols; 70 | }; 71 | 72 | struct bs_col { 73 | bs_doc *doc; 74 | bs_def *def; 75 | 76 | char *name; 77 | size_t name_len; 78 | 79 | char *ref_name; 80 | size_t ref_name_len; 81 | 82 | bs_type_enum type; 83 | 84 | bs_col_list *cols; 85 | }; 86 | 87 | struct bs_enum { 88 | char *name; 89 | size_t name_len; 90 | bs_type_enum type; 91 | 92 | bs_enum_item_list *items; 93 | }; 94 | 95 | struct bs_enum_item { 96 | char *name; 97 | size_t name_len; 98 | long value; 99 | }; 100 | 101 | struct bs_type { 102 | bs_pkg *pkg; 103 | 104 | char *name; 105 | size_t name_len; 106 | 107 | bs_col_list *cols; 108 | }; 109 | 110 | bs_doc *bs_doc_new(); 111 | 112 | void bs_doc_free(bs_doc *doc); 113 | 114 | bs_pkg *bs_pkg_new(bs_doc *doc, bs_pkg *pkg, int id, const char *name, size_t name_len); 115 | 116 | void bs_pkg_free(bs_pkg *pkg); 117 | 118 | bs_def *bs_def_new(bs_pkg *pkg, int id, const char *name, size_t name_len); 119 | 120 | void bs_def_free(bs_def *def); 121 | 122 | bs_col *bs_col_new(const char *name, size_t name_len, const char *ref_name, size_t ref_name_len, bs_type_enum type); 123 | 124 | void bs_col_free(bs_col *col); 125 | 126 | bs_enum *bs_enum_new(bs_pkg *pkg, const char *name, size_t name_len, bs_type_enum type); 127 | 128 | void bs_enum_free(bs_enum *em); 129 | 130 | bs_enum_item *bs_enum_item_new(const char *name, size_t name_len, long value); 131 | 132 | void bs_enum_item_free(bs_enum_item *item); 133 | 134 | bs_type *bs_type_new(bs_pkg *pkg, const char *name, size_t name_len); 135 | 136 | void bs_type_free(bs_type *type); 137 | 138 | #endif 139 | 140 | -------------------------------------------------------------------------------- /include/bs_parse.h: -------------------------------------------------------------------------------- 1 | #ifndef _BS_PARSE_H_ 2 | #define _BS_PARSE_H_ 3 | 4 | #include "bs_ast.h" 5 | 6 | typedef enum { 7 | BS_ERR_UNKNOW = 1, 8 | BS_ERR_MISS_ID = 2, 9 | BS_ERR_BAD_TYPE = 3, 10 | BS_ERR_MISS_NAME = 4, 11 | BS_ERR_MISS_ENUM_VALUE = 5, 12 | BS_ERR_MISS_EQUI = '=', 13 | BS_ERR_MISS_SEMI = ';', 14 | BS_ERR_MISS_COLON = ':', 15 | BS_ERR_MISS_COMMA = ',', 16 | BS_ERR_MISS_OBRACE = '{', 17 | BS_ERR_MISS_CBRACE = '}', 18 | BS_ERR_MISS_LT = '<', 19 | BS_ERR_MISS_GT = '>' 20 | } bs_parse_error; 21 | 22 | typedef struct { 23 | bs_parse_error error; 24 | int line; 25 | int column; 26 | 27 | bs_doc *doc; 28 | } bs_parse_result; 29 | 30 | bs_parse_result *bs_parse(char *code, size_t code_len); 31 | 32 | void bs_parse_result_free(bs_parse_result *r); 33 | 34 | char *bs_get_error_msg(bs_parse_error error); 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /include/bs_php.h: -------------------------------------------------------------------------------- 1 | #ifndef _BS_PHP_H 2 | #define _BS_PHP_H 3 | 4 | #include "bs_ast.h" 5 | 6 | void bs_gen_php(bs_doc *doc, FILE *file); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /include/bs_print.h: -------------------------------------------------------------------------------- 1 | #ifndef _BS_PRINT_H_ 2 | #define _BS_PRINT_H_ 3 | 4 | #include "bs_ast.h" 5 | 6 | void bs_print_doc(bs_doc *doc); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /include/gen_list.h: -------------------------------------------------------------------------------- 1 | #ifndef _BSS_LIST_H_ 2 | #define _BSS_LIST_H_ 3 | 4 | #define LIST_DEF(LIST, T) \ 5 | typedef struct { \ 6 | T *items; \ 7 | int cap; \ 8 | int len; \ 9 | } LIST; \ 10 | \ 11 | LIST *LIST##_new(int cap); \ 12 | \ 13 | void LIST##_free(LIST *list); \ 14 | \ 15 | int LIST##_add(LIST *list, T item); \ 16 | \ 17 | void LIST##_del(LIST *list, int pos); \ 18 | \ 19 | T LIST##_get(LIST *list, int pos); \ 20 | 21 | 22 | #define LIST_IMP_(LIST, T, ALLOC, REALLOC, FREE, ERROR, FREE_ITEMS, TFREE) \ 23 | LIST *LIST##_new(int cap) { \ 24 | LIST *list = (LIST *)(ALLOC)(sizeof(LIST)); \ 25 | list->items = (T *)(ALLOC)(sizeof(T *) * cap); \ 26 | list->cap = cap; \ 27 | list->len = 0; \ 28 | return list; \ 29 | } \ 30 | \ 31 | void LIST##_free(LIST *list) { \ 32 | FREE_ITEMS \ 33 | (FREE)(list->items); \ 34 | (FREE)(list); \ 35 | } \ 36 | \ 37 | int LIST##_add(LIST *list, T item) { \ 38 | if (list->len == list->cap) { \ 39 | int new_cap = list->cap * 2; \ 40 | list->items = (T *)(REALLOC)(list->items, sizeof(T) * new_cap); \ 41 | if (list->items == NULL) { \ 42 | (ERROR)(#LIST" realloc failed!\n"); \ 43 | exit(1); \ 44 | } \ 45 | list->cap = new_cap; \ 46 | } \ 47 | int pos = list->len; \ 48 | list->items[pos] = item; \ 49 | list->len += 1; \ 50 | return pos; \ 51 | } \ 52 | \ 53 | void LIST##_del(LIST *list, int pos) { \ 54 | if (pos >= list->len) \ 55 | return; \ 56 | \ 57 | TFREE(list->items[pos]); \ 58 | \ 59 | int i; \ 60 | \ 61 | for (i = pos + 1; i < list->len; i ++) { \ 62 | list->items[i - 1] = list->items[i]; \ 63 | } \ 64 | \ 65 | list->len -= 1; \ 66 | } \ 67 | \ 68 | T LIST##_get(LIST *list, int pos) { \ 69 | if (pos >= list->len) \ 70 | return 0; \ 71 | \ 72 | return list->items[pos]; \ 73 | } \ 74 | 75 | 76 | #define LIST_FREE_ITEMS(TFREE) \ 77 | int i = 0; \ 78 | for(i = 0; i < list->len; i ++) { \ 79 | TFREE(list->items[i]); \ 80 | } \ 81 | 82 | 83 | #define LIST_IMP(LIST, T) \ 84 | LIST_IMP_( \ 85 | LIST, T, malloc, realloc, free, printf, , \ 86 | ) \ 87 | 88 | 89 | #define LIST_IMP_P(LIST, T, TFREE) \ 90 | LIST_IMP_( \ 91 | LIST, T, malloc, realloc, free, printf, \ 92 | LIST_FREE_ITEMS(TFREE), TFREE \ 93 | ) \ 94 | 95 | #endif 96 | -------------------------------------------------------------------------------- /src/bs_ast.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "bs_ast.h" 6 | #include "bs_parse.h" 7 | 8 | LIST_IMP_P(bs_pkg_list, bs_pkg *, bs_pkg_free); 9 | LIST_IMP_P(bs_def_list, bs_def *, bs_def_free); 10 | LIST_IMP_P(bs_col_list, bs_col *, bs_col_free); 11 | LIST_IMP_P(bs_enum_list, bs_enum *, bs_enum_free); 12 | LIST_IMP_P(bs_enum_item_list, bs_enum_item *, bs_enum_item_free); 13 | LIST_IMP_P(bs_type_list, bs_type *, bs_type_free); 14 | 15 | inline static char *bs_strcpy(const char *str, size_t str_len) { 16 | size_t str2_len = str_len + 1; 17 | char *str2 = (char *)malloc(str2_len); 18 | bzero(str2, str2_len); 19 | strncpy(str2, str, str_len); 20 | return str2; 21 | } 22 | 23 | bs_doc *bs_doc_new() { 24 | bs_doc *doc = (bs_doc *)malloc(sizeof(bs_doc)); 25 | 26 | doc->root_pkg = bs_pkg_new(doc, NULL, 0, NULL, 0); 27 | 28 | return doc; 29 | } 30 | 31 | void bs_doc_free(bs_doc *doc) { 32 | bs_pkg_free(doc->root_pkg); 33 | free(doc); 34 | } 35 | 36 | bs_pkg *bs_pkg_new(bs_doc *doc, bs_pkg *parent, int id, const char *name, size_t name_len) { 37 | bs_pkg *pkg = (bs_pkg *)malloc(sizeof(bs_pkg)); 38 | 39 | pkg->pkgs = bs_pkg_list_new(10); 40 | pkg->defs = bs_def_list_new(20); 41 | pkg->enums = bs_enum_list_new(5); 42 | pkg->types = bs_type_list_new(2); 43 | 44 | if (name != NULL) { 45 | pkg->name = bs_strcpy(name, name_len); 46 | pkg->name_len = name_len; 47 | } else { 48 | pkg->name = NULL; 49 | pkg->name_len = 0; 50 | } 51 | 52 | pkg->doc = doc; 53 | pkg->pkg = parent; 54 | pkg->id = id; 55 | 56 | return pkg; 57 | } 58 | 59 | void bs_pkg_free(bs_pkg *pkg) { 60 | bs_pkg_list_free(pkg->pkgs); 61 | bs_def_list_free(pkg->defs); 62 | bs_enum_list_free(pkg->enums); 63 | bs_type_list_free(pkg->types); 64 | 65 | free(pkg->name); 66 | free(pkg); 67 | } 68 | 69 | bs_def *bs_def_new(bs_pkg *pkg, int id, const char *name, size_t name_len) { 70 | bs_def *def = (bs_def *)malloc(sizeof(bs_def)); 71 | 72 | def->cols = bs_col_list_new(20); 73 | def->name = bs_strcpy(name, name_len); 74 | def->name_len = name_len; 75 | def->doc = pkg->doc; 76 | def->pkg = pkg; 77 | def->id = id; 78 | 79 | return def; 80 | } 81 | 82 | void bs_def_free(bs_def *def) { 83 | bs_col_list_free(def->cols); 84 | 85 | free(def->name); 86 | free(def); 87 | } 88 | 89 | bs_col *bs_col_new(const char *name, size_t name_len, const char *ref_name, size_t ref_name_len, bs_type_enum type) { 90 | bs_col *col = (bs_col *)malloc(sizeof(bs_col)); 91 | 92 | col->cols = bs_col_list_new(5); 93 | col->name = bs_strcpy(name, name_len); 94 | col->name_len = name_len; 95 | 96 | if (ref_name != NULL) { 97 | col->ref_name = bs_strcpy(ref_name, ref_name_len); 98 | col->ref_name_len = ref_name_len; 99 | } else { 100 | col->ref_name = NULL; 101 | col->ref_name_len = 0; 102 | } 103 | 104 | col->type = type; 105 | 106 | return col; 107 | } 108 | 109 | void bs_col_free(bs_col *col) { 110 | bs_col_list_free(col->cols); 111 | 112 | free(col->name); 113 | free(col->ref_name); 114 | free(col); 115 | } 116 | 117 | bs_enum *bs_enum_new(bs_pkg *pkg, const char *name, size_t name_len, bs_type_enum type) { 118 | bs_enum *em = (bs_enum *)malloc(sizeof(bs_enum)); 119 | 120 | em->items = bs_enum_item_list_new(5); 121 | em->name = bs_strcpy(name, name_len); 122 | em->name_len = name_len; 123 | em->type = type; 124 | 125 | return em; 126 | } 127 | 128 | void bs_enum_free(bs_enum *em) { 129 | bs_enum_item_list_free(em->items); 130 | 131 | free(em->name); 132 | free(em); 133 | } 134 | 135 | bs_enum_item *bs_enum_item_new(const char *name, size_t name_len, long value) { 136 | bs_enum_item *item = (bs_enum_item *)malloc(sizeof(bs_enum_item)); 137 | 138 | item->name = bs_strcpy(name, name_len); 139 | item->name_len = name_len; 140 | item->value = value; 141 | 142 | return item; 143 | } 144 | 145 | void bs_enum_item_free(bs_enum_item *item) { 146 | free(item->name); 147 | free(item); 148 | } 149 | 150 | bs_type *bs_type_new(bs_pkg *pkg, const char *name, size_t name_len) { 151 | bs_type *type = (bs_type *)malloc(sizeof(bs_type)); 152 | 153 | type->pkg = pkg; 154 | type->cols = bs_col_list_new(5); 155 | type->name = bs_strcpy(name, name_len); 156 | type->name_len = name_len; 157 | 158 | return type; 159 | } 160 | 161 | void bs_type_free(bs_type *type) { 162 | bs_col_list_free(type->cols); 163 | free(type->name); 164 | free(type); 165 | } 166 | -------------------------------------------------------------------------------- /src/bs_parse.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "bs_parse.h" 8 | 9 | #define KEYWORD_INDEX_SIZE 26 10 | #define KEYWORD_CHUNK_SIZE 8 11 | 12 | typedef enum { 13 | TK_UNKNOW = 0, 14 | TK_EOF, 15 | 16 | TK_NAME, // [a-zA-Z_][a-zA-Z0-9_]* 17 | TK_FULL, // [a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z][a-zA-Z0-9_]*)* 18 | TK_NUM, // [0-9]+ 19 | TK_PKG, // pkg 20 | TK_DEF, // def 21 | TK_ENUM, // enum 22 | TK_TYPE, // type 23 | TK_EQUAL = '=', 24 | TK_COLON = ':', 25 | TK_SEMI = ';', 26 | TK_COMMA = ',', 27 | TK_OBRACE = '{', 28 | TK_CBRACE = '}', 29 | TK_LT = '<', 30 | TK_GT = '>', 31 | 32 | // int[8|16|32|64] 33 | TK_I8 = 130, TK_I16, TK_I32, TK_I64, 34 | 35 | // uint[8|16|32|64] 36 | TK_U8, TK_U16, TK_U32, TK_U64, 37 | 38 | // str[8|16|32|64] 39 | TK_S8, TK_S16, TK_S32, TK_S64, 40 | 41 | // list[8|16|32|64] 42 | TK_L8, TK_L16, TK_L32, TK_L64, 43 | 44 | // enum 45 | TK_EM, 46 | 47 | // type 48 | TK_TY, 49 | } token_type; 50 | 51 | typedef struct { 52 | token_type type; 53 | char* str; 54 | size_t len; 55 | } token; 56 | 57 | typedef struct { 58 | char *cur; 59 | char *end; 60 | int error; 61 | int line; 62 | int column; 63 | } context; 64 | 65 | typedef struct { 66 | char *str; 67 | size_t len; 68 | token_type type; 69 | } keyword; 70 | 71 | typedef struct { 72 | char c; 73 | keyword chunks[KEYWORD_CHUNK_SIZE]; 74 | } keyword_tree; 75 | 76 | static keyword_tree keywords[KEYWORD_INDEX_SIZE] = { 77 | {'a', {}}, 78 | {'b', { 79 | {"byte", 4, TK_I8} 80 | }}, 81 | {'c', {}}, 82 | {'d', { 83 | {"def", 3, TK_DEF}, 84 | }}, 85 | {'e', { 86 | {"enum", 4, TK_ENUM}, 87 | }}, 88 | {'f', {}}, 89 | {'g', {}}, 90 | {'h', {}}, 91 | {'i', { 92 | {"int", 3, TK_I32}, 93 | {"int8", 4, TK_I8}, 94 | {"int16", 5, TK_I16}, 95 | {"int32", 5, TK_I32}, 96 | {"int64", 5, TK_I64}, 97 | }}, 98 | {'j', {}}, 99 | {'k', {}}, 100 | {'l', { 101 | {"long", 4, TK_I64}, 102 | {"list", 4, TK_L8}, 103 | {"list8", 5, TK_L8}, 104 | {"list16", 6, TK_L16}, 105 | {"list32", 6, TK_L32}, 106 | {"list64", 6, TK_L64} 107 | }}, 108 | {'m', {}}, 109 | {'n', {}}, 110 | {'o', {}}, 111 | {'p', { 112 | {"pkg", 3, TK_PKG}, 113 | }}, 114 | {'q', {}}, 115 | {'r', {}}, 116 | {'s', { 117 | {"short", 5, TK_I16}, 118 | {"string", 6, TK_S8}, 119 | {"string8", 7, TK_S8}, 120 | {"string16", 8, TK_S16}, 121 | {"string32", 8, TK_S32}, 122 | {"string64", 8, TK_S64}, 123 | }}, 124 | {'t', { 125 | {"type", 4, TK_TYPE} 126 | }}, 127 | {'u', { 128 | {"uint", 4, TK_U32}, 129 | {"uint8", 5, TK_U8}, 130 | {"uint16", 6, TK_U16}, 131 | {"uint32", 6, TK_U32}, 132 | {"uint64", 6, TK_U64}, 133 | {"ubyte", 5, TK_U8}, 134 | {"ushort", 6, TK_U16}, 135 | {"ulong", 5, TK_U64} 136 | }}, 137 | {'v', {}}, 138 | {'w', {}}, 139 | {'x', {}}, 140 | {'y', {}}, 141 | {'z', {}} 142 | }; 143 | 144 | #define NEXT_CHAR() { ctx->column ++; ctx->cur ++; c = *(ctx->cur); } 145 | 146 | #define LOOKUP(tk, err) \ 147 | t = next_token(ctx); \ 148 | if (tk != t.type) { \ 149 | ctx->error = err; \ 150 | return; \ 151 | } \ 152 | 153 | #define LOOKUP2(tk, err) \ 154 | if (tk != t.type) { \ 155 | ctx->error = err; \ 156 | return; \ 157 | } \ 158 | 159 | inline static int parse_int(char *str, size_t len) { 160 | int val = 0; 161 | int i; 162 | for (i = 0; i < len; i ++) { 163 | val += (str[i] - '0') * pow(10, len - i - 1); 164 | } 165 | return val; 166 | } 167 | 168 | inline static token_type match_keyword(char *code, size_t len) { 169 | int i = code[0] - 'a'; 170 | 171 | if (i < 0 || i >= KEYWORD_INDEX_SIZE) 172 | return TK_UNKNOW; 173 | 174 | keyword *chunk = keywords[i].chunks; 175 | 176 | int j; 177 | 178 | for (j = 0; j < KEYWORD_CHUNK_SIZE; j ++) { 179 | if (chunk[j].len != len) 180 | continue; 181 | 182 | if (0 == strncmp(code, chunk[j].str, len)) 183 | return chunk[j].type; 184 | } 185 | 186 | return TK_NAME; 187 | } 188 | 189 | inline static void ignore_wac(context *ctx) { 190 | char c = *(ctx->cur); 191 | 192 | while (ctx->cur != ctx->end) { 193 | if (' ' == c || '\t' == c) { 194 | NEXT_CHAR(); 195 | continue; 196 | } else if ('\r' == c || '\n' == c) { 197 | char t = c; 198 | ctx->line ++; 199 | ctx->column = 1; 200 | 201 | NEXT_CHAR(); 202 | 203 | if ('\r' == t && '\n' == c && ctx->cur != ctx->end) 204 | NEXT_CHAR(); 205 | 206 | continue; 207 | } else if (ctx->cur != ctx->end - 1 && '/' == c && '/' == *(ctx->cur + 1)) { 208 | NEXT_CHAR(); 209 | 210 | while (ctx->cur != ctx->end) { 211 | NEXT_CHAR(); 212 | 213 | if ('\r' == c || '\n' == c) 214 | break; 215 | } 216 | 217 | continue; 218 | } 219 | 220 | break; 221 | } 222 | } 223 | 224 | inline static token next_token(context *ctx) { 225 | ignore_wac(ctx); 226 | 227 | token t = {TK_UNKNOW, ctx->cur, 1}; 228 | 229 | /* end of file */ 230 | if (ctx->cur == ctx->end) { 231 | t.type = TK_EOF; 232 | return t; 233 | } 234 | 235 | char c = *(ctx->cur); 236 | 237 | /* operators */ 238 | switch (c) { 239 | case '=' : t.type = '='; ctx->cur ++; return t; 240 | case ':' : t.type = ':'; ctx->cur ++; return t; 241 | case ',' : t.type = ','; ctx->cur ++; return t; 242 | case '{' : t.type = '{'; ctx->cur ++; return t; 243 | case '}' : t.type = '}'; ctx->cur ++; return t; 244 | case ';' : t.type = ';'; ctx->cur ++; return t; 245 | case '<' : t.type = '<'; ctx->cur ++; return t; 246 | case '>' : t.type = '>'; ctx->cur ++; return t; 247 | } 248 | 249 | /* numeric */ 250 | if (isdigit(c)) { 251 | NEXT_CHAR(); 252 | while (ctx->cur != ctx->end && isdigit(c)) { 253 | NEXT_CHAR(); 254 | t.len ++; 255 | } 256 | t.type = TK_NUM; 257 | return t; 258 | } 259 | 260 | /* name */ 261 | NAME: 262 | if (isalpha(c) || '_' == c) { 263 | NEXT_CHAR(); 264 | while (ctx->cur != ctx->end && 265 | (isalpha(c) || isdigit(c) || '_' == c)) { 266 | NEXT_CHAR(); 267 | t.len ++; 268 | } 269 | 270 | if (ctx->cur != ctx->end && '.' == c) { 271 | NEXT_CHAR(); 272 | t.len ++; 273 | 274 | if (ctx->cur != ctx->end) { 275 | t.type = TK_FULL; 276 | goto NAME; 277 | } 278 | } else if (TK_FULL != t.type) { 279 | t.type = match_keyword(t.str, t.len); 280 | } 281 | 282 | return t; 283 | } 284 | 285 | return t; 286 | } 287 | 288 | static void parse_cols(context *ctx, bs_col_list *list) { 289 | token t = next_token(ctx); 290 | 291 | while (TK_EOF != t.type && '}' != t.type) { 292 | LOOKUP2(TK_NAME, BS_ERR_MISS_NAME); 293 | 294 | char *name = t.str; 295 | size_t name_len = t.len; 296 | 297 | char *ref_name = NULL; 298 | size_t ref_name_len = 0; 299 | 300 | bs_type_enum type = t.type; 301 | 302 | LOOKUP(':', ':'); 303 | 304 | t = next_token(ctx); 305 | 306 | if (TK_ENUM == t.type || TK_TYPE == t.type) { 307 | type = t.type == TK_ENUM ? TK_EM : TK_TY; 308 | 309 | LOOKUP('<', '<'); 310 | 311 | t = next_token(ctx); 312 | 313 | if (TK_NAME != t.type && TK_FULL != t.type) { 314 | ctx->error = BS_ERR_MISS_NAME; 315 | return; 316 | } 317 | 318 | ref_name = t.str; 319 | ref_name_len = t.len; 320 | 321 | LOOKUP('>', '>'); 322 | } else if (TK_I8 > t.type) { 323 | ctx->error = BS_ERR_BAD_TYPE; 324 | return; 325 | } else { 326 | type = t.type; 327 | } 328 | 329 | bs_col *col = bs_col_new(name, name_len, ref_name, ref_name_len, type); 330 | bs_col_list_add(list, col); 331 | 332 | if (TK_L8 <= t.type && TK_L64 >= t.type) { 333 | LOOKUP('{', '{'); 334 | 335 | parse_cols(ctx, col->cols); 336 | 337 | if (ctx->error != 0) 338 | return; 339 | } 340 | 341 | if (',' == (t = next_token(ctx)).type) 342 | t = next_token(ctx); 343 | else { 344 | LOOKUP2('}', '}'); 345 | break; 346 | } 347 | } 348 | } 349 | 350 | static void parse_def(context *ctx, bs_pkg *pkg) { 351 | token t; 352 | 353 | LOOKUP(TK_NAME, BS_ERR_MISS_NAME); 354 | 355 | char *name = t.str; 356 | size_t name_len = t.len; 357 | int id = 0; 358 | 359 | t = next_token(ctx); 360 | 361 | if ('=' == t.type) { 362 | LOOKUP(TK_NUM, BS_ERR_MISS_ID); 363 | id = parse_int(t.str, t.len); 364 | t = next_token(ctx); 365 | } 366 | 367 | LOOKUP2('{', '{'); 368 | 369 | bs_def *def = bs_def_new(pkg, id, name, name_len + 0); 370 | bs_def_list_add(pkg->defs, def); 371 | 372 | parse_cols(ctx, def->cols); 373 | } 374 | 375 | static void parse_enum(context *ctx, bs_pkg *pkg) { 376 | token t; 377 | 378 | LOOKUP(TK_NAME, BS_ERR_MISS_NAME); 379 | 380 | char *name = t.str; 381 | size_t name_len = t.len; 382 | 383 | bs_enum *em = bs_enum_new(pkg, name, name_len, BS_TI8); 384 | bs_enum_list_add(pkg->enums, em); 385 | 386 | t = next_token(ctx); 387 | 388 | if (':' == t.type) { 389 | t = next_token(ctx); 390 | if (TK_I8 > t.type || TK_U64 < t.type) { 391 | ctx->error = BS_ERR_BAD_TYPE; 392 | return; 393 | } 394 | em->type = t.type; 395 | t = next_token(ctx); 396 | } 397 | 398 | LOOKUP2('{', '{'); 399 | 400 | t = next_token(ctx); 401 | 402 | while (TK_EOF != t.type && '}' != t.type) { 403 | LOOKUP2(TK_NAME, BS_ERR_MISS_NAME); 404 | 405 | name = t.str; 406 | name_len = t.len; 407 | 408 | LOOKUP('=', '='); 409 | LOOKUP(TK_NUM, BS_ERR_MISS_ENUM_VALUE); 410 | 411 | long value = parse_int(t.str, t.len); 412 | 413 | bs_enum_item *item = bs_enum_item_new(name, name_len, value); 414 | bs_enum_item_list_add(em->items, item); 415 | 416 | if (',' == (t = next_token(ctx)).type) 417 | t = next_token(ctx); 418 | else { 419 | LOOKUP2('}', '}'); 420 | break; 421 | } 422 | } 423 | } 424 | 425 | static void parse_type(context *ctx, bs_pkg *pkg) { 426 | token t; 427 | 428 | LOOKUP(TK_NAME, BS_ERR_MISS_NAME); 429 | 430 | char *name = t.str; 431 | size_t name_len = t.len; 432 | 433 | bs_type *type = bs_type_new(pkg, name, name_len); 434 | bs_type_list_add(pkg->types, type); 435 | 436 | LOOKUP('{', '{'); 437 | 438 | parse_cols(ctx, type->cols); 439 | } 440 | 441 | static void parse_pkg(context *ctx, bs_doc *doc, bs_pkg *parent) { 442 | token t; 443 | 444 | LOOKUP(TK_NAME, BS_ERR_MISS_NAME); 445 | 446 | char *name = t.str; 447 | size_t name_len = t.len; 448 | int id = 0; 449 | 450 | t = next_token(ctx); 451 | 452 | if ('=' == t.type) { 453 | LOOKUP(TK_NUM, BS_ERR_MISS_ID); 454 | id = parse_int(t.str, t.len); 455 | t = next_token(ctx); 456 | } 457 | 458 | LOOKUP2('{', '{'); 459 | 460 | bs_pkg *pkg = bs_pkg_new(doc, parent, id, name, name_len); 461 | bs_pkg_list_add(parent->pkgs, pkg); 462 | 463 | while (TK_EOF != (t = next_token(ctx)).type && '}' != t.type) { 464 | switch (t.type) { 465 | case TK_DEF: parse_def(ctx, pkg); break; 466 | case TK_PKG: parse_pkg(ctx, doc, pkg); break; 467 | case TK_ENUM: parse_enum(ctx, pkg); break; 468 | case TK_TYPE: parse_type(ctx, pkg); break; 469 | default: ctx->error = BS_ERR_UNKNOW; break; 470 | } 471 | 472 | if (ctx->error != 0) 473 | return; 474 | } 475 | 476 | LOOKUP2('}', '}'); 477 | } 478 | 479 | bs_parse_result *bs_parse(char *code, size_t code_len) { 480 | token t; 481 | bs_doc *doc = bs_doc_new(); 482 | context ctx = {code, code + code_len, 0, 1, 0}; 483 | 484 | while (TK_EOF != (t = next_token(&ctx)).type && ctx.error == 0) { 485 | if (TK_PKG == t.type) { 486 | parse_pkg(&ctx, doc, doc->root_pkg); 487 | } else { 488 | ctx.error = BS_ERR_UNKNOW; 489 | } 490 | } 491 | 492 | bs_parse_result *result = (bs_parse_result *)malloc(sizeof(bs_parse_result)); 493 | 494 | if (ctx.error == 0) { 495 | result->doc = doc; 496 | result->error = 0; 497 | result->line = 0; 498 | result->column = 0; 499 | } else { 500 | result->doc = NULL; 501 | result->error = ctx.error; 502 | result->line = ctx.line; 503 | result->column = ctx.column; 504 | 505 | bs_doc_free(doc); 506 | } 507 | 508 | return result; 509 | } 510 | 511 | void bs_parse_result_free(bs_parse_result *r) { 512 | if (r->doc != NULL) 513 | bs_doc_free(r->doc); 514 | 515 | free(r); 516 | } 517 | 518 | char *bs_get_error_msg(bs_parse_error error) { 519 | switch (error) { 520 | case BS_ERR_UNKNOW: return "unknow token"; 521 | case BS_ERR_MISS_ID: return "missing id"; 522 | case BS_ERR_BAD_TYPE: return "bad type"; 523 | case BS_ERR_MISS_NAME: return "missing name"; 524 | case BS_ERR_MISS_ENUM_VALUE: return "missing enum value"; 525 | case BS_ERR_MISS_EQUI: return "missing '='"; 526 | case BS_ERR_MISS_SEMI: return "missing ';'"; 527 | case BS_ERR_MISS_COLON: return "missing ':'"; 528 | case BS_ERR_MISS_COMMA: return "missing ','"; 529 | case BS_ERR_MISS_OBRACE: return "missing '{'"; 530 | case BS_ERR_MISS_CBRACE: return "missing '}'"; 531 | case BS_ERR_MISS_LT: return "missing '<'"; 532 | case BS_ERR_MISS_GT: return "missing '>'"; 533 | } 534 | return ""; 535 | } 536 | -------------------------------------------------------------------------------- /src/bs_php.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "bs_php.h" 4 | 5 | static void gen_pkg_list(FILE *file, bs_pkg_list *pkgs, int level); 6 | static void gen_def_list(FILE *file, bs_def_list *defs, int level); 7 | static void gen_col_list(FILE *file, bs_col_list *cols, int level); 8 | static void gen_enum_list(FILE *file, bs_enum_list *pkgs, int level); 9 | static void gen_enum_item_list(FILE *file, bs_enum_item_list *items, int level); 10 | static void gen_type_list(FILE *file, bs_type_list *types, int level); 11 | static char *get_php_type(bs_type_enum type); 12 | 13 | void bs_gen_php(bs_doc *doc, FILE *file) { 14 | fprintf(file, 15 | "pkgs = $pkgs;\n" 41 | " $this->enums = $enums;\n" 42 | " }\n" 43 | " }\n\n" 44 | " class bs_pkg {\n" 45 | " public $id;\n" 46 | " public $name;\n" 47 | " public $enums;\n" 48 | " public $types;\n" 49 | " public $pkgs;\n" 50 | " public $defs;\n\n" 51 | " function __construct($id, $name, $enums, $pkgs, $types, $defs) {\n" 52 | " $this->id = $id;\n" 53 | " $this->name = $name;\n" 54 | " $this->enums = $enums;\n" 55 | " $this->types = $types;\n" 56 | " $this->pkgs = $pkgs;\n" 57 | " $this->defs = $defs;\n" 58 | " }\n" 59 | " }\n\n" 60 | " class bs_def {\n" 61 | " public $id;\n" 62 | " public $name;\n" 63 | " public $cols;\n\n" 64 | " function __construct($id, $name, $cols) {\n" 65 | " $this->id = $id;\n" 66 | " $this->name = $name;\n" 67 | " $this->cols = $cols;\n" 68 | " }\n" 69 | " }\n\n" 70 | " class bs_col {\n" 71 | " public $name;\n" 72 | " public $type;\n" 73 | " public $ref_name;\n" 74 | " public $cols;\n\n" 75 | " function __construct($name, $type, $ref_name, $cols) {\n" 76 | " $this->name = $name;\n" 77 | " $this->type = $type;\n" 78 | " $this->ref_name = $ref_name;\n" 79 | " $this->cols = $cols;\n" 80 | " }\n" 81 | " }\n\n" 82 | " class bs_enum {\n" 83 | " public $name;\n" 84 | " public $type;\n" 85 | " public $items;\n\n" 86 | " function __construct($name, $type, $items) {\n" 87 | " $this->name = $name;\n" 88 | " $this->type = $type;\n" 89 | " $this->items = $items;\n" 90 | " }\n" 91 | " }\n\n" 92 | " class bs_enum_item {\n" 93 | " public $name;\n" 94 | " public $value;\n\n" 95 | " function __construct($name, $value) {\n" 96 | " $this->name = $name;\n" 97 | " $this->value = $value;\n" 98 | " }\n" 99 | " }\n\n" 100 | " class bs_type {\n" 101 | " public $name;\n" 102 | " public $cols;\n\n" 103 | " function __construct($name, $cols) {\n" 104 | " $this->name = $name;\n" 105 | " $this->cols = $cols;\n" 106 | " }\n" 107 | " }\n\n" 108 | " function bs_get_doc() {\n" 109 | " return new bs_doc(array(\n", 110 | BS_TI8, BS_TI16, BS_TI32, BS_TI64, 111 | BS_TU8, BS_TU16, BS_TU32, BS_TU64, 112 | BS_TL8, BS_TL16, BS_TL32, BS_TL64, 113 | BS_TS8, BS_TS16, BS_TS32, BS_TS64, 114 | BS_TE, BS_TY 115 | ); 116 | 117 | gen_pkg_list(file, doc->root_pkg->pkgs, 0); 118 | 119 | fprintf(file, 120 | " ));\n" 121 | " }\n" 122 | "?>\n" 123 | ); 124 | } 125 | 126 | #define TAB() { fprintf(file, " "); int i_; for (i_ = 0; i_ < level; i_ ++) fprintf(file, " "); } 127 | 128 | static void gen_pkg_list(FILE *file, bs_pkg_list *pkgs, int level) { 129 | int i; 130 | for (i = 0; i < pkgs->len; i++) { 131 | bs_pkg *pkg = bs_pkg_list_get(pkgs, i); 132 | 133 | TAB(); 134 | fprintf(file, "new bs_pkg(%d, '%s', array(", pkg->id, pkg->name); 135 | 136 | if (pkg->enums->len > 0) { 137 | fprintf(file, "\n"); 138 | gen_enum_list(file, pkg->enums, level + 1); 139 | TAB(); 140 | } 141 | 142 | fprintf(file, "), array("); 143 | 144 | if (pkg->types->len > 0) { 145 | fprintf(file, "\n"); 146 | gen_type_list(file, pkg->types, level + 1); 147 | TAB(); 148 | } 149 | 150 | fprintf(file, "), array("); 151 | 152 | if (pkg->pkgs->len > 0) { 153 | fprintf(file, "\n"); 154 | gen_pkg_list(file, pkg->pkgs, level + 1); 155 | TAB(); 156 | } 157 | 158 | fprintf(file, "), array("); 159 | 160 | if (pkg->defs->len > 0) { 161 | fprintf(file, "\n"); 162 | gen_def_list(file, pkg->defs, level + 1); 163 | TAB(); 164 | } 165 | 166 | fprintf(file, "))"); 167 | 168 | if (i != pkgs->len - 1) 169 | fprintf(file, ","); 170 | 171 | fprintf(file, "\n"); 172 | } 173 | } 174 | 175 | static void gen_enum_list(FILE *file, bs_enum_list *enums, int level) { 176 | int i; 177 | for (i = 0; i < enums->len; i ++) { 178 | bs_enum *em = bs_enum_list_get(enums, i); 179 | 180 | TAB(); 181 | fprintf(file, "new bs_enum('%s', %s, array(", em->name, get_php_type(em->type)); 182 | 183 | if (em->items->len > 0) { 184 | fprintf(file, "\n"); 185 | gen_enum_item_list(file, em->items, level + 1); 186 | TAB(); 187 | } 188 | 189 | fprintf(file, "))"); 190 | 191 | if (i != enums->len - 1) 192 | fprintf(file, ","); 193 | 194 | fprintf(file, "\n"); 195 | } 196 | } 197 | 198 | static void gen_enum_item_list(FILE *file, bs_enum_item_list *items, int level) { 199 | int i; 200 | for (i = 0; i < items->len; i ++) { 201 | bs_enum_item *item = bs_enum_item_list_get(items, i); 202 | 203 | TAB(); 204 | fprintf(file, "new bs_enum_item('%s', %ld)", item->name, item->value); 205 | 206 | if (i != items->len - 1) 207 | fprintf(file, ","); 208 | 209 | fprintf(file, "\n"); 210 | } 211 | } 212 | 213 | static void gen_type_list(FILE *file, bs_type_list *types, int level) { 214 | int i; 215 | for (i = 0; i < types->len; i++) { 216 | bs_type *type = bs_type_list_get(types, i); 217 | 218 | TAB(); 219 | fprintf(file, "new bs_type('%s', array(", type->name); 220 | 221 | if (type->cols->len > 0) { 222 | fprintf(file, "\n"); 223 | gen_col_list(file, type->cols, level + 1); 224 | TAB(); 225 | } 226 | 227 | fprintf(file, "))"); 228 | 229 | if (i != types->len - 1) 230 | fprintf(file, ","); 231 | 232 | fprintf(file, "\n"); 233 | } 234 | } 235 | 236 | static void gen_def_list(FILE *file, bs_def_list *defs, int level) { 237 | int i; 238 | for (i = 0; i < defs->len; i++) { 239 | bs_def *def = bs_def_list_get(defs, i); 240 | 241 | TAB(); 242 | fprintf(file, "new bs_def(%d, '%s', array(", def->id, def->name); 243 | 244 | if (def->cols->len > 0) { 245 | fprintf(file, "\n"); 246 | gen_col_list(file, def->cols, level + 1); 247 | TAB(); 248 | } 249 | 250 | fprintf(file, "))"); 251 | 252 | if (i != defs->len - 1) 253 | fprintf(file, ","); 254 | 255 | fprintf(file, "\n"); 256 | } 257 | } 258 | 259 | static void gen_col_list(FILE *file, bs_col_list *cols, int level) { 260 | int i; 261 | for (i = 0; i < cols->len; i++) { 262 | bs_col *col = bs_col_list_get(cols, i); 263 | 264 | TAB(); 265 | fprintf(file, "new bs_col('%s', %s, '%s', array(", 266 | col->name, 267 | get_php_type(col->type), 268 | col->ref_name == NULL ? "" : col->ref_name 269 | ); 270 | 271 | if (col->cols->len > 0) { 272 | fprintf(file, "\n"); 273 | gen_col_list(file, col->cols, level + 1); 274 | TAB(); 275 | } 276 | 277 | fprintf(file,"))"); 278 | 279 | if (i != cols->len - 1) 280 | fprintf(file, ","); 281 | 282 | fprintf(file, "\n"); 283 | } 284 | } 285 | 286 | char *get_php_type(bs_type_enum type) { 287 | switch (type) { 288 | case BS_TI8: return "bs_types::int8"; 289 | case BS_TI16: return "bs_type:int16"; 290 | case BS_TI32: return "bs_types::int32"; 291 | case BS_TI64: return "bs_types::int64"; 292 | case BS_TU8: return "bs_types::uint8"; 293 | case BS_TU16: return "bs_types::uint16"; 294 | case BS_TU32: return "bs_types::uint32"; 295 | case BS_TU64: return "bs_types::uint64"; 296 | case BS_TL8: return "bs_types::list8"; 297 | case BS_TL16: return "bs_types::list16"; 298 | case BS_TL32: return "bs_types::list32"; 299 | case BS_TL64: return "bs_types::list64"; 300 | case BS_TS8: return "bs_types::string8"; 301 | case BS_TS16: return "bs_types::string16"; 302 | case BS_TS32: return "bs_types::string32"; 303 | case BS_TS64: return "bs_types::string64"; 304 | case BS_TE: return "bs_types::enumtype"; 305 | case BS_TY: return "bs_types::usertype"; 306 | } 307 | return ""; 308 | } 309 | -------------------------------------------------------------------------------- /src/bs_print.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "bs_print.h" 5 | 6 | #define TAB(level) { int i_; for(i_ = 0; i_ < level; i_ ++) printf(" "); } 7 | 8 | static void bs_print_pkg_list(bs_pkg_list *, int); 9 | static void bs_print_enum_list(bs_enum_list *, int); 10 | static void bs_print_type_list(bs_type_list *, int); 11 | static void bs_print_def_list(bs_def_list *, int); 12 | static void bs_print_col_list(bs_col_list *, int); 13 | static void bs_print_type(bs_type_enum); 14 | 15 | void bs_print_doc(bs_doc *doc) { 16 | bs_print_pkg_list(doc->root_pkg->pkgs, 0); 17 | } 18 | 19 | static void bs_print_pkg_list(bs_pkg_list *list, int level) { 20 | int i; 21 | for (i = 0; i < list->len; i ++) { 22 | bs_pkg *pkg = bs_pkg_list_get(list, i); 23 | 24 | TAB(level); 25 | printf("pkg %s = %d\n", pkg->name, pkg->id); 26 | TAB(level); 27 | printf("{\n"); 28 | 29 | bs_print_enum_list(pkg->enums, level + 1); 30 | bs_print_type_list(pkg->types, level + 1); 31 | bs_print_pkg_list(pkg->pkgs, level + 1); 32 | bs_print_def_list(pkg->defs, level + 1); 33 | 34 | TAB(level); 35 | printf("}\n\n"); 36 | } 37 | } 38 | 39 | static void bs_print_type_list(bs_type_list *list, int level) { 40 | int i; 41 | for (i = 0; i < list->len; i ++) { 42 | bs_type *type = bs_type_list_get(list, i); 43 | 44 | TAB(level); 45 | printf("type %s\n", type->name); 46 | 47 | bs_print_col_list(type->cols, level + 1); 48 | 49 | printf("\n"); 50 | 51 | printf("\n"); 52 | } 53 | } 54 | 55 | static void bs_print_enum_list(bs_enum_list *list, int level) { 56 | int i; 57 | for (i = 0; i < list->len; i ++) { 58 | bs_enum *em = bs_enum_list_get(list, i); 59 | 60 | TAB(level); 61 | printf("enum %s : ", em->name); 62 | 63 | bs_print_type(em->type); 64 | 65 | printf("\n"); 66 | TAB(level); 67 | printf("{\n"); 68 | 69 | int max_len = 0; 70 | 71 | int j; 72 | for (j = 0; j < em->items->len; j ++) { 73 | bs_enum_item *item = bs_enum_item_list_get(em->items, j); 74 | max_len = max_len < item->name_len ? item->name_len : max_len; 75 | } 76 | 77 | for (j = 0; j < em->items->len; j ++) { 78 | bs_enum_item *item = bs_enum_item_list_get(em->items, j); 79 | 80 | TAB(level + 1); 81 | printf("%s", item->name); 82 | 83 | int k; 84 | for (k = 0; k <= max_len - item->name_len; k ++) { 85 | printf(" "); 86 | } 87 | 88 | printf("= %ld", item->value); 89 | 90 | if (j != em->items->len - 1) 91 | printf(","); 92 | 93 | printf("\n"); 94 | } 95 | 96 | TAB(level); 97 | printf("}\n\n"); 98 | } 99 | } 100 | 101 | static void bs_print_def_list(bs_def_list *list, int level) { 102 | int i; 103 | for (i = 0; i < list->len; i ++) { 104 | bs_def *def = bs_def_list_get(list, i); 105 | 106 | TAB(level); 107 | printf("def %s = %d\n", def->name, def->id); 108 | 109 | bs_print_col_list(def->cols, level + 1); 110 | 111 | printf("\n"); 112 | 113 | if (i != list->len - 1) 114 | printf("\n"); 115 | } 116 | } 117 | 118 | static void bs_print_col_list(bs_col_list *list, int level) { 119 | TAB(level - 1); 120 | printf("{\n"); 121 | 122 | int k; 123 | int max_len = 0; 124 | for (k = 0; k < list->len; k ++) { 125 | bs_col *col = bs_col_list_get(list, k); 126 | max_len = max_len < col->name_len ? col->name_len : max_len; 127 | } 128 | 129 | for (k = 0; k < list->len; k ++) { 130 | bs_col *col = bs_col_list_get(list, k); 131 | 132 | TAB(level); 133 | printf("%s", col->name); 134 | 135 | int i; 136 | for (i = 0; i <= max_len - col->name_len; i ++) { 137 | printf(" "); 138 | } 139 | 140 | printf(": "); 141 | 142 | bs_print_type(col->type); 143 | 144 | if (BS_TE == col->type || BS_TY == col->type) { 145 | printf("<%s>", col->ref_name); 146 | } else if (BS_TL8 <= col->type && col->type <= BS_TL64) { 147 | printf("\n"); 148 | bs_print_col_list(col->cols, level + 1); 149 | } 150 | 151 | if (k != list->len - 1) 152 | printf(","); 153 | 154 | printf("\n"); 155 | } 156 | 157 | TAB(level - 1); 158 | printf("}"); 159 | } 160 | 161 | static void bs_print_type(bs_type_enum type) { 162 | switch (type) { 163 | case BS_TI8: printf("int8"); break; 164 | case BS_TI16: printf("int16"); break; 165 | case BS_TI32: printf("int32"); break; 166 | case BS_TI64: printf("int64"); break; 167 | 168 | case BS_TU8: printf("uint8"); break; 169 | case BS_TU16: printf("uint16"); break; 170 | case BS_TU32: printf("uint32"); break; 171 | case BS_TU64: printf("uint64"); break; 172 | 173 | case BS_TE: printf("enum"); break; 174 | 175 | case BS_TY: printf("type"); break; 176 | 177 | case BS_TL8: printf("list8"); break; 178 | case BS_TL16: printf("list16"); break; 179 | case BS_TL32: printf("list32"); break; 180 | case BS_TL64: printf("list64"); break; 181 | 182 | case BS_TS8: printf("string8"); break; 183 | case BS_TS16: printf("string16"); break; 184 | case BS_TS32: printf("string32"); break; 185 | case BS_TS64: printf("string64"); break; 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "bs_ast.h" 7 | #include "bs_parse.h" 8 | #include "bs_print.h" 9 | #include "bs_php.h" 10 | 11 | typedef enum { 12 | bs_mode_test, 13 | bs_mode_format, 14 | bs_mode_convert 15 | } bs_mode; 16 | 17 | static char *file_get_contents(const char *path, size_t *size); 18 | static void print_file(const char *path); 19 | 20 | int main(int argc, char **argv) { 21 | if (argc < 2) { 22 | HELP: 23 | printf( 24 | "\n[HOW TO USE]\n\n" 25 | "1) Test BS code :\n" 26 | " bs -t test.bs\n\n" 27 | "2) Format BS code :\n" 28 | " bs -f test.bs > output.bs\n\n" 29 | "3) Convert BS code to PHP :\n" 30 | " bs -c test.bs > output.php\n\n" 31 | "4) Convert BS code and merge with template :\n" 32 | " bs -c test.bs template.php > output.php\n\n" 33 | "5) Convert BS code and merge then execute :\n" 34 | " bs -c test.bs template.php | php\n\n" 35 | ); 36 | return 0; 37 | } 38 | 39 | bs_mode mode; 40 | 41 | if (0 == strcmp("-t", argv[1])) 42 | mode = bs_mode_test; 43 | else if (0 == strcmp("-f", argv[1])) 44 | mode = bs_mode_format; 45 | else if (0 == strcmp("-c", argv[1])) 46 | mode = bs_mode_convert; 47 | else 48 | goto HELP; 49 | 50 | size_t file_size; 51 | char *file = NULL; 52 | 53 | 54 | file = file_get_contents(argv[2], &file_size); 55 | 56 | if (-2 == file_size) { 57 | printf("file not found: %s\n", argv[2]); 58 | return 1; 59 | } else if (-1 == file_size) { 60 | printf("out of memory\n"); 61 | return 1; 62 | } else if (0 == file_size) { 63 | printf("read file failed\n"); 64 | return 1; 65 | } 66 | 67 | bs_parse_result *result = bs_parse(file, file_size); 68 | 69 | free(file); 70 | 71 | if (result->doc != NULL) { 72 | if (bs_mode_test == mode) 73 | printf("ok\n"); 74 | else if (bs_mode_format == mode) 75 | bs_print_doc(result->doc); 76 | else if (bs_mode_convert == mode) { 77 | bs_gen_php(result->doc, stdout); 78 | if (argc == 4) 79 | print_file(argv[3]); 80 | } 81 | } else { 82 | printf("%s at line %d column %d\n", bs_get_error_msg(result->error), result->line, result->column); 83 | } 84 | 85 | bs_parse_result_free(result); 86 | 87 | return 0; 88 | } 89 | 90 | static char *file_get_contents(const char *path, size_t *size) { 91 | FILE *file; 92 | char *buffer; 93 | 94 | if (NULL == (file = fopen(path, "rb"))) { 95 | *size = -2; 96 | return NULL; 97 | } 98 | 99 | fseek(file, 0, SEEK_END); 100 | *size = ftell(file); 101 | rewind(file); 102 | 103 | if (NULL == (buffer = (char *)malloc(sizeof(char) * (*size) + 1))) { 104 | fclose(file); 105 | *size = -1; 106 | return NULL; 107 | } 108 | 109 | if (*size != fread(buffer, 1, *size, file)) { 110 | fclose(file); 111 | free(buffer); 112 | *size = 0; 113 | return NULL; 114 | } 115 | 116 | fclose(file); 117 | 118 | return buffer; 119 | } 120 | 121 | static void print_file(const char *path) { 122 | FILE *file; 123 | 124 | if (NULL == (file = fopen(path, "r"))) { 125 | return; 126 | } 127 | 128 | int c = 0; 129 | while ((c = getc(file)) != EOF) { 130 | putc(c, stdout); 131 | } 132 | 133 | fclose(file); 134 | } 135 | 136 | -------------------------------------------------------------------------------- /test.bs: -------------------------------------------------------------------------------- 1 | // just a demo 2 | pkg product = 123 3 | { 4 | type my_type 5 | { 6 | id : int, 7 | name : string 8 | } 9 | 10 | // get product list 11 | pkg get_list 12 | { 13 | // colors 14 | enum color : int { 15 | red = 1, 16 | green = 2, 17 | blue = 3 18 | } 19 | 20 | def in = 1 { 21 | cat_id : uint, // category 22 | page_num : uint // page number 23 | } 24 | 25 | def out = 2 { 26 | products : list { 27 | id : uint, 28 | name : string, 29 | color : enum 30 | } 31 | } 32 | } 33 | 34 | def my_type_test 35 | { 36 | field : type 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /test.php: -------------------------------------------------------------------------------- 1 | 2 | --------------------------------------------------------------------------------