About
RSS

Bit Focus


句读探析 - 又一个 Javascript 生成器

Posted at 2012-11-16 14:18:34 | Updated at 2018-05-25 13:09:40

    前段时间博客没怎么更新, 因为不才去刷一个编译器项目了. 这项目是个 Javascript 产生器.
    起这个念头的契机是看到连 M$ 都动心思搞 JS 生成器了 (然而不得不吐槽的是, 这语言看起来跟 JS 简直一个德行). 而模仿的对象则是 CoffeeScript, 可是我又不想去分支 CoffeeScript 改点语法什么的改成 LatteScript 或 CappuccinoScript (我对 Coffee 的某些语法不很满意), 于是乎自己折腾了一个.
    不过另一方面, 这个编译器 (暂定名是 Stekinscript) 也不是完全无中生有, 而是从我的毕业项目修改过来的. 要说这样有什么不好的话, 就是 --- 这东西用 C++ 实现的 (准确地说是 C++11); 要说有什么好的话, 就是貌似这样可以直接集成 v8, 嗯 (自黑一下, 我没考察过这个, 也没有做这个事情的计划).

    下面介绍一下这个语言本身, 撇开日常的什么分支语句啦函数定义返回这些 (详情可参考文档), 先说说重点.
    首先 Stekinscript 支持缩进语法, 类似 Python 跟 CoffeeScript. 代码很重要的一点就是要看着舒服不是么?
    其次就是非强制类型, 强制类型给人的感觉呢, 就像三岁小孩子学写字旁边有个执戒尺的先生一样, 错一点就打手, 我们已经是断奶的程序员了好么, 所以什么 Dart 就先搁下不用了.
    最后是实际使用的感觉, 这一点我排斥 CoffeeScript 的 lambda 语法如 (x)-> x * x 这种. 要知道输入类似 -> 这样的符号两个字符都在右手无名指跟小指区, 还是一上一下, 而且减号不用按住 Shift 而大于号又需要, 这是有多别扭设计语言的人没知觉么? 还是说已经练成星际争霸九段这点微操不算什么?

    吐槽就到此为止, 下面贴个 Stekinscript 的例子
x: []
setTimeout(():
    if x.length = 10
        return
    x.push(document.getElementById('input').value)
, 2000)
    这个例子首先定义了 x 为一个空列表. 然后调用 setTimeout 函数, 第一个参数是无参 lambda, 其实呢, 刚才黑 CoffeeScript 那么多, 现在只是把 -> 改成了 : 作为 lambda 的标识 (我是不是该去买点节操了); 第二个参数是 2000, 在上面例子的最后一行. 注意到这一行 , 2000) 的缩进与 setTimeout 所在的行缩进一致表示它与上面 lambda 的函数体并不是一路的, 而是继续之前的 setTimeout((): 这一行.
    在函数体中是一个分支语句, if 的条件不需要括弧 (顺便黑一下 Go 语言, 没有括弧而在后面加上个大括号看起来不刺眼么), 分支中 x.length = 10 是在判断两个表达式是否相等而不是赋值运算 (实际上 Stekinscript 暂不提供任何赋值运算符, 包括 += 这种); 分支成功块中只有一句 return, 否则继续执行下面的 x.push 一行, 缩进表示很明显了吧.
    大家可以准备一下吐槽这个了 (誘受的口吻...)

    话说既然抄了 Python 的缩进语法, 一不做二不休, 列表切片我也笑纳了. 只不过 Stekinscript 中的列表切片稍微有些不一样, 是以逗号分隔切片项的, 如 x[2, 4]y[,, -1] 这样. 这么做的原因跟 lambda 的语法有些冲突 (虽然后来用类似解决算符优先的方式解决了此问题), 如 x[(m): 4] 这个写法可以认为是以匿名函数 (m): 4 作为键去索引字典, 又可以误认为是以 (m) 为起始位置 4 为结束位置切片. 于是就弃用了冒号而改为逗号作切片分隔符.

    Python 水太深, 还有很多奇妙的语法, 比如 list comprehension, 不过本着单抄一门语言是抄袭, 但如果抄很多语言那一定是借鉴的指导思想, 其它的语言特性就从别的地方挖好了. 下面再介绍一个语法特性替代 list comprehension, 这个称之为管道, 如下
x: [1, 1, 2, 3, 5]
y: x |: $ * $
z: x |? $ % 2 != 0
    虽然听起来就是从 Shell 里面抠出来的, 然而实际长得略有不同, 不是单独的竖线 (单竖线作为位运算操作符暂时保留, 其实位运算还没实现呢), 而是竖线之后再加上一个冒号或者问号. 其中加冒号的版本是映射而加问号的版本是过滤, 上面例子中 y 的取值将是 [1, 1, 4, 9, 25]z 的取值为 [1, 1, 3, 5].
    在管道操作符右侧的表达式中, 出现的 $ 表示列表中的每个元素, $i (虽然上例中没出现过) 表示元素索引.
    对于映射管道, 管道右侧的表达式是映射元素的方式; 而对于过滤管道, 右侧的表达式则是过滤元素的条件.
    多个管道还可以套接起来, 比如
x: [1, 1, 2, 3, 5]
y: x |? $ % 2 != 0 |: $ * $
    这样 y 先经过过滤管道再经过映射管道, 最后值为 [1, 1, 9, 25].
    (看来我果然是个发明各种奇葩语法的奇葩)

    更多例子可以参考案例测试集, 其中每个后缀为 stkn 的文件是 Stekinscript 源码, 而对应的 expected 后缀文件则是参考输出文件.
    如果现在想体验 Stekinscript 的话, 请参考这篇文档, 目前已测试 ArchLinux/Ubuntu 系统.

    接下来, 鄙博客将持续介绍

Post tags:   Compiler Construction  Stekin  C++

Leave a comment:




Creative Commons License Your comment will be licensed under
CC-NC-ND 3.0


. Back to Bit Focus
NijiPress - Copyright (C) Neuron Teckid @ Bit Focus
About this site