上一篇已经实现 源代码(sass) 到 字符流 到 Token流的处理
本篇主要专注 Token流到AST(Abstract Syntax Tree)语法树的生成, 可以在astexplorer体验下各种源码跟 AST 的映射关系
目标例子
输入:
$primary-color: #333;
.test{
color: $primary-color;
}
输出:
.test {
color: #333;
}
实现parse函数将Token流转为AST语法树:
以下对Token流的AST解析是参照前一篇的AST节点定义来实现的,有兴趣的可以先移步上一篇:
function parse(input: LexicalStream) {
function delimited(start: puncType, stop: puncType, separator: puncType, parser: Function) {// FIFO
let statements: any[] = [], first = true;
skipPunc(start);
while (!input.eof()) {
if (isPuncToken(stop)) break;
if (first) {
first = false;
} else {
if (separator === ';') {
skipPuncSilent(separator)
} else {
skipPunc(separator);
}
}
if (isPuncToken(stop)) break;
statements.push(parser());
}
skipPunc(stop);
return statements;
}
// Token的解析分发
function dispatchParser() {
// predict 下一个Token的类型来判定下一步的解析
let tok = input.peek();
// VARIABLE类型就直接返回Token作为语法树的一部分,(说明 VARIABLE既可以是Token的类型,也可以是AST Node类型)
if (tok.type === NodeTypes.VARIABLE) {
return input.next();
}
// 同上
if (tok.type === NodeTypes.PUNC) {
return input.next()
}
if (tok.type === NodeTypes.TEXT) {
return input.next()
}
}
// 解析 DECLARATION 节点
function parseDeclaration(left: DeclarationStatement['left']): DeclarationStatement {
input.next(); // skip ':'
return {
type: NodeTypes.DECLARATION,
left: left,
// 读取 Text value
right: input.next()
}
}
// 解析 RULE节点
function parseRule(selector: RuleStatement['selector']): RuleStatement {
let children = delimited("{", "}", ";", parseStatement);
return {
type: NodeTypes.RULE,
selector,
children
}
}
// 通过predict下一个 Token类型来判断解析的 AST Node类型
function maybeDeclaration(exp) {
let expr = exp();
if (isAssignToken()) {
if (expr.type === NodeTypes.VARIABLE) {
return parseDeclaration(expr)
}
}
if (isPuncToken('{')) {
return parseRule({
type: NodeTypes.SELECTOR,
value: expr
}) //passin selector
}
return expr;
}
// 基础 Statement节点的 parser
function parseStatement() {
return maybeDeclaration(function () {
return dispatchParser()
})
}
// parse 入口的 children,可以参见上一篇的 RootNode节点定义
function parsechildren(): Statement[] {
let children: Statement[] = [];
while (!isEnd()) {
children.push(parseStatement());
skipPuncSilent(";");
}
return children
}
// parser的入口
function parseProgram(): RootNode {
return {
type: NodeTypes.RootNode,
children: parsechildren()
}
}
return parseProgram()
}
parse出源码(sass源代码)对应的抽象语法树如下:
{
"type": "RootNode",
"children": [
{
"type": "DECLARATION",
"left": {
"type": "VARIABLE",
"value": "$primary-color"
},
"right": {
"type": "TEXT",
"value": "#333"
}
},
{
"type": "RULE",
"selector": {
"type": "SELECTOR",
"value": {
"type": "TEXT",
"value": ".test"
}
},
"children": [
{
"type": "DECLARATION",
"left": {
"type": "TEXT",
"value": "color"
},
"right": {
"value": {
"type": "VARIABLE",
"value": "$primary-color"
}
}
}
]
}
]
}
可以看出是由第一篇里面定义 的AST节点组合而成
结语
以上是伪代码,实际会比这个复杂一些,完整代码可以查看这里
最后预祝大家元旦快乐