锡兰,2422字节
(我认为这是我最长的高尔夫课程。)
import ceylon.language{sh=shared,va=variable,fo=formal,O=Object}import ceylon.language.meta.model{F=Function}interface X{sh fo V v(S t);sh fo G g;}class G(va Map<S,V>m)satisfies X{v(S t)=>m[t]else nV;g=>this;sh S d(S s,V v){assert(!s in m);m=map{s->v,*m};return s;}}V nV=>nothing;class LC(G c,Map<S,V>m)satisfies X{g=>c;v(S t)=>m[t]else g.v(t);}alias C=>V|Co;interface Co{sh fo C st();}interface V{sh fo C l(X c,V[]a);sh default Boolean b=>0<1;sh fo C vO(X c);sh default V vF(X c){va C v=vO(c);while(is Co n=v){v=n.st();}assert(is V r=v);return r;}}class L(sh V*i)satisfies V{vO(X c)=>if(nonempty i)then i[0].vF(c).l(c,i.rest)else this;equals(O o)=>if(is L o)then i==o.i else 1<0;b=>!i.empty;string=>"(``" ".join(i)``)";hash=>i.hash;sh actual C l(X c,V[]p){value[h,ns,x]=i.size<3then[f,i[0],i[1]]else[m,i[1],i[2]];value n=if(is L ns)then[*ns.i.narrow<S>()]else ns;assert(is S|S[]n,is V x);V[]a=h(c,p);LC lC=if(is S n)then LC(c.g,map{n->L(*a)})else LC(c.g,map(zipEntries(n,a)));return object satisfies Co{st()=>x.vO(lC);};}}class S(String n)satisfies V{vO(X c)=>c.v(this);l(X c,V[]a)=>nV;equals(O o)=>if(is S o)then n==o.n else 1<0;hash=>n.hash;string=>n;}class I(sh Integer i)satisfies V{vO(X c)=>this;l(X c,V[]a)=>nV;equals(O o)=>if(is I o)then i==o.i else 1<0;hash=>i;b=>!i.zero;string=>i.string;}V[]f(X c,V[]a)=>[for(v in a)v.vF(c)];V[]m(X c,V[]a)=>a;L c(X c,V h,L t)=>L(h,*t.i);V h(X c,L l)=>l.i[0]else L();V t(X c,L l)=>L(*l.i.rest);I s(X c,I f,I s)=>I(f.i-s.i);I l(X c,I f,I s)=>I(f.i<s.i then 1else 0);I e(X c,V v1,V v2)=>I(v1==v2then 1else 0);C v(X c,V v)=>v.vO(c);V q(X c,V a)=>a;C i(X c,V d,V t,V f)=>d.vF(c).b then t.vO(c)else f.vO(c);S d(X c,S s,V x)=>c.g.d(s,x.vF(c));class B<A>(F<C,A>nat,V[](X,V[])h=f)satisfies V given A satisfies[X,V+]{vO(X c)=>nV;string=>nat.declaration.name;l(X c,V[]a)=>nat.apply(c,*h(c,a));}{<S->V>*}b=>{S("c")->B(`c`),S("h")->B(`h`),S("t")->B(`t`),S("s")->B(`s`),S("l")->B(`l`),S("e")->B(`e`),S("v")->B(`v`),S("q")->B(`q`,m),S("i")->B(`i`,m),S("d")->B(`d`,m)};[V*]p(String inp){value ts=inp.split(" \n()".contains,1<0,1<0);va[[V*]*]s=[];va[V*]l=[];for(t in ts){if(t in" \n"){}else if(t=="("){s=[l,*s];l=[];}else if(t==")"){l=[L(*l.reversed),*(s[0]else[])];s=s.rest;}else if(exists i=parseInteger(t),i>=0){l=[I(i),*l];}else{l=[S(t),*l];}}return l.reversed;}sh void run(){va value u="";while(exists l=process.readLine()){u=u+l+"\n";}V[]e=p(u);X c=G(map(b));for(v in e){print(v.vF(c));}}
我可能会打更多的字节,因为在某些地方使用了两个字母的标识符,但是对于那些字母,我已经用完了一些有意义的单个字母。尽管就这样,它看起来也不像锡兰。
这是一个面向对象的实现。
我们有一个V
实现类的值接口L
(列表–只是围绕Ceylon序列的包装器V
),S
(符号–围绕字符串的包装器),I
(整数–围绕Ceylon整数的包装器)和B
(内置函数或宏,围绕a的包装器)锡兰功能)。
我通过实现equals
方法(和hash
属性,实际上仅是符号所需要的)以及string
输出的标准属性来使用标准的锡兰等式符号。
我们有一个Boolean属性b
(默认情况下为true,将被覆盖I
并L
为空列表返回false),还有两个方法l
(调用,即将此对象用作函数)和vO
(评估一个步骤)。两者都返回一个值或一个Continuation对象,该对象然后允许再进行一步vF
评估,并且(完全评估)循环直到结果不再是连续。
上下文接口允许访问变量。有两种实现方式,分别G
是全局上下文(允许使用d
内置LC
函数添加变量)和局部上下文,它们在评估用户函数的表达式时有效(它会退回到全局上下文)。
符号评估访问上下文,列表(如果非空)首先评估其第一个元素,然后调用其call方法进行评估。调用仅由列表和内建函数实现–它首先评估参数(如果是函数,而不是宏),然后进行实际有趣的操作–对于内建函数,硬编码的是内建函数,对于列表则创建新的本地上下文并返回一个继续。
对于内建函数,我使用了与Shift Interpreter中类似的技巧,该技巧使我可以使用所需的参数类型来定义它们,但可以使用反射使用通用序列来调用它们(类型将在调用时进行检查)。这避免了函数/宏内部的类型转换/声明麻烦,但是需要顶级函数,因此我可以获得它们的元模型Function
对象。
该p
(解析)函数把在空格,换行和括号的字符串,然后遍历的标记,并建立使用堆栈和运行列表清单。
然后,解释器(在run
方法中,是入口)在列表中使用表达式(只是值),对每个表达式求值并打印结果。
以下是带有注释并通过格式化程序运行的版本。
我开始打高尔夫球之前的较早版本(仍然有一些关于列表评估的误解)可以在我的Github存储库中找到,我会尽快将其放在那里(因此,如果需要原始版本,请确保查看第一个版本)。
// Tiny Lisp, tiny interpreter
//
// An interpreter for a tiny subset of Lisp, from which most of the
// rest of the language can be bootstrapped.
//
// Question: https://codegolf.stackexchange.com/q/62886/2338
// My answer: https://codegolf.stackexchange.com/a/63352/2338
//
import ceylon.language {
sh=shared,
va=variable,
fo=formal,
O=Object
}
import ceylon.language.meta.model {
F=Function
}
// context
interface X {
sh fo V v(S t);
sh fo G g;
}
// global (mutable) context, with the buildins
class G(va Map<S,V> m) satisfies X {
// get entry throws error on undefined variables.
v(S t) => m[t] else nV;
g => this;
sh S d(S s, V v) {
// error when already defined
assert (!s in m);
// building a new map is cheaper (code-golf wise) than having a mutable one.
m = map { s->v, *m };
return s;
}
}
// This is simply a shorter way of writing "this is not an allowed operation".
// It will throw an exception when trying to access it.
// nV stands for "no value".
V nV => nothing;
// local context
class LC(G c, Map<S,V> m) satisfies X {
g => c;
v(S t) => m[t] else g.v(t);
// sh actual String string => "[local: ``m``, global: ``g``]";
}
// continuation or value
alias C => V|Co;
// continuation
interface Co {
sh fo C st();
}
// value
interface V {
// use this as a function and call with arguments.
// will not work for all types of stuff.
sh fo C l(X c, V[] a);
// check the truthiness. Defaults to true, as
// only lists and integers can be falsy.
sh default Boolean b => 0 < 1;
// evaluate once (return either a value or a continuation).
// will not work for all kinds of expression.
sh fo C vO(X c);
/// evaluate fully
sh default V vF(X c) {
va C v = vO(c);
while (is Co n = v) {
v = n.st();
}
assert (is V r = v);
return r;
}
}
class L(sh V* i) satisfies V {
vO(X c) => if (nonempty i) then i[0].vF(c).l(c, i.rest) else this;
equals(O o) => if (is L o) then i == o.i else 1 < 0;
b => !i.empty;
string => "(``" ".join(i)``)";
hash => i.hash;
sh actual C l(X c, V[] p) {
value [h, ns, x] =
i.size < 3
then [f, i[0], i[1]]
else [m, i[1], i[2]];
// parameter names – either a single symbol, or a list of symbols.
// If it is a list, we filter it to throw out any non-symbols.
// Should throw an error if there are any, but this is harder.
value n = if (is L ns) then [*ns.i.narrow<S>()] else ns;
assert (is S|S[] n, is V x);
V[] a = h(c, p);
// local context
LC lC = if (is S n) then
LC(c.g, map { n -> L(*a) })
else
LC(c.g, map(zipEntries(n, a)));
// return a continuation instead of actually
// calling it here, to allow stack unwinding.
return object satisfies Co {
st() => x.vO(lC);
};
}
}
// symbol
class S(String n) satisfies V {
// evaluate: resolve
vO(X c) => c.v(this);
// call is not allowed
l(X c, V[] a) => nV;
// equal if name is equal
equals(O o) => if (is S o) then n == o.n else 1 < 0;
hash => n.hash;
string => n;
}
// integer
class I(sh Integer i) satisfies V {
vO(X c) => this;
l(X c, V[] a) => nV;
equals(O o) => if (is I o) then i == o.i else 1 < 0;
hash => i;
b => !i.zero;
string => i.string;
}
// argument handlers for functions or macros
V[] f(X c, V[] a) => [for (v in a) v.vF(c)];
V[] m(X c, V[] a) => a;
// build-in functions
// construct
L c(X c, V h, L t) => L(h, *t.i);
// head
V h(X c, L l) => l.i[0] else L();
// tail
V t(X c, L l) => L(*l.i.rest);
// subtract
I s(X c, I f, I s) => I(f.i - s.i);
// lessThan
I l(X c, I f, I s) => I(f.i < s.i then 1 else 0);
// equal
I e(X c, V v1, V v2) => I(v1 == v2 then 1 else 0);
// eval (returns potentially a continuation)
C v(X c, V v) => v.vO(c);
// build-in macros
// quote
V q(X c, V a) => a;
// if (also returns potentially a continuation)
C i(X c, V d, V t, V f) => d.vF(c).b then t.vO(c) else f.vO(c);
// define symbol in global context
S d(X c, S s, V x) => c.g.d(s, x.vF(c));
// buildin function or macro, made from a native function and an argument handler
class B<A>(F<C,A> nat, V[](X, V[]) h = f)
satisfies V
given A satisfies [X, V+] {
vO(X c) => nV;
string => nat.declaration.name;
// this "apply" is a hack which breaks type safety ...
// but it will be checked at runtime.
l(X c, V[] a) => nat.apply(c, *h(c, a));
}
// define buildins
{<S->V>*} b => {
S("c") -> B(`c`),
S("h") -> B(`h`),
S("t") -> B(`t`),
S("s") -> B(`s`),
S("l") -> B(`l`),
S("e") -> B(`e`),
S("v") -> B(`v`),
S("q") -> B(`q`, m),
S("i") -> B(`i`, m),
S("d") -> B(`d`, m)
};
// parses a string into a list of expressions.
[V*] p(String inp) {
// split string into tokens (retain separators, don't group them –
// whitespace and empty strings will be sorted out later in the loop)
value ts = inp.split(" \n()".contains, 1 < 0, 1 < 0);
// stack of not yet finished nested lists, outer most at bottom
va [[V*]*] s = [];
// current list, in reverse order (because appending at the start is shorter)
va [V*] l = [];
for (t in ts) {
if (t in " \n") {
// do nothing for empty tokens
} else if (t == "(") {
// push the current list onto the stack, open a new list.
s = [l, *s];
l = [];
} else if (t == ")") {
// build a lisp list from the current list,
// pop the latest list from the stack, append the created lisp list.
l = [L(*l.reversed), *(s[0] else [])];
s = s.rest;
} else if (exists i = parseInteger(t), i >= 0) {
// append an integer to the current list.
l = [I(i), *l];
} else {
// append a symbol to the current list.
l = [S(t), *l];
}
}
return l.reversed;
}
// Runs the interpreter.
// This handles input and output, calls the parser and evaluates the expressions.
sh void run() {
va value u = "";
while (exists l = process.readLine()) {
u = u + l + "\n";
}
V[] e = p(u);
// create global context
X c = G(map(b));
// iterate over the expressions, ...
for (v in e) {
// print(" '``v``' → ...");
// ... evaluate each (fully) and print the result.
print(v.vF(c));
}
}