-
Notifications
You must be signed in to change notification settings - Fork 0
/
compile1.html
156 lines (142 loc) · 3.96 KB
/
compile1.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
</body>
<script>
// 构造parse函数
const State = {
initial: 1, // 初始状态
tagOpen: 2, // 标签开始状态
tagName: 3, // 标签名称状态
text: 4, // 文本状态
tagEnd: 5, // 结束标签状态
tagEndName: 6 // 结束标签名称状态
}
const isAlpha = (char) => { // 用于判断是否是字母
return char >= 'a' && char <= 'z' || char >= 'A' && char <= 'Z'
}
const tokenize = (str) => {
let currentState = State.initial
const chars = [] // 用于缓存字符
const tokens = [] // 存储生成的token,并作为函数返回值进行返回
while (str) { // 使用while循环开启自动机,只要str没有被消费尽,自动机就会一直运行
const char = str[0]
switch (currentState) {
case State.initial:
if (char === '<') {
currentState = State.tagOpen
str = str.slice(1)
} else if (isAlpha(char)) {
currentState = State.text
chars.push(char)
str = str.slice(1)
}
break;
case State.tagOpen:
if (isAlpha(char)) {
currentState = State.tagName
chars.push(char)
str = str.slice(1)
} else if (char === '/') {
currentState = State.tagEnd
str = str.slice(1)
}
break;
case State.tagName:
if (isAlpha(char)) {
chars.push(char)
str = str.slice(1)
} else if (char === '>') {
currentState = State.initial
tokens.push({
type: 'tag',
name: chars.join('')
})
chars.length = 0
str = str.slice(1)
}
break;
case State.text:
if (isAlpha(char)) {
chars.push(char)
str = str.slice(1)
} else if (char === '<') {
currentState = State.tagOpen
tokens.push({
type: 'text',
content: chars.join('')
})
chars.length = 0
str = str.slice(1)
}
break;
case State.tagEnd:
if (isAlpha(char)) {
currentState = State.tagEndName
chars.push(char)
str = str.slice(1)
}
break;
case State.tagEndName:
if (isAlpha(char)) {
chars.push(char)
str = str.slice(1)
} else if (char === '>') {
currentState = State.initial
tokens.push({
type: 'tagEnd',
name: chars.join('')
})
chars.length = 0
str = str.slice(1)
}
break;
}
}
return tokens
}
// console.log(tokenize('<div><p>vue</p><p>template</p></div>'));
const parse = (str) => {
const tokens = tokenize(str)
const root = {
type: 'Root',
children: []
}
const elementStack = [root] // 及其重要,构造父子关系的关键角色
while (tokens.length) {
const parent = elementStack.at(-1)
const t = tokens[0]
switch (t.type) {
case 'tag':
const elementNode = {
type: 'Element',
tag: t.name,
children: []
}
parent.children.push(elementNode)
elementStack.push(elementNode)
break;
case 'text':
const textNode = {
type: 'Text',
content: t.content
}
parent.children.push(textNode)
break;
case 'tagEnd':
elementStack.pop()
break;
}
tokens.shift()
}
return root
}
console.log(parse('<div><p>vue</p><p>template</p></div>'));
</script>
</html>