LPC 进阶－１：各种资料型态

一、阵列：

  LPC 是一个程式语言，目前用来在 MudOS(及 LPmud)作业系统下撰写物件，
  LPC 的阵列不是由型态关键字宣告，有两种方法可以让使用阵列：

	int	*a = ({ 1, 2, 3 });

  上面宣告整数阵列 a，并且给予初始值 ({ 1, 2, 3 })，注意阵列是用({})
把阵列的元素括起来，这一点请特别记住。

	mixed	a;
	a = allocate (3);
	a = ({ 1, 2, 3 });

  配备记忆体给二维阵列跟宣告的方式可以如下:

        mixed   a;
        a = allocate(10);
        a[0] = allocate(10);
        a[1] = allocate(10);
        ...等等...

    如果这样宣告, 你可以把二维阵列想成阵列的阵列,
  也就是一维阵列的每个元素又是一个阵列, 因此可以如下的方式使用:

        i = a[0][0]             //  <-- 第 0 维阵列的第 0 个元素.

    就算是使用 * 宣告, 你也无法直接宣告超过一维的阵列, 如:
        int     *a;
    但是你却可以像上面的方式, 而拥有并使用超过一维的阵列,
  也就是透过 mixed 的方式, 让编译器不检查资料型态而达到目的.

    底下是另外一种宣告并使用二维阵列的例子:

        mixed a;
        a = ({ ({ 1, 2, 3 }), ({ 4, 5, 6 }) });

    以上面的例子来说, a[0] 会是 ({ 1, 2, 3 }), 而 a[0][2] 则是 3.

    底下也是一个合法的使用方式:

        mixed a;
        a = ({ 0, 0, 0, 0 });  // 这样只是让阵列大小为 4
        a[0] = ({ 1, 2, 3 });  // 改变 a[0] 从 0 变成 ({ 1, 2, 3 })
        a[1] = ({ 1, 2, 3 });  // 改变 a[1] 从 0 变成 ({ 1, 2, 3 })
        ...等等...

  不知各位有没有发现我们是利用 mixed 型态?
  如果您想利用 int *a[]; 或 int **a; 都会出错
  只有 mixed 可以解决, 因为 mixed 的型态, 是告诉系统说,
  这个变数(也可以是函数)的型态你不要去管,
  我高兴传 int, string, array, mapping 都好,
  既然有这种特性, 我们就给它来个二维阵列,
  就是这样

  至于 allocate() 是要求记忆空间的意思, 如果像下面这样就不行了

  mixed a;
  a[0] = ({ xxx, xxx, xxx .... });

  因为 a 还没给它记忆空间, 所以 a[0] 会出错

  但是
  mixed a;
  a = ({ ....... });
  则可以, 那样系统会自动给它记忆空间, 怪吧, 上面的方式跟下面的一模一样:
  mixed a = ({ .... });

  如: mixed a = ({ ({ 0, 1, 2 }), ({ 2, 4, 6 }), ({ 1, 5, 10 }) });
  上面是个 3X3 的二维阵列

二、buffer

  buffer 是 LPC 特有的资料型态。
    buffer 是有点类似阵列跟字串的混合, 它可以让人很容易就处理二进位资
  料. 值得注意的是, buffer 模式不是以零值当结尾(跟字串的方式不同), 那
  么 buffer 如何知道长度或者说是哪里是结尾? buffer 在最前面记录了 buffer
  的长度, 这一点跟 Pascal 的字串的使用方式一样. buffer 可以看成是位元
  组的阵列, 也就是 buffer 的元素大小是位元组. 底下有几个使用例子:
        buf[0] = x;
        x = buf[0];
        i = sizeof(buf);
        if (bufferp(buf)) return;
        str = buf[i..j];
        buf = read_buffer(file_name, ...);      // 跟 read_bytes() 一样
        buf = buf1 + buf2;
        buf = allocate_buffer(100);

三、浮点数：

    LPC 提供了单精度的浮点数, 精确度约有七位有效数字, 宣告方式如下:
        float pi;
        float r;
    底下是几个使用的例子:
        pi = 3.1415926;
        r = 1.0;
        area = pi * r * r;

    基本上, 浮点数的运算方式跟整数几乎没差别, 除了一些像三角函数之类的以外.

