Crystal Crystal 语言, 书写 Ruby 式语法, 编译执行代码

jiyinyiyong · December 29, 2013 · Last by wilikeit replied at May 13, 2018 · 11928 hits

玩的时候用了 .cr 作为后缀,结果被 GitHub 识别成其他语言了, 跑过去一看,真有这么个编程语言抢我后缀.. Crystal 语言,Ruby 的语法,代码是编译执行的:

http://crystal-lang.org/

# Compute prime numbers up to 100 with the Sieve of Eratosthenes
max = 100

sieve = Array.new(max, true)
sieve[0] = false
sieve[1] = false

(2...max).each do |i|
  if sieve[i]
    (2 * i).step(max - 1, i) do |j|
      sieve[j] = false
    end
  end
end

sieve.each_with_index do |prime, number|
  puts number if prime
end

搜索论坛被提到过一次 http://ruby-china.org/topics/10389

Crystal 的博客上提到下面几点:

  • Garbage Collector
  • Bootstraped the compiler
  • Type inference (part 1)
  • Shortcut syntax for writing one-argument blocks
  • Null Pointer Exception

具体代码还是 pre-alpha 的... 等正式发布玩一下... 不过不知道发布要几几年啊..

用的 ruby-llvm 之前也想用来着 但是本地一直没装上 T_T

我也初步玩了下 感觉确实不赖啊 不过很奇怪为何单引号没有实现??

语法好亲切。

疑惑起来了.. 在知乎发了个帖问 http://www.zhihu.com/question/22399630 自己在学做很简单的脚本语言,, 类型推断究竟能多大程度让动态语言有静态语言的效率呢? 是所有的语言,能够类型推断的话,就都能够被编译成二进制代码吗?

#4 楼 @jiyinyiyong

不能啊,类型推断算法本身有复杂度限制啊 ...

Inferring Recursive Data Types 里面总结了几种不同的类型系统,对应的类型推断算法的复杂度,有几种是 P/NP HARD 的 ...

是所有的语言,能够类型推断的话,就都能够被编译成二进制代码吗?

不需要类型推断,也能编译成二进制代码的。只是运行起来可能还没解释执行快

看了下官网,这个语言好像最近几个月才有 GC...

#5 楼 @bhuztez 我理解的类型推断,就是基于递归的,对前束范式,把全称量词都替换掉,除此之外还有其他途径吗?

#7 楼 @rasefon 我看不懂那么多术语 ....

#8 楼 @bhuztez 就是倒 Ax x->y,然后把 x,y 用各种实际的类型去替换,比如 int,bool 这类,然后就可以做比较了。

有时候这种类型变量(占位符)可能不是只有一种类型,会是一个可能的类型的集合,这种时候就比较难办了。

#5 楼 @bhuztez #4 楼 @jiyinyiyong

JIT 不就是编译成二进制码执行吗

#10 楼 @rasefon

我理解的类型推断相当于在编译期运行个类似 Prolog 的东西

比如

fun f(a, b) { return a+b; }

改写成

f(A, B, Ret) :-
  +(A, B, Ret)

就好了

#12 楼 @bhuztez 这个例子里面的类型,假如这个语言只支持 int,那么类型就应该是 int x int -> int。

#4 楼 @jiyinyiyong 编译到二进制很简单的,把 vm 指令都改成函数调用就可以了,其实和解释执行区别不大... 如果把 vm 指令的实现内联进来,就能省掉 call 的开销,但编译出来的结果就会很大 (这里很矛盾的,如果用提取公共子表达式之类的优化手段,就退化回前面那种代码了). 所以一般都选择性的内联,和选择性的优化 (编译时间比执行时间还长的话,即时编译也没有意义了)

编译期类型作用就是去掉运行时的类型检查和方法查找开销,如果调用目标代码固定了就能内联进来 -- 但动态语言就是不固定的,很多脚本的大部分代码就是只运行一遍,延迟到运行时检查速度更快。还有个作用是利用数据类型的知识,减少从 tagged pointer 拆箱/装箱的开销,减少从对象取成员/写成员的开销 -- 但是这种优化之前往往要做逃逸分析,对象逃不出去才能这么做,而只要语言支持 continuation 或者比较强的运行时反射,基本都逃逸了... 另外,声明成 immutable 的变量就可以不用做逃逸分析直接拆箱。

#14 楼 @luikore 都设计成传值调用不就不用关心逃逸变量了嘛哈哈。

#14 楼 @luikore 术语超多 >_< 还只会很基础的解释器... 直观感觉是一路 type assertion...

if block.Tag == "block" {
  if item, ok := block.Value.(context); ok {
    runtime := Env{}
    for i, para := range item.args {
      // println("i is:", i)
      // debugPrint(xs)
      if token, ok := para.(cirru.Token); ok {
        runtime[token.Text] = cirruGet(env, xs[i+1:i+2])
      }
    }
    for _, line := range item.code {
      if exp, ok := line.(cirru.List); ok {
        ret = Evaluate(&runtime, exp)
      }
    }
    return
  }
}

https://github.com/Cirru/cirru-gopher/blob/master/block.go#L25

距离编译好远..

#16 楼 @jiyinyiyong 果断 Erlang 搞起啊 ...

#17 楼 @bhuztez 直接 elixir 啊

#18 楼 @krazy Elixir 丑爆了 ...

#17 楼 @bhuztez 太跳跃啊,为啥要 Erlang?

#19 楼 @bhuztez 仔细一想 =/= =:=操作符,各种 module attributes 和表达式后面的逗号句号以及字符串处理也没什么不可以接受的...

#16 楼 @jiyinyiyong 编译器和解释器代码构造相似,真正干事情的地方改成打印或者拼字符串就可以了。你可以先弄个输出 js/C 代码的编译器,会玩了就可以尝试输出 llvm 字节码了 (如果用 llvm 的话,建议实现语言选 C++, 可以少走很多弯路). 想直接输出 x86 也不难,可以看看 libjit 和 xbyak, 不过要先学习 calling convention 和熟悉 stack layout.

.NET DLR 上做语言更简单,把树给它就好了。

关键词都给你了,自己看 wiki 咯。

#22 楼 @luikore thanks, long way to go.

ruby 出世的时候,为什么没做成静态语言,那就没 jruby、crystal 什么事了

Reply to wilikeit

感觉做成静态的会丢失很多的用户... Gradual Type 还是不大一样

Reply to jiyinyiyong

不过静态语言的话,做成 ruby 那种,性能估计也不能保证

You need to Sign in before reply, if you don't have an account, please Sign up first.