Gox語(yǔ)言語(yǔ)法速覽-GX4.2

Gox語(yǔ)言的語(yǔ)法基本脫胎于初始的Q語(yǔ)言(Qlang,現(xiàn)已改變路線,感謝原作者)锈嫩,下面是Q語(yǔ)言語(yǔ)法速覽供參考。

Q Language Manual (Q語(yǔ)言手冊(cè))

運(yùn)算符

基本上 Go 語(yǔ)言的操作符都支持垦搬,且操作符優(yōu)先級(jí)相同呼寸。包括:

  • '+'、'-'猴贰、'*'等舔、'/'、'%'糟趾、'='
  • ''、'&'甚牲、'&'义郑、'|'、'<<'丈钙、'>>'
  • '+='非驮、'-='、'*='雏赦、'/='劫笙、'%='芙扎、'++'、'--'
  • '='填大、'&='戒洼、'&='、'|='允华、'<<='圈浇、'>>='
  • '!'、'>='靴寂、'<='磷蜀、'>'、'<'百炬、'=='褐隆、'!='、'&&'剖踊、'||'
  • '<-' (chan操作符)

類型

原理上支持所有 Go 語(yǔ)言中的類型庶弃。典型有:

  • 基本類型:int、float (在 Go 語(yǔ)言里面是 float64)蜜宪、string虫埂、byte、bool圃验、var(在 Go 語(yǔ)言里面是 interface{})掉伏。
  • 復(fù)合類型:slice、map澳窑、chan
  • 用戶自定義:函數(shù)(閉包)斧散、類成員函數(shù)、類

常量

  • 布爾類型:true摊聋、false(由 builtin 模塊支持)
  • var 類型:nil(由 builtin 模塊支持)
  • 浮點(diǎn)類型:pi鸡捐、e、phi (由 math 模塊支持)

變量及初始化

基本類型

a = 1 // 創(chuàng)建一個(gè) int 類型變量麻裁,并初始化為 1
b = "hello" // string 類型
c = true // bool 類型
d = 1.0 // float 類型
e = 'h' // byte 類型

string 類型

a = "hello, world"

和 Go 語(yǔ)言類似箍镜,string 有如下內(nèi)置的操作:

a = "hello" + "world" // + 為字符串連接操作符
n = len(a) // 取 a 字符串的長(zhǎng)度
b = a[1] // 取 a 字符串的某個(gè)字符,得到的 b 是 byte 類型
c = a[1:4] // 取子字符串

slice 類型

a = [1, 2, 3] // 創(chuàng)建一個(gè) int slice煎源,并初始化為 [1, 2, 3]
b = [1, 2.3, 5] // 創(chuàng)建一個(gè) float slice
c = ["a", "b", "c"] // 創(chuàng)建一個(gè) string slice
d = ["a", 1, 2.3] // 創(chuàng)建一個(gè) var slice (等價(jià)于 Go 語(yǔ)言的 []interface{})
e = make([]int, len, cap) // 創(chuàng)建一個(gè) int slice色迂,并將長(zhǎng)度設(shè)置為 len,容量設(shè)置為 cap
f = make([][]int, len, cap) // 創(chuàng)建一個(gè) []int 的 slice手销,并將長(zhǎng)度設(shè)置為 len歇僧,容量設(shè)置為 cap
g = []byte{1, 2, 3} // 創(chuàng)建一個(gè) byte slice,并初始化為 [1, 2, 3]
h = []byte(nil) // 創(chuàng)建一個(gè)空 byte slice

和 Go 語(yǔ)言類似锋拖,slice 有如下內(nèi)置的操作:

a = append(a, 4, 5, 6) // 含義與 Go 語(yǔ)言完全一致
n = len(a) // 取 a 的元素個(gè)數(shù)
m = cap(a) // 取 slice a 的容量
ncopy = copy(a, b) // 復(fù)制 b 的內(nèi)容到 a诈悍,復(fù)制的長(zhǎng)度 ncopy = min(len(a), len(b))
b1 = b[2] // 取 b 這個(gè) slice 中 index=2 的元素
b[2] = 888 // 設(shè)置 b 這個(gè) slice 中 index=2 的元素值為 888
b[1], b[2], b[3] = 777, 888, 999 // 設(shè)置 b 這個(gè) slice 中 index=1, 2, 3 的三個(gè)元素值
b2 = b[1:4] // 取子slice

