parser.js
parser 功能为生成 AST
抽象语法树
Demo示例一
将<h1 id="title"><span>hello</span>world</h1>
生成 AST
抽象语法树
分析
转换规则为:
jsxElement: <JSXIdentifier attribute*> child* </JSXIdentifier>
attribute: AttributeKey="AttributeStringValue"
child: jsxElement | JSXText
实现
const tokenizer = require("./tokenizer");
const tokenTypes = require("./tokenTypes");
const nodeTypes = require("./nodeTypes.md");
function parser(sourceCode) {
const tokens = tokenizer(sourceCode);
let current = 0;
function walk(parent) {
let token = tokens[current];
let nextToken = tokens[current + 1];
if (
token.type === tokenTypes.LeftParentheses &&
nextToken.type === tokenTypes.JSXIdentifier
) {
let node = {
type: nodeTypes.JSXElement,
openingElement: null,
closingElement: null,
children: [],
};
token = tokens[++current];
node.openingElement = {
type: nodeTypes.JSXOpeningElement,
name: {
type: nodeTypes.JSXIdentifier,
name: token.value,
},
attributes: [],
};
token = tokens[++current];
while (token.type === tokenTypes.AttributeKey) {
node.openingElement.attributes.push(walk());
token = tokens[current];
}
token = tokens[++current];
nextToken = tokens[current + 1];
while (
token.type != tokenTypes.LeftParentheses ||
(token.type === tokenTypes.LeftParentheses &&
nextToken.type !== tokenTypes.BackSlash)
) {
node.children.push(walk());
token = tokens[current];
nextToken = tokens[current + 1];
}
node.closingElement = walk(node);
return node;
} else if (token.type === tokenTypes.AttributeKey) {
let nextToken = tokens[++current];
let node = {
type: nodeTypes.JSXAttribute,
name: {
type: nodeTypes.JSXIdentifier,
name: token.value,
},
value: {
type: nodeTypes.Literal,
value: nextToken.value,
},
};
current++;
return node;
} else if (token.type === tokenTypes.JSXText) {
current++;
return {
type: nodeTypes.JSXText,
value: token.value,
};
} else if (
parent &&
token.type === tokenTypes.LeftParentheses &&
nextToken.type === tokenTypes.BackSlash
) {
current++;
current++;
token = tokens[current];
current++;
current++;
if (parent.openingElement.name.name !== token.value) {
throw new TypeError(
`开始标签${parent.openingElement.name.name}和结束标签${token.value}不匹配`
);
}
return {
type: nodeTypes.JSXClosingElement,
name: {
type: nodeTypes.JSXIdentifier,
name: token.value,
},
};
}
throw new TypeError("非法字符");
}
let ast = {
type: nodeTypes.Program,
body: [
{
type: nodeTypes.ExpressionStatement,
expression: walk(),
},
],
};
return ast;
}
module.exports = parser;
测试
const parser = require("./parser");
const traverse = require("./traverse");
const sourceCode = '<h1 id="title"><span>hello</span>world</h1>';
const ast = parser(sourceCode);
console.log(JSON.stringify(ast,null,2))