四、函数指标：

    LPC 提供了 function 这种变数型态, 可以用来方便的指向函数.
  也许你会想要把函数传给某些 efun, 例如某些过滤的函数. 函数的
  指标可以用 (: 函数名称 :) 来引用, 配合函数变数则:

        function f = (: local_func :);

    你可以把 f 当成变数传给其它函数, 或者是把它当成函数指标:

        foo(f);                         // 当成变数.
        map_array( ({ 1, 2 }), f);      // 当成指标.

    或者, 有一个更强的用法, 在后来的时机里再"求值":

        x = evaluate(f, "hi");

When the last line is run, the function that f points to is called, and "hi"
is passed to it.  This will create the same effect as if you had done:
    执行到上一行的时候, 实际上会呼叫 f 所指的函数去, 并传 "hi" 给它当参数.
  上面的方式跟下面的程式码意义相同:

        x = local_func("hi");

    以函数变数来指向函数的好处是, 你可以改变指标, 让它指向别的函数.
  尤其是配合 evaluate() 时, 在此, 先提醒一下, 如果传给 evaluate() 的
  参数不是函数指标的话, 会传回它的值, 因此下列方式可见函数变数的优点:

        void set_short(mixed x) { short = x; }
        mixed query_short() { return evaluate(short); }

    如上面的宣告, 你可以如下的使用 set_short():
        set_short("Whatever");
        set_short( (: short_func :) );

    如此一来, 物件可以自己定义 short_func(), 可增加许多弹性.


各种函数变数或指标
------------------

  １、
    最简单的函数指标的使用已经如上所述, 即: (: 函数名称 :), 这种
  方式可以方便的用在同一物件的函数上, 也可以把参数包括进来, 如:
 
        string foo(string a, string b)
        {
          return "(" + a "," + b + ")";
        }

        void create()
        {
          function f = (: foo, "left" :);

          printf( "%s %s\n", evaluate(f), evaluate(f, "right") );
        }

        ==> 结果: (left,0) (left,right)

　２、
    第二种用法是 efun 指标, 跟同一物件里的局部函数指标很类似. 例如:
  objects() 会传回所有 mud 载入的物件, 只要该物件满足某函数, 如:

        objects( (: clonep :) )

  因为所有物件都是 "clonep", 也就是, 所有物件 ob->clonep() 都传回非 0 值,
  因此上述例子会传回所有的物件阵列.

    底下再示范一个例子:

        void create()
        {
          int i;
          function f = (: write, "Hello, world!\n" :);

          for (i=0; i<3; i++) { evaluate(f); }
        }

    结果是:
        Hello, world!
        Hello, world!
        Hello, world!

    simul_efun 会跟 efun 一样的效果(否则怎会称为 simul_efun?!).
    而 efun 会跟局部的函数很像, 是因为 efun 是整体性的函数, 因此
  可以想成是跟局部的函数同样地位.

  ３、
    第三种型式的是 call_other 函数指标。它很类似于 MudOS 所支援的函数
  指标的用法, 格式是:

        (: 物件, 函数名称 :)

  假如有参数的话, 可以把所有参数组成阵列加在函数名称之后. 例如:

        void create()
        {
          string *ret;
          function f = (: this_player(), "query" :);

          ret = map( ({ "name", "short", "long" }), f );
          write(implode(ret, "\n"));
        }

    上面的程式片断相当于:
        this_player()->query("name");
        this_player()->query("short");
        this_player()->query("long");

    如果要让函数指标直接指向 query("short") 的话, 可以是:

        f = (: this_player(), ({ "query", "short" }) :)

        (: 物件, 函数名称 :)

  假如有参数的话, 可以把所有参数组成阵列加在函数名称之后. 例如:

        void create()
        {
          string *ret;
          function f = (: this_player(), "query" :);

          ret = map( ({ "name", "short", "long" }), f );
          write(implode(ret, "\n"));
        }

    上面的程式片断相当于:
        this_player()->query("name");
        this_player()->query("short");
        this_player()->query("long");

    如果要让函数指标直接指向 query("short") 的话, 可以是:

        f = (: this_player(), ({ "query", "short" }) :)

    底下的例子跟上面的一模一样, 供你做参考:

        f = (: call_other, this_player(), "query", "short" :)
        // 透过指向 call_other efun, 来呼叫定义在 this_player() 物件中
        // 的 query("short")

        f = (: this_player()->query("short") :)
        // 这是函数的表示式, 见底下的说明.