特別地祸轮,在 qlang 中可以這樣賦值:

x, y, z = [1, 2, 3]

結(jié)果是 x = 1, y = 2, z = 3。

實(shí)際上侥钳,qlang 支持多返回值就是通過 slice 的多賦值完成:

  • 對(duì)于那些返回了多個(gè)值的 Go 函數(shù)适袜,在 qlang 會(huì)理解為返回 var slice,也就是 []interface{}慕趴。

舉個(gè)例子:

f, err = os.Open(fname)

這個(gè)例子痪蝇,在 Go 里面返回的是 (*os.File, error)。但是 qlang 中是 var slice冕房。

map 類型

a = {"a": 1, "b": 2, "c": 3} // 得到 map[string]int 類型的對(duì)象
b = {"a": 1, "b", 2.3, "c": 3} // 得到 map[string]float64 類型的對(duì)象
c = {1: "a", 2: "b", 3: "c"} // 得到 map[int]string 類型的對(duì)象
d = {"a": "hello", "b": 2.0, "c": true} // 得到 map[string]interface{} 類型的對(duì)象
e = make(map[string]int) // 創(chuàng)建一個(gè)空的 map[string]int 類型的對(duì)象
f = make(map[string]map[string]int) // 創(chuàng)建一個(gè) map[string]map[string]int 類型的對(duì)象
g = map[string]int16{"a": 1, "b": 2} // 創(chuàng)建一個(gè) map[string]int16 類型的對(duì)象

和 Go 語(yǔ)言類似躏啰,map 有如下內(nèi)置的操作:

n = len(a) // 取 a 的元素個(gè)數(shù)
x = a["b"] // 取 a map 中 key 為 "b" 的元素
x = a.b // 含義同上,但如果 "b" 元素不存在會(huì) panic
a["e"], a["f"], a["g"] = 4, 5, 6 // 同 Go 語(yǔ)言
a.e, a.f, a.g = 4, 5, 6 // 含義同 a["e"], a["f"], a["g"] = 4, 5, 6
delete(a, "e") // 刪除 a map 中的 "e" 元素

需要注意的是耙册,a["b"] 的行為和 Go 語(yǔ)言中略有不同给僵。在 Go 語(yǔ)言中,常見的范式是:

x := map[string]int{"a": 1, "b": 2}
a, ok := x["a"] // 結(jié)果:a = 1, ok = true
if ok { // 判斷a存在的邏輯
    ...
}
c, ok2 := x["c"] // 結(jié)果:c = 0, ok2 = false
d := x["d"] // 結(jié)果:d = 0

而在 qlang 中是這樣的:

x = {"a": 1, "b": 2}
a = x["a"] // 結(jié)果:a = 1
if a != undefined { // 判斷a存在的邏輯
    ...
}
c = x["c"] // 結(jié)果:c = undefined详拙,注意不是0帝际,也不是nil
d = x["d"] // 結(jié)果:d = undefined,注意不是0饶辙,也不是nil

chan 類型

ch1 = make(chan bool, 2) // 得到 buffer = 2 的 chan bool
ch2 = make(chan int) // 得到 buffer = 0 的 chan int
ch3 = make(chan map[string]int) // 得到 buffer = 0 的 chan map[string]int

和 Go 語(yǔ)言類似蹲诀,chan 有如下內(nèi)置的操作:

n = len(ch1) // 取得chan當(dāng)前的元素個(gè)數(shù)
m = cap(ch1) // 取得chan的容量
ch1 <- true // 向chan發(fā)送一個(gè)值
v = <-ch1 // 從chan取出一個(gè)值
close(ch1) // 關(guān)閉chan,被關(guān)閉的chan是不能寫弃揽,但是還可以讀(直到已經(jīng)寫入的值全部被取完為止)

需要注意的是脯爪,在 chan 被關(guān)閉后,<-ch 取得 undefined 值矿微。所以在 qlang 中應(yīng)該這樣:

v = <-ch1
if v != undefined { // 判斷chan沒有被關(guān)閉的邏輯
    ...
}

流程控制

if 語(yǔ)句

if booleanExpr1 {
    // ...
} elif booleanExpr2 {
    // ...
} elif booleanExpr3 {
    // ...
} else {
    // ...
}

