<!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>