qlang Q Language (Q语言) qlang

agolangf • 5103 次点击    
这是一个分享于 的项目,其中的信息可能已经有所发展或是发生改变。
# The Q Language (Q语言) # 下载 ### 源代码 <pre class="brush:shell;toolbar: true; auto-links: false;">go get -u qlang.io/qlang</pre> 或者在 src 目录执行如下命令: <pre class="brush:shell;toolbar: true; auto-links: false;">mkdir qiniupkg.com git clone https://github.com/qiniu/qlang.git qlang.io git clone https://github.com/qiniu/text.git qiniupkg.com/text</pre> ### 二进制(qlang.v1) * linux/amd64: [qnlang-linux-amd64.tar.gz](http://open.qiniudn.com/qnlang-linux-amd64.tar.gz) * Mac OS: [qnlang-darwin-amd64.tar.gz](http://open.qiniudn.com/qnlang-darwin-amd64.tar.gz) ### 二进制(qlang.v2) * 性能比qlang.v2更优。暂未提供二进制下载包,请通过源代码自行编译。 # 语言特色 * 最大卖点:与 Go 语言有最好的互操作性。所有 Go 语言的社区资源可以直接为我所用。 * 有赖 Go 语言的互操作性,这门语言不需要自己实现标准库。尽管年轻,但是这门语言已经具备商用的可行性。 * 微内核:语言的核心只有 1200 行代码。所有功能以可插拔的 module 方式提供。 预期的商业场景: * 由于与 Go 语言的无缝配合,qlang 在嵌入式脚本领域有 lua、python、javascript 所不能比拟的优越性。 * 比如:网络游戏中取代 lua 的位置。 ## 样例 ### 最大素数 输入 n,求 &lt; n 的最大素数: * [maxprime.ql](https://github.com/qiniu/qlang/blob/develop/app/qlang/maxprime.ql) 用法: <pre class="brush:shell;toolbar: true; auto-links: false;">qlang maxprime.ql &lt;N&gt;</pre> ### 计算器 实现一个支持四则运算及函数调用的计算器: * [calc.ql](https://github.com/qiniu/qlang/blob/develop/app/qlang/calc.ql) 用法: <pre class="brush:shell;toolbar: true; auto-links: false;">qlang calc.ql</pre> ### qlang自举 qlang的自举(用qlang实现一个qlang): * [qlang.ql](https://github.com/qiniu/qlang/blob/develop/app/qlang/qlang.ql) 交互模式跑 qlang 版本的 qlang(可以认为是上面计算器的增强版本): <pre class="brush:shell;toolbar: true; auto-links: false;">qlang.v1 qlang.ql  #目前暂时只能用qlang.v1版本完成自举</pre> 当然你还可以用 qlang 版本的 qlang 来跑最大素数问题: <pre class="brush:shell;toolbar: true; auto-links: false;">qlang.v1 qlang.ql maxprime.ql &lt;N&gt;</pre> # 使用说明 ## 运算符 基本上除了位运算:&#39;&amp;&#39;、&#39;|&#39;、&#39;&gt;&gt;&#39;、&#39;&lt;&lt;&#39; 和 chan 操作 &#39;&lt;-&#39; 之外,Go 语言的操作符都支持。包括: * &#39;+&#39;、&#39;-&#39;、&#39;*&#39;、&#39;/&#39;、&#39;%&#39;、&#39;=&#39; * &#39;+=&#39;、&#39;-=&#39;、&#39;*=&#39;、&#39;/=&#39;、&#39;%=&#39;、&#39;++&#39;、&#39;--&#39; * &#39;!&#39;、&#39;&gt;=&#39;、&#39;&lt;=&#39;、&#39;&gt;&#39;、&#39;&lt;&#39;、&#39;==&#39;、&#39;!=&#39;、&#39;&amp;&amp;&#39;、&#39;||&#39; ## 类型 原理上支持所有 Go 语言中的类型。典型有: * 基本类型:int、float (在 Go 语言里面是 float64)、string、byte、bool、var(在 Go 语言里面是 interface{})。 * 复合类型:slice、map * 用户自定义:函数(闭包)、类成员函数、类 ## 常量 * 布尔类型:true、false(由 builtin 模块支持) * var 类型:nil(由 builtin 模块支持) * 浮点类型:pi、e、phi (由 math 模块支持) ## 变量及初始化 ### 基本类型 <pre class="brush:cpp ;toolbar: true; auto-links: false;">a = 1 // 创建一个 int 类型变量,并初始化为 1 b = &#34;hello&#34; // string 类型 c = true // bool 类型 d = 1.0 // float 类型 e = &#39;h&#39; // byte 类型</pre> ### string 类型 <pre class="brush:cpp ;toolbar: true; auto-links: false;">a = &#34;hello, world&#34;</pre> 和 Go 语言类似,string 有如下内置的操作: <pre class="brush:cpp ;toolbar: true; auto-links: false;">a = &#34;hello&#34; + &#34;world&#34; // + 为字符串连接操作符 n = len(a) // 取 a 字符串的长度 b = a[1] // 取 a 字符串的某个字符,得到的 b 是 byte 类型 c = a[1:4] // 取子字符串</pre> ### slice 类型 <pre class="brush:cpp ;toolbar: true; auto-links: false;">a = [1, 2, 3] // 创建一个 int slice,并初始化为 [1, 2, 3] b = [1, 2.3, 5] // 创建一个 float slice c = [&#34;a&#34;, &#34;b&#34;, &#34;c&#34;] // 创建一个 string slice d = [&#34;a&#34;, 1, 2.3] // 创建一个 var slice (等价于 Go 语言的 []interface{}) e = slice(&#34;int&#34;, len, cap) // 创建一个 int slice,并将长度设置为 len,容量设置为 cap f = slice(type(e), len, cap) // 创建一个 int slice 的 slice,也就是 Go 语言里面的 [][]int</pre> 和 Go 语言类似,slice 有如下内置的操作: <pre class="brush:cpp ;toolbar: true; auto-links: false;">a = append(a, 4, 5, 6) // 含义与 Go 语言完全一致 n = len(a) // 取 a 的元素个数 b1 = b[2] // 取 b 这个 slice 的第二个元素 set(b, 2, 888) // 设置 b 这个 slice 的第二个元素的值为 888。在 Go 语言里面是 b[2] = 888 set(b, 1, 777, 2, 888, 3, 999) // Go 里面是:b[1], b[2], b[3] = 777, 888, 999 b2 = b[1:4] // Go 里面是: b2 = b[1:4]</pre> 特别地,在 qlang 中可以这样赋值: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x, y, z = [1, 2, 3]</pre> 结果是 x = 1, y = 2, z = 3。这是 qlang 和 Go 语言的基础设计不同导致的: * qlang 不支持多返回值。对于那些返回了多个值的 Go 函数,在 qlang 会理解为返回 var slice,也就是 []interface{}。 举个例子: <pre class="brush:cpp ;toolbar: true; auto-links: false;">f, err = os.open(fname)</pre> 这个例子,在 Go 里面返回的是 (*os.File, error)。但是 qlang 中是 var slice。 ### map 类型 <pre class="brush:cpp ;toolbar: true; auto-links: false;">a = {&#34;a&#34;: 1, &#34;b&#34;: 2, &#34;c&#34;: 3} // 得到 map[string]int 类型的对象 b = {&#34;a&#34;: 1, &#34;b&#34;, 2.3, &#34;c&#34;: 3} // 得到 map[string]float64 类型的对象 c = {1: &#34;a&#34;, 2: &#34;b&#34;, 3: &#34;c&#34;} // 得到 map[int]string 类型的对象 d = {&#34;a&#34;: &#34;hello&#34;, &#34;b&#34;: 2.0, &#34;c&#34;: true} // 得到 map[string]interface{} 类型的对象 e = map(&#34;string:int&#34;) // 创建一个空的 map[string]int 类型的对象 f = map(mapOf(&#34;string&#34;, type(e))) // 创建一个 map[string]map[string]int 类型的对象</pre> 和 Go 语言类似,map 有如下内置的操作: <pre class="brush:cpp ;toolbar: true; auto-links: false;">n = len(a) // 取 a 的元素个数 x = a[&#34;b&#34;] // 取 a map 中 key 为 &#34;b&#34; 的元素 x = a.b // 含义同上 set(a, &#34;e&#34;, 4, &#34;f&#34;, 5, &#34;g&#34;, 6) // 在 Go 语言里面是 a[&#34;e&#34;], b[&#34;f&#34;], b[&#34;g&#34;] = 4, 5, 6 delete(a, &#34;e&#34;) // 删除 a map 中的 &#34;e&#34; 元素</pre> 需要注意的是,a[&#34;b&#34;] 的行为和 Go 语言中略有不同。在 Go 语言中,常见的范式是: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x := map[string]int{&#34;a&#34;: 1, &#34;b&#34;: 2} a, ok := x[&#34;a&#34;] // 结果:a = 1, ok = true if ok { // 判断a存在的逻辑     ... } c, ok2 := x[&#34;c&#34;] // 结果:c = 0, ok2 = false d := x[&#34;d&#34;] // 结果:d = 0</pre> 而在 qlang 中是这样的: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x = {&#34;a&#34;: 1, &#34;b&#34;: 2} a = x[&#34;a&#34;] // 结果:a = 1 if a != undefined { // 判断a存在的逻辑     ... } c = x[&#34;c&#34;] // 结果:c = undefined,注意不是0,也不是nil d = x[&#34;d&#34;] // 结果:d = undefined,注意不是0,也不是nil</pre> ## 类型转换 ### 自动类型转换 大部分情况下,我们不会自动进行类型转换。一些例外是: * 如果一个函数接受的是 float,但是传入的是 int,会进行自动类型转换。 ### 强制类型转换 <pre class="brush:cpp ;toolbar: true; auto-links: false;">a = int(&#39;a&#39;) // 强制将 byte 类型转为 int 类型 b = float(b) // 强制将 int 类型转为 float 类型 c = string(&#39;a&#39;) // 强制将 byte 类型转为 string 类型</pre> ## 流程控制 ### if语句 <pre class="brush:cpp ;toolbar: true; auto-links: false;">if booleanExpr1 {     // ... } elif booleanExpr2 {     // ... } elif booleanExpr3 {     // ... } else {     // ... }</pre> 需要注意的是,if 语句是有值的。比如: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x = if a &lt; b { a } else { b } // x 取 a 和 b 两者中的小值。即 x = min(a, b)</pre> 如果你希望使用 if 表达式的值,建议不要写成多行: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x = if a &lt; b {     a } else {     b }</pre> 这段代码不会如你所愿工作。原因是编译器会在行末自动插入 &#39;;&#39;,所以相当于是: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x = if a &lt; b { a; } else { b; }</pre> 结果是 `x = nil`。 ### switch语句 <pre class="brush:cpp ;toolbar: true; auto-links: false;">switch expr { case expr1:     // ... case expr2:     // ... default:     // ... }</pre> 或者: <pre class="brush:cpp ;toolbar: true; auto-links: false;">switch { case booleanExpr1:     // ... case booleanExpr2:     // ... default:     // ... }</pre> ### for语句 除了不支持 for range 文法,也不支持中途 continue、break(但是支持 return)。其他和 Go 语言完全类似: <pre class="brush:cpp ;toolbar: true; auto-links: false;">for { // 无限循环,需要在中间 return,或者 os.exit(code),否则不能退出     ... } for booleanExpr { // 类似很多语言的 while 循环     ... } for initExpr; conditionExpr; stepExpr {     ... }</pre> 典型例子: <pre class="brush:cpp ;toolbar: true; auto-links: false;">for i = 0; i &lt; 10; i++ {     ... }</pre> ## 函数 ### 函数和闭包 基本语法: <pre class="brush:cpp ;toolbar: true; auto-links: false;">funcName = fn(arg1, arg2, argN) {         //...     return expr }</pre> 这就定义了一个名为 funcName 的函数。 本质上来说,函数只是和 1、&#34;hello&#34; 类似的一个值,只是值的类型是函数类型。 所有的用户自定义函数,在 Go 里面实际类型均为 func(args ...interface{}) interface{}。 你可以在一个函数中引用外层函数的变量。如: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x = fn(a) {     b = 1     y = fn(t) {         return b + t     }     return y(a) } println(x(3)) // 结果为 4</pre> 但是如果你直接修改外层变量会报错: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x = fn(a) {     b = 1     y = fn(t) {         b = t // 这里会抛出异常,因为不能确定你是想定义一个新的 b 变量,还是要修改外层 x 函数的 b 变量     }     y(a)     return b }</pre> 如果你想修改外层变量,需要先引用它,如下: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x = fn(a) {     b = 1     y = fn(t) {         b; b = t // 现在正常了,我们知道你要修改外层的 b 变量     }     y(a)     return b } println(x(3)) // 输出 3</pre> ### 私有变量 在 qlang 中,我们引入了私有变量的概念。 私有变量以 _ 开头来标识。私有变量只能在本函数内使用,并且互不干扰(没有名字冲突)。如: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x = fn(a) {     _b = 1     y = fn(t) {         _b = t     }     y(a)     return _b } println(x(3)) // 输出 1,因为 y(a) 这个调用并不影响 x 函数中的 _b 变量</pre> ### 可变参数 如 Go 语言类似,qlang 也支持可变参数的函数。例如内置的 max、min 都是可变参数的: <pre class="brush:cpp ;toolbar: true; auto-links: false;">a = max(1.2, 3, 5, 6) // a 的值为 float 类型的 6 b = max(1, 3, 5, 6) // b 的值为 int 类型的 6</pre> 也可以自定义一个可变参数的函数,如: <pre class="brush:cpp ;toolbar: true; auto-links: false;">x = fn(fmt, args...) {         printf(fmt, args...) }</pre> 这样就得到了一个 x 函数,功能和内建的 printf 函数一模一样。 ## defer 是的,qlang 也支持 defer。这在处理系统资源(如文件、锁等)释放场景非常有用。一个典型场景: <pre class="brush:cpp ;toolbar: true; auto-links: false;">f, err = os.open(fname) if err != nil {     // 做些出错处理     return } defer f.close() // 正常操作这个 f 文件</pre> 值得注意的是: 在一个细节上 qlang 的 defer 和 Go 语言处理并不一致,那就是 defer 表达式中的变量值。在 Go 语言中,所有 defer 引用的变量均在 defer 语句时刻固定下来(如上面的 f 变量),后面任何修改均不影响 defer 语句的行为。但 qlang 是会受到影响的。例如,假设你在 defer 之后,调用 f = nil 把 f 变量改为 nil,那么后面执行 f.close() 时就会 panic。 ## 类 一个用户自定义类型的基本语法如下: <pre class="brush:cpp ;toolbar: true; auto-links: false;">Foo = class {     fn setAB(a, b) {         set(this, &#34;a&#34;, a, &#34;b&#34;, b)     }     fn getA() {         return this.a     } }</pre> 有了这个 class Foo,我们就可以创建 Foo 类型的 object 了: <pre class="brush:cpp ;toolbar: true; auto-links: false;">foo = new Foo foo.setAB(3, &#34;hello&#34;) a = foo.getA() println(a) // 输出 3</pre> ### 构造函数 在 qlang 中,构造函数只是一个名为 _init 的成员方法(method): <pre class="brush:cpp ;toolbar: true; auto-links: false;">Foo = class {     fn _init(a, b) {         set(this, &#34;a&#34;, a, &#34;b&#34;, b)     } }</pre> 有了这个 class Foo 后,我们 new Foo 时就必须携带2个构造参数了: <pre class="brush:cpp ;toolbar: true; auto-links: false;">foo = new Foo(3, &#34;hello&#34;) println(foo.a) // 输出 3</pre> ## 模块及模块import 在 qlang 中的确有模块的概念,但是并不像大部分脚本语言那样,有模块的 import 语法。 要理解这一点,最关键是理解 qlang 的核心理念: * qlang 是一个嵌入式语言,它的定位是作为 Go 语言应用的运行时嵌入脚本。 * 我们鼓励依据应用按需求对 qlang 进行裁剪。这也是 qlang 采用微内核设计的原因。 看起来 qlang 将自己的使用范围收得非常窄,但考虑到 Go 语言在未来的霸主地位,qlang 有着广阔的应用空间。 而在 Go 语言应用的嵌入脚本领域而言,qlang 有无可比拟的核心优势: * 任何 Go 语言的函数,都可以不做任何包装直接在 qlang 中使用 这太爽了! ### 定制qlang 尽管 qlang 本身没有 import,但是 qlang 的 Go 语言包支持 import。通过 import 你可以自由定制你想要的 qlang 的样子。在没有引入任何模块的情况下,qlang 连最基本的 &#39;+&#39;、&#39;-&#39;、&#39;*&#39;、&#39;/&#39; 都做不了,因为提供这个能力的是 builtin 包。 所以一个最基础版本的 qlang 应该是这样的: <pre class="brush:cpp ;toolbar: true; auto-links: false;">import (     &#34;fmt&#34;     &#34;qlang.io/qlang.v2/qlang&#34;     _ &#34;qlang.io/qlang/builtin&#34; // 导入 builtin 包 ) func main() {     lang, err := qlang.New(nil) // 参数 nil 也可以改为 qlang.InsertSemis     if err != nil {         // 错误处理         return     }     err = lang.SafeEval(`1 + 2`)     if err != nil {         // 错误处理         return     }     v, _ := lang.Ret()     fmt.Println(v) // 输出 3 }</pre> 在大部分正式的场合,qlang.New 传入 qlang.InsertSemis 会更多一些。它表示在各行的末尾智能的插入 &#39;;&#39;。但是在本例中我们希望执行的是一个表达式,而不是语句,所以传入 nil 参数更为合适。如果我们改为传入 qlang.InsertSemis,那么得到的结果就不再是 3,而是 nil。因为表达式 `1+2` 的结果是 3,但是表达式 `1+2;` 的结果是 nil。 有了这个基础版本以后,我们可以自由添加各种模块,如: <pre class="brush:cpp ;toolbar: true; auto-links: false;">import (     &#34;qlang.io/qlang/math&#34;     &#34;qlang.io/qlang/strconv&#34;     &#34;qlang.io/qlang/strings&#34;     ... ) func main() {     qlang.Import(&#34;math&#34;, math.Exports)     qlang.Import(&#34;strconv&#34;, strconv.Exports)     qlang.Import(&#34;strings&#34;, strings.Exports)     ... }</pre> 这样,在 qlang 中就可以用 math.sin, strconv.itoa 等函数了。 如果你嫌 math.sin 太长,还可以将 math 模块作为 builtin 功能导入。这只需要略微修改下导入的文法: <pre class="brush:cpp ;toolbar: true; auto-links: false;">qlang.Import(&#34;&#34;, math.Exports) // 如此,你就可以直接用 sin 而不是 math.sin 了</pre> ### 制作qlang模块 制作qlang的模块成本极其低廉。我们打开 `qlang.io/qlang/strings` 看看它是什么样的: <pre class="brush:cpp ;toolbar: true; auto-links: false;">package strings import (     &#34;strings&#34; ) var Exports = map[string]interface{}{     &#34;contains&#34;:  strings.Contains,     &#34;index&#34;:     strings.Index,     &#34;indexAny&#34;:  strings.IndexAny,     &#34;join&#34;:      strings.Join,     &#34;title&#34;:     strings.Title,     &#34;toLower&#34;:   strings.ToLower,     &#34;toTitle&#34;:   strings.ToTitle,     &#34;toUpper&#34;:   strings.ToUpper,     &#34;trim&#34;:      strings.Trim,     &#34;reader&#34;:    strings.NewReader,     &#34;replacer&#34;:  strings.NewReplacer,     ... }</pre> 值得注意的一个细节是,我们并没有对 Go 语言的 strings package 进行包装。比如上面的我们导出了 strings.NewReplacer,但是我们不必去包装 strings.Replacer 类。这个类的所有功能可以直接使用。如: <pre class="brush:cpp ;toolbar: true; auto-links: false;">strings.replacer(&#34;?&#34;, &#34;!&#34;).replace(&#34;hello, world???&#34;) // 得到 &#34;hello, world!!!&#34;</pre> 这是 qlang 最强大的地方,近乎免包装。甚至,你可以写一个自动的 Go package 转 qlang 模块的工具,找到 Go package 所有导出的全局函数,加入到 Exports 表即完成了该 Go package 的包装,几乎零成本。 ## 反射 在任何时候,你都可以用 type 函数来查看一个变量的实际类型,结果在 Go 语言中是 reflect.Type。如: <pre class="brush:cpp ;toolbar: true; auto-links: false;">t1 = type(1) // 相当于调用 Go 语言中的 reflect.TypeOf</pre> 用 type 可以很好地研究 qlang 的内在实现。比如: <pre class="brush:cpp ;toolbar: true; auto-links: false;">t2 = type(fn() {})</pre> 我们得到了 *qlang.Function。这说明尽管用户自定义的函数原型多样,但是其 Go 类型是一致的。 我们也可以看看用户自定义的类型: <pre class="brush:cpp ;toolbar: true; auto-links: false;">Foo = class { fn f() {} } t1 = type(Foo) t2 = type(Foo.f) foo = new Foot3 = type(foo) t4 = type(foo.f)</pre> 可以看到,class Foo 的 Go 类型是 *qlang.Class,而 object foo 的 Go 类型是 *qlang.Object。而 Foo.f 和普通用户自定义函数一致,也是 *qlang.Function,但 foo.f 不一样,它是 *qlang.thisDref 类型。 # 附录 ## 样例代码 ### 求最大素数 <pre class="brush:cpp ;toolbar: true; auto-links: false;">primes = [2, 3] n = 1 limit = 9 isPrime = fn(v) {     for i = 0; i &lt; n; i++ {         if v % primes[i] == 0 {             return false         }     }     return true } listPrimes = fn(max) {     v = 5     for {         for v &lt; limit {             if isPrime(v) {                 primes = append(primes, v)                 if v * v &gt;= max {                     return                 }             }             v += 2         }         v += 2         n; n++         limit = primes[n] * primes[n]     } } maxPrimeOf = fn(max) {     if max % 2 == 0 {         max--     }     listPrimes(max)     n; n = len(primes)     for {         if isPrime(max) {             return max         }         max -= 2     } } // Usage: maxprime &lt;Value&gt; // if len(os.args) &lt; 2 {     fprintln(os.stderr, &#34;Usage: maxprime &lt;Value&gt;&#34;)     return } max, err = strconv.parseInt(os.args[1], 10, 64) if err != nil {     fprintln(os.stderr, err)     return 1 } if max &lt; 8 { // &lt;8 的情况下,可直接建表,答案略     return } max-- v = maxPrimeOf(max) println(v)</pre> ### 计算器 <pre class="brush:cpp ;toolbar: true; auto-links: false;">grammar = ` term = factor *(&#39;*&#39; factor/mul | &#39;/&#39; factor/quo | &#39;%&#39; factor/mod) doc = term *(&#39;+&#39; term/add | &#39;-&#39; term/sub) factor =     FLOAT/pushFloat |     &#39;-&#39; factor/neg |     &#39;(&#39; doc &#39;)&#39; |     (IDENT &#39;(&#39; doc %= &#39;,&#39;/ARITY &#39;)&#39;)/call ` fntable = nil Stack = class {     fn _init() {         set(this, &#34;stk&#34;, [])     }     fn clear() {         set(this, &#34;stk&#34;, this.stk[:0])     }     fn pop() {         n = len(this.stk)         if n &gt; 0 {             v = this.stk[n-1]             set(this, &#34;stk&#34;, this.stk[:n-1])             return [v, true]         }         return [nil, false]     }     fn push(v) {         set(this, &#34;stk&#34;, append(this.stk, v))     }     fn popArgs(arity) {         n = len(this.stk)         if n &lt; arity {             panic(&#34;Stack.popArgs: unexpected&#34;)         }         args = sliceFrom(this.stk[n-arity:n]...)         set(this, &#34;stk&#34;, this.stk[:n-arity])         return args     } } Calculator = class {     fn _init() {         set(this, &#34;stk&#34;, new Stack)     }     fn grammar() {         return grammar     }     fn stack() {         return this.stk     }     fn fntable() {         return fntable     }     fn ret() {         v, _ = this.stk.pop()         this.stk.clear()         return v     }     fn call(name) {         f = fntable[name]         if f == nil {             panic(&#34;function not found: &#34; + name)         }         arity, _ = this.stk.pop()         args = this.stk.popArgs(arity)         ret = f(args...)         this.stk.push(ret)     } } fntable = {     &#34;sin&#34;: sin,     &#34;cos&#34;: cos,     &#34;pow&#34;: pow,     &#34;max&#34;: max,     &#34;min&#34;: min,     &#34;$mul&#34;: fn(a, b) { return a*b },     &#34;$quo&#34;: fn(a, b) { return a/b },     &#34;$mod&#34;: fn(a, b) { return a%b },     &#34;$add&#34;: fn(a, b) { return a+b },     &#34;$sub&#34;: fn(a, b) { return a-b },     &#34;$neg&#34;: fn(a) { return -a },     &#34;$call&#34;: Calculator.call,     &#34;$pushFloat&#34;: Stack.push,     &#34;$ARITY&#34;: Stack.push, } calc = new Calculator engine, err = interpreter(calc, nil) if err != nil {     fprintln(os.stderr, err)     return 1 } scanner = bufio.scanner(os.stdin) for scanner.scan() {     line = strings.trim(scanner.text(), &#34; \t\r\n&#34;)     if line != &#34;&#34; {         err = engine.eval(line)         if err != nil {             fprintln(os.stderr, err)         } else {             printf(&#34;&gt; %v\n\n&#34;, calc.ret())         }     } }</pre>
授权协议:
其他
开发语言:
Google Go 查看源码»
操作系统:
跨平台
5103 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传