switch 語(yǔ)句

switch expr {
case expr1:
    // ...
case expr2:
    // ...
default:
    // ...
}

或者:

switch {
case booleanExpr1:
    // ...
case booleanExpr2:
    // ...
default:
    // ...
}

for 語(yǔ)句

for { // 無限循環(huán)痕慢,需要在中間 break 或 return 結(jié)束
    ...
}

for booleanExpr { // 類似很多語(yǔ)言的 while 循環(huán)
    ...
}

for initExpr; conditionExpr; stepExpr {
    ...
}

典型例子:

for i = 0; i < 10; i++ {
    ...
}

另外我們也支持 for..range 語(yǔ)法:

for range collectionExpr { // 其中 collectionExpr 可以是 slice, map 或 chan
    ...
}

for index = range collectionExpr {
    ...
}

for index, value = range collectionExpr {
    ...
}

函數(shù)

函數(shù)和閉包

基本語(yǔ)法:

funcName = fn(arg1, arg2, argN) {
    //...
    return expr
}

這就定義了一個(gè)名為 funcName 的函數(shù)。

本質(zhì)上來說涌矢,函數(shù)只是和 1掖举、"hello" 類似的一個(gè)值,只是值的類型是函數(shù)類型娜庇。

所有的用戶自定義函數(shù)塔次,在 Go 里面實(shí)際類型均為 func(args ...interface{}) interface{}。

你可以在一個(gè)函數(shù)中引用外層函數(shù)的變量名秀。如:

x = fn(a) {
    b = 1
    y = fn(t) {
        return b + t
    }
    return y(a)
}

println(x(3)) // 結(jié)果為 4

但是如果你直接修改外層變量會(huì)報(bào)錯(cuò):

x = fn(a) {
    b = 1
    y = fn(t) {
        b = t // 這里會(huì)拋出異常励负,因?yàn)椴荒艽_定你是想定義一個(gè)新的 b 變量,還是要修改外層 x 函數(shù)的 b 變量
    }
    y(a)
    return b
}

如果你想修改外層變量泰偿,需要先引用它,如下:

x = fn(a) {
    b = 1
    y = fn(t) {
        b; b = t // 現(xiàn)在正常了蜈垮,我們知道你要修改外層的 b 變量
    }
    y(a)
    return b
}

println(x(3)) // 輸出 3

不定參數(shù)

和 Go 語(yǔ)言類似耗跛,qlang 也支持不定參數(shù)的函數(shù)裕照。例如內(nèi)置的 max、min 都是不定參數(shù)的:

a = max(1.2, 3, 5, 6) // a 的值為 float 類型的 6
b = max(1, 3, 5, 6) // b 的值為 int 類型的 6

也可以自定義一個(gè)不定參數(shù)的函數(shù)调塌,如:

x = fn(fmt, args...) {
    printf(fmt, args...)
}

這樣就得到了一個(gè) x 函數(shù)晋南,功能和內(nèi)建的 printf 函數(shù)一模一樣。

多賦值

形式上羔砾,qlang 和 Go 語(yǔ)言一樣支持多賦值负间,也支持函數(shù)返回多個(gè)值:

x, y, z = 1, 2, 3.5
a, b, c = fn() {
    return 1, 2, 3.5 // 返回的是 var slice
}()

另外我們也支持:

x, y, z = [1, 2, 3.5]
a, b, c = fn() {
    return [1, 2, 3.5] // 返回的是 float slice
}()

需要注意的是,帶上[]進(jìn)行多賦值和不帶[]進(jìn)行多賦值在語(yǔ)義上有一點(diǎn)點(diǎn)不同姜凄。下面是例子:

x1, y1, z1 = 1, 2, 3.5
x2, y2, z2 = [1, 2, 3.5]
println(type(x1), type(x2))

結(jié)果表明 x1 的類型為 int政溃,而 x2 的類型是 float。

defer

是的态秧,qlang 也支持 defer董虱。這在處理系統(tǒng)資源(如文件、鎖等)釋放場(chǎng)景非常有用申鱼。一個(gè)典型場(chǎng)景:

f, err = os.Open(fname)
if err != nil {
    // 做些出錯(cuò)處理
    return
}
defer f.Close()

// 正常操作這個(gè) f 文件

值得注意的是:

