原作:2019~2021年
此次共享,腾讯内网 / 外网同步发布。
内部代码地址:https://git.code.oa.com/fatboyli/QBDF
外部代码地址:GitHub - ventureli/QBDF
两年前的一个混淆包(无源码):GitHub - ventureli/VLOCInterpreter
作者:腾讯fatboyli(李文强)
说到这个章节,我特别的烦躁,主要还有两个原因,1.涉及的理论有点多。2.其实我也没完全搞明白,反正就是从实用性出发,最后也写出来了,所以这章节我可能会有些概念阐释的不是很清楚,甚至是错误的,但是最重要的我会很明白的说明QBDF是怎么完成的。
BNF:巴科斯范式(BNF是Backus-Naur form首字母的缩写)
一种形式化的语法表示方法,用来描述语法的一种形式体系,是一种典型的元语言。又称巴科斯-诺尔形式(Backus-Naur form)。它不仅能严格地表示语法规则,而且所描述的语法是与上下文无关的。它具有语法简单,表示明确,便于语法分析和编译的特点。它以递归方式描述语言中的各种成分的。
在双引号中的字"word"代表着这些字符本身。而double_quote用来代表双引号;
在双引号外的字(有可能有下划线)代表着语法部分;
尖括号<>内包含的为必选项;方括号[]内包含的为可选项;
大括号{}内包含的为可重复0至无数次的项;
圆括号()内包含的所有项为一组,用来控制表达式的优先级;
竖线|表示在其左右两边任选一项,相当于"OR"的意思;::=是“被定义为”的意思;
...表示术语符号;
斜体字:参数,在其它地方有解释;
举个例子:比如一个简单得数字的表达方式:
数字> ::= 0x<十六进制数字串> | 0<八进制数字串> | <十进制数字串>
<十六进制数字串> ::= <十六进制数字> | <十六进制数字串><十六进制数字>
<八进制数字串> ::= <八进制数字> | <八进制数字串><八进制数字>
<十进制数字串> ::= <十进制数字> | <十进制数字串><十进制数字>
<十六进制数字> ::= <十进制数字> | A | B | C | D | E | F
<十进制数字> ::= <八进制数字> | 8 | 9
<八进制数字> ::= 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7
通过以上的描述其实我们能很清楚的理解一个数字是怎么构成的。
语法解析是一个听起来不是很复杂但是一旦开始写程序就无从下手的功能。如果没有完成的理论结构去支持,我们就只能用IF-ELSE 这种去处理,最后发现怎么写都不对,总有例外的情况,无论往探测多少个单词都没办法解决语法递归的问题。(我一开始就是这么弄的)只能回过头来先搞定BNF,然后按照他的指导,写起代码来就非常简单了。
说起这个比较惭愧,我不会写BNF,在做QBDF的过程中,虽然重新研究了下编译原理,但是也没把握重新搞定整个BNF的编写,所以只能从已有的开源的BNF表达式中改写。主要有一下两个考虑。
1)我们需要一个简化了的OC语法集合,比如NotNull,enum这种第一版本完全可以抛弃掉,因为这些语法结构并不影响能力的无限性,只是在书写OC代码的时候减少便捷性。这个是完全可以接受的。毕竟这是一个内部人用的框架。
2)OC语法来源与C,表达式的文法完全可以采用C的。
3)OC的特有的语法属性非常的明显(比如@interface 开头,‘-(’ ,'[标示符' 开头等)而且他们存在的地方都是最高的运算符优先级,可以完全当作原子操作。
所以我当时采用C89的BNF,然后进行了小改动使其支持QBDF的OC语法。由于年代久远我也也不是特别的确认这是C89的。但是我从网上搜索后,发现这个确实可以用。
translation_unit : external_decl
| translation_unit external_decl
;
external_decl : function_definition
| decl
;
function_definition : decl_specs declarator decl_list compound_stat
| declarator decl_list compound_stat
| decl_specs declarator compound_stat
| declarator compound_stat
;
decl : decl_specs init_declarator_list ';'
| decl_specs ';'
;
decl_list : decl
| decl_list decl
;
decl_specs : storage_class_spec decl_specs
| storage_class_spec
| type_spec decl_specs
| type_spec
| type_qualifier decl_specs
| type_qualifier
;
storage_class_spec : 'auto' | 'register' | 'static' | 'extern' | 'typedef'
;
type_spec : 'void' | 'char' | 'short' | 'int' | 'long' | 'float'
| 'double' | 'signed' | 'unsigned'
| struct_or_union_spec
| enum_spec
| typedef_name
;
type_qualifier : 'const' | 'volatile'
;
struct_or_union_spec : struct_or_union id '{' struct_decl_list '}'
| struct_or_union '{' struct_decl_list '}'
| struct_or_union id
;
struct_or_union : 'struct' | 'union'
;
struct_decl_list : struct_decl
| struct_decl_list struct_decl
;
init_declarator_list : init_declarator
| init_declarator_list ',' init_declarator
;
init_declarator : declarator
| declarator '=' initializer
;
struct_decl : spec_qualifier_list struct_declarator_list ';'
;
spec_qualifier_list : type_spec spec_qualifier_list
| type_spec
| type_qualifier spec_qualifier_list
| type_qualifier
;
struct_declarator_list : struct_declarator
| struct_declarator_list ',' struct_declarator
;
struct_declarator : declarator
| declarator ':' const_exp
| ':' const_exp
;
enum_spec : 'enum' id '{' enumerator_list '}'
| 'enum' '{' enumerator_list '}'
| 'enum' id
;
enumerator_list : enumerator
| enumerator_list ',' enumerator
;
enumerator : id
| id '=' const_exp
;
declarator : pointer direct_declarator
| direct_declarator
;
direct_declarator : id
| '(' declarator ')'
| direct_declarator '[' const_exp ']'
| direct_declarator '[' ']'
| direct_declarator '(' param_type_list ')'
| direct_declarator '(' id_list ')'
| direct_declarator '(' ')'
;
pointer : '*' type_qualifier_list
| '*'
| '*' type_qualifier_list pointer
| '*' pointer
;
type_qualifier_list : type_qualifier
| type_qualifier_list type_qualifier
;
param_type_list : param_list
| param_list ',' '...'
;
param_list : param_decl
| param_list ',' param_decl
;
param_decl : decl_specs declarator
| decl_specs abstract_declarator
| decl_specs
;
id_list : id
| id_list ',' id
;
initializer : assignment_exp
| '{' initializer_list '}'
| '{' initializer_list ',' '}'
;
initializer_list : initializer
| initializer_list ',' initializer
;
type_name : spec_qualifier_list abstract_declarator
| spec_qualifier_list
;
abstract_declarator : pointer
| pointer direct_abstract_declarator
| direct_abstract_declarator
;
direct_abstract_declarator: '(' abstract_declarator ')'
| direct_abstract_declarator '[' const_exp ']'
| '[' const_exp ']'
| direct_abstract_declarator '[' ']'
| '[' ']'
| direct_abstract_declarator '(' param_type_list ')'
| '(' param_type_list ')'
| direct_abstract_declarator '(' ')'
| '(' ')'
;
typedef_name : id
;
stat : labeled_stat
| exp_stat
| compound_stat
| selection_stat
| iteration_stat
| jump_stat
;
labeled_stat : id ':' stat
| 'case' const_exp ':' stat
| 'default' ':' stat
;
exp_stat : exp ';'
| ';'
;
compound_stat : '{' decl_list stat_list '}'
| '{' stat_list '}'
| '{' decl_list '}'
| '{' '}'
;
stat_list : stat
| stat_list stat
;
selection_stat : 'if' '(' exp ')' stat
| 'if' '(' exp ')' stat 'else' stat
| 'switch' '(' exp ')' stat
;
iteration_stat : 'while' '(' exp ')' stat
| 'do' stat 'while' '(' exp ')' ';'
| 'for' '(' exp ';' exp ';' exp ')' stat
| 'for' '(' exp ';' exp ';' ')' stat
| 'for' '(' exp ';' ';' exp ')' stat
| 'for' '(' exp ';' ';' ')' stat
| 'for' '(' ';' exp ';' exp ')' stat
| 'for' '(' ';' exp ';' ')' stat
| 'for' '(' ';' ';' exp ')' stat
| 'for' '(' ';' ';' ')' stat
;
jump_stat : 'goto' id ';'
| 'continue' ';'
| 'break' ';'
| 'return' exp ';'
| 'return' ';'
;
exp : assignment_exp
| exp ',' assignment_exp
;
assignment_exp : conditional_exp
| unary_exp assignment_operator assignment_exp
;
assignment_operator : '=' | '*=' | '/=' | '%=' | '+=' | '-=' | '<<='
| '>>=' | '&=' | '^=' | '|='
;
conditional_exp : logical_or_exp
| logical_or_exp '?' exp ':' conditional_exp
;
const_exp : conditional_exp
;
logical_or_exp : logical_and_exp
| logical_or_exp '||' logical_and_exp
;
logical_and_exp : inclusive_or_exp
| logical_and_exp '&&' inclusive_or_exp
;
inclusive_or_exp : exclusive_or_exp
| inclusive_or_exp '|' exclusive_or_exp
;
exclusive_or_exp : and_exp
| exclusive_or_exp '^' and_exp
;
and_exp : equality_exp
| and_exp '&' equality_exp
;
equality_exp : relational_exp
| equality_exp '==' relational_exp
| equality_exp '!=' relational_exp
;
relational_exp : shift_expression
| relational_exp '<' shift_expression
| relational_exp '>' shift_expression
| relational_exp '<=' shift_expression
| relational_exp '>=' shift_expression
;
shift_expression : additive_exp
| shift_expression '<<' additive_exp
| shift_expression '>>' additive_exp
;
additive_exp : mult_exp
| additive_exp '+' mult_exp
| additive_exp '-' mult_exp
;
mult_exp : cast_exp
| mult_exp '*' cast_exp
| mult_exp '/' cast_exp
| mult_exp '%' cast_exp
;
cast_exp : unary_exp
| '(' type_name ')' cast_exp
;
unary_exp : postfix_exp
| '++' unary_exp
| '--' unary_exp
| unary_operator cast_exp
| 'sizeof' unary_exp
| 'sizeof' '(' type_name ')'
;
unary_operator : '&' | '*' | '+' | '-' | '~' | '!'
;
postfix_exp : primary_exp
| postfix_exp '[' exp ']'
| postfix_exp '(' argument_exp_list ')'
| postfix_exp '(' ')'
| postfix_exp '.' id
| postfix_exp '->' id
| postfix_exp '++'
| postfix_exp '--'
;
primary_exp : id
| const
| string
| '(' exp ')'
;
argument_exp_list : assignment_exp
| argument_exp_list ',' assignment_exp
;
const : int_const
| char_const
| float_const
| enumeration_const
;
然后对这个进行小幅度更改即可。比如针对 后缀表达式,我们做了更改如下:
/*
postfix-expression:
(identifier | constant | string | "(" expression ")" | [identify OCArglist]
"[" expression "]" |
"(" assignment-expression% ")" |
"." identifier |
"->" identifier |
"++" |
"--"
"^(assignment-expression){}" | block 作为一个独立的结构不单独解析了,代码放在外面
)*
OCArglist:
identify | (_QBDF_EXP_Postfix:expression)*
*/
这样就完全把 "[[UIView alloc] initWithFrame:xxxx]" 给包括进去了。所以整体改动非常小。这里有同学可能要问了,那些“类定义”,“protocol 定义”,“类实现”这些代码不是C里的BNF啊,要怎么处理呢?
这个回答其实很简单,这就是我学了个这个半吊子编译原理的菜鸟的折中方法(这个项目我当初只写了不到两个半月,这里确实没设计好。)我在第一个BNF表达式里,就是translation_unit 这个定义对应的递归子程序实现里就处理掉了。怎么处理的呢?大家还记得在上一章节我在做词法分析的时候 把“@interface”这种字符串给组成一个特殊的单词。那么只要由个数单词开头的token,肯定要进入到不同的分支里了。代码如下。其实就是
语法解析入口程序
好了这一章节就到此结束了,我深知这一章节我讲的很不好,大家兴趣还是去看看“编译原理”的课件,我看的是哈工大版本的,就能明白了。我也不是很懂那么复杂的理论(鞠躬)!