网络编程
位置:首页>> 网络编程>> JavaScript>> 极致之美——百行代码实现全新智能语言Lisp(2)

极致之美——百行代码实现全新智能语言Lisp(2)

作者:月影 来源:51js 发布时间:2010-07-13 13:07:00 

标签:Lisp,javascript

LispScript静态对象构成了LispScript解析器的主体,它只有一个Run方法,该方法用向下递归的方式解析传递进来的LispScript代码,代码的类型——相信细心的读者已经发现了——直接用的是javascript的数组,也就是一系列“[”、“]”和分隔符“,”构成的序列。

用javascript天然的数组特性,使得我们的解析器可以设计得十分简洁——不用去拆分和解析每一个token,于是一段简短到不到50行的代码惊人地实现了整个LispScript解析器的核心!

三个辅助函数的作用分别是为函数迭代提供解析(toEvalString),检测序列异常(Assert,后面的具体实现中其实并没有用到),以及解析指令单词(Element)

接下来我们先定义表达式.表达式或是一个原子[atom],它是一个字母序列(如 foo),或是一个由零个或多个表达式组成的表(list), 表达式之间用逗号分开, 放入一对中括号中. 以下是一些表达式:
(注:原Lisp语法的表达式用空格隔开,放入一对括号中。因是javascript的实现,所以用中括号和逗号较为简洁)

foo
[]
[foo]
[foo,bar]
[a,b,[c],d]

最后一个表达式是由四个元素组成的表, 第三个元素本身是由一个元素组成的表.
在算术中表达式 1 + 1 得出值2. 正确的Lisp表达式也有值. 如果表达式e得出值v,我们说e返回v. 下一步我们将定义几种表达式以及它们的返回值.

如果一个表达式是表,我们称第一个元素为操作符,其余的元素为自变量.我们将定义七个原始(从公理的意义上说)操作符: quote,atom,eq,car,cdr,cons,和 cond.

[quote,x] 返回x. 我们把[quote,x]简记为[_,x].

> [quote,a]
a
> [_,a]
a
> [quote,[a b c]]
[a,b,c][atom,x]返回原子true如果x的值是一个原子或是空表,否则返回[]. 在Lisp中我们按惯例用原子true表示真, 而用空表表示假.

> [atom,[_,a]]
true
> [atom,[_,[a,b,c]]]
[]
> [atom,[_,[]]]
true




既然有了一个自变量需要求值的操作符, 我们可以看一下quote的作用. 通过引用(quote)一个表,我们避免它被求值. 一个未被引用的表作为自变量传给象 atom这样的操作符将被视为代码:

> [atom,[atom,[_,a]]]
true

反之一个被引用的表仅被视为表, 在此例中就是有两个元素的表:

> [atom,[_,[atom,[_,a]]]]
[]

这与我们在英语中使用引号的方式一致. Cambridge(剑桥)是一个位于麻萨诸塞州有90000人口的城镇. 而"Cambridge"是一个由9个字母组成的单词.

引用看上去可能有点奇怪因为极少有其它语言有类似的概念. 它和Lisp最与众不同的特征紧密联系:代码和数据由相同的数据结构构成, 而我们用quote操作符来区分它们.

[eq,x,y]返回t如果x和y的值是同一个原子或都是空表, 否则返回[].

> [eq,[_,a],[_,a]]
true
> [eq,[_,a],[_,b]]
[]
> [eq,[_,[]],[_,[]]]
true


equal = eq = function(arg1, arg2)
{
 var tmp1 = LispScript.Run(arg1);
 var tmp2 = LispScript.Run(arg2);   //先对参数求值

 if(!(tmp1 instanceof Array) && !(tmp2 instanceof Array) &&
  tmp1.toString() == tmp2.toString() ||
  (tmp1 instanceof Function) && (tmp2 instanceof Function) && tmp1.toString() == tmp2.toString() ||
  (tmp1 instanceof Array) && (tmp2 instanceof Array) && (tmp1.length == 0) && (tmp2.length == 0))
  return true;
 else
  return [];
};


[car,x]期望x的值是一个表并且返回x的第一个元素.

> [car,[_,[a b c]]]
a


car = function(arg)
{
 var tmp = LispScript.Run(arg);  //先对参数求值

 if(tmp instanceof Array && tmp.length > 0)
  return tmp[0];
 else
  return [];
};

[cdr,x]期望x的值是一个表并且返回x的第一个元素之后的所有元素.

> [cdr,[_,[a b c]]]
[b,c]


cdr = function(arg)
{
 var tmp = LispScript.Run(arg);  //先对参数求值

 if(tmp instanceof Array && tmp.length > 0)
  return tmp.slice(1);
 else
  return [];
};

[cons,x,y]期望y的值是一个表并且返回一个新表,它的第一个元素是x的值, 后面跟着y的值的各个元素.

> [cons,[_,a],[_,[b,c]]]
[a,b,c]
> [cons,[_,a],[cons,[_,b],[cons,[_,c],[_,[]]]]]
[a,b,c]
> [car,[cons,[_,a],[_,[b c]]]]
a
> [cdr,[cons,[_,a],[_,[b,c]]]]
[b,c]

[cond [...] ...[...]] 的求值规则如下. p表达式依次求值直到有一个返回t. 如果能找到这样的p表达式,相应的e表达式的值作为整个cond表达式的返回值.

> [cond,[[eq,[_,a],[_,b]],[_,first]],
      [,[atom,[_,a]], [_,second]]]
second

当表达式以七个原始操作符中的五个开头时,它的自变量总是要求值的.2 我们称这样 的操作符为函数.

0
投稿

猜你喜欢

手机版 网络编程 asp之家 www.aspxhome.com