在一個(gè)細(xì)節(jié)上 qlang 的 defer 和 Go 語(yǔ)言處理并不一致愤诱,那就是 defer 表達(dá)式中的變量值。在 Go 語(yǔ)言中捐友,所有 defer 引用的變量均在 defer 語(yǔ)句時(shí)刻固定下來(如上面的 f 變量)淫半,后面任何修改均不影響 defer 語(yǔ)句的行為。但 qlang 是會(huì)受到影響的匣砖。例如科吭,假設(shè)你在 defer 之后,調(diào)用 f = nil 把 f 變量改為 nil脆粥,那么后面執(zhí)行 f.Close() 時(shí)就會(huì) panic砌溺。

匿名函數(shù)

所謂匿名函數(shù),是指:

fn {
    ... // 一段復(fù)雜代碼
}

它等價(jià)于:

fn() {
    ... // 一段復(fù)雜代碼
}()

以前在 defer 要執(zhí)行一段很復(fù)雜的代碼段時(shí)变隔,我們往往這樣寫:

defer fn() {
    ... // 一段復(fù)雜代碼
}()

有了匿名函數(shù)规伐,我們可以簡(jiǎn)寫為:

defer fn {
    ... // 一段復(fù)雜代碼
}

一個(gè)用戶自定義類型的基本語(yǔ)法如下:

Foo = class {
    fn SetAB(a, b) {
        this.a, this.b = a, b
    }
    fn GetA() {
        return this.a
    }
}

有了這個(gè) class Foo,我們就可以創(chuàng)建 Foo 類型的 object 了:

foo = new Foo
foo.SetAB(3, "hello")
a = foo.GetA()
println(a) // 輸出 3

構(gòu)造函數(shù)

在 qlang 中匣缘,構(gòu)造函數(shù)只是一個(gè)名為 _init 的成員方法(method):

Foo = class {
    fn _init(a, b) {
        this.a, this.b = a, b
    }
}

有了這個(gè) class Foo 后猖闪,我們 new Foo 時(shí)就必須攜帶2個(gè)構(gòu)造參數(shù)了:

foo = new Foo(3, "hello")
println(foo.a) // 輸出 3

goroutine

和 Go 語(yǔ)言一樣,qlang 中通過 go 關(guān)鍵字啟動(dòng)一個(gè)新的 goroutine肌厨。如:

go println("this is a goroutine")

一個(gè)比較復(fù)雜的例子:

wg = sync.NewWaitGroup()
wg.Add(2)

go fn {
    defer wg.Done()
    println("in goroutine1")
}

go fn {
    defer wg.Done()
    println("in goroutine2")
}

wg.Wait()

這是一個(gè)經(jīng)典的 goroutine 使用場(chǎng)景培慌,把一個(gè) task 分為 2 個(gè)子 task,交給 2 個(gè) goroutine 執(zhí)行柑爸。

include

在 qlang 中吵护,一個(gè) .ql 文件可以通過 include 文法來將另一個(gè) .ql 的內(nèi)容包含進(jìn)來。所謂包含,其實(shí)際的能力類似于將代碼拷貝粘貼過來馅而。例如祥诽,在某個(gè)目錄下有 a.ql 和 b.ql 兩個(gè)文件。

其中 a.ql 內(nèi)容如下:

println("in script A")

foo = fn() {
    println("in func foo:", a, b)
}

其中 b.ql 內(nèi)容如下:

a = 1
b = 2

include "a.ql"

println("in script B")
foo()

如果 include 語(yǔ)句的文件名不是以 .ql 為后綴瓮恭,那么 qlang 會(huì)認(rèn)為這是一個(gè)目錄名雄坪,并為其補(bǔ)上 "/main.ql" 后綴。也就是說:

include "foo/bar.v1"

等價(jià)于:

include "foo/bar.v1/main.ql"

模塊及 import

在 qlang 中屯蹦,模塊(module)是一個(gè)目錄维哈,該目錄下要求有一個(gè)名為 main.ql 的文件。模塊中的標(biāo)識(shí)(ident)默認(rèn)都是私有的登澜。想要導(dǎo)出一個(gè)標(biāo)識(shí)(ident)阔挠,需要用 export 語(yǔ)法。例如:

a = 1
b = 2

println("in script A")

f = fn() {
    println("in func foo:", a, b)
}

export a, f

