Feel 语言设计历程 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
Kulics
V2EX    程序员

Feel 语言设计历程

  •  3
     
  •   Kulics
    kulics 2020-07-18 18:28:56 +08:00 4720 次点击
    这是一个创建于 1915 天前的主题,其中的信息可能已经有所发展或是发生改变。

    设计背景

    几年前我在开发多平台的 XyKey 时,由于当时的跨平台方案还未成熟,所以我没有选择跨平台实现,而是选择了每个平台都使用官方指定的语言进行开发,为此我接触了 Java/Kotlin ( Android )、Swift/OC ( iOS )、C#( UWP )。于此同时我本职工作方向是区块链技术,现有主流区块链方案也大量使用了 JS + Go 的组合开发前后端产品。

    在不同语言之间来回切换学习之后,我对不同语言表达同一种功能的语法差异性产生了兴趣,随后开始了语法设计方面的研究探索,最终诞生了 Feel 语言。

    语言设计问题之一

    很多语言里面,函数存在不止一种表达方法。

    我们需要为同样的需求设计不同的语法吗?

    以下我举一些我使用过的语言中函数的表示方法,所有的 eg 都是函数。

    Go: 大部分时候函数都使用 func 开头声明,算是一致性比较好的设计之一,但在 interface 中还是使用了不一样的描述方式。

    func eg1(x int) int { return 1 } var eg2 = func(x int) int { return 1 } type foo struct { eg3 func(int) int } type bar interface { eg4(int) int } 

    C#: 大部分时候都使用了 C 式的描述方式,但在函数类型中使用了反直觉的泛型类型,并且 Lambda 语法也看不出与函数的联系。

    int eg1(int x) { Func<int,int> eg2 = (int x) => { return 1; }; return 1; } interface foo { int eg3(int x); } Action<int> eg4; 

    Kotlin: 函数定义、函数类型、Lambda 是三种风格。

    fun eg1(x: Int): Int { val eg2: (Int) -> Int = { i -> 1 } return 1 } val eg3 = fun(x: Int): Int { return 1 } interface name { fun eg4(x: Int) val eg5: (Int) -> Unit } 

    Swift: swift 比较好的地方是函数定义和函数类型使用了同样的箭头表示,但在 Lambda 中却使用了 in 来分割。

    func eg1(x: Int) -> Int { let eg2: (Int) -> Int = { i in return 1 } return 1 } protocol name { func eg3(x: Int) } var eg4: (Int) -> () 

    为一个资源绑定一个名称,同样也有很多不同的写法。

    以下我举一些我使用过的语言中标识符表示方法,所有的 eg 都是某个资源的标识符。

    Swift: 为不同类型绑定标识符使用不同前缀,算是比较好的实践之一。

    var eg1 = 1 let eg2 = 1 func eg3() {} class eg4 {} protocol eg5 {} 

    Go: 变量、常量和函数使用了一种风格,定义类型使用了另一种风格。

    var eg1 = 1 const eg2 = 1 func eg3() {} type eg4 struct{} type eg5 interface{} 

    C#: 类和接口使用了一种风格,变量、常量、函数使用了三种不同的风格。

    int eg1 = 1 const int eg2 = 2 int eg3() {} class eg4 {} interface eg5 {} 

    语言设计问题之二

    一旦我们开始开发一个具备一定规模的项目,就一定会反复强调编码规范的重要性。形式不一的代码风格会给我们的协作带来不小的困难。

    如果规范如此重要,我们是否应该在语言级别强制?

    下面我给出几种不同的代码风格,在不统一风格的情况下,我们可能会见到如下几种代码并存:

    if (foo == 0) { print(foo) } else if (foo == 1) { print(foo) } else { print(foo) } ////////////// if (foo == 0) { print(foo) } else if (foo == 1) { print(foo) } else { print(foo) } ////////////// if (foo == 0) { print(foo) } else if (foo == 1) { print(foo) } else { print(foo) } 

    当然,也可能有人会写出下面这样的:

    if (foo == 0) { print(foo) } else if (foo == 1) { print(foo) } else { print(foo) } ////////////// if (foo == 0) print(foo) else if (foo == 1) print(foo) else print(foo) 

    语言设计问题之三

    一些语言是动态的,一些语言是静态的,它们各有各的好,但是我们却尝试在动态语言中加入静态特性( TypeScript ),也尝试在静态语言中加入动态特性( Go )。

    这静态与动态中间是否存在一个平衡的方案?

    TypeScript:通过隐式接口,给动态类型加上类型检查,只有满足接口要求的对象才能被使用。

    interface LabelledValue { label: string; } function printLabel(labelledObj: LabelledValue) { console.log(labelledObj.label); } let myObj = { size: 10, label: "Size 10 Object" }; printLabel(myObj); 

    Go:通过隐式接口,实现了静态鸭子类型,只有满足函数签名要求的对象才能被使用。

    type LabelledValue interface { Label() string } func printLabel(labelledObj LabelledValue) { println(labelledObj.Label()) } type Obj struct { size int } func (this Obj) Label() string { return "Size 10 Object" } 

    语言设计问题之四

    不同语言的关键字有多有少,但实际上大部分语言都可以实现图灵完备。

    我们需要很多关键字吗?或者说,如果没有关键字,是否也可以?

    下面是某个语言的关键字,还有部分上下文关键字未展示出来。

    | keyword | | | | | --------- | ---------- | --------- | --------- | | abstract | as | base | bool | | break | byte | case | catch | | char | checked | class | const | | continue | decimal | default | delegate | | do | double | else | enum | | event | explicit | extern | false | | finally | fixed | float | for | | foreach | goto | if | implicit | | in | int | interface | internal | | is | lock | long | namespace | | new | null | object | operator | | out | override | params | private | | protected | public | readonly | ref | | return | sbyte | sealed | short | | sizeof | stackalloc | static | string | | struct | switch | this | throw | | true | try | typeof | uint | | ulong | unchecked | unsafe | ushort | | using | virtual | void | volatile | | while | | | | 

    关键问题

    一、同一种功能是否需要多种语法?

    我们不需要多种语法,相同的需求可以有机统一。

    二、强制规范是否有必要?

    强制规范可以减少代码阅读和维护的压力,在语言级实施规范可以提升所有使用者的协作效率。

    三、我们想要的是静态类型还是动态类型?

    我们既想要静态检查,也想要动态自由度。静态鸭子类型可能是一个方案。

    四、关键字是必要的吗?

    如果语法结构足够少,我们可以试试移除关键字。

    函数

    我们对函数语法需求可以总结为如下几点:

    • 函数也是值,定义函数的方式与定义函数变量的方式应该一致。
    • 函数定义里的类型,与函数的类型应该一致。
    • Lambda 表达式与函数定义应该具有明显的相似度。

    假设我们所有资源的定义方式都使用 let id = XXX的形式,并且统一使用 type {}的形式构建某种类型的值。

    那么我们可以先给出这样一个函数语法:

    let foo = func(x: int) int { return 1 } 

    这个语法非常普通,使用func关键字定义类型,()里声明形参,后面声明返回值。

    接下来我们思考一下现代函数设计通常允许多返回值,因此我们需要使用()包装更多返回值。

    let foo = func(x: int, y: bool) (int, bool) { return (1, true) } 

    这样形参和返回值类型的()太接近了,阅读起来比较费力,需要一个分隔符,我们可以引入->

    let foo = func(x: int, y: bool) -> (int, bool) { return (1, true) } 

    ->加入之后,()->()就可以构成一个函数的类型结构,此时func的存在就没有必要性了,所以我们去掉这个关键字。

    let foo = (x: int, y: bool) -> (int, bool) { return (1, true) } 

    每次都要写两遍()也挺麻烦的,其实我们不太需要两个(),可以将形参和返回值类型放在一起,使用->分割。顺便也可以将 return()也省略掉。

    let foo = (x: int, y: bool -> int, bool) { return 1, true } 

    等等,我们还需要return这个关键字吗?我想应该是不需要了,我们可以使用更好看的<-

    let foo = (x: int, y: bool -> int, bool) { <- 1, true } 

    函数左右好像有点不平衡,我们可以强制返回值类型也加上一样的名称描述,让它们看起来更一致,并且也能给使用者更友好的说明。

    let foo = (x: int, y: bool -> a: int, b: bool) { <- 1, true } 

    到这里我们的函数描述方式已经成型,使用(->)表达函数类型。Lambda 语法也可以非常自然用->分割,使用{}定义整个函数的内容。

    let foo: (int, bool -> int, bool) = { x, y -> 1, true } 

    无参函数与函数参数的例子:

    let foo = (->) {} let bar = (fn: (->) -> ) {} 

    定义

    我们对定义语法需求可以总结为如下几点:

    • 所有创建名称的行为都是定义,应该使用同一种方式。
    • 名称比类型的阅读优先级更高,名称应该在前。
    • 区分可变与不可变。

    假设我们先使用 let xxx = value 定义不变量,使用 var xxx = value 定义变量。

    let foo: int = 0 var bar: int = 0 

    var相当于多了一种定义声明的方式,不如使用mut来声明可变性,这样可能具备一致性。

    let foo: int = 0 let mut bar: int = 0 

    我们思考一下,这种结构其实不需要let这个关键字也能成立,所以我们去掉它。

    foo: int = 0 mut bar: int = 0 

    现在只剩下mut这个关键字了,我们也去掉,使用!替换它,表示可变性、不确定性。

    foo: int = 0 !bar: int = 0 

    到这里,只要支持类型推导,我们也不需要明确写类型了,省略掉类型后,可以组合成:=

    foo := 0 !bar := 0 

    当然,如果不一定带值,我们也可以省略右边,保留类型。

    foo: int !bar: int 

    现在我们的定义语法已经完成了,替换前面函数的例子,不需要换多少东西。

    foo := (x: int, y: bool -> a: int, b: bool) { <- 1, true } 

    选择结构

    现有的选择结构一般包含 if 与 switch 两种,它们的形态和功能已经非常成熟了,但我们仍有从格式上进一步简化的可能性。

    我们先给出一个常见的 if 语句,并且假定 {不可以换行。

    if (foo == 0) { ...... } else if (foo == 1) { ...... } else if (foo == 2) { ...... } else { ...... } 

    去掉()似乎不会影响什么,我们先去掉它。

    if foo == 0 { ...... } else if foo == 1 { ...... } else if foo == 2 { ...... } else { ...... } 

    我们假定 else if也不可以换行,必须跟在}后面。有了这个约束,我们就可以省略 else if

    if foo == 0 { ...... } foo == 1 { ...... } foo == 2 { ...... } else { ...... } 

    同样的道理,else其实也可以不需要,我们可以使用_来替换它。

    if foo == 0 { ...... } foo == 1 { ...... } foo == 2 { ...... } _ { ...... } 

    现在只剩下一个if了,我们用?来表示可选择性,替换掉它。

    ? foo == 0 { ...... } foo == 1 { ...... } foo == 2 { ...... } _ { ...... } 

    现在我们完成了选择结构的基本形态,通过强制规范来压缩更多代码。

    对于 switch,与if最大的区别是一个是对单值的匹配,一个是不同值的匹配。所以很显然我们可以在现在的基础上增加语法适配,这里使用[]来表示对单个目标的匹配。

    ? [foo] 0 { ...... } 1 { ...... } 2 { ...... } _ { ...... } 

    这样我们就得到了两种基础的选择结构。

    循环结构

    现有的循环结构一般包含 Foreach 、For 三段式、While 三种,我们一样可以从格式上进一步简化。

    先给出最简单的例子:

    for (let i in foo) { ...... } 

    省略掉作用不大的()

    for let i in foo { ...... } 

    letin都是为了定义从集合里获取的对象,我们可以改成前面的定义语法。

    for i := foo { ...... } 

    现在也只剩for这个关键字了,键盘上可用的符号也不多,我这里取了@替换掉它。

    @ i := foo { ...... } 

    这样我们就得到了循环结构的基本形态。

    我们再来看看传统的三段式结构:

    for (i := 0; i < 10; i++) { ...... } 

    这种结构形式比较复杂,不少新语言都放弃了这种写法,而使用区间运算符代替,大部分形式为 begin .. end

    我们最常使用的区间包含递增左闭右开递增全闭递减左闭右开递增全闭四种。

    因此只需要组合四种形式的区间运算符就足够我们使用了,以下按顺序给出。

    begin ..< end begin .. end begin ..> end begin ... end 

    现在我们可以使用区间运算符来实现大部分三段式的需求。

    @ i := 0 ..< 10 { ...... } 

    最后直接传入一个表达式,就能满足 While 的需求。

    @ foo < bar { ...... } 

    对象模版

    类型是我们描述对象的数据和行为的一种方式,我们既可以用它充当构造数据的模版,也应该能把它视为描述行为的接口。

    这样我们就可以将它作为静态的鸭子类型使用,同时承载了数据、行为以及约束的能力。

    假定我们将它称为 class

    foo := class { label := "I am Label" show := (->) { print(label) } } 

    foo 同时包含了字段及函数,它们能被一致的使用。

    我们可以将唯一的关键字class去掉,替换为另一个经常用来表示模版的$

    foo := $ { label := "I am Label" show := (->) { print(label) } } 

    然后 foo 就能被视为 type,我们可以一致地构建了。

    a := foo{} a.show() 

    基于鸭子类型的特性,我们可以定义一个 shower 的接口,去使用 foo 的行为。

    shower := $ { show: (->) } useShower := (s: shower->) { s.show() } useShower(a) 

    因为 foo 具备了 show 方法,它就能被视为 shower 自然的使用。

    我们再引入一个语法,在对象模版内引入一个类型,就会自动隐含它所有的东西,这样很便利复用代码。

    例如 reader 包含 shower,就包含了它的 show:

    reader := $ { shower read: (->v: string) } 

    那么我们也可以让 bar 包含 foo,就包含了 label 和 show 。

    bar := $ { foo read := (->v: string) { <- label } } 

    这时我们就能将 bar 视为 reader 使用了。

    useReader := (r: reader->) { r.show() print(r.read()) } b := bar{} useReader(b) 

    最后

    欢迎 star https://github.com/kulics-works/feel

    Feel 语言目前还处于实验阶段,不具备生产条件,部分设计可能会随着 llvm 端的推进而改变,欢迎讨论邮件([email protected])或 issue 讨论。

    再贴一段 leetcode#16 的代码供参考

    threeSumClosest := (nums: [list int], target: int->v: int) { length := nums.len nums.sort() !closs := nums[0]+nums[1]+nums[2] @ i := 0 ..< length { l, r := i+1, length-1 @ l < r { sum := nums[i]+nums[l]+nums[r] ? abs(sum-target) < abs(closs-target) { closs = sum } sum > target { r -= 1 } _ { l += 1 } } } <- closs } 
    43 条回复    2024-10-29 14:17:02 +08:00
    BugenZhao
        1
    BugenZhao  
       2020-07-18 18:46:52 +08:00
    有点意思
    Jirajine
        2
    Jirajine  
       2020-07-18 18:48:02 +08:00 via Android   3
    虽然设计语言不一定要懂 PLT,但你这也太民科了点。
    不知道你为什么那么痛恨关键字,cpp 那样疯狂加关键字固然恶心,但你这样用符号替代关键字纯粹是换汤不换药,完全是 go 加了一堆不甜的糖顺带降低了可读性增加了输入难度。
    用变量赋值代替所有声明更是无力吐槽,建议你还是多用几门语言再谈设计吧,haskell,f#rocket,rust 都用一用。
    devret
        3
    devret  
       2020-07-18 19:06:21 +08:00 via Android
    可读性降低,语言更加抽象化,比如 if 条件,就算没有学过编程的人读到这个语句也能够推算出大概要满足什么东西,但是你这个完全看不出来在干啥,个看来是更像是加密混淆
    SingeeKing
        4
    SingeeKing  
    PRO
       2020-07-18 19:22:43 +08:00
    上面看起来都挺好,但是到了最后看代码实在费劲。这种带来的问题就是显著降低代码可读性,对语言不熟悉的人会非常难受
    nguoidiqua
        5
    nguoidiqua  
       2020-07-18 19:50:36 +08:00 via Android
    强迫症要不得,追求一致性可能让设计者感觉很舒服,但对于别人来说也许 feel uncomfortable.
    VDimos
        6
    VDimos  
       2020-07-18 19:59:06 +08:00 via Android
    很厉害哦。
    虽然我不是很喜欢这个语法
    justin2018
        7
    justin2018  
       2020-07-18 20:28:11 +08:00
    感觉可读性不强~ 没看语法 直接拉到最后面 发现 有点看不懂~~
    laoyur
        8
    laoyur  
       2020-07-18 20:38:38 +08:00
    你只是设计了一种自己喜欢的语言罢了
    Leigg
        9
    Leigg  
       2020-07-18 21:04:20 +08:00 via Android
    func,return 这种已经是各语言通用的关键字,直接换作符号代替,大型工程中看代码可能会非常难受的事情
    pabupa
        10
    pabupa  
       2020-07-18 21:08:21 +08:00
    符号满天飞,看起来不是疯了……
    dremy
        11
    dremy  
       2020-07-19 00:11:32 +08:00 via iPhone
    佩服 lz 能有这样的脑洞,如果很多 API 能像这样多站在使用者的角度做简化,能省去很多读文档的时间

    不过对于这种对编程语言语法的简化,还是要适可而止,毕竟写出来的代码不光是给机器用的,更多的还是给别人以及自己读的,不如多来些自然语言更清晰易懂
    Mistwave
        12
    Mistwave  
       2020-07-19 01:45:43 +08:00 via iPhone
    痛恨不同语法和关键字的话,去写 lisp 嘛,看看 r5rs 就够了(
    yuk1no
        13
    yuk1no  
       2020-07-19 01:53:09 +08:00 via iPhone
    楼主每年都发明一门新语言?
    之前的 XyLang 和 Lite 还维护吗(滑稽
    aguesuka
        14
    aguesuka  
       2020-07-19 04:37:51 +08:00 via Android
    居然有 impl,能把轮子跑起来已经比经比某大厂强了。这么多程序员总有需要用 lua,不过又和楼主一样对单词关键字深痛恶觉的人
    gantleman
        15
    gantleman  
       2020-07-19 04:46:22 +08:00
    我不喜欢语法糖创新,但作者的思考能力和动手能力值的我学习。
    Kulics
        16
    Kulics  
    OP
       2020-07-19 06:35:16 +08:00
    @yuk1no 科技以改名为本。微软:???
    因为 GitHub 的规则被迫改名,现在可以稳定使用 feel 这个名称先,取个名字不容易。
    Kulics
        17
    Kulics  
    OP
       2020-07-19 06:44:39 +08:00
    @Mistwave 我不痛恨呀,我是顺着问题去探索设计上的可能性,总不能找个语言抄下来换个皮说是自己创造了个新语言吧。lisp 我也写啊,Feel 里面也借鉴了 lisp 的 s-expr,不过篇幅太长这里没提到。
    Kulics
        18
    Kulics  
    OP
       2020-07-19 06:50:52 +08:00
    @dremy 我也认同你的观点。不过试试能不能用符号化的方式描述也是个值得探讨的问题,像数学那样已经完全符号化了,并不影响阅读。
    Kulics
        19
    Kulics  
    OP
       2020-07-19 07:01:57 +08:00
    @Leigg 那就不写大工程 23333333. 能写小的玩也不错了。
    Kulics
        20
    Kulics  
    OP
       2020-07-19 07:05:08 +08:00
    @SingeeKing 搬砖搬着就习惯了哈哈哈哈哈,熟悉了就有可读性。
    qdwang
        21
    qdwang  
       2020-07-19 07:15:56 +08:00 via iPhone
    按照你的几点要求,直接用 lambda calculus 就行了……
    nonduality
        22
    nonduality  
       2020-07-19 10:29:00 +08:00
    “Readability counts.” The Zen of Python

    一门语言设计得符号满天飞,可读性都不考虑了吗?这语言还是拿来自娱自乐吧。
    Acoolda
        23
    Acoolda  
       2020-07-19 11:07:27 +08:00 via Android
    虽然我没设计过语言的学问,但是我想问问,你这个语言解决了什么业界痛点吗?你这大多是跟一些关键字较劲,大可不必用反直觉的方式去设计语言吧,该常用的关键字,还是常用关键字。go 语言并发让人印象深刻,C 类语言性能强劲,系统级语言,Python 易学易用,Java 约束性强,工业软件开发稳。基本每种就行语言都有特点,解决部分问题。就目前来说,这个新语言,没啥好的特性(如果你认为语法上标新立异算的话)语法太标新立异最大问题就是,老一辈的程序员没人愿意去尝试,新人难入门,基本判了死刑了。不过如果是玩玩,那还挺可以的。
    waruqi
        24
    waruqi  
       2020-07-19 11:08:59 +08:00 via Android
    写的代码以后只能凭第六感猜了 果然很 feel
    Comdex
        25
    Comdex  
       2020-07-19 13:07:57 +08:00
    这样追求一致性反而降低了可读性,增加了输入难度,编程语言的发展个人认为方向是应该趋向于接近人类自然语言吧。。。
    leer
        26
    leer  
       2020-07-19 13:09:36 +08:00
    几个点都挺有意思的,就是完全去关键字化,符号化,提高了入门门槛和增加了思维转换成本。
    函数那部分挺有意思的。
    jsteward
        27
    jsteward  
       2020-07-19 13:12:47 +08:00
    法是一程序言最要的部分了吧,型系,代生成,研究啥不比研究法有意思。。搞法搞到最後就是了套 BNF 而已,不如 Lisp 程序直接 AST
    Licsber
        28
    Licsber  
       2020-07-19 13:24:46 +08:00   1
    英文编程 X
    中文编程 X
    符号编程 √
    maoxs2
        29
    maoxs2  
       2020-07-19 13:57:48 +08:00 via Android
    我现在对语言要求也不高,能像 rust 那样轻轻松松编个干净的 wasm 我就来用,帮做基建都没问题
    nonduality
        30
    nonduality  
       2020-07-19 16:29:34 +08:00
    我忽然觉得,有必要建一个页面:Programming Language Cemetery (编程语言坟场),把过去曾有人发明的各种死掉的小众语言弄到一起,方便大家有空去静静凭吊下多少无聊的、空虚的、雄心壮志的、自以为是的人搞出的一堆废物。
    cyspy
        31
    cyspy  
       2020-07-19 20:51:37 +08:00
    就这几门语言视野还是太窄了点,至少把 scala/haskell/clojure/racket/typed racket/python/rust/lua 这几种风格都过一遍吧
    maemolee
        32
    maemolee  
       2020-07-19 22:43:01 +08:00
    过多的符号会导致理解成本的提高,和歧义的降低。
    看着你的语言,我想起了我大一挂掉的两门高数课。
    sunzhenyucn
        33
    sunzhenyucn  
       2020-07-20 04:40:04 +08:00 via iPhone
    @centreon #30 臣附议!
    sunzhenyucn
        34
    sunzhenyucn  
       2020-07-20 04:40:39 +08:00 via iPhone
    看到后面的 example 我傻了,得道成仙光靠鬼画符是不行滴
    aoxig
        35
    aoxig  
       2020-07-20 21:16:33 +08:00
    个人兴趣做一做是可以的,新的语言一般是迎合市场痛点,然后有大公司率先使用才能热起来
    Kulics
        36
    Kulics  
    OP
       2020-07-20 22:07:07 +08:00
    @aoxig 研究语言是个本身就有意思的事情,Feel 显然不是那种为了迎合市场做出来的。
    Kulics
        37
    Kulics  
    OP
       2020-07-20 22:10:20 +08:00
    @maemolee 我在其它地方探讨过,除了自然语言化的 PL,数学化的 PL 也会有另一种可能性,所以 Feel 也不是为了降低入门门槛设计的,实际上很复杂,但是好玩。
    Kulics
        38
    Kulics  
    OP
       2020-07-20 22:12:25 +08:00
    @cyspy 过了 2/3,我没有觉得应该提这些。
    Kulics
        39
    Kulics  
    OP
       2020-07-20 22:13:07 +08:00
    @Licsber 意识流编程 √
    Kulics
        40
    Kulics  
    OP
       2020-07-20 22:30:28 +08:00
    @leer 有点类似数学,有入门门槛,学会了 1+1 之后就很少会写 一加一 。
    Kulics
        41
    Kulics  
    OP
       2020-07-20 22:34:35 +08:00
    @Comdex 以前我探讨过两个方向,一个是自然语言,这个很容易想到。另一个是类似数学那样成为纯粹的符号语言,在部分场景可能会有空间。
    leer
        42
    leer  
       2020-07-31 18:04:06 +08:00
    @nonduality 我看行,应该会挺有意思
    Kulics
        43
    Kulics  
    OP
       351 天前
    没想到后来我成为了仓颉编程语言设计师。
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5839 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 29ms UTC 02:42 PVG 10:42 LAX 19:42 JFK 22:42
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86