　４、
　　第四种用法是「函数指标运算式”。这种用法是透过 (: 运算式 :) 达成。
在函数指标运算式中，参数可以用 $1, $2, $3,... 参考到。举例来看：

    evaluate( (: $1 + $2 :), 3, 4)  // 传回 7

    这种方式可以在使用 sort_array() 时非常方便，举例来看：

top_ten = sort_array( player_list,
          (: $2->query_level() - $1->query_level() :) )[0..9];
 

  ５、
　　第五种用法称为「无名函数”：

void create() {
    // 底下的 f 是一个函数指标，指向的函数其实并没有名称
    function f = function(int x) {
        int y;

        switch(x) {
        case 1: y = 3;
        case 2: y = 5;
        }
        return y - 2;
    };

    printf("%i %i %i\n", (*f)(1), (*f)(2), (*f)(3));
}

    会印出： 1 3 -2
    值得注意的是 (*f)(...) 跟 evaluate(f, ...) 一样。


五、对应：

  对应的关键字是「mapping”， 有点类似阵列，我们可以称为关联阵列，其
语法很简单：mapping 识别字。对应的初始值有两种方法：

  mapping	x;

  x = ([ ]);	<-- 空对应
  x = ([ key0 : value0,
	 key1 : value1,
	 .
	 .
      ]);

  注意对应一定要透过上面两种方法之一初始化，否则无法给对应任何新值。

  mapping	x;

  x[key0] = value0;

  上面这种用法是错的，原因就是没有初始化。
  诚如上例，事实上若该对应有先初始化，则给对应新值的方法是对的，所以

  mapping	x;

  x = ([ ]);
  x[key0] = value0;

  是正常用法。不过读者要知道的是，上述作法对驱动程式而言其实颇为复杂
首先，对应 x  初始成空的，下一叙述则指定 (key0, value0) 给它，但驱动
程式找不到此对应，且找不到 key0 这个键（key）， 该对应目前没空间存新
对应，会产生一个新对应，其大小为１，并设定键 key0， 其值为 value0，
最后，清掉原先的空对应。
  若对应大小常常改变，驱动程式会常常利用 allocate() 配置记忆体，如此
会造成没效率，最好的改良方法是事先利用 allocate() 给定足够空间，如：

  x = (mapping)allocate (10);

  对应的引用法如下：

  write (x[key0] + "\n");	--> 会印出 value0

  有了设定对应值的方法，也该知道删除的方法：

  map_delete(x, key0);

  若用上面的方法删除对应键 key0 之后， x[key0] 就找不到值，下面的值
会是 1：

  undefinedp (x[key0])

  现在举个实例：

   mixed idx;
   map x;

   x = ([ "x" : 3, "y" : 4]);
   idx = keys(x);

  此时 idx 会等于 ({ "x", "y" })，注意到 keys() 函数了吧，这是取得对
应中所有键的方法。事实上， idx 的元素顺序会是随机的，因为对应是以
hash table  法储存的。下面的例子则继承上例：

   mixed value;
   value = values (x);

  上例的 value 值是 ({ 3, 4 })。此是利用 values() 函取得键值，其顺序
会跟 keys() 传回的值的顺序相对应。

  注意，若我们要处理对应的每一个元素，利用 each() 是不错的选择：

   mixed *pair;

   while ((pair = each(x)) != ({})) {
      write("key   = " + pair[0] + "\n");
      write("value = " + pair[1] + "\n");
   }

  不过要让 each() 运作的话，每一对应会增加 12 bit 的记忆空间。
  就如开头所让的，对应也称为关联阵列，它也可以是多维的，方法同阵列。

   mapping x, y;

   x = ([]);
   y = ([]);

   y["a"] = "c";
   x["b"] = y;

   write (x["b"]["a"]);

  上例中会印出 "c" 来。对应的增加除了先前的方法外，还可以用加法：

  mapping x, y, z;

  x = ([ "x" : 1 ]);
  y = ([ "y" : 2 ]);

  z = x + y;

  上例 z == ([ "x" : 1, "y" : 2 ])，就如同阵列一样。不过，我们也可以
