. N9 v6 ^6 o( W5 f7 O9 `0 \ nfunction IsPrime(Num:integer):boolean; 7 d+ h8 `( ?! m% g" Pvar i:integer;4 s5 x7 @: G, X5 M% x
begin; ]3 P2 z: u; W# E- W$ y$ T
result:=true; {这是delphi设置返回值的方法,此处借用。exit用于退出函数,result变量仅仅用于设置返回值}# \6 H& L# ?! _$ M. n
if Num<2 then : E; Y) H7 r& N1 ?+ F) Tresult:=false; ; _3 B# f' G( C0 k2 y4 x/ oelse if Num>2 then% h1 B7 {( ^$ G, ?
for i:=2 to Num-1 do : v7 l* } Y- J" V+ eif Num mod i=0 then $ W1 x+ V$ {; s- |$ @result:=false; 1 K) c! l/ u% W; z# cend;/ s& ~ p6 {* [ w0 {' n/ b* C
$ Q+ M. w3 c+ R+ Fend." K9 G9 j, K: @/ W) {6 j) k
( n' T& ]9 u" \- Z. B5 z( q语法的介绍就到此结束了。在这里发一下牢骚。虽然我们知道C++很强大,但是其语法却是很不利于分析的。举个例子:/ n3 Z7 `/ f+ }
A*B;知道是什么吗?乘法?指针声明?3 b) s) @% l) x/ Q( }8 k) S, \ R
a<b,c>d;知道是什么吗?逗号表达式?一个类型为某模板类的变量?0 T$ @; e" ~+ C( ?, H# J g# t9 Y
因此,各位有志于分析C++语法的大大们注意了,传统的先语法分析后语义分析的方法在C++面前基本上是一点用都没有。如果你不知道上述代码中两个A代表着什么(类型还是对象),你就无法正确得到你想要的语法树,那么你就惨了。所以,要分析C++,想个办法吧语法分析和语义分析揉在一起吧。在这里我很想知道早期的gcc为什么能用yacc来搞,用yacc写出来的C/C++编译器的代码肯定很难看的,虽然写得出来。1 W* r& @# u( l4 K/ \; B) B
/ ?0 a8 \/ q( n3 `& S; u, F( R回到我们的主题中。这个语言拥有可以递归调用的函数以及全局变量,我们需要准备一个堆栈和一个堆才可以支撑所有的内存操作。堆栈有很多种实现的方法,可以放在堆里也可以不放在堆里。这个决策将对接下去的指令集将会有一点小影响。 ~: t8 S0 J: }) {) |# _2 F' i/ E& o
现在让我们考虑一下各种类型的结构。首先,boolean、integer、char和real都是实体类型,只需要那么一段数据就行了。在32位的机器上分别是1、4、1、8个字节。其次是函数指针。我们可以使用一个全局的ID指向一个函数,就跟我们拿函数去编号一样,一个函数一个编号。那么,函数指针跟integer就一致了,区别在于函数指针不能计算也不能转换类型。8 o# R$ P4 e0 [- }( e: G* k1 g; a; y
. Q/ a. T) L2 F! ]* Z5 v) g, \接下来是字符串和数组,字符串和数组的结构都是一致的,我们可以使用引用计数来达到垃圾收集的功能。根据类型理论我们可以知道我们刚刚设计的语言是不可能存在内存泄漏的(如果所有的数据都只让脚本修改)。于是,我们可以让数组和字符串的结构如下: " @) Q# S, |% v& R[引用计数:int][数据] % H9 \+ H4 Q/ Q" w7 O4 r9 @当创建一个数组变量的时候,我们让数组的值为nil,让其为空,需要使用new创建一个数组。new创建的数组的引用计数是1。如果这个数组被复制的话,那么引用计数也会随之增大。当引用计数为0,也就是所有的变量都不指向这个数组的时候,数组就该释放了。而且刚刚设计的这门语言是保险的,也就是说,只要我们无法访问到这个数组,那么这个数组就一定会被释放。至于原因就留给大家思考了。; ?; |: F( H" \6 Y. i
3 F7 P2 m) X: l+ s! x
字符串的结构跟array of char是一致的,但是字符串有一个特殊的地方。我们将一个字符串赋值给另一个字符串的时候,两个字符串变量其实指向相同的空间。但是我们对其中一个字符串进行修改的时候,是不影响到另一个字符串的。我们可以在修改之前将被修改的字符串进行复制。举个例子:! d @% n0 H) j) M
a="vczh";* L2 q5 O# h f+ l, n
b=a;4 O' o% b+ k1 K+ I& |9 P, O! A6 L
这个时候字符串的引用计数是2。当我们修改b(而不是对b赋值),譬如说b[0]= 'V'的时候,我们对b进行复制。这个时候内存中就有两个引用计数为1而且内容都是vczh,但是指向的空间不同的字符串了。这个时候我们对b指向的空间进行修改的时候,a指向的空间是不变的。这种方法是经常被使用的。 & s: ^* ~5 V& J L6 f5 W( P5 e3 u7 t" e0 k% y
接下来我们考虑堆栈的构造。堆栈是用来存放不支持闭包的语言的函数中的参数和变量的。对于我们刚刚说的这门语言来说,堆栈是相当合适的数据结构。堆栈是分段的,一个段记录的内容有参数、变量、临时信息、函数参数起始位置以及函数的执行位置。函数的执行位置用于记录当前函数在调用新函数之前所执行的指令。有了这个信息之后,我们就可以在函数返回的时候找到合适的指令继续执行了。. l# _; y0 s; _
0 l0 D0 P. u- W& u: d' Q. j如果堆栈中存放字符串或者数组的话,在堆栈的一个段被销毁的同时,我们需要减少相应的字符串或数组的引用计数,并在适当的时候释放他们。那么,我们如何知道堆栈的什么地方记录着什么类型的变量呢?因为表达式也会频繁地使用堆栈的临时空间进行计算,因此类型信息有必要放在堆栈里面。如果不这样做的话,我们就要在指令集里面加入各种不同的pop指令,并在函数的很多地方使用。这两种做法各有利弊,在实现的时候需要衡量一下。( a2 u$ x- F6 a2 {4 S/ A V