這個(gè)模塊導(dǎo)出了兩個(gè)標(biāo)識(shí)(ident):整型變量 a 和 函數(shù) f帖渠。

要引用這個(gè)模塊谒亦,我們需要用 import 文法:

import "foo/bar.v1"
import "foo/bar.v1" as bar2

bar.a = 100 // 將 bar.a 值設(shè)置為 100
println(bar.a, bar2.a) // bar.a, bar2.a 的值現(xiàn)在都是 100

bar.f()

qlang 會(huì)在環(huán)境變量 QLANG_PATH 指示的目錄列表中查找 foo/bar.v1/main.ql 文件。如個(gè)沒有設(shè)置環(huán)境變量 QLANG_PATH空郊,則會(huì)在 ~/qlang 目錄中查找份招。

將一個(gè)模塊 import 多次并不會(huì)出現(xiàn)什么問題,事實(shí)上第二次導(dǎo)入不會(huì)發(fā)生什么狞甚,只是增加了一個(gè)別名锁摔。

include vs. import

include 是拷貝粘貼,比較適合用于模塊內(nèi)的內(nèi)容組織哼审。比如一個(gè)模塊比較復(fù)雜谐腰,全部寫在 main.ql 文件中過于冗長(zhǎng),則可以用 include 語(yǔ)句分解到多個(gè)文件中涩盾。include 不會(huì)通過 QLANG_PATH 來找文件女仰,它永遠(yuǎn)基于 __dir__(即 include 代碼所在腳本的目錄) 來定位文件脱盲。

import 是模塊引用祖乳,適合用于作為業(yè)務(wù)分解的主要方式指攒。import 基于 QLANG_PATH 這個(gè)環(huán)境變量搜尋被引用的模塊,而不是基于 __dir__址儒。

與 Go 語(yǔ)言的互操作性

qlang 是一個(gè)嵌入式語(yǔ)言芹枷,它的定位是作為 Go 語(yǔ)言應(yīng)用的運(yùn)行時(shí)嵌入腳本。

作為 Go 語(yǔ)言的伴生語(yǔ)言莲趣,它與 Go 語(yǔ)言有極佳的互操作性鸳慈。任何 Go 語(yǔ)言的函數(shù),可以幾乎不做任何包裝就可以直接在 qlang 中使用喧伞。

這太爽了走芋!

定制 qlang

除了 qlang 語(yǔ)言的 import 支持外绩郎,qlang 的 Go 語(yǔ)言開發(fā)包也支持 Go package 編寫 qlang 模塊。

qlang 采用微內(nèi)核設(shè)計(jì)翁逞,大部分你看到的功能嗽上,都通過 Go package 形式編寫的 qlang 模塊提供。你可以按需定制 qlang熄攘。

你可以自由定制你想要的 qlang 的樣子。在沒有引入任何模塊的情況下彼念,qlang 連最基本的 '+'挪圾、'-'、'*'逐沙、'/' 都做不了哲思,因?yàn)樘峁┻@個(gè)能力的是 builtin 包。

在前面“快速入門”給出的精簡(jiǎn)版本基礎(chǔ)上吩案,我們可以自由添加各種模塊棚赔,如:

import (
    "qlang.io/lib/math"
    "qlang.io/lib/strconv"
    "qlang.io/lib/strings"
    ...
)

func main() {

    qlang.Import("math", math.Exports)
    qlang.Import("strconv", strconv.Exports)
    qlang.Import("strings", strings.Exports)

    ...
}

這樣,在 qlang 中就可以用 math.sin, strconv.itoa 等函數(shù)了徘郭。

如果你嫌 math.sin 太長(zhǎng)靠益,還可以將 math 模塊作為 builtin 功能導(dǎo)入。這只需要略微修改下導(dǎo)入的文法:

qlang.Import("", math.Exports) // 如此残揉,你就可以直接用 sin 而不是 math.sin 了

制作 qlang 模塊

制作 qlang 模塊的成本極其低廉胧后。我們打開 qlang.io/lib/strings 看看它是什么樣的:

package strings

import (
    "strings"
)

var Exports = map[string]interface{}{
    "Contains":  strings.Contains,
    "Index":     strings.Index,
    "IndexAny":  strings.IndexAny,
    "Join":      strings.Join,
    "Title":     strings.Title,
    "ToLower":   strings.ToLower,
    "ToTitle":   strings.ToTitle,
    "ToUpper":   strings.ToUpper,
    "Trim":      strings.Trim,

    "NewReader":   strings.NewReader,
    "NewReplacer": strings.NewReplacer,
    ...
}

