表达式,是编程语言里最重要的语法。

编程语言的主要内容可以分成两部分,计算和逻辑。
表达式就是计算的过程和结果,而 if / else / while / for等语句表示的都是逻辑。
从广义上来说,逻辑也是二进制的计算。
if (flag)
i += 1;
else
i += 2;
也可以表示成。
编程的哲学思维先说到这里,接着说说表达式的语法分析。
表达式的语法分析也与其他模块一样,源代码怎么写,语法分析就怎么写。
当然,表达式的语法分析比其他模块的复杂,我在scf框架里写了1000行左右。
表达式,是由变量、单目运算符、双目运算符、常量、小括号、中括号等组成的,以逗号或分号结尾。
小括号在表达式里的语义有多种:
1,表示子表达式,修改原表达式的计算顺序。
例如:
(1 + 3) / 2 就需要先计算1 + 3,而1 + 3 / 2 就需要先计算3 / 2,跟小学数学里的一样。
2,表示类型转换,T* p = (T*) malloc(sizeof(T));
左小括号后面跟类型名,然后再跟右小括号,然后跟子表达式,是类型转换的语法。
3,表示函数调用,malloc(16),这里的小括号就表示函数调用。
函数调用的语法,是函数名后面跟左小括号,然后跟实参列表,然后跟右小括号。
实参列表可以为空,也可以为1个子表达式,或多个以逗号分隔的子表达式。
中括号在表达式里的语义是取数组的成员,语法是数组名字后面跟左中括号,然后跟表示数组索引的子表达式,然后跟右中括号。
不管是数组名、函数名、还是类型名,都是标志符。
变量名也是标志符。
它们表示的具体语法元素,可以通过从抽象语法树的当前位置往根节点方向搜索得到,从而实现对源代码的作用域控制。
int a = 1;
{
int a = 2;
{
int a = 3;
int c = 4 + a; //这个a肯定是当前作用域的3
}
}
上述代码的抽象语法树
如上图,在给a = 3赋值时,先找到的肯定是当前作用域的变量a,而不可能是父作用域的变量a。
这在编程语言里叫变量名字的隐藏,即使用的变量是最近的作用域里的变量。
与使用位置在同一个作用域里的变量,肯定是最最近的[呲牙]
c = 4 + a这行表达式,使用的a也肯定是最近的a。
常量和变量在语法分析阶段的表达式里,几乎没有区别。它们之间的区别可以留待语义分析时再处理。
if (1 == a) 如果被写成 if (1 = a)了,在语法分析时可以认为这两个条件表达式都是对的,到了语义分析时再判断数字常量1不能被赋值。
这样在语法分析时就只需要检查表达式的语法错误,而不必检查语义错误。
这样在语法分析时就不需要把赋值运算符=做特别处理,把它当作普通的双目运算符就行。
表达式的运算符主要分单目运算符和双目运算符。
单目运算符(unary operator)可以位于表达式的开头,一般都是右结合的。
双目运算符(binary operator)必须位于两个变量或常量(或子表达式)的中间,一般都是左结合的,但赋值类的运算符是右结合的。
sizeof也是一个单目运算符,但它的操作数放在小括号里,这个小括号要做特别处理,我在scf框架里给它单独做了一个sizeof模块。
取数组成员的运算符是[],语法是array [ index ],它的两个操作数是数组array和索引index,写法有点特别,要对左右中括号做特别处理。
结构体指针的运算符->,语法是s->member,它的两个操作数是结构体指针s和成员member,其中member要去结构体自己的作用域里查找。
星号的语义有2个,一是双目运算符乘法,二是单目运算符指针解引用。
当星号位于两个变量或常量(或子表达式)之间的时候是乘号(a * b)。
当星号*位于表达式的开头(*p),或者其他运算符的后面(!*p)时是指针解引用。
表达式语法的具体编辑如下,已加注释,共4张图。
构建抽象语法树的代码就不解释了,1000多行实在太多了,估计也没几个人有耐心看[捂脸]


还没有评论,来说两句吧...