3个版本
使用旧的Rust 2015
0.0.2 | 2021年5月3日 |
---|---|
0.0.1 | 2020年8月3日 |
0.0.0 | 2020年7月25日 |
在过程宏中排名第2099
46KB
1K SLoC
Cwl是一个使用Rust过程宏实现的Web前端语言。
要安装,您需要
然后
git clone https://github.com/thisminute/cascading-wasm-language.git
cd cascading-wasm-language
git submodule init
git submodule update
cd create-cwl-app/www
npm install
npm start
对于Windows用户,在运行 npm start
之前在根目录下运行
rustup toolchain install stable-x86_64-pc-windows-gnu
rustup default stable-x86_64-pc-windows-gnu
理解代码
执行步骤
Rust文件夹结构
"lib.rs" 和 "mod.rs" 是特殊名称,是它们所在文件夹的入口点。顶级是 ./src/lib.rs
。整个项目是一个库,每个后续目录都包含一个从那里导入的私有模块。
src/lib.rs
代码的执行从lib.rs导出的3个过程宏之一开始。 cwl
是主要的,cwl_document
和 cwl_header
是用于编写测试的辅助工具。
数据流
此图有助于记住步骤发生的顺序,请参考此部分并帮助您在导航代码时不会迷失方向!
cwl -> lex ->
tokens -> parse* ->
ast -> analyze ->
semantics -> render ->
semantics -> write ->
compiled code!
* 此库从解析步骤开始(src/transform/parse.rs
),因为一些库已经处理了词法分析,所以我们有一些标记来开始
用词说:当一段CWL代码被编译时,它首先被词法分析成标记,然后被解析成一个抽象语法树(AST)。在AST上执行语义分析以生成表示代码含义的对象。这个“语义”对象被渲染成HTML DOM的抽象表示,最终编译成包含HTML和CSS的文本字符串和包含Webassembly的可执行文件。
数据结构(图左的 data
模块)之间通过结构之间的转换(图右的 transform
模块)进行数据流动。所有代码都位于 src
目录中,从 lib.rs
开始,然后传递到 transform/parse.rs
创建由 data/ast.rs
描述的对象,然后传递到 transform/analyze.rs
创建由 data/semantics.rs
定义的语义对象,依此类推。write
转换定义在几个部分,每个部分对应几个不同的输出——HTML、CSS和编译成 Wasm 二进制的 Rust 代码。每个输出都使用在 Semantics 结构上实现的不同 trait 生成。
这是 CWL 的核心(也可以很容易地适应其他语法!)。在 src/misc
中有一些不属于这个核心流程的文件,例如在语义分析期间使用的辅助 context
。
转换的详细情况
转换之间并不完全独立,有些转换执行的任务可能可以放在不同的转换中以达到相同的结果。例如,在解析过程中,不同类型的块(类、元素或事件)被写入不同的数组中,但同样的事情可以通过在解析时将内容写入单个 blocks
数组来完成,然后在分析过程中确定每个块的类型。
遵守的一个基本原则是,尽可能早地放置逻辑,同时不丢失我们之后需要的信息。在我们的块解析示例中,通过在解析过程中确定一个块是类、元素还是事件,我们就失去了判断某个类是出现在另一个元素之前还是之后的能力。例如
// 1
.some_rule {}
some_rule {}
// 2
some_rule {}
.some_rule {}
这两段代码解析成完全相同的 AST,因为它们都创建了一个包含一个项目的 classes
数组和包含一个项目的 elements
数组,在解析后无法判断它们原始的顺序。这实际上是一种期望的行为!CWL 语法不应该要求我们在应用规则之前将类放在元素之前,并且由于目前没有计划在将元素和类以任意顺序放置之间进行区分(注意:另一方面,任何元素相对于其他 元素 的顺序非常重要,并且保存在 elements
数组中),我们可以在非常早期的转换中完全消除这些信息,这可以让我们不必担心这些信息会影响后续的转换。
解析
解析从某些 CWL 输入中提取的令牌,并由 syn
包提供给我们,然后将它们转换成 AST。AST 是输入的最小表示——它与原始代码接近 1:1 的对应,但它不像输入代码那样是一个令牌序列,而是一个树。AST 应该代表生成 CWL 程序所需的最小信息。例如
.box {
content {}
}
box {}
box {}
从这段代码解析出的 AST 的根将包含一个包含内部元素块的类块,然后是 2 个单独的元素块,如下所示
// AST (made of blocks)
/ .box - content
page - box
\ box
分析和渲染
分析 AST 生成一个不同的树结构,我们称之为“语义”,它以反映输入结构的方式表示代码的意义,但填充了将树转换成不同形状所需的信息,以及我们在过程中需要收集的任何其他信息。为了说明为什么必须转换树,我们可以查看上述代码期望输出的树结构
// DOM (made of elements)
page - box - content
\ box - content
分析遍历抽象语法树(我们称之为由块组成),并创建一个根据其构建的树(我们称之为由组组成),然后运行我们称之为渲染的步骤,这些步骤在组中收集和填写信息,在元素节点和类节点之间创建双向链接,除了正常的树结构之外。在这种情况下,这些允许我们从 box
组遍历到 .box
组,然后从那里到 content
元素组
// semantics (made of groups, looks like AST)
/ .box - content
page - box
\ box
// also has paths to be walked in this order
page - box - .box - content
\ box /
对于 .box
类只存在一个组节点,对于 content
元素也只存在一个,但这些元素可以从两个地方访问。现在可以使用这个语义树在渲染时生成 DOM。
编译
语义树编译成 HTML 文档和要编译成 WebAssembly 的 Rust 代码 TokenStream。我们创建 HTML 文档的步骤在 compile/html.rs
中描述了如何递归生成包含 HTML 文档的单个字符串,然后将其写入文件,利用 compile/css.rs
完成样式标签属性的填充。在单独的步骤中,TokenStream 由 compile/wasm
中的几个文件中概述的过程生成,并在宏调用的地方由 Rust 编译。HTML 编译步骤相对简单,但 WebAssembly 编译步骤是整个项目中最为复杂的部分之一(待续)。
测试
由于添加了全局静态 CLASSES 变量,当前测试已损坏 :)
./tests
包含一组 CWL 示例,这些示例渲染不同的功能,然后检查 DOM 是否按照预期渲染!使用 wasm-pack test --headless --firefox
运行它们。 --chrome
也可以使用,您必须安装您选择的浏览器。
调试
我们使用 create-cwl-app
提供服务器和一些调试工具来处理 cwl。使用 git submodule update
拉取目录的内容。在 cd create-cwl-app/www
之后,您将可以访问一些 npm 命令。首先运行 npm install
。使用 npm start
编译测试应用程序,并使用 npm run debug
运行一些步骤,这些步骤将在 create-cwl-app/target/cwl_macro_output_formatted.rs
中生成宏输出并尝试编译它。这在 cwl 代码中存在构建错误时很有用,但库可以编译,因为它是通过宏生成的代码,错误消息只指向宏,以查看生成的代码中的错误消息。
依赖项
~2MB
~45K SLoC