运用 += 运算子：

  a += ([ "x" : 1 ]);

  上面的例子很像先前的例子：

  a["x"] = 1;

  不过，两者比较起来，后者通常会比较有效率，因前者很肯定会产生新对应
当然，会有记忆体配置动作，因此我们希望大家使用后者。

  对应还可以用 * 运算元做复合（composed）运算，如：

   mapping r1, r2, a;

   r1 = ([]);
   r2 = ([]);

   r1["driver"] = "mudlib";
   r2["mudlib"] = "castle";

   a = r1 * r2;

　上例的 a["driver"] == "castle"。

  最后，对应的元素个数可以用 sizeof() 来取得。

六、字串与子结构：

  字串可看成字元的阵列，我们不特别说明字串，此处我们用阵列泛指字串与
阵列深入探讨子结构。所谓子结构是泛指「子字串”、「子阵列”，在此我们
引用英文的两个词：

  indexing	<-- 索引，由

		LPC Substructures 


1. Indexing and Ranging - General Introduction
----------------------------------------------

	Since v20.25a6, MudOS provides a way of indexing or getting
slices (which I will, following common use, call 'ranging') of 
strings/buffers/arrays/mappings (for mappings only indexing is 
available) as well as means of changing the values of data 
via lvalues (i.e. 'assignable values') formed by indexing/ranging.

	As an example, if we set str as "abcdefg", str[0] will 
be 'a', str[1] 'b' etc. Similarly, the nth element of an array 
arr is accessed via arr[n-1], and the value corresponding to 
key x of mapping m, m[x]. The '<' token can be used to denote
indexing from the right, i.e. str[<x] means str[strlen(str) - x]
if str is a string. More generally arr[<x] means arr[sizeof(arr)-x].
(Note that sizeof(arr) is the same as strlen if arr is a string).

	Indexed values are reasonable lvalues, so one could do for e.g.
str[0] = 'g' to change the 1st character of str to g. Although it is
possible to use ({ 1,2 })[1] as a value (which is currently optimized
in MudOS to compile to 2 directly), it is not possible to use it as an
lvalue. It is similarly not possible to use ([ "a" : "b" ])["c"] as an
lvalue (Even if we did support it, it would be useless, since there
is no other reference to the affected mapping). I will describe in
more detail later what are the actually allowed lvalues.

	Another method of obtaining a subpart of an LPC value is via
ranging. An example of this is str[1..2], where for str being
"abcdefg", gives "bc". In general str[n1..n2] returns a substring
consisting of the (n1+1) to (n2+1)th characters of str. If n1 or n2
is negative, and the driver is compiled with OLD_RANGE_BEHAVIOR
defined, then it would take the negative indices to mean counting
from the end. Unlike indexing though, ranges with indexes which are 
out of bounds do not give an error. Instead if a maximal subrange 
can be found within the range requested that lies within the bounds
of the indexed value, it will be used. So for e.g., without 
OLD_RANGE_BEHAVIOR, str[-1..2] is the same as str[0..2]. All other 
out of bounds ranges will return "" instead, which corresponds
to the idea that there is no (hence there is one, namely the empty one)
subrange within the range provided that is within bounds. Similarly,
for array elements, arr[n1..n2] represents the slice of the array
with elements (n1+1) to (n2+1), unless out of bounds occur. 
OLD_RANGE_BEHAVIOR is only supported for buffers and strings. However,
I suggest you not use it since it maybe confusing at times (i.e. in
str[x..y] when x is not known at hand, it may lead to an unexpected
result if x is negative). One can however, also use < in ranging 
to mean counting from the end. So str[<x..<y] means 
str[strlen(str)-x..strlen(str)-y]. 