值得注意的一個(gè)細(xì)節(jié)是,我們幾乎不需要對(duì) Go 語(yǔ)言的 strings package 中的函數(shù)進(jìn)行任何包裝抱环,你只需要把這個(gè)函數(shù)加入到導(dǎo)出表(Exports)即可壳快。你也無需包裝 Go 語(yǔ)言中的類,比如上面的我們導(dǎo)出了 strings.NewReplacer镇草,但是我們不必去包裝 strings.Replacer 類眶痰。這個(gè)類的所有功能可以直接使用。如:

strings.NewReplacer("?", "!").Replace("hello, world???") // 得到 "hello, world!!!"

這是 qlang 最強(qiáng)大的地方梯啤,近乎免包裝竖伯。甚至,你可以寫一個(gè)自動(dòng)的 Go package 轉(zhuǎn) qlang 模塊的工具条辟,找到 Go package 所有導(dǎo)出的全局函數(shù)黔夭,加入到 Exports 表即完成了該 Go package 的包裝,幾乎零成本羽嫡。

導(dǎo)出 Go 結(jié)構(gòu)體

假設(shè)我們有 Go 結(jié)構(gòu)體如下:

package foo

type Bar struct {
    X int
    Y string
}

func (p *Bar) GetX() int {
    return p.X
}

我們一樣可以把它加入到 Export 表進(jìn)行導(dǎo)出:

import (
    qlang "qlang.io/spec"
)

var Exports = map[string]interface{}{
    "Bar": qlang.StructOf((*foo.Bar)(nil)),
}

這樣你就可以在 qlang 里面使用這個(gè)結(jié)構(gòu)體:

bar = &foo.Bar{X: 1, Y: "hello"}
x = bar.GetX()
y = bar.Y
println(x, y)

反射

在任何時(shí)候本姥,你都可以用 type 函數(shù)來查看一個(gè)變量的實(shí)際類型,結(jié)果在 Go 語(yǔ)言中是 reflect.Type杭棵。如:

t1 = type(1) // 相當(dāng)于調(diào)用 Go 語(yǔ)言中的 reflect.TypeOf

用 type 可以很好地研究 qlang 的內(nèi)在實(shí)現(xiàn)婚惫。比如:

t2 = type(fn() {})

我們得到了 *Function氛赐。這說明盡管用戶自定義的函數(shù)原型多樣,但是其 Go 類型是一致的先舷。

我們也可以看看用戶自定義的類型:

Foo = class { fn f() {} }
t1 = type(Foo)
t2 = type(Foo.f)

foo = new Foo
t3 = type(foo)
t4 = type(foo.f)

可以看到艰管,class Foo 的 Go 類型是 *Class,而 object foo 的 Go 類型是 *Object蒋川。而 Foo.f 和普通用戶自定義函數(shù)一致牲芋,也是 *Function,但 foo.f 不一樣捺球,它是 *Method 類型缸浦。

附錄

樣例代碼

求最大素?cái)?shù)

輸入 n,求 < n 的最大素?cái)?shù)氮兵。用法:

  • qlang maxprime.ql <N>
primes = [2, 3]
n = 1
limit = 9

isPrime = fn(v) {
    for i = 0; i < n; i++ {
        if v % primes[i] == 0 {
            return false
        }
    }
    return true
}