/* Remark: If OLD_RANGE_BEHAVIOR is defined, then the priority of <
	   is higher than the priority of checking if it's negative.
           That is, if you do str[<x..y], it will mean the same
	   as str[strlen(str)-x..y], meaning therefore that it will
	   check only now if strlen(str)-x is negative and if so,
	   takes it to be from the end, leading you back to x again 
 */

Thus far, str[<x..<y], str[<x..y], str[x..<y], str[<x..] (meaning the
same as str[<x..<1]) and str[x..] (same as str[x..<1]) are supported.
The same holds for arrays/buffers.

	Perhaps this might seem strange at first, but ranges also
are allowed to be lvalues! The meaning of doing

	str[x..y] = foo;  (1)

is basically 'at least' doing

	str = str[0..x-1] + foo + str[y+1..];  (2)

	Here x can range from 0..sizeof(str) and y from -1 to 
sizeof(str) - 1. The reason for these bounds is because, if I 
wanted to add foo to the front,

	str = foo + str;

this is essentially the same as

	str = str[0..-1] + foo + str;

since str[0..-1] is just "".

/* Remark: As far as range lvalues are concerned, negative indexes
	   do not translate into counting from the end even if 
	   OLD_RANGE_BEHAVIOR is defined. Perhaps this is confusing, 
	   but there is no good way of allowing for range lvalue 
           assignments which essentially result in the addition 
	   of foo to the front as above otherwise
 */

Hence, that's the same as doing
	
	str = str[0..0-1] + foo + str[0..];
	
or, what's the same

	str = str[0..0-1] + foo + str[-1+1..];

Now if you compare this to (1) and (2) you see finally that that
conforms to the prescription there if we do str[0..-1] = foo!! (Yes,
those exclamation marks are not part of the code, and neither is 
this :))

	Similarly, I will leave it to you to verify that 

	str[sizeof(str)..sizeof(str)-1] = foo; (3)
	
would lead to str = str + foo. Now, we can use < in range lvalues 
as well, so (3) could have been written as

	str[<0..<1] = foo; (4)

or even

	str[<0..] = foo; (5)

which is more compact and faster.

/* Remark: The code for str[<0..] = foo; is generated at compile time
	   to be identical to that for str[<0..<1] = foo; so neither
	   should be faster than the other (in principle) at runtime,
	   but (4) is faster than (3).
 */

Now we come to the part where I mentioned 'at least' above. For strings,
we know that when we do x = "abc"; y = x;, y has a new copy of the string
"abc". (This isn't always done immediately in the driver, but whenever
y does not have a new copy, and a change is to be made to y, then y
will make a new copy of itself, hence effectively, y has a new copy
in that all simple direct changes to it such as y[0] = 'g' does not
change x) For buffers and arrays, however, when we do y = x, we don't get
a new copy. So what happens is if we change one, we could potentially
change the other. This is indeed true (as has always been) for assignments
to indexed lvalues (i.e. lvalues of the form y[0]). For range lvalues
(i.e. x[n1..n2]), the rule is if the change of the lvalue will not
affect it's size (determined by sizeof for e.g.), i.e. essentially if
n1 and n2 are within x's bounds and the value on the right hand side
has size n2 - n1 + 1, then indeed changing x affects y, otherwise 
it will not (i.e. if you do x[0..-1] = ({ 2,3 }). This only applies 
to arrays/buffers, for strings it will never affect y if we assign to a 
range of x.

2. More complex lvalues and applications
----------------------------------------

	Since arrays/mappings can contain other arrays/mappings, it 
is possible in principle to index them twice or more. So for e.g.
if arr is ({ ({ 1,"foo",3 }),4 }) then arr[0][1] (read as the 2nd
element of arr[0], which is the 1st element of arr) is "foo". If we
do, for e.g. arr[0][2] = 5, then arr will be ({ ({ 1, "foo", 5 }), 4 }).

/* Remark: by the 'will be' or 'is' above, I mean technically: recursively
	   equal. (This is just if some people are confused)
 */

	Similarly, arr[0][1][1] = 'g' changes arr to ({ ({ 1, "fgo", 3 }),
4 }), and arr[0][1][0..1] = "heh" (note that the right hand side can 
have a different length, imagine this being like taking the 1st two
characters out from arr[0][1], which is currently "foo", and putting
"heh" in place, resulting in "heho") gives arr as ({ ({ 1, "heho", 3 }),
4 }). You should now be able to generate more examples at your fancy.

	Now I want to discuss some simple applications. 

	Some of you may know that when we are doing 

	sscanf("This is a test", "This %s %s test", x, y) (6)

that x and y are technically lvalues. This is since what the driver
does is to parse the original string into the format you give, and
then tries to assign the results (once all of them are parsed) to
the lvalues that you give (here x and y). So, now that we have more
general lvalues, we may do

	x = "simmetry";
	arr = ({ 1, 2, 3, ({ "This is " }) });

	sscanf("Written on a roadside: the char for 'y' has value 121\n",
	       "Written on %sside: the char for 'y' has value %d\n",
	       arr[3][0][<0..], x[1]);   
	                                                   (7)

will result in arr being ({ 1, 2, 3, ({ "This is a road" }) }) and
x being "symmetry". (See how we have extended the string in arr[3][0]
via sscanf?) The driver essentially parses the string to gives the
matches "a road" and 121, it then does the assignments to the lvalues,
which is how we got them as above.

	The ++,--,+= and -= operators are supported for char lvalues,
i.e. lvalues obtained by indexing strings. Thus for e.g. to get
an array consisting of 26 elements "test.a","test.b", .., "test.z",
one might use a global var tmp as follows:

	mixed tmp;

	create(){
	   string *arr;
	 
	   ...

	   tmp = "test.`";
	   arr = map_array(allocate(26), (: tmp[4]++ && tmp :));

	   ...
        }

							  (8)

3. General syntax of valid lvalues
----------------------------------

	Finally, as a reference, I will just put here the grammar of
valid lvalues accepted by the driver.

	By a basic lvalue I mean a global or local variable name, or a 
parameter in a functional function pointer such as $2. 

	A basic lvalue is a valid lvalue, and so are indexed lvalues of
basic or indexed lvalues. (Notice that I did not say indexed lvalues of 
just basic lvalues to allow for a[1][2]). I will generally call an lvalue 
obtained from a basic lvalue by indexing only indexed lvalues. 

	The following lvalues are also valid at compile time:

	   (<basic lvalue> <assignment token> <rhs>)[a_1][a_2]...[a_n] 
	   (<indexed lvalue> <assignment token> <rhs>)[a_1][a_2]...[a_n]
	
	                                                        (9)
	/* Remark: n >= 1 here */

	assignment token is one of +=, -=, *=, &=, /=, %=, ^=, |=, =, <<=,
>>=.
	
	However, because of the same reason that when we assign to a 
string, we obtain a new copy, (x = "foo")[2] = 'a' is invalidated at
runtime. (One way to think about this is, essentially, assignment leaves
the rhs as a return value, so x = "foo" returns "foo", the right hand
side, which is not the same "foo" as the one in x. For arrays/buffers
this is no problem because by assigning, we share the array/buffer)

        Call the lvalues in (9) complex lvalues. Then the following is
also a valid lvalue:

	(<complex lvalue> <assignment token> <rhs>)[a_1][a_2]...[a_n]
	
	                                                         (10)
 
and if we now call the above lvalues also complex lvalues, it would
still be consistent, i.e. (((a[0] = b)[1] = c)[2] = d)[3] is an okay
lvalue (though I wouldn't suggest using it for clarity's sake :)).

	Now, the last class of valid lvalues are range lvalues, which
are denoted by ranging either a basic, indexed or complex lvalue:

	<basic lvalue>[n1..n2]
	<indexed lvalue>[n1..n2]
	<complex lvalue>[n1..n2] 
	
	plus other ranges such as [<n1..<n2] etc.
             	                                                 (11)	

	There is maximally only one 'range' you can take to obtain a
valid lvalue, i.e. arr[2..3][0..1] is not a valid lvalue (note that
a naive interpretation of this syntax is one equivalent to using 
arr[2..3] itself)

	
4. Compile-time errors that occur and what they mean
----------------------------------------------------

	Here I put some notes on compile-time errors for valid lvalues,
hopefully to be useful for you.

Err 1: Can't do range lvalue of range lvalue

Diagnosis: You have done 'ranging' twice, e.g. something like
	   x[2][0..<2][1..2] isn't a valid lvalue

Err 2: Can't do indexed lvalue of range lvalue.

Diagnosis: Something like x[0..<2][3] was done.

Err 3: Illegal lvalue, a possible lvalue is (x <assign> y)[a]

Diagnosis: Something like (x = foo)[2..3] or (x = foo) was taken to
	   be an lvalue.

Err 4: Illegal to have (x[a..b] <assign> y) to be the beginning of 
       an lvalue

Diagnosis: You did something as described, i.e. (x[1..6] = foo)[3] is
	   not allowed.

Err 5: Illegal lvalue

Diagnosis: Oops, we are out of luck here :) Try looking at your lvalue
	   more carefully, and see that it obeys the rules described 
	   in section 3 above.


5. Coming attractions
---------------------

	Perhaps a pointer type will be introduced to allow passing
by reference into functions. Mappings may be multivalued and
multi-indexable.


Author       : Symmetry@Tmi-2, IdeaExchange
Last Updated : Tue Jan 10 11:02:40 EST 1995



七、运算子：

  底下以优先顺序列出所有运算子的语法：

expr1 , expr2   先计算 'expr1' 再计算 'expr2'。传回值为 expr2 的结果

var = expr      计算 'expr'，再指定给变数 var，传回值为 var 的新值。

var += expr     计算 'var' + 'expr'，再指定给 'var'，相当于：
                var = var + expr

  底下的部份都类似 += 运算子：
var -= expr
var &= expr
var |= expr
var ^= expr
var <<= expr
var >>= expr
var *= expr
var %= expr
var /= expr

expr1 || expr2  当 expr1 为非○（真）时，其值为「真”（expr1），且
		expr2 的值不会被计算到，若 expr1 的值为 0， 则其值
		expr2， 此时才会计算到 expr2。

expr1 && expr2  The result is true i 'expr1' and 'expr2' is true. 'expr2' is
                not evaluated if 'expr1' was false.

expr1 | expr2   The result is the bitwise or of 'expr1' and 'expr2'.

expr1 ^ expr2   The result is the bitwise xor of 'expr1' and 'expr2'.

expr1 & expr2   The result is the bitwise and of 'expr1' and 'expr2'.

expr1 == expr2  Compare values. Valid for strings and numbers.

expr1 != expr1  Compare values. Valid for strings and numbers.

expr1 > expr2   Valid for strings and numbers.

expr1 >= expr2  Valid for strings and numbers.

expr1 < expr2   Valid for strings and numbers.

expr1 <= expr2  Valid for strings and numbers.

expr1 << expr2  Shift 'expr1' left 'expr2' bits.

expr1 >> expr2  Shift 'expr1' right 'expr2' bits.

expr1 + expr2   Add 'expr1' and 'expr2'. If numbers, then arithmetic addition
                is used. If one of the expressions are a string, then that
                string is concatenated with the other value.

expr1 - expr2   Subtract 'expr2' from 'expr1'. Only valid for numeric values.

expr1 * expr2   Multiply 'expr1' with 'expr2'.

expr1 % expr2   The modulo operator of numeric arguments.

expr1 / expr2   Integer division.

++ var          Icrement the value of variable 'var', and return the new
                value.

-- var          Decrement the value of variable 'var', and return the new
                value.

- var           Compute the negative value of 'var'.

! var           Compute the logical 'not' of an integer.

~ var           The boolean 'not' of an integer.

var++           Increment the value of variable 'var', and return the old
                value.
 
var--           Decrement the value of variable 'var', and return the old
                value.

expr1[expr2]    The array given by 'expr1' is indexed by 'expr2'.

expr1->name(...) 'expr1' gives either an object or a string which is converted
                to an object, and calls the function 'name' in this object.