listPrimes = fn(max) {

    v = 5
    for {
        for v < limit {
            if isPrime(v) {
                primes = append(primes, v)
                if v * v >= 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 <Value>
//
if len(os.Args) < 2 {
    fprintln(os.Stderr, "Usage: maxprime <Value>")
    return
}

max, err = strconv.ParseInt(os.Args[1], 10, 64)
if err != nil {
    fprintln(os.Stderr, err)
    return 1
}
if max < 8 { // <8 的情況下裂逐,可直接建表,答案略
    return
}

max--
v = maxPrimeOf(max)
println(v)

計(jì)算器

實(shí)現(xiàn)一個(gè)支持四則運(yùn)算及函數(shù)調(diào)用的計(jì)算器:


grammar = `

term = factor *('*' factor/mul | '/' factor/quo | '%' factor/mod)

doc = term *('+' term/add | '-' term/sub)

factor =
    FLOAT/pushFloat |
    '-' factor/neg |
    '(' doc ')' |
    (IDENT '(' doc %= ','/ARITY ')')/call
`

fntable = nil

Stack = class {

    fn _init() {
        this.stk = []
    }

    fn clear() {
        this.stk = this.stk[:0]
    }

    fn pop() {
        n = len(this.stk)
        if n > 0 {
            v = this.stk[n-1]
            this.stk = this.stk[:n-1]
            return v, true
        }
        return nil, false
    }

    fn push(v) {
        this.stk = append(this.stk, v)
    }

    fn popArgs(arity) {
        n = len(this.stk)
        if n < arity {
            panic("Stack.popArgs: unexpected")
        }
        args = make([]var, arity)
        copy(args, this.stk[n-arity:])
        this.stk = this.stk[:n-arity]
        return args
    }
}

Calculator = class {

    fn _init() {
        this.stk = 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 == undefined {
            panic("function not found: " + name)
        }
        arity, _ = this.stk.pop()
        args = this.stk.popArgs(arity)
        ret = f(args...)
        this.stk.push(ret)
    }
}

fntable = {
    "sin": sin,
    "cos": cos,
    "pow": pow,
    "max": max,
    "min": min,

    "$mul": fn(a, b) { return a*b },
    "$quo": fn(a, b) { return a/b },
    "$mod": fn(a, b) { return a%b },
    "$add": fn(a, b) { return a+b },
    "$sub": fn(a, b) { return a-b },
    "$neg": fn(a) { return -a },

    "$call": Calculator.call,
    "$pushFloat": Stack.push,
    "$ARITY": Stack.push,
}

main { // 使用main關(guān)鍵字將主程序括起來泣栈,是為了避免其中用的局部變量比如 err 對(duì)其他函數(shù)造成影響

    calc = new Calculator
    engine, err = interpreter(calc, nil)
    if err != nil {
        fprintln(os.Stderr, err)
        return 1
    }

    scanner = bufio.NewScanner(os.Stdin)
    for scanner.Scan() {
        line = strings.Trim(scanner.Text(), " \t\r\n")
        if line != "" {
            err = engine.Eval(line)
            if err != nil {
                fprintln(os.Stderr, err)
            } else {
                printf("> %v\n\n", calc.ret())
            }
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末卜高,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子南片,更是在濱河造成了極大的恐慌掺涛,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,635評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疼进,死亡現(xiàn)場(chǎng)離奇詭異鸽照,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)颠悬,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門矮燎,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人赔癌,你說我怎么就攤上這事诞外。” “怎么了灾票?”我有些...
    開封第一講書人閱讀 168,083評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵峡谊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我刊苍,道長(zhǎng)既们,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評(píng)論 1 296
  • 正文 為了忘掉前任正什,我火速辦了婚禮啥纸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘婴氮。我一直安慰自己斯棒,他們只是感情好盾致,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,640評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著荣暮,像睡著了一般庭惜。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上穗酥,一...
    開封第一講書人閱讀 52,262評(píng)論 1 308
  • 那天护赊,我揣著相機(jī)與錄音,去河邊找鬼砾跃。 笑死百揭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蜓席。 我是一名探鬼主播,決...
    沈念sama閱讀 40,833評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼课锌,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼厨内!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起渺贤,我...
    開封第一講書人閱讀 39,736評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤雏胃,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后志鞍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞭亮,經(jīng)...
    沈念sama閱讀 46,280評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,369評(píng)論 3 340
  • 正文 我和宋清朗相戀三年固棚,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了统翩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,503評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡此洲,死狀恐怖厂汗,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情呜师,我是刑警寧澤娶桦,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站汁汗,受9級(jí)特大地震影響衷畦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜知牌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,870評(píng)論 3 333
  • 文/蒙蒙 一祈争、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧角寸,春花似錦铛嘱、人聲如沸暖释。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)球匕。三九已至,卻和暖如春帖烘,著一層夾襖步出監(jiān)牢的瞬間亮曹,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工秘症, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留照卦,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,909評(píng)論 3 376
  • 正文 我出身青樓乡摹,卻偏偏與公主長(zhǎng)得像役耕,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子聪廉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,512評(píng